diff --git a/runtime/host.h b/runtime/host.h index d46336b9..7bcc152a 100644 --- a/runtime/host.h +++ b/runtime/host.h @@ -262,7 +262,8 @@ uint64_t res_heap_extra_bytes(void); // added to the appli uint64_t res_heap_extra_kib(void); // added to the HeapDef texture heap and its parent; 0 = none uint64_t res_tex_heap_extra_bytes(void); // TOTK_TEX_HEAP_EXTRA_MB: added to the application pool and // the heap cap (mem_init), 2 MiB steps; 0 = none -int res_tex_heap_from_mod(void); // a switched-on mod's HeapDef grows the texture heap +int res_heapdef_tex_grown(const void *in, size_t n); // the HeapDef table (.byml.zs) grows TextureHeap over the + // shipped size (an HD texture pack's) int res_heapdef_build(const void *in, size_t n, void **out, size_t *out_n); // the HeapDef table (.byml.zs) with // the grown heaps, malloc'd; 0 = cannot (logged) void res_heapdef_served(void); // romfs.c serves the grown table diff --git a/runtime/resolution.c b/runtime/resolution.c index ac4fa75c..20b73d06 100644 --- a/runtime/resolution.c +++ b/runtime/resolution.c @@ -293,22 +293,31 @@ static int heapdef_open(const void *in, size_t n, Byml *b, uint32_t *heaps, cons #define HEAPDEF_REL "System/HeapSize/Product.Nin_NX_NVN.HeapDef.byml.zs" static struct { int done; uint64_t kib; } MT; -static void mod_heapdef(const char *name, const char *romfs, void *ctx) { - (void)ctx; - char path[2300]; - if (snprintf(path, sizeof path, "%s/%s", romfs, HEAPDEF_REL) >= (int)sizeof path) return; - uint64_t n = 0; - uint8_t *in = plat_file_read(path, &n); - if (!in) return; +// "TextureHeap" of the HeapDef table in[0..n) in KiB; 0 when the table cannot be read or has no such size (the failure +// is logged with why). +static int32_t heapdef_tex_kib(const void *in, size_t n, const char *why) { Byml b; uint32_t heaps; size_t th_at = 0; int32_t th = 0; - if (heapdef_open(in, (size_t)n, &b, &heaps, "its texture heap is left out") && - (th_at = heap_size_field(&b, heaps, "TextureHeap"))) - memcpy(&th, b.p + th_at, 4); + if (!heapdef_open(in, n, &b, &heaps, why)) return 0; + if ((th_at = heap_size_field(&b, heaps, "TextureHeap"))) memcpy(&th, b.p + th_at, 4); + else rt_log("resolution: the HeapDef table has no TextureHeap size; %s", why); free(b.p); + return th; +} + +static void mod_heapdef(const char *name, const char *romfs, void *ctx) { + (void)ctx; + char path[2300], why[400]; + if (snprintf(path, sizeof path, "%s/%s", romfs, HEAPDEF_REL) >= (int)sizeof path) return; + uint64_t n = 0; + uint8_t *in = plat_file_read(path, &n); + if (!in) return; + snprintf(why, sizeof why, "mod %s: its texture heap growth is left out", name); + int32_t th = heapdef_tex_kib(in, (size_t)n, why); free(in); + if (!th) return; int64_t grow = (int64_t)th - (int64_t)TEX_HEAP_KIB; if (grow > (int64_t)(4096u << 10)) grow = 4096u << 10; if (grow > 0 && (uint64_t)grow > MT.kib) MT.kib = (uint64_t)grow; @@ -321,7 +330,11 @@ static uint64_t mod_tex_heap_kib(void) { return MT.kib; } -int res_tex_heap_from_mod(void) { return mod_tex_heap_kib() > 0; } +// Only such a table is swapped for the game's own (romfs.c heapdef_overlay): a mod table that changes other heaps +// keeps them. A table it cannot read stays the base (logged). +int res_heapdef_tex_grown(const void *in, size_t n) { + return heapdef_tex_kib(in, n, "the mod's table stays the base") > (int32_t)TEX_HEAP_KIB; +} int res_heapdef_build(const void *in, size_t n, void **out, size_t *out_n) { uint64_t extra = res_heap_extra_kib(), tex = tex_heap_extra_kib(); diff --git a/runtime/romfs.c b/runtime/romfs.c index 38de9ab2..484c6c0d 100644 --- a/runtime/romfs.c +++ b/runtime/romfs.c @@ -533,9 +533,8 @@ static void added_init(void) { rt_log("RomFS overlay: %zu files added to the RomFS", nadded); } -// RomFS file `rel` as the game would read it: from an overlay that replaces it (*base), else from the image. malloc'd; -// NULL when it is missing, unreadable or 16 MiB or more (logged with `keeps`, what the game does without it). -// RomFS file rel as the RomFS holds it, whatever overlays replace it; NULL (logged) when it cannot be read. +// RomFS file `rel` as the image holds it, whatever overlays replace it. malloc'd; NULL when it is missing, unreadable +// or 16 MiB or more (logged with `keeps`, what the game does without it). static void *read_original(const char *rel, size_t *n, const char *keeps) { uint64_t i = lower(rel); if (i >= file_count || strcmp(entry_name(entry(i)), rel)) return NULL; @@ -550,6 +549,8 @@ static void *read_original(const char *rel, size_t *n, const char *keeps) { return in; } +// RomFS file `rel` as the game would read it: from an overlay that replaces it (*base), else from the image. malloc'd; +// NULL when it is missing, unreadable or 16 MiB or more (logged with `keeps`, what the game does without it). static void *read_served(const char *rel, Overlay **base, size_t *n, const char *keeps) { uint64_t i = lower(rel); *base = NULL; @@ -846,9 +847,10 @@ static void restbl_overlay(void) { // The HeapDef table a bigger game resolution or TOTK_TEX_HEAP_EXTRA_MB needs (resolution.c): its texture heaps and // their parents grow. The base is an overlay that replaces the table (a mod's heap sizes stay), else the RomFS -// original; the grown table is written to /resolution/ and served in the base's place. A mod's table whose -// texture heap is grown (an HD texture pack's, resolution.c mod_tex_heap_kib) is not the base: that growth is already -// in the amount, so the RomFS original grows by it and is served in the mod's place. +// original; the grown table is written to /resolution/ and served in the base's place. A served mod table +// whose own TextureHeap is grown (an HD texture pack's, resolution.c res_heapdef_tex_grown) is not the base: its +// growth is already in the amount (resolution.c mod_tex_heap_kib), so the RomFS original grows by it and is served in +// the mod's place. A mod table that changes other heaps only stays the base. #define HEAPDEF "System/HeapSize/Product.Nin_NX_NVN.HeapDef.byml.zs" static void heapdef_overlay(void) { if (!res_heap_extra_kib() && !res_tex_heap_extra_bytes()) return; @@ -857,11 +859,12 @@ static void heapdef_overlay(void) { size_t n; void *in = read_served(HEAPDEF, &base, &n, keeps), *out = NULL; size_t out_n = 0; - if (in && base && base->mod && res_tex_heap_from_mod()) { + if (in && base && base->mod && res_heapdef_tex_grown(in, n)) { free(in); in = read_original(HEAPDEF, &n, keeps); - rt_log("RomFS: %s: the game's own HeapDef with the mod's texture heap growth in place of the mod's table", - mods_at(base->mod - 1)->name); + if (in) + rt_log("RomFS: %s: the game's own HeapDef with the mod's texture heap growth in place of the mod's table", + mods_at(base->mod - 1)->name); } int ok = in && res_heapdef_build(in, n, &out, &out_n); free(in);