A mod file whose name carries Product.<n> and which the RomFS lacks was counted as made for another game version and
never served. The game does ask for such names: on 1.4.3 with mods on, the file trace (trace = fs) shows it opening
Sound/Resource/Pack_MasterSword.Product.110.bars.zs, while its RomFS holds only the Product.140 and the unversioned
file. A sound mod that ships its pack under all those names was silent here and works on a console, where LayeredFS
shows the game every file of a mod. Such files are now added overlays like any other new file. Another version's
resource size table is still not served and still lends its entries; the files are still counted, and the Mods tab
and docs/mods.md now say they are read only when asked for by name.
tests/mods_romfs_check.py gets a case for it. Not run here: the test and --probe-fs need a build and the game's files.
runtime/romfs.c compiles with llvm-mingw.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TgQM2zwuVagwScByEJhT8V
An IPS, IPS32 or pchtxt file for main that writes main's .rodata, .data or .bss was refused as a whole. Such writes
need no lifting: tools/exefs_patches.py now puts them in build-cache/exefs.data, and runtime/mods.c stores them into
the loaded main before any game code runs, as emulators and Atmosphere apply a patch file to the module before its
dynamic linker runs. A DT_RELR word gets main's base added; bytes over words an explicit-addend relocation fills change
nothing and are dropped; a write into the dynamic linker's tables, a lazily bound function pointer or outside main's
image keeps the file unsupported. A mod whose data lines do not all fit the build keeps its code patches off too.
The mods are applied from the bottom one up (mods_exefs_apply), so where two mods write the same bytes the higher one
wins, as it does for files. --probe-mods checks it with two mods writing one byte.
The lifted code reads some of main's bytes at lift time (the jump tables). recomp/gen.py lists those ranges in
<gen>/main/folded.txt, and the setup checks the data writes against them after the lift: a file that writes them
becomes unsupported, since the change would not reach the generated code. Generated C without mods is unchanged.
This is the data part of #790 (ericdallo's ExeFS work), split out on its own; the plugin part follows separately.
Co-Authored-By: itaskaev2 <165683571+itaskaev2@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HD texture packs made for the emulators ship their own HeapDef with TextureHeap and its parent grown for the
emulators' 8 GB memory option (HDPacks.com's 4K pack: by 3,419,000 KiB). Served as it is, the game asked for room the
application pool does not have and crashed at start (#673); without the file it looped the panic Blood Moon.
Before mem_init sizes the pool, resolution.c reads the HeapDef of every switched-on mod (mods.c mods_peek_romfs, which
lists the mods' RomFS roots without the rest of the scan: mods_scan needs paths main sets later) and counts the largest
TextureHeap growth over the shipped 506,880 KiB like TOTK_TEX_HEAP_EXTRA_MB (the larger of the two, at most 4 GiB). The
pool and the heap cap grow by it, and romfs.c heapdef_overlay serves the game's own table grown by that amount in the
mod's place, so the other heaps keep the sizes of the game version that runs.
Tested with the HDPacks.com 4K Texture Pack v1.0 as players install it, on 1.4.3 and 1.2.1 (RTX 4080 SUPER): the game
loads a save and runs at 60 fps with 0 validation errors, and holds 4.5 GB of game memory pools against 1.2 GB without
the pack. docs/mods.md, README.md and totk.ini.example say so; other packs come with no guarantee.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A mod that patched the game code (.ips, .pchtxt) stayed off as a whole.
tools/exefs_patches.py now reads IPS, IPS32 and IPSwitch files from each
mod's exefs folder and from atmosphere/exefs_patches/<name>/. It keeps the
ones whose build id is main's and turns their writes into main's code into
one patch set per file. The writes go through the same rules as the code
cheats (tools/cheats.py convert). A file that writes main's data, or that the
rules cannot express, is unsupported as a whole. The setup's lift runs it
after cheats.py and writes build-cache/exefs.status.
runtime/mods.c checks each patch file against that status:
- a built file's set is switched on with its mod;
- a file the build lacks keeps the mod off and starts the rebuild at the next
start, like a code cheat does;
- an unsupported file keeps the mod off, and the Mods tab shows the reason;
- a file for another version is counted and left out.
A mod patch on a site of one of the port's own sets turns that set off while
the mod is on, and the Mods tab names it. The Mods tab also gets the "Rebuild
the game now" row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A cheat file is now part of a mod: mods/<mod>/cheats/<build id>.txt, or the
Atmosphere places inside the mod folder, the file name in any case. The port
ships no cheat files any more (the MyNXCheats files and their notice row are
gone).
- tools/cheats.py --mods builds the code cheats of every mod folder; a
cheat's key hashes "<mod folder>/<name>", so two mods' cheats of one name
keep their own switches.
- The setup reads the cheats from <install>/mods and writes the status to
build-cache/cheats.status.
- runtime/cheats.c reads the files of the enabled mods the port can use, in
the Mods tab order, and the status from build-cache.
- A mod with only cheats is usable. The Cheats tab groups the cheats by mod,
and the Mods tab says how many cheats a mod brought.
- The cheat texts are in all four menu languages.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One folder per mod as emulators take them: romfs, exefs and cheats folders, the Atmosphere layout and TKMM's output
(romfslite counts as romfs). A mod with its own code (subsdk or a replaced main) stays off as a whole, so does one with
ExeFS patches until a rebuild applies them; cheat files are listed, not loaded yet. The scan goes through the platform
layer, so Linux builds it. probe-mods covers the layouts and the order keys; probe-cfg fails on a key listed twice in
the defaults table.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>