Swap a mod's HeapDef for the game's own only when that table grows TextureHeap

heapdef_overlay replaced the served mod table whenever any switched-on mod grew the texture heap. With an HD pack and
another mod that ships a HeapDef for other heaps on top of it, the other mod's sizes were lost. It now checks the
served table itself (resolution.c res_heapdef_tex_grown): only a table with TextureHeap over the shipped size is
swapped, any other stays the base and gets the growth on top.

A mod HeapDef the port cannot read no longer logs "TextureHeap 0 KiB": the failure is logged with the mod's name.
read_original sat between read_served and its comment; each function has its own comment again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Valiantsin DzerakhandClaude Opus 5.5 committed 2026-11-06 15:23:15 +03:00
1 parent caf53eb764
commit df9ba65a91
3 files changed
+38 -21

No files matched your search

+2 -1
View File
@@ -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
+24 -11
View File
@@ -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();
+12 -9
View File
@@ -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 <profile>/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 <profile>/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);