feat(mods): a Mods tab, merged resource size tables, ExeFS patches and Atmosphere cheats (HumanBean's draft from Hookshot, with the lift fix) #134

Open
snaildev99 wants to merge 7 commits from snaildev99/Longshot:feat/mods-exefs-patches into main
Contributor

Type: feat · Risk: medium · Tested on: Windows 11, RTX 3090 (the head before the last rebase); Linux, RTX 4090 (an earlier head) · Needs: Linux, any GPU: one game-free test at this head

Why

Mods that change the game's code (ExeFS patches: .ips, .pchtxt) do nothing on Longshot, two mods that each ship a resource size table cannot load together, and there is no place in the game to see or order the mods. HumanBean's draft for all of this was on Hookshot (https://github.com/NotRexed/Hookshot/pull/2478) and never came over; he asked for it to be brought here.

What changed

HumanBean's two commits are ported as they were, under his name; the changes of the port and of the review follow them.

  • mods (HumanBean): host/mods.c reads the mods' order and switches from longshot_mods.txt, serves one size table merged from every enabled mod's (host/restbl.c, host/zs.c, written to modcache/rsizetable.zs), and gives the menu its list (hos_mod_info, hos_mod_edit, hos_mods_changed).
  • ExeFS patches (HumanBean): nx2/exefs_patches.py reads mods/<name>/exefs/*.ips and *.pchtxt when the game is built, nx2/image.py writes them into main before it is relocated and lifted (a write into the padding after the code makes it code), nx2/make_game.py puts them in the lift's key and writes exefs_patches.txt.
  • cheats (HumanBean): host/cheats.c, Atmosphere cheats of the mods that are on, data cheats switched in the menu.
  • menu (HumanBean): the Mods tab in gpu/vk/vk_menu.cpp (draw_mods, mods_tick), shown when there are mods; MkwVkHost gains five functions (gpu/vk/vk_api.h, host/rx_backend.c).
  • menu (snaildev99): the compact bar, which main gained after the draft, draws the Mods tab's full page, dimmed as the full menu, when the Mods tab is the current one (draw_tune_bar).

What the port changed in HumanBean's commits (7 files conflicted with main):

  • gpu/vk/vk_api.h, host/rx_backend.c: the five functions come after main's take_text; MKWVK_API_VERSION is 24 (the draft's was 18; main took 22 for its text fields and 23 for the settings file's path while this was open), and can_mods() asks for 24.
  • gpu/vk/vk_menu.cpp: the Mods block sits before draw_options; tab_titles (at file scope on main) gains "Mods"; the row handling of menu_tick is main's, inside the draft's else for the tabs that are not Mods.
  • host/mods.c: main's hos_mods_file (an exact path) with the draft's rule for the size table in it.
  • nx2/build.py: zs.c joins ZSTD_HOST. tests/test_mods_size_table.py: zs.c beside the others.
  • README.md: the draft's Mods section with Longshot's names; main's note on tables for several game versions kept. The Mods tab's file is longshot_mods.txt.
  • tests/test_mods_menu.py: version 23 and the new order of the host's functions. tests/test_menu_text_field.py (main's): its harness cuts restart_values out of the menu, so it declares the Mods tab's slot.

What the review changed (the last commit):

  • host/mods.c hos_mods_file: no table for prompts.c when the merged one is served.
  • host/mods.c mod_text, exefs_status: a mod that is off says "Its ExeFS patches are not applied while it is off", in the tab and in the log, instead of asking for a build.
  • host/mods.c: "Longshot" in the Mods tab's texts and in longshot_mods.txt's header.
  • host/mods.c fit_sizes, game_case: a mod file whose path is in another case than the game's takes the game's entry. The game's case is looked up only when the mod's own case misses the table, so a mod whose paths match costs no extra reads at the start.
  • gpu/vk/vk_menu.cpp menu_tick: a Restart or Quit waiting for its second press is cleared on the Mods tab.
  • nx2/exefs_patches.py classify: bytes into a relocated word are left out as before, the file's note in exefs_patches.txt now says how many, and the comment says what happens to DT_RELR words (tests/test_exefs_patches.py, the README).

Measurements

Verdict: it works, at a small cost at a start with mods. A mod's code patches run (on main they do nothing), several mods' size tables load together, and the only added time is the size table's merge at a start: 84 ms median with one mod (56 to 258 ms over the starts timed, the largest for a table of 1222 entries), none without a mods folder.

The table is of 5142c2d7 against main @ 6b7fa356. The head before the last rebase (56fd6f8d) was built and played again (Testing, the second item), and the head now (4f8b975d, on main @ 6a013efd) differs from it in our commits only by the API version (24) and the host table's order after main's options_path; the rebases since changed nothing the merge or the patching runs through, and what they did change is named there.

Hardware: RTX 3090, Windows 11
Scene: a child save in Kokiri Forest and Hyrule Field, 2048x1152, 60 fps
Method: one start and about twelve minutes of play by the mod's author, with the mod of the build lines below as the only mod; then a second start; the numbers are longshot_log.txt lines. The merge's time is from the two starts of this PR's build (the first right after the build, the second with the files read before). main has no merge to time against it, and without a mods folder hos_mods_init returns before any of it, so there is no cost when no mods are installed (read in the code, not timed)
How to reproduce: a mod with ExeFS patches in mods, nx2/make_game.py into that game folder, start the game

Before: main @ 6b7fa356 After: this PR @ 5142c2d7
A mod with ExeFS code patches its exefs folder is left out built in: the lines below
The size table's merge at the start none: main has no such step 111 ms and 56 ms in two starts, median 84 ms (1 mod's table merged, 90 entries; 15 entries raised for mod files). With eight mods, four of them with a table: 679 ms at the first start after the build, 69 ms at a later one, 58 ms with one of the four off (3 tables)
The mod in play its code patches do nothing its menu page, picking characters, the swap in the field, its cloth and voice hooks: all work
The Mods tab none lists the mod; moving in it and switching tabs behave
Frame rate in play 60.0 fps in each 5 s line of the log

The build and the run are of 3c23346a, the same changes before the rebases onto 6b7fa356 (main's commits since touch release and CI files and the README's download links only; the docs commit came after), Windows 11, llvm-mingw:

exefs: code        OoT Character Select/exefs/E0F3CCFAF50065AE4EA01EB5AEB13B1A45DA3348.ips (4935 bytes)
exefs: main's code reaches 0x3c47000 now (a code cave)
exefs: 4935 bytes of main patched by 1 files
functions with code below their entry, started at it by a goto: 28
compiled in 5s, 0 failed
the build took 0:02:17, compiled in 0:00:11 (the last one 0:15:18)

(The second build, after the review's changes: mods.c and vk_menu.cpp compiled again, no warning in either. The first, of the port itself, took 15 min 18 s.)

The run's log:

mods: OoT Character Select: 534 RomFS files (16 replace the game's, 518 added), ExeFS patches
mods: OoT Character Select: ExeFS E0F3CCFAF50065AE4EA01EB5AEB13B1A45DA3348.ips: built into the game
mods: resource size table: 1 tables of mods merged (90 entries), 15 entries raised for mod files (0 added files estimated), served from modcache, 111 ms
(second start) mods: resource size table: 1 tables of mods merged (90 entries), 15 entries raised for mod files (0 added files estimated), served from modcache, 56 ms
mods: 1 mods from <game folder>\mods: 551 RomFS entries
mods: System/Resource/ResourceSizeTable.Product.111.Nin_Ounce_NVN2.rsizetable.zs <- <game folder>\modcache/rsizetable.zs (merged)
vi: 9492 frames presented (60.0 fps over the last 5.0 s, interval 1)

Flags and settings

This PR adds a menu tab, one file in the game folder and two kinds of mod files that are read now; no RX_* / HOS_* switch and no ini key.

Flag What it is Before this PR After this PR Effect Helps / doesn't help / untested Recommendation
Mods tab (Insert menu) a tab that lists the mods, each On or Off, in an order that can be changed, and their cheats none shown when there are mods the player sees, orders and switches the mods; it applies at the next start helps on every system; on Linux the list, a switch and a move were kept over a quit and a start in an agent's windowless runs, the restart question there needs a window and was not tried on
longshot_mods.txt (game folder) the Mods tab's order and switches none written when the tab changes something; without it the order is the documented one (the mods' paths), all on which mod wins a file two mods ship, and which mods are off helps on every system on
mods/<name>/exefs/*.ips, *.pchtxt a mod's code patches left out, with a log line applied when the game is built a code mod works after the next build helps on every system; a .pchtxt file: two mods work for one tester on Windows (Serfrost, in Testing), not tried by me on
mods/<name>/cheats/<build id>.txt Atmosphere cheats not read listed on the Mods tab, data cheats off until switched a switched cheat runs every frame helps on Windows for a cheat that changes memory (one tried, in Testing); a cheat that writes code and {Master} codes were not tried on

Testing

Tested:

  • python -I -B tests/run_all.py on Windows 11 at 4f8b975d (rebased onto main @ 6a013efd, with llvm-mingw's bin on the path): 516 passed, 74 skipped, 21 failed, the same twenty-one as main @ 6a013efd on this PC in the same run (512 passed there): test_appimage_player_build, test_auto_handheld, test_cpp_preflight, test_crash_dialog, test_env_on, test_fpx_sticky, test_lift_cleanup_error, test_linux_preflight, test_make_game_cache, test_make_game_prepare, test_missing_nup, test_oot_state_tap, test_options_budget_once, test_portable_builder_home, test_portable_builder_startup, test_readback_late, test_spawn_emit, test_tested_configurations, test_vk_occlusion_queries, test_vk_pgo, test_walk_key; none of them is a mods or menu check. gpu/vk/vk_menu.cpp compiles alone with the build's flags. This head is not built into a game or played: the played head is 56fd6f8d (the next item).

  • The head before the last rebase, built and played on Windows by hand (56fd6f8d, API version 23; Windows 11, RTX 3090, a child save; 2026-11-14, the mod's author), with a larger mod than the runs below: the same mod's v0.2.0 (https://github.com/snaildev99/OoT-Character-Select/releases/tag/v0.2.0), one folder of 5804 RomFS files, a size table and one .ips of seven code patches. The build: 10 min 25 s (compiled in 7 min 55 s, 0 failed; the patch changes code, so the lift ran again), exefs: code ... (7353 bytes), main's code reaches 0x3c47000 now (a code cave). The start: mods: ... 5804 RomFS files (20 replace the game's, 5784 added), ExeFS patches, ExeFS ....ips: built into the game, the size table merged in 258 ms (1 table, 1222 entries, 15 entries raised). In the game he was asked for six things and reports all of it working: the Mods tab lists the mod On with no patch waiting; the mod's page with seven pages of pictures; a pick and the swap in the field with the character's own voice; a dress as cloth; the mod's Customize mode (hair colour, hidden equipment: hooks that run every frame) showing in the field; one of the new characters. The mod replaces Mals/USen...sarc.zs (the game's texts) among others, and they load.

  • A Windows build with a mod that has six ExeFS code patches in one file (https://github.com/snaildev99/OoT-Character-Select): the lines above.

  • In the game (Windows 11, RTX 3090, a child save), played by the mod's author: the Insert menu's Mods tab lists the mod, and moving in it and switching tabs both ways behave as on the other tabs; the mod's own page opens from the game's menu, a character is picked and is the player in the field at once (young Zelda with her cloth, which goes through one of the code patches, and Saria), and both speak with their own voices (another of the patches). No crash, 60 fps.

  • Linux, by mooki for T-3AW7 at 5142c2d7 (Arch Linux, kernel 7.2.15, i9-13900K, RTX 4090, NVIDIA 615.78.08; his comment of 2026-11-13 has the whole result): a full build with nx2/make_game.py with the same mod in mods, 0 failed compiles, the same three exefs: lines as on Windows; the Insert menu's Mods tab shows the mod On with its code patch built in; the size table's merge took 30 ms (1 table, 90 entries, 15 raised); the mod's page opens from the game's own menu, young Zelda is picked and is the player in Kokiri Forest. A windowless Vulkan run of 189 s with a normal exit.

  • Eight mods at once, by hand (the mod's author, Windows 11, RTX 3090, an adult save, a build of 5142c2d7; 2026-11-13). The mod of the runs above as the eight folders it is developed in: four ship a size table, six ship an .ips file each for the same build id (4935 bytes of main in all, the same bytes as the one merged file), and four game files are shipped by more than one of them. longshot_mods.txt was written by hand first, the folders in the reverse of their names' order, because the emulators these folders come from let the first folder by name win a file and here the lower one wins. The steps, each one seen:

    1. Start the game and load a save: as before.
    2. F1, the Mods tab: eight mods, all On, in the file's order.
    3. The game's own menu: the mod's fourth tab and its Characters page (they come from three of the folders' patches).
    4. Pick a character there and close the menu: the character is the player in the field.
    5. On the Mods tab, Enter on a mod, Up and Down: the list follows, the tab stays usable.
    6. Switch one mod Off (one with a size table, a game file another mod also ships, and a patch file) and close the menu: the restart question; Restart now.
    7. With it Off: the game runs, what that mod's file did is gone (the other mod's copy of the file is served), its patch stays built in as the log says.
    8. Switch it On again, Restart now: all back.

    The log of the three starts: 8 mods, 4 tables of mods merged (90 entries), 15 entries raised (679 ms at the first start after the build, 69 ms at the third), with the one mod off 3 tables of mods merged, 14 entries raised (58 ms) and 1 ExeFS patch files built into the game belong to mods that are off or gone: they stay in it until it is built again; the file two mods ship is served from the lower one, and from the other one while the lower is off; longshot_mods.txt was written again by the game; 60 fps, no crash.

  • Linux again, by mooki at 5142c2d7 (his second comment of 2026-11-13, windowless): the mod moved below a second RomFS mod of one file, then switched Off; after a quit and a start the order and the Off state were kept in the Mods tab, its RomFS files were no longer served, and the log said that its built-in ExeFS patch stays until another build.

  • Two .pchtxt mods, by Serfrost (Windows 11, 32 GB, RTX 4080, driver 595.79; his comments of 2026-11-14 and 2026-11-15, by hand). His build is a nightly of his own, main with pending pull requests merged in, this one at its latest head among them (his log names source 5b33f6ba, which is not a commit of this branch). Six mods in mods, two with a .pchtxt file each: Auto Jump (https://gamebanana.com/mods/739034, three records, two of them new code in the padding between functions) and Sprint Speed +20%. The log lists both as ExeFS ...pchtxt: built into the game, two size tables merged in 34 ms, and he reports both working as intended in the game. An earlier crash of Auto Jump there came from an edit of the mod's offset_shift flag, not from the patcher; with the mod as published it works.

  • A cheat file and a mod that is only a code module, by hand (the mod's author, Windows 11, RTX 3090, the build of 56fd6f8d of the second item, no rebuild; 2026-11-15). Two folders added to mods beside the mod of the runs above. (1) cheats/E0F3CCFAF50065AE.txt with one cheat written for this test, six lines: a condition on a pointer in main's data (opcode 1), two loads that follow it (opcode 5), an add (opcode 7), a store of a 4-byte value (opcode 6), the block's end. The same file was first tried on an emulator, where it sets the rupee count. Here the log says cheats: E0F3CCFAF50065AE.txt in 1 mods: 1 cheats, 0 on; 1 data, 0 master, 0 headings, 0 not supported, the Mods tab lists the cheat under its mod, Off; switched On there, the rupee count was the cheat's value as soon as the menu closed, without a start. (2) A published mod that is one file, exefs/subsdk7 (a module of its own code, which Longshot does not load): the log lists it as 0 RomFS files (0 replace the game's, 0 added), ExeFS patches, the Mods tab lists it without a note, and nothing of it happens in the game; no crash. That line and the tab do not say why the mod does nothing: see Reviewer notes.

What has been tried, and how:

Windows 11, RTX 3090, by hand Linux, RTX 4090, an agent's windowless runs (T-3AW7)
One mod: RomFS files and a size table works works
One mod: one .ips file of six code patches works works
Eight mods: four size tables merged works not tried
Eight mods: six .ips files for one build id works not tried
A game file two mods ship: the lower one's is served works not tried
The Mods tab: the list, the selection, the tabs works seen
The Mods tab: Off and On, the restart question, Restart now works Off is kept over a quit and a start; the question needs a window: not tried
The Mods tab: moving a mod works (moved and moved back; no start with another order) works: the new order is kept over a quit and a start
A mod Off whose patch is built in works: its files and table are left out, the patch stays, the log says so works: its files are left out, the log says the patch stays
longshot_mods.txt written by hand, and by the game works by the game: works
A .pchtxt patch works for another tester (Serfrost, RTX 4080, a build with other pull requests merged in): two mods not tried
A cheat file works: one cheat that changes memory, listed Off, switched On in the game, in effect at once not tried
A mod that is only a code module (exefs/subsdk7) listed, nothing happens, no note that says why not tried

Not tested:

  • The mouse on the tab's lines.
  • The compact bar with Mods as the current tab.
  • On Linux, beyond the run above: sound and the voices, physical input, long play, the longshot_mods.txt file being written.
  • A cheat that writes the game's code, {Master} codes, a mod whose paths differ in case from the game's, a patch that writes a relocated word.
  • An ExeFS mod that ships a module of its own (a subsdk9 file): not handled by this pull request, which patches main and adds no module.

Must be tested:

  • Linux, any GPU: python -I -B tests/test_mods_size_table.py at 4f8b975d, expecting its ok: documented RomFS example lines and no failure. It is the example of docs/mods.md (#93), whose expected log line for a mod with only an exefs folder I changed by reading the code; its file checks run on Linux only (by hand: asked of mooki in a comment; any Linux checkout will do, no game needed).

Screenshots

this PR @ 3c23346a (the build of the run above), Windows 11, RTX 3090, from the mod's author:

this PR @ 3c23346a: the mod's Characters page in the game's own menu, King Zora picked, in a window whose title bar says Longshot

The mod's Characters page in the game's own menu with King Zora picked, the game in a window (its title bar says Longshot). The page's fourth tab, the picked character's model and its view all come from the mod's ExeFS code patches, so this is those patches at work in a Longshot build. On main the patches are left out: nothing to show beside it.

this PR @ 3c23346a: the Insert menu's Mods tab, the mod listed and On

The Insert menu (F1) of the same run: the Mods tab, with the mod listed and On.

Risks

What could break: with mods, the size table the game gets is the merged one, where before it was the last mod's own; if the merge fails the last mod's table is served as before. The Mods tab adds a tab to the Insert menu's strip only when there are mods. A build with ExeFS patches lifts patched code; without such patches the lift and its key are as before.

Turning it off: there is no switch. Without a mods folder none of it runs; one mod is switched off on the Mods tab, or by a - or _ before its folder's name. Reverting the commits is enough.

Compatibility: saves and longshot_options.ini are untouched. MKWVK_API_VERSION is 24 (the Mods tab's five functions after options_path), so the game and its renderer have to be of one build. A game folder with mods gets modcache/rsizetable.zs, and longshot_mods.txt once the tab is used. A mod with code patches takes effect only after the game is built again, and switched Off its patches stay built in until the next build (the Mods tab and the log say so). The shader and pipeline caches after updating were not looked at.

Reviewer notes

  • Not done here, a follow-up I would send once this is in: a mod whose exefs folder holds a code module (subsdk0 to subsdk9, or a main) is listed as having ExeFS patches and gets no note. It should say that code modules are not supported, on the Mods tab and in the log, since most of such a mod (and any .ips beside the module that only jumps into it) does nothing here.

  • The lift fix a code-cave routine needs is on main (#131), so its commit is no longer here.

  • The compact bar on the Mods tab: I draw the full Mods page (dimmed) from draw_tune_bar, because tests/test_menu_tune.py asks for if (tune_mode) draw_tune_bar(k); as it is. The other way would be to leave the Mods tab out of the strip while the bar is up. Say if you prefer that.

  • DT_RELR words: I took the second of your two ways (leave them out and say so). Applying them needs the addends read again after apply(); I can do that here or as a follow-up.

  • The draft's third commit on that branch (the banner's header) is not here: another topic.

Issues

See #42

Checklist

Everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description) of the status line's type.
  • Why gives the reason plainly and links the issues that fit (the tracker searched).
  • Measurements has a verdict, or one line saying why there is nothing to measure.
  • Testing names what was tested, what wasn't, and what must still be tested, where and how.
  • Rebased on current main.
  • python -I -B tests/run_all.py passes (any failure that also fails on main is named).
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

When it applies

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • Numbers from the same PC, scene and settings, at least 2 interleaved runs each, the median reported.
  • Switches documented (docs/development.md; the README for a menu row) and shipped at their recommended value.
  • Screenshots of the same spot, captioned like the Measurements columns, for a change that shows on screen.
  • MKWVK_API_VERSION bumped (gpu/vk/vk_api.h's MkwVkHost or MkwVkRenderer changed).
  • A game-free test added for the fix, failing on main and passing here.
  • A check under tests/ that reads changed source updated in this PR, not weakened.
  • Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths.
  • Docs updated.

🤖 Generated with Claude Code

**Type:** feat · **Risk:** medium · **Tested on:** Windows 11, RTX 3090 (the head before the last rebase); Linux, RTX 4090 (an earlier head) · **Needs:** Linux, any GPU: one game-free test at this head ## Why Mods that change the game's code (ExeFS patches: `.ips`, `.pchtxt`) do nothing on Longshot, two mods that each ship a resource size table cannot load together, and there is no place in the game to see or order the mods. HumanBean's draft for all of this was on Hookshot (https://github.com/NotRexed/Hookshot/pull/2478) and never came over; he asked for it to be brought here. ## What changed HumanBean's two commits are ported as they were, under his name; the changes of the port and of the review follow them. - mods (HumanBean): `host/mods.c` reads the mods' order and switches from `longshot_mods.txt`, serves one size table merged from every enabled mod's (`host/restbl.c`, `host/zs.c`, written to `modcache/rsizetable.zs`), and gives the menu its list (`hos_mod_info`, `hos_mod_edit`, `hos_mods_changed`). - ExeFS patches (HumanBean): `nx2/exefs_patches.py` reads `mods/<name>/exefs/*.ips` and `*.pchtxt` when the game is built, `nx2/image.py` writes them into main before it is relocated and lifted (a write into the padding after the code makes it code), `nx2/make_game.py` puts them in the lift's key and writes `exefs_patches.txt`. - cheats (HumanBean): `host/cheats.c`, Atmosphere cheats of the mods that are on, data cheats switched in the menu. - menu (HumanBean): the **Mods** tab in `gpu/vk/vk_menu.cpp` (`draw_mods`, `mods_tick`), shown when there are mods; `MkwVkHost` gains five functions (`gpu/vk/vk_api.h`, `host/rx_backend.c`). - menu (snaildev99): the compact bar, which main gained after the draft, draws the Mods tab's full page, dimmed as the full menu, when the Mods tab is the current one (`draw_tune_bar`). What the port changed in HumanBean's commits (7 files conflicted with main): - `gpu/vk/vk_api.h`, `host/rx_backend.c`: the five functions come after main's `take_text`; `MKWVK_API_VERSION` is 24 (the draft's was 18; main took 22 for its text fields and 23 for the settings file's path while this was open), and `can_mods()` asks for 24. - `gpu/vk/vk_menu.cpp`: the Mods block sits before `draw_options`; `tab_titles` (at file scope on main) gains "Mods"; the row handling of `menu_tick` is main's, inside the draft's `else` for the tabs that are not Mods. - `host/mods.c`: main's `hos_mods_file` (an exact path) with the draft's rule for the size table in it. - `nx2/build.py`: `zs.c` joins `ZSTD_HOST`. `tests/test_mods_size_table.py`: `zs.c` beside the others. - `README.md`: the draft's Mods section with Longshot's names; main's note on tables for several game versions kept. The Mods tab's file is `longshot_mods.txt`. - `tests/test_mods_menu.py`: version 23 and the new order of the host's functions. `tests/test_menu_text_field.py` (main's): its harness cuts `restart_values` out of the menu, so it declares the Mods tab's slot. What the review changed (the last commit): - `host/mods.c` `hos_mods_file`: no table for prompts.c when the merged one is served. - `host/mods.c` `mod_text`, `exefs_status`: a mod that is off says "Its ExeFS patches are not applied while it is off", in the tab and in the log, instead of asking for a build. - `host/mods.c`: "Longshot" in the Mods tab's texts and in `longshot_mods.txt`'s header. - `host/mods.c` `fit_sizes`, `game_case`: a mod file whose path is in another case than the game's takes the game's entry. The game's case is looked up only when the mod's own case misses the table, so a mod whose paths match costs no extra reads at the start. - `gpu/vk/vk_menu.cpp` `menu_tick`: a Restart or Quit waiting for its second press is cleared on the Mods tab. - `nx2/exefs_patches.py` `classify`: bytes into a relocated word are left out as before, the file's note in `exefs_patches.txt` now says how many, and the comment says what happens to DT_RELR words (`tests/test_exefs_patches.py`, the README). ## Measurements **Verdict:** it works, at a small cost at a start with mods. A mod's code patches run (on main they do nothing), several mods' size tables load together, and the only added time is the size table's merge at a start: 84 ms median with one mod (56 to 258 ms over the starts timed, the largest for a table of 1222 entries), none without a `mods` folder. The table is of 5142c2d7 against main @ 6b7fa356. The head before the last rebase (56fd6f8d) was built and played again (Testing, the second item), and the head now (4f8b975d, on main @ 6a013efd) differs from it in our commits only by the API version (24) and the host table's order after main's `options_path`; the rebases since changed nothing the merge or the patching runs through, and what they did change is named there. Hardware: RTX 3090, Windows 11 Scene: a child save in Kokiri Forest and Hyrule Field, 2048x1152, 60 fps Method: one start and about twelve minutes of play by the mod's author, with the mod of the build lines below as the only mod; then a second start; the numbers are `longshot_log.txt` lines. The merge's time is from the two starts of this PR's build (the first right after the build, the second with the files read before). main has no merge to time against it, and without a `mods` folder `hos_mods_init` returns before any of it, so there is no cost when no mods are installed (read in the code, not timed) How to reproduce: a mod with ExeFS patches in `mods`, `nx2/make_game.py` into that game folder, start the game | | Before: main @ 6b7fa356 | After: this PR @ 5142c2d7 | |---|---|---| | A mod with ExeFS code patches | its `exefs` folder is left out | built in: the lines below | | The size table's merge at the start | none: main has no such step | 111 ms and 56 ms in two starts, median 84 ms (1 mod's table merged, 90 entries; 15 entries raised for mod files). With eight mods, four of them with a table: 679 ms at the first start after the build, 69 ms at a later one, 58 ms with one of the four off (3 tables) | | The mod in play | its code patches do nothing | its menu page, picking characters, the swap in the field, its cloth and voice hooks: all work | | The Mods tab | none | lists the mod; moving in it and switching tabs behave | | Frame rate in play | | 60.0 fps in each 5 s line of the log | The build and the run are of 3c23346a, the same changes before the rebases onto 6b7fa356 (main's commits since touch release and CI files and the README's download links only; the docs commit came after), Windows 11, llvm-mingw: ``` exefs: code OoT Character Select/exefs/E0F3CCFAF50065AE4EA01EB5AEB13B1A45DA3348.ips (4935 bytes) exefs: main's code reaches 0x3c47000 now (a code cave) exefs: 4935 bytes of main patched by 1 files functions with code below their entry, started at it by a goto: 28 compiled in 5s, 0 failed the build took 0:02:17, compiled in 0:00:11 (the last one 0:15:18) ``` (The second build, after the review's changes: `mods.c` and `vk_menu.cpp` compiled again, no warning in either. The first, of the port itself, took 15 min 18 s.) The run's log: ``` mods: OoT Character Select: 534 RomFS files (16 replace the game's, 518 added), ExeFS patches mods: OoT Character Select: ExeFS E0F3CCFAF50065AE4EA01EB5AEB13B1A45DA3348.ips: built into the game mods: resource size table: 1 tables of mods merged (90 entries), 15 entries raised for mod files (0 added files estimated), served from modcache, 111 ms (second start) mods: resource size table: 1 tables of mods merged (90 entries), 15 entries raised for mod files (0 added files estimated), served from modcache, 56 ms mods: 1 mods from <game folder>\mods: 551 RomFS entries mods: System/Resource/ResourceSizeTable.Product.111.Nin_Ounce_NVN2.rsizetable.zs <- <game folder>\modcache/rsizetable.zs (merged) vi: 9492 frames presented (60.0 fps over the last 5.0 s, interval 1) ``` ### Flags and settings This PR adds a menu tab, one file in the game folder and two kinds of mod files that are read now; no `RX_*` / `HOS_*` switch and no ini key. | Flag | What it is | Before this PR | After this PR | Effect | Helps / doesn't help / untested | Recommendation | |---|---|---|---|---|---|---| | Mods tab (Insert menu) | a tab that lists the mods, each On or Off, in an order that can be changed, and their cheats | none | shown when there are mods | the player sees, orders and switches the mods; it applies at the next start | helps on every system; on Linux the list, a switch and a move were kept over a quit and a start in an agent's windowless runs, the restart question there needs a window and was not tried | on | | `longshot_mods.txt` (game folder) | the Mods tab's order and switches | none | written when the tab changes something; without it the order is the documented one (the mods' paths), all on | which mod wins a file two mods ship, and which mods are off | helps on every system | on | | `mods/<name>/exefs/*.ips`, `*.pchtxt` | a mod's code patches | left out, with a log line | applied when the game is built | a code mod works after the next build | helps on every system; a `.pchtxt` file: two mods work for one tester on Windows (Serfrost, in Testing), not tried by me | on | | `mods/<name>/cheats/<build id>.txt` | Atmosphere cheats | not read | listed on the Mods tab, data cheats off until switched | a switched cheat runs every frame | helps on Windows for a cheat that changes memory (one tried, in Testing); a cheat that writes code and `{Master}` codes were not tried | on | ## Testing **Tested:** - `python -I -B tests/run_all.py` on Windows 11 at 4f8b975d (rebased onto main @ 6a013efd, with llvm-mingw's `bin` on the path): 516 passed, 74 skipped, 21 failed, the same twenty-one as main @ 6a013efd on this PC in the same run (512 passed there): `test_appimage_player_build`, `test_auto_handheld`, `test_cpp_preflight`, `test_crash_dialog`, `test_env_on`, `test_fpx_sticky`, `test_lift_cleanup_error`, `test_linux_preflight`, `test_make_game_cache`, `test_make_game_prepare`, `test_missing_nup`, `test_oot_state_tap`, `test_options_budget_once`, `test_portable_builder_home`, `test_portable_builder_startup`, `test_readback_late`, `test_spawn_emit`, `test_tested_configurations`, `test_vk_occlusion_queries`, `test_vk_pgo`, `test_walk_key`; none of them is a mods or menu check. `gpu/vk/vk_menu.cpp` compiles alone with the build's flags. This head is not built into a game or played: the played head is 56fd6f8d (the next item). - **The head before the last rebase, built and played on Windows by hand** (56fd6f8d, API version 23; Windows 11, RTX 3090, a child save; 2026-11-14, the mod's author), with a larger mod than the runs below: the same mod's v0.2.0 (https://github.com/snaildev99/OoT-Character-Select/releases/tag/v0.2.0), one folder of 5804 RomFS files, a size table and one `.ips` of seven code patches. The build: 10 min 25 s (compiled in 7 min 55 s, 0 failed; the patch changes code, so the lift ran again), `exefs: code ... (7353 bytes)`, `main's code reaches 0x3c47000 now (a code cave)`. The start: `mods: ... 5804 RomFS files (20 replace the game's, 5784 added), ExeFS patches`, `ExeFS ....ips: built into the game`, the size table merged in 258 ms (1 table, 1222 entries, 15 entries raised). In the game he was asked for six things and reports all of it working: the Mods tab lists the mod On with no patch waiting; the mod's page with seven pages of pictures; a pick and the swap in the field with the character's own voice; a dress as cloth; the mod's Customize mode (hair colour, hidden equipment: hooks that run every frame) showing in the field; one of the new characters. The mod replaces `Mals/USen...sarc.zs` (the game's texts) among others, and they load. - A Windows build with a mod that has six ExeFS code patches in one file (https://github.com/snaildev99/OoT-Character-Select): the lines above. - In the game (Windows 11, RTX 3090, a child save), played by the mod's author: the Insert menu's Mods tab lists the mod, and moving in it and switching tabs both ways behave as on the other tabs; the mod's own page opens from the game's menu, a character is picked and is the player in the field at once (young Zelda with her cloth, which goes through one of the code patches, and Saria), and both speak with their own voices (another of the patches). No crash, 60 fps. - Linux, by mooki for T-3AW7 at 5142c2d7 (Arch Linux, kernel 7.2.15, i9-13900K, RTX 4090, NVIDIA 615.78.08; his comment of 2026-11-13 has the whole result): a full build with `nx2/make_game.py` with the same mod in `mods`, 0 failed compiles, the same three `exefs:` lines as on Windows; the Insert menu's Mods tab shows the mod On with its code patch built in; the size table's merge took 30 ms (1 table, 90 entries, 15 raised); the mod's page opens from the game's own menu, young Zelda is picked and is the player in Kokiri Forest. A windowless Vulkan run of 189 s with a normal exit. - Eight mods at once, by hand (the mod's author, Windows 11, RTX 3090, an adult save, a build of 5142c2d7; 2026-11-13). The mod of the runs above as the eight folders it is developed in: four ship a size table, six ship an `.ips` file each for the same build id (4935 bytes of main in all, the same bytes as the one merged file), and four game files are shipped by more than one of them. `longshot_mods.txt` was written by hand first, the folders in the reverse of their names' order, because the emulators these folders come from let the first folder by name win a file and here the lower one wins. The steps, each one seen: 1. Start the game and load a save: as before. 2. F1, the Mods tab: eight mods, all On, in the file's order. 3. The game's own menu: the mod's fourth tab and its Characters page (they come from three of the folders' patches). 4. Pick a character there and close the menu: the character is the player in the field. 5. On the Mods tab, Enter on a mod, Up and Down: the list follows, the tab stays usable. 6. Switch one mod Off (one with a size table, a game file another mod also ships, and a patch file) and close the menu: the restart question; Restart now. 7. With it Off: the game runs, what that mod's file did is gone (the other mod's copy of the file is served), its patch stays built in as the log says. 8. Switch it On again, Restart now: all back. The log of the three starts: `8 mods`, `4 tables of mods merged (90 entries), 15 entries raised` (679 ms at the first start after the build, 69 ms at the third), with the one mod off `3 tables of mods merged, 14 entries raised` (58 ms) and `1 ExeFS patch files built into the game belong to mods that are off or gone: they stay in it until it is built again`; the file two mods ship is served from the lower one, and from the other one while the lower is off; `longshot_mods.txt` was written again by the game; 60 fps, no crash. - Linux again, by mooki at 5142c2d7 (his second comment of 2026-11-13, windowless): the mod moved below a second RomFS mod of one file, then switched Off; after a quit and a start the order and the Off state were kept in the Mods tab, its RomFS files were no longer served, and the log said that its built-in ExeFS patch stays until another build. - Two `.pchtxt` mods, by Serfrost (Windows 11, 32 GB, RTX 4080, driver 595.79; his comments of 2026-11-14 and 2026-11-15, by hand). His build is a nightly of his own, main with pending pull requests merged in, this one at its latest head among them (his log names source 5b33f6ba, which is not a commit of this branch). Six mods in `mods`, two with a `.pchtxt` file each: Auto Jump (https://gamebanana.com/mods/739034, three records, two of them new code in the padding between functions) and Sprint Speed +20%. The log lists both as `ExeFS ...pchtxt: built into the game`, two size tables merged in 34 ms, and he reports both working as intended in the game. An earlier crash of Auto Jump there came from an edit of the mod's `offset_shift` flag, not from the patcher; with the mod as published it works. - A cheat file and a mod that is only a code module, by hand (the mod's author, Windows 11, RTX 3090, the build of 56fd6f8d of the second item, no rebuild; 2026-11-15). Two folders added to `mods` beside the mod of the runs above. (1) `cheats/E0F3CCFAF50065AE.txt` with one cheat written for this test, six lines: a condition on a pointer in main's data (opcode 1), two loads that follow it (opcode 5), an add (opcode 7), a store of a 4-byte value (opcode 6), the block's end. The same file was first tried on an emulator, where it sets the rupee count. Here the log says `cheats: E0F3CCFAF50065AE.txt in 1 mods: 1 cheats, 0 on; 1 data, 0 master, 0 headings, 0 not supported`, the Mods tab lists the cheat under its mod, Off; switched On there, the rupee count was the cheat's value as soon as the menu closed, without a start. (2) A published mod that is one file, `exefs/subsdk7` (a module of its own code, which Longshot does not load): the log lists it as `0 RomFS files (0 replace the game's, 0 added), ExeFS patches`, the Mods tab lists it without a note, and nothing of it happens in the game; no crash. That line and the tab do not say why the mod does nothing: see Reviewer notes. What has been tried, and how: | | Windows 11, RTX 3090, by hand | Linux, RTX 4090, an agent's windowless runs (T-3AW7) | |---|---|---| | One mod: RomFS files and a size table | works | works | | One mod: one `.ips` file of six code patches | works | works | | Eight mods: four size tables merged | works | not tried | | Eight mods: six `.ips` files for one build id | works | not tried | | A game file two mods ship: the lower one's is served | works | not tried | | The Mods tab: the list, the selection, the tabs | works | seen | | The Mods tab: Off and On, the restart question, Restart now | works | Off is kept over a quit and a start; the question needs a window: not tried | | The Mods tab: moving a mod | works (moved and moved back; no start with another order) | works: the new order is kept over a quit and a start | | A mod Off whose patch is built in | works: its files and table are left out, the patch stays, the log says so | works: its files are left out, the log says the patch stays | | `longshot_mods.txt` written by hand, and by the game | works | by the game: works | | A `.pchtxt` patch | works for another tester (Serfrost, RTX 4080, a build with other pull requests merged in): two mods | not tried | | A cheat file | works: one cheat that changes memory, listed Off, switched On in the game, in effect at once | not tried | | A mod that is only a code module (`exefs/subsdk7`) | listed, nothing happens, no note that says why | not tried | **Not tested:** - The mouse on the tab's lines. - The compact bar with Mods as the current tab. - On Linux, beyond the run above: sound and the voices, physical input, long play, the `longshot_mods.txt` file being written. - A cheat that writes the game's code, `{Master}` codes, a mod whose paths differ in case from the game's, a patch that writes a relocated word. - An ExeFS mod that ships a module of its own (a `subsdk9` file): not handled by this pull request, which patches `main` and adds no module. **Must be tested:** - Linux, any GPU: `python -I -B tests/test_mods_size_table.py` at 4f8b975d, expecting its `ok: documented RomFS example` lines and no failure. It is the example of `docs/mods.md` (#93), whose expected log line for a mod with only an `exefs` folder I changed by reading the code; its file checks run on Linux only (by hand: asked of mooki in a comment; any Linux checkout will do, no game needed). ## Screenshots this PR @ 3c23346a (the build of the run above), Windows 11, RTX 3090, from the mod's author: ![this PR @ 3c23346a: the mod's Characters page in the game's own menu, King Zora picked, in a window whose title bar says Longshot](https://git.magicbean.dev/attachments/8155b8ed-38c5-4647-a19b-ddf20785c95d) The mod's Characters page in the game's own menu with King Zora picked, the game in a window (its title bar says Longshot). The page's fourth tab, the picked character's model and its view all come from the mod's ExeFS code patches, so this is those patches at work in a Longshot build. On main the patches are left out: nothing to show beside it. ![this PR @ 3c23346a: the Insert menu's Mods tab, the mod listed and On](https://git.magicbean.dev/attachments/69cd4070-b7a0-4f4a-9171-9d2a34ce7e66) The Insert menu (F1) of the same run: the Mods tab, with the mod listed and On. ## Risks **What could break:** with mods, the size table the game gets is the merged one, where before it was the last mod's own; if the merge fails the last mod's table is served as before. The Mods tab adds a tab to the Insert menu's strip only when there are mods. A build with ExeFS patches lifts patched code; without such patches the lift and its key are as before. **Turning it off:** there is no switch. Without a `mods` folder none of it runs; one mod is switched off on the Mods tab, or by a `-` or `_` before its folder's name. Reverting the commits is enough. **Compatibility:** saves and `longshot_options.ini` are untouched. `MKWVK_API_VERSION` is 24 (the Mods tab's five functions after `options_path`), so the game and its renderer have to be of one build. A game folder with mods gets `modcache/rsizetable.zs`, and `longshot_mods.txt` once the tab is used. A mod with code patches takes effect only after the game is built again, and switched Off its patches stay built in until the next build (the Mods tab and the log say so). The shader and pipeline caches after updating were not looked at. ## Reviewer notes - Not done here, a follow-up I would send once this is in: a mod whose `exefs` folder holds a code module (`subsdk0` to `subsdk9`, or a `main`) is listed as having ExeFS patches and gets no note. It should say that code modules are not supported, on the Mods tab and in the log, since most of such a mod (and any `.ips` beside the module that only jumps into it) does nothing here. - The lift fix a code-cave routine needs is on main (#131), so its commit is no longer here. - The compact bar on the Mods tab: I draw the full Mods page (dimmed) from `draw_tune_bar`, because `tests/test_menu_tune.py` asks for `if (tune_mode) draw_tune_bar(k);` as it is. The other way would be to leave the Mods tab out of the strip while the bar is up. Say if you prefer that. - DT_RELR words: I took the second of your two ways (leave them out and say so). Applying them needs the addends read again after `apply()`; I can do that here or as a follow-up. - The draft's third commit on that branch (the banner's header) is not here: another topic. ## Issues See #42 ## Checklist ### Everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`) of the status line's type. - [x] Why gives the reason plainly and links the issues that fit (the tracker searched). - [x] Measurements has a verdict, or one line saying why there is nothing to measure. - [x] Testing names what was tested, what wasn't, and what must still be tested, where and how. - [x] Rebased on current `main`. - [x] `python -I -B tests/run_all.py` passes (any failure that also fails on `main` is named). - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### When it applies - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] Numbers from the same PC, scene and settings, at least 2 interleaved runs each, the median reported. - [x] Switches documented (docs/development.md; the README for a menu row) and shipped at their recommended value. - [x] Screenshots of the same spot, captioned like the Measurements columns, for a change that shows on screen. - [x] `MKWVK_API_VERSION` bumped (`gpu/vk/vk_api.h`'s `MkwVkHost` or `MkwVkRenderer` changed). - [x] A game-free test added for the fix, failing on `main` and passing here. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. - [x] Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. - [x] Docs updated. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Mods that each grow files (HD texture packs) could not be combined: only one
resource size table was used. Hookshot now merges every enabled mod's table
into the game's at start (the button prompts' entries kept) and raises the
entry of every file a mod serves to fit its size (host/mods.c, host/restbl.c,
host/zs.c). #2270's merge of the prompts' entries into the winning mod's table
stays as the fallback when no merged table can be made.

The Insert menu gets a Mods tab (shown only when there are mods): each mod on
or off and its place in the list, kept in hookshot_mods.txt and applied at the
next start (the restart question names them), and the cheats of the enabled
mods, switched at once.

ExeFS patches (IPS, IPS32, IPSwitch .pchtxt) are applied to main's image when
the game is built (nx2/exefs_patches.py, nx2/image.py), so the code is
translated from the patched instructions; a write into the padding after the
code (a code cave) makes it code. exefs_patches.txt in the game folder lists
what a build applied; the Mods tab says when a patch waits for a build.

Atmosphere cheat-code files (separate from the Debug tab's built-in cheats)
run in a cheat VM after dmnt's (host/cheats.c), every presented frame, on
guest memory the game was given; cheats that write the game's code are
listed as not supported.

The list, order and switches, the size table's merge, the patch formats and
the cheat VM follow valentderah/totk-pc-recomp (MIT), whose notice is kept in
host/restbl.h and nx2/exefs_patches.py.

Co-authored-by: Valiantsin Dzerakh <[email protected]>
Co-authored-by: conongnang <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The control bridge's bridge_fill names each MenuTab and checks the count: with the Mods tab
added before Debug, its table needs "mods" there.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
a64-kit's emit_function writes a function's instructions in address order, so
the C function began at its lowest instruction. A function is its entry and
everything reachable from it without crossing another entry: code it branches
to at a lower address, which is no entry of its own, came first. 26 functions
of build e0f3ccfa are like that (25 in libcurl, which has one unwind entry for
all of its code, and the return address of a setjmp call), and so is a mod's
patch routine that goes on in spare code below its entry.

nx2/lift_fixes.py wraps emit_function: such a function starts with a goto to
its entry. tests/test_lift_entry_below.py checks it without the game.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
fix(menu): the compact bar leaves the Mods tab its full page
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 9s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
1faee42e07
The compact bar (added to main after the Mods tab was written) draws the
selected row of the current tab. The Mods tab has no such rows: its lines come
from the host. With the bar asked for on the Mods tab the full page is drawn.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
HumanBean left a comment

Code review only: I read the diff and the code around it, and did not build or run anything.

The port is careful. The API bump to 22 with can_mods() checking it, the posix shims that zs.c and mods.c now need, the exefs patches going into the lift key through the environment, and the checks that read source being updated all look right. Some things need to change before this can merge:

  • The size table when the merged one is served: hos_mods_file still gives prompts.c the last mod's own table, which goes against its own comment and the description (inline).
  • Mods that are off are counted as waiting for a build: an off mod's ExeFS files are never in exefs_patches.txt, so the Mods tab tells the player to rebuild to apply patches that the build will always skip (inline).
  • The player sees Hookshot's name in Longshot: in the Mods tab text and in the header of longshot_mods.txt (inline).
  • Rebase on main: main already has #131 (cf0dbb34). The lift commit here (e20dc232) is the same change and becomes empty after a rebase. The "Rebased on current main" box is ticked, but the PR still sits on 1e5fcc20, and the Reviewer notes about merging 131 first are out of date.

There are also smaller points inline: RELR words in exefs_patches.py, the case of mod paths in the size table keys, and the Restart/Quit confirmation on the Mods tab. The size table is now merged at every start with mods (decoding the game's table and reading the header of every mod .zs), so please put that startup time in the Measurements along with the in-game run.

Code review only: I read the diff and the code around it, and did not build or run anything. The port is careful. The API bump to 22 with `can_mods()` checking it, the posix shims that `zs.c` and `mods.c` now need, the exefs patches going into the lift key through the environment, and the checks that read source being updated all look right. Some things need to change before this can merge: - **The size table when the merged one is served:** `hos_mods_file` still gives prompts.c the last mod's own table, which goes against its own comment and the description (inline). - **Mods that are off are counted as waiting for a build:** an off mod's ExeFS files are never in `exefs_patches.txt`, so the Mods tab tells the player to rebuild to apply patches that the build will always skip (inline). - **The player sees Hookshot's name in Longshot:** in the Mods tab text and in the header of `longshot_mods.txt` (inline). - **Rebase on main:** main already has https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/131 (cf0dbb34). The lift commit here (e20dc232) is the same change and becomes empty after a rebase. The "Rebased on current main" box is ticked, but the PR still sits on 1e5fcc20, and the Reviewer notes about merging 131 first are out of date. There are also smaller points inline: RELR words in exefs_patches.py, the case of mod paths in the size table keys, and the Restart/Quit confirmation on the Mods tab. The size table is now merged at every start with mods (decoding the game's table and reading the header of every mod `.zs`), so please put that startup time in the Measurements along with the in-game run.
@ -5105,1 +5247,3 @@
changed = true;
Row& r = rows[tab_row_to_global(cur_tab, selected)];
const bool confirm_selected = &r == &rows[kQuitRow] || &r == &rows[kRestartRow];
if (pending && (pending != &r || !mine)) pending = nullptr, changed = true;
Owner

Nit: now that this sits in the else branch for the tabs that aren't Mods, a pending Restart/Quit confirmation is no longer cleared when the player goes to the Mods tab. It stays armed for when they come back to that row. Clearing pending whenever cur_tab == TAB_MODS would keep main's behaviour.

Nit: now that this sits in the `else` branch for the tabs that aren't Mods, a pending Restart/Quit confirmation is no longer cleared when the player goes to the Mods tab. It stays armed for when they come back to that row. Clearing `pending` whenever `cur_tab == TAB_MODS` would keep main's behaviour.
host/mods.c Outdated
@ -167,3 +286,3 @@
return;
}
qsort(mods, (size_t)nmods, sizeof *mods, mod_cmp);
fputs("# Hookshot's mods: their order and switches, as the Mods tab sets them (Insert menu). One line a mod, top of the\n"
Owner

This header goes into the player's longshot_mods.txt: it should say Longshot's mods, not Hookshot's.

This header goes into the player's `longshot_mods.txt`: it should say Longshot's mods, not Hookshot's.
host/mods.c Outdated
@ -180,0 +350,4 @@
while (line && line != built && line[-1] != '\n') line = strstr(line + 1, want);
const char *kind = line ? line + strlen(want) : "";
if (!line) {
m->exefs_waiting++;
Owner

exefs_status also runs over mods that are off, but write_list only writes the files of mods that are on. So every ExeFS file of an off mod lands here as exefs_waiting, and mod_text tells the player to "build the game again (Hookshot Builder) to apply" patches that the build will skip. Count waiting files only for enabled mods. For an off mod, either say nothing or say that its patches are not applied while it is off.

`exefs_status` also runs over mods that are off, but `write_list` only writes the files of mods that are on. So every ExeFS file of an off mod lands here as `exefs_waiting`, and `mod_text` tells the player to "build the game again (Hookshot Builder) to apply" patches that the build will skip. Count waiting files only for enabled mods. For an off mod, either say nothing or say that its patches are not applied while it is off.
host/mods.c Outdated
@ -180,0 +454,4 @@
const Entry *e = &table[i];
if (!e->key || e->is_dir) continue;
char key[MAX_PATH], game[MAX_PATH], host[MAX_PATH];
restbl_key(e->rel, key, sizeof key);
Owner

Suggestion: the key uses the mod file's own path case (e->rel comes from fd.cFileName). RESTBL keys are CRC32s of the game's case-sensitive paths, so a mod that ships ui/... where the game has UI/... gets an entry under the wrong hash. On Windows, game_file still finds the game file, so restbl_get(base, key) misses and this continues as if the file were streamed. Taking the case from the game's own path (or from the shipped table's key) would make the raise work for those mods.

Suggestion: the key uses the mod file's own path case (`e->rel` comes from `fd.cFileName`). RESTBL keys are CRC32s of the game's case-sensitive paths, so a mod that ships `ui/...` where the game has `UI/...` gets an entry under the wrong hash. On Windows, `game_file` still finds the game file, so `restbl_get(base, key)` misses and this `continue`s as if the file were streamed. Taking the case from the game's own path (or from the shipped table's key) would make the raise work for those mods.
host/mods.c Outdated
@ -202,2 +609,4 @@
snprintf(key, sizeof key, "%s", rel);
lower_slash(key);
if (sizes_key[0] && !strcmp(key, sizes_key)) {
if (!sizes_fallback[0]) return 0;
Owner

This returns the last mod's own table even when sizes_served (the merged table) is what gets served. The comment above and the PR description both say "none when the merged one is served". prompts.c build() then checks and merges into that mod table. If prompts can't merge it (a frame without a content size, or a table not in the game's format), build() returns 0 and the player gets the game's own icons in texts, even though the served merged table already has the prompts' entries. Suggested fix: if (sizes_served[0] || !sizes_fallback[0]) return 0;.

This returns the last mod's own table even when `sizes_served` (the merged table) is what gets served. The comment above and the PR description both say "none when the merged one is served". prompts.c `build()` then checks and merges into that mod table. If prompts can't merge it (a frame without a content size, or a table not in the game's format), `build()` returns 0 and the player gets the game's own icons in texts, even though the served merged table already has the prompts' entries. Suggested fix: `if (sizes_served[0] || !sizes_fallback[0]) return 0;`.
host/mods.c Outdated
@ -238,0 +669,4 @@
k = 0, d = m->note;
if (m->overridden) ADD("%d of its files are taken by a mod lower in the list. ", m->overridden);
if (m->exefs_waiting)
ADD("%d ExeFS patch file%s %s not in the game yet: build the game again (Hookshot Builder) to apply %s. ", m->exefs_waiting,
Owner

The player sees this in Longshot's Mods tab: "Hookshot Builder" should be "Longshot Builder". The same goes for "Nothing Hookshot uses." a few lines up (668).

The player sees this in Longshot's Mods tab: "Hookshot Builder" should be "Longshot Builder". The same goes for "Nothing Hookshot uses." a few lines up (668).
@ -0,0 +237,4 @@
if word is not None and self.bound[word] == "JUMP_SLOT":
return "unsupported", "it writes a function pointer the dynamic linker binds at main+%X" % word, {}
if word is not None:
continue # (an explicit-addend relocation's word: the dynamic linker writes over it, as on the console. A DT_RELR
Owner

nso2.py turns DT_RELR words into RELATIVE records whose addend is read from the file before the patch, so they are in self.bound and their patch bytes are dropped here. On the console, the RELR addend is the (patched) word, so the patch takes effect there. The comment says the opposite of what happens. Either apply RELR words, which needs the addend recomputed after apply(), or fix the comment and treat them like the explicit-addend ones, with a note in exefs_patches.txt saying so.

nso2.py turns DT_RELR words into `RELATIVE` records whose `addend` is read from the file before the patch, so they are in `self.bound` and their patch bytes are dropped here. On the console, the RELR addend is the (patched) word, so the patch takes effect there. The comment says the opposite of what happens. Either apply RELR words, which needs the addend recomputed after `apply()`, or fix the comment and treat them like the explicit-addend ones, with a note in exefs_patches.txt saying so.
Owner

You cooking 🔥

You cooking 🔥
snaildev99 force-pushed feat/mods-exefs-patches from 1faee42e07
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 9s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
to 841d80ed5d
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 7s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
2026-11-13 06:42:47 +00:00
Compare
Author
Contributor

Thanks for the review. Rebased onto main @ 0eb3a3c6 (the lift commit dropped out, as #131 is in), and the last commit (841d80ed) takes each point:

  • The size table when the merged one is served: hos_mods_file now returns 0 then (sizes_served[0] || !sizes_fallback[0]), as you suggested. I checked that fit_sizes starts the merged table from prompts/rsizetable.zs, so the prompts' entries are in what is served.
  • Mods that are off: their ExeFS files are no longer told as waiting. The tab says "Its ExeFS patches are not applied while it is off", and the log's line for such a file had the same mistake and says "not applied: its mod is off" now.
  • Hookshot's name: the two texts of the Mods tab and the header of longshot_mods.txt.
  • Restart/Quit on the Mods tab: pending is cleared there.
  • The case of mod paths: game_case gives the path as the game's folders spell it, and fit_sizes asks for it only when the mod's own case misses the table, so a mod whose paths match the game's does no extra reads at the start. Windows only: where the file system tells the cases apart the path is left as it is.
  • RELR words: the comment now says what happens, and a file whose bytes into relocated words were left out says how many in its note in exefs_patches.txt (and the README says so). Applying them instead needs the addends read again after apply(): say if you want that here.

Built on Windows after these changes (mods.c and vk_menu.cpp compile without a warning; the build's lines are in the description). The full run_all is 462 passed with the same 10 failures as main on this PC. Not run in the game yet: that is next, with the merge's start-up time from the log.

Thanks for the review. Rebased onto main @ 0eb3a3c6 (the lift commit dropped out, as #131 is in), and the last commit (841d80ed) takes each point: - **The size table when the merged one is served:** `hos_mods_file` now returns 0 then (`sizes_served[0] || !sizes_fallback[0]`), as you suggested. I checked that `fit_sizes` starts the merged table from `prompts/rsizetable.zs`, so the prompts' entries are in what is served. - **Mods that are off:** their ExeFS files are no longer told as waiting. The tab says "Its ExeFS patches are not applied while it is off", and the log's line for such a file had the same mistake and says "not applied: its mod is off" now. - **Hookshot's name:** the two texts of the Mods tab and the header of `longshot_mods.txt`. - **Restart/Quit on the Mods tab:** `pending` is cleared there. - **The case of mod paths:** `game_case` gives the path as the game's folders spell it, and `fit_sizes` asks for it only when the mod's own case misses the table, so a mod whose paths match the game's does no extra reads at the start. Windows only: where the file system tells the cases apart the path is left as it is. - **RELR words:** the comment now says what happens, and a file whose bytes into relocated words were left out says how many in its note in `exefs_patches.txt` (and the README says so). Applying them instead needs the addends read again after `apply()`: say if you want that here. Built on Windows after these changes (`mods.c` and `vk_menu.cpp` compile without a warning; the build's lines are in the description). The full run_all is 462 passed with the same 10 failures as main on this PC. Not run in the game yet: that is next, with the merge's start-up time from the log.
snaildev99 force-pushed feat/mods-exefs-patches from 841d80ed5d
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 7s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
to ca32fd3b1a
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 9s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
2026-11-13 06:48:17 +00:00
Compare
Author
Contributor

Run in the game (Windows 11, RTX 3090, a child save, the build of the review's changes): the Mods tab lists the mod and behaves; the mod's code patches all work (its menu page, picking a character, the swap in the field, the cloth and the voice hooks); the size table's merge took 111 ms at the start (1 table merged, 90 entries; 15 raised); 60 fps. The log's lines are in the description. Also: a line on longshot_mods.txt in docs/development.md, rebased onto main @ 39647e1a, and a test request for Linux (T-3AW7). Still to come: a picture of the Mods tab, and what the description lists under Not tested (switching and moving mods on the tab, a cheat file, a pchtxt mod).

Run in the game (Windows 11, RTX 3090, a child save, the build of the review's changes): the Mods tab lists the mod and behaves; the mod's code patches all work (its menu page, picking a character, the swap in the field, the cloth and the voice hooks); the size table's merge took 111 ms at the start (1 table merged, 90 entries; 15 raised); 60 fps. The log's lines are in the description. Also: a line on longshot_mods.txt in docs/development.md, rebased onto main @ 39647e1a, and a test request for Linux (T-3AW7). Still to come: a picture of the Mods tab, and what the description lists under Not tested (switching and moving mods on the tab, a cheat file, a pchtxt mod).
Author
Contributor

A picture from the run is in the description's Screenshots section.

A picture from the run is in the description's Screenshots section.
Author
Contributor

Two pictures from the run are in the description now: the mod's page in the game's menu (its code patches at work), and the Insert menu's Mods tab with the mod listed and On.

Two pictures from the run are in the description now: the mod's page in the game's menu (its code patches at work), and the Insert menu's Mods tab with the mod listed and On.
Author
Contributor

A second start of the same build: the size table's merge took 56 ms (111 ms on the first start, right after the build). Both lines are in the description. What keeps this WIP: the Linux build and run (T-3AW7 is open for it), which I cannot do here.

A second start of the same build: the size table's merge took 56 ms (111 ms on the first start, right after the build). Both lines are in the description. What keeps this WIP: the Linux build and run (T-3AW7 is open for it), which I cannot do here.
Author
Contributor

A correction for whoever takes T-3AW7: for its second step, do not use the mod's public v0.1.2 zip. Its patch file enters one routine with bl and comes back with a plain b, which the console and the emulators take as a return and the lift cannot (the rest of the calling function runs twice): it crashes at the first character picked. That is a fault of the mod, fixed in its source (one byte, the bl is a b) and in the build I ran here; a v0.1.3 of the mod with it is on its way, and I will link it here. The first step (the Mods tab with any RomFS mod) is unaffected.

A correction for whoever takes T-3AW7: for its second step, do not use the mod's public v0.1.2 zip. Its patch file enters one routine with `bl` and comes back with a plain `b`, which the console and the emulators take as a return and the lift cannot (the rest of the calling function runs twice): it crashes at the first character picked. That is a fault of the mod, fixed in its source (one byte, the `bl` is a `b`) and in the build I ran here; a v0.1.3 of the mod with it is on its way, and I will link it here. The first step (the Mods tab with any RomFS mod) is unaffected.
Author
Contributor

A mod package for testing this, for T-3AW7 and anyone who wants to see ExeFS patches at work:

https://github.com/snaildev99/OoT-Character-Select/releases/tag/v0.1.3 (OoT-Character-Select-v0.1.3-lite.zip, 171 MB, SHA-256 ff60cd9bd4f7c5ca1cdbfbb2242fd8fd2dfa08e221e856dd1c2c65d64dd2d5e1)

It is the mod of the measurements above: one mod folder with RomFS files, a resource size table and one .ips file of six code patches, for the game's 1.1.1 build.

  1. Copy the folder OoT Character Select from the zip's mods folder into the game folder's mods.
  2. Build from this branch into that game folder (nx2/make_game.py). Expected in the build's output: exefs: code OoT Character Select/exefs/E0F3CCFAF50065AE4EA01EB5AEB13B1A45DA3348.ips (4935 bytes) and exefs: main's code reaches 0x3c47000 now (a code cave).
  3. Start the game. Expected in longshot_log.txt: ExeFS ... built into the game and the mods: resource size table: 1 tables of mods merged line; the Insert menu shows the Mods tab with the mod On.
  4. In a save: open the − menu, go left from Key Items to the Characters page, pick a character with A, close the menu. The character is the player in the field.

Take v0.1.3, not v0.1.2: the older package has a routine that is entered with bl and goes back with a branch, which the lift cannot run (the function's tail runs twice), and it crashes at the first pick. That was the mod's fault and is fixed in v0.1.3; it is the one byte between the two packages.

Still open here: the checklist's Linux box, which is why this stays WIP. Nothing changed on the branch since ca32fd3b.

A mod package for testing this, for T-3AW7 and anyone who wants to see ExeFS patches at work: https://github.com/snaildev99/OoT-Character-Select/releases/tag/v0.1.3 (`OoT-Character-Select-v0.1.3-lite.zip`, 171 MB, SHA-256 `ff60cd9bd4f7c5ca1cdbfbb2242fd8fd2dfa08e221e856dd1c2c65d64dd2d5e1`) It is the mod of the measurements above: one mod folder with RomFS files, a resource size table and one `.ips` file of six code patches, for the game's 1.1.1 build. 1. Copy the folder `OoT Character Select` from the zip's `mods` folder into the game folder's `mods`. 2. Build from this branch into that game folder (`nx2/make_game.py`). Expected in the build's output: `exefs: code OoT Character Select/exefs/E0F3CCFAF50065AE4EA01EB5AEB13B1A45DA3348.ips (4935 bytes)` and `exefs: main's code reaches 0x3c47000 now (a code cave)`. 3. Start the game. Expected in `longshot_log.txt`: `ExeFS ... built into the game` and the `mods: resource size table: 1 tables of mods merged` line; the Insert menu shows the Mods tab with the mod On. 4. In a save: open the − menu, go left from Key Items to the Characters page, pick a character with A, close the menu. The character is the player in the field. Take v0.1.3, not v0.1.2: the older package has a routine that is entered with `bl` and goes back with a branch, which the lift cannot run (the function's tail runs twice), and it crashes at the first pick. That was the mod's fault and is fixed in v0.1.3; it is the one byte between the two packages. Still open here: the checklist's Linux box, which is why this stays WIP. Nothing changed on the branch since ca32fd3b.
Author
Contributor

Rebased onto main 6b7fa356 (one commit, the launcher download links): no conflict, the five commits are unchanged (range-diff: all equal), head 5142c2d7. tests/run_all.py at this head: 498 passed, 36 skipped, 8 failed, and the same 8 fail on main here (named in the description). Not built again: the rebase brought no host, renderer or lift change.

Rebased onto main 6b7fa356 (one commit, the launcher download links): no conflict, the five commits are unchanged (range-diff: all equal), head 5142c2d7. tests/run_all.py at this head: 498 passed, 36 skipped, 8 failed, and the same 8 fail on main here (named in the description). Not built again: the rebase brought no host, renderer or lift change.
snaildev99 force-pushed feat/mods-exefs-patches from ca32fd3b1a
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 9s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
to 5142c2d7b9
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 3s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
2026-11-13 07:37:41 +00:00
Compare
Member

Linux result for T-3AW7: all four steps passed on 5142c2d7b9adc782077830cb6d655186494e7633, source 1d80fd67 (v3.0.0-rc.1-15-g5142c2d7b). Arch Linux, kernel 7.2.15-1-omarchy, i9-13900K, RTX 4090, NVIDIA 615.78.08. This was a windowless Vulkan run with a disposable child save.

  1. Pass: installed OoT Character Select v0.1.3 lite before building. The ZIP's SHA-256 matched ff60cd9bd4f7c5ca1cdbfbb2242fd8fd2dfa08e221e856dd1c2c65d64dd2d5e1. v0.1.2 was not used.
  2. Pass: a full Linux build with nx2/make_game.py completed with zero failed compiles. It reported the build took 0:09:39, compiled in 0:07:30 and:
    exefs: code        OoT Character Select/exefs/E0F3CCFAF50065AE4EA01EB5AEB13B1A45DA3348.ips (4935 bytes)
    exefs: main's code reaches 0x3c47000 now (a code cave)
    exefs: 4935 bytes of main patched by 1 files
    
  3. Pass: the Insert menu's Mods tab showed OoT Character Select On, with its code patch built in. The runtime logged:
    mods: OoT Character Select: 534 RomFS files (16 replace the game's, 518 added), ExeFS patches
    mods: OoT Character Select: ExeFS E0F3CCFAF50065AE4EA01EB5AEB13B1A45DA3348.ips: built into the game
    mods: resource size table: 1 tables of mods merged (90 entries), 15 entries raised for mod files (0 added files estimated), served from modcache, 30 ms
    
  4. Pass: opened the game's own menu with −, moved left to Characters, selected Zelda (child) with A, closed it and moved in Kokiri Forest as her. Inspected screenshots show the roster, selected model and character in play. This early save initially opened Map and had the two ordinary adjacent pages locked; Characters was reachable three left-tab presses away.

The game exited normally after 189 seconds. No fatal, crash, device-loss or kernel-driver fault was recorded; the kernel-log read completed. One 20-second frame wait expired during the first load with shader preparation disabled; the game subsequently advanced and completed the character test. This was not a frame-rate measurement.

Not covered by this result: Windows, other GPUs, physical input, sound/voices, long play, every character, the compact menu, mouse interaction, pchtxt patches, cheats, path-case differences, relocated-word patches, or rebuilding after disabling a code mod. No comparison build was needed for these four acceptance steps.

Linux result for T-3AW7: all four steps passed on `5142c2d7b9adc782077830cb6d655186494e7633`, source `1d80fd67` (`v3.0.0-rc.1-15-g5142c2d7b`). Arch Linux, kernel 7.2.15-1-omarchy, i9-13900K, RTX 4090, NVIDIA 615.78.08. This was a windowless Vulkan run with a disposable child save. 1. **Pass:** installed OoT Character Select **v0.1.3 lite** before building. The ZIP's SHA-256 matched `ff60cd9bd4f7c5ca1cdbfbb2242fd8fd2dfa08e221e856dd1c2c65d64dd2d5e1`. v0.1.2 was not used. 2. **Pass:** a full Linux build with `nx2/make_game.py` completed with zero failed compiles. It reported `the build took 0:09:39, compiled in 0:07:30` and: ```text exefs: code OoT Character Select/exefs/E0F3CCFAF50065AE4EA01EB5AEB13B1A45DA3348.ips (4935 bytes) exefs: main's code reaches 0x3c47000 now (a code cave) exefs: 4935 bytes of main patched by 1 files ``` 3. **Pass:** the Insert menu's Mods tab showed OoT Character Select **On**, with its code patch built in. The runtime logged: ```text mods: OoT Character Select: 534 RomFS files (16 replace the game's, 518 added), ExeFS patches mods: OoT Character Select: ExeFS E0F3CCFAF50065AE4EA01EB5AEB13B1A45DA3348.ips: built into the game mods: resource size table: 1 tables of mods merged (90 entries), 15 entries raised for mod files (0 added files estimated), served from modcache, 30 ms ``` 4. **Pass:** opened the game's own menu with −, moved left to Characters, selected **Zelda (child)** with A, closed it and moved in Kokiri Forest as her. Inspected screenshots show the roster, selected model and character in play. This early save initially opened Map and had the two ordinary adjacent pages locked; Characters was reachable three left-tab presses away. The game exited normally after 189 seconds. No fatal, crash, device-loss or kernel-driver fault was recorded; the kernel-log read completed. One 20-second frame wait expired during the first load with shader preparation disabled; the game subsequently advanced and completed the character test. This was not a frame-rate measurement. Not covered by this result: Windows, other GPUs, physical input, sound/voices, long play, every character, the compact menu, mouse interaction, pchtxt patches, cheats, path-case differences, relocated-word patches, or rebuilding after disabling a code mod. No comparison build was needed for these four acceptance steps.
Author
Contributor

Thank you, mooki: that is the Linux build and run this was waiting for (T-3AW7: a full build with 0 failed compiles, the Mods tab with the mod On, the mod in play, the merge in 30 ms).

What changed in the description:

  • The checklist's "Host code builds on both Windows (llvm-mingw) and Linux" is ticked: Windows from my builds, Linux from mooki's.
  • Testing: his run is under Tested with his setup, Needs other testers is None, and Not tested now says what his run did not cover on Linux (sound and the voices, physical input, long play, longshot_mods.txt being written).
  • Flags and settings: Linux is no longer listed as untested for the Mods tab and for ExeFS patches.

With that I am taking the WIP mark off. Nothing changed on the branch: the head is still 5142c2d7, the commit mooki tested.

main has moved 28 commits since this branch's base (6b7fa356, now fdc2840b). I tried the rebase in a throwaway worktree and did not push it, so the head on the page stays the tested one: no conflict, and tests/run_all.py there gives 489 passed, 33 skipped, 6 failed. The 6 fail on main itself on this PC: four of the eight named in the description (test_check_third_party, test_portable_builder_home, test_portable_builder_startup, test_release) and two tests that are new on main, test_display_pointer (it calls g++, which this PC does not have) and test_texture_staging_publication (it asks for --headers); I ran those two on main @ fdc2840b alone and they fail the same way. Say if you want the rebase pushed before the merge; a build of it would then be new, untested code on Linux.

Still tested by nobody, as the description says: switching a mod Off and On and moving it on the tab, the compact bar with Mods current, a pchtxt mod, a cheat file, two mods with a size table each, a patch into a relocated word.

T-3AW7 stays open for now, in case someone with another GPU on Linux wants to add a result.

Thank you, mooki: that is the Linux build and run this was waiting for (T-3AW7: a full build with 0 failed compiles, the Mods tab with the mod On, the mod in play, the merge in 30 ms). What changed in the description: - The checklist's "Host code builds on both Windows (llvm-mingw) and Linux" is ticked: Windows from my builds, Linux from mooki's. - Testing: his run is under Tested with his setup, Needs other testers is None, and Not tested now says what his run did not cover on Linux (sound and the voices, physical input, long play, `longshot_mods.txt` being written). - Flags and settings: Linux is no longer listed as untested for the Mods tab and for ExeFS patches. With that I am taking the WIP mark off. Nothing changed on the branch: the head is still 5142c2d7, the commit mooki tested. main has moved 28 commits since this branch's base (6b7fa356, now fdc2840b). I tried the rebase in a throwaway worktree and did not push it, so the head on the page stays the tested one: no conflict, and `tests/run_all.py` there gives 489 passed, 33 skipped, 6 failed. The 6 fail on main itself on this PC: four of the eight named in the description (`test_check_third_party`, `test_portable_builder_home`, `test_portable_builder_startup`, `test_release`) and two tests that are new on main, `test_display_pointer` (it calls `g++`, which this PC does not have) and `test_texture_staging_publication` (it asks for `--headers`); I ran those two on main @ fdc2840b alone and they fail the same way. Say if you want the rebase pushed before the merge; a build of it would then be new, untested code on Linux. Still tested by nobody, as the description says: switching a mod Off and On and moving it on the tab, the compact bar with Mods current, a pchtxt mod, a cheat file, two mods with a size table each, a patch into a relocated word. T-3AW7 stays open for now, in case someone with another GPU on Linux wants to add a result.
snaildev99 changed title from WIP: feat(mods): a Mods tab, merged resource size tables, ExeFS patches and Atmosphere cheats (HumanBean's draft from Hookshot, with the lift fix) to feat(mods): a Mods tab, merged resource size tables, ExeFS patches and Atmosphere cheats (HumanBean's draft from Hookshot, with the lift fix) 2026-11-13 15:51:59 +00:00
Author
Contributor

A correction to my last comment: the reasons I gave for the two checks that are new failures on main were guesses from running them alone, and they are wrong. Both are known on main and have fixes open: test_texture_staging_publication fails since #177 (dyst's #202), and test_display_pointer has not linked since #178 (d4nkn3ss's #187). What I measured stands: both fail on main @ fdc2840b on this PC as they do on the trial rebase, and this branch touches neither.

A correction to my last comment: the reasons I gave for the two checks that are new failures on main were guesses from running them alone, and they are wrong. Both are known on main and have fixes open: `test_texture_staging_publication` fails since #177 (dyst's #202), and `test_display_pointer` has not linked since #178 (d4nkn3ss's #187). What I measured stands: both fail on main @ fdc2840b on this PC as they do on the trial rebase, and this branch touches neither.
Member

Two additional Linux checks on 5142c2d7: moved OoT Character Select below a second RomFS mod containing only one invented text file, then switched Character Select Off. After quitting and starting again, both the order and Off state persisted in the Mods tab; its RomFS files were no longer indexed. The log correctly warned that its already-built ExeFS patch remains until another build.

This was still windowless: the restart question requires a window, so this checks a manual quit/start, not "Restart now". No rebuild to remove the code patch, file-conflict priority, mouse interaction or compact-menu check was performed.

Two additional Linux checks on `5142c2d7`: moved OoT Character Select below a second RomFS mod containing only one invented text file, then switched Character Select Off. After quitting and starting again, both the order and Off state persisted in the Mods tab; its RomFS files were no longer indexed. The log correctly warned that its already-built ExeFS patch remains until another build. This was still windowless: the restart question requires a window, so this checks a manual quit/start, not "Restart now". No rebuild to remove the code patch, file-conflict priority, mouse interaction or compact-menu check was performed.
Author
Contributor

A hand test of the parts that were still open: several mods at once, on Windows (the mod's author played it; RTX 3090, an adult save, a build of 5142c2d7, nothing changed on the branch).

The mod of the earlier runs was put in as the eight folders it is developed in, instead of its one merged folder: four of them ship a size table, six ship an .ips file each for the same build id, and four game files are shipped by more than one of them. All eight steps passed; they are in the description under Tested, with a table of what has been tried on which system and how.

  • Several size tables: 4 tables of mods merged (90 entries), 15 entries raised, 679 ms at the first start after the build and 69 ms at a later one; with one of the four mods off, 3 tables of mods merged, 14 entries raised, 58 ms.
  • Several patch files: the build took all six (exefs: 4935 bytes of main patched by 6 files, the same bytes as the merged file), 2 min 16 s after the last build, and the mod plays as before.
  • Off and On, with a restart: a mod with a table, a patch and a file another mod also ships was switched Off, the game restarted from the question, played, switched On, restarted. Off, its file is served from the other mod and its table is left out; its patch stays built in and the log says so (1 ExeFS patch files built into the game belong to mods that are off or gone).
  • Moving a mod on the tab works; longshot_mods.txt was written by the game afterwards.

One thing for mod authors who come from the emulators, which I ran into setting this up: there the first folder by name wins a file two mods ship, here the lower one in the list wins. A set of folders that relies on the emulators' order needs its order turned round (I wrote longshot_mods.txt for it). The README says which one wins; nothing to change in the code, but it may be worth a line for people moving a set of mods over.

Three notes from the tester on the restart texts, none of them a fault of the merge or the patches. I will put them up as a follow-up on top of this one rather than change this branch after its Linux run:

  • The question and the gold line say "the mods" whatever changed: "Restart to apply the mods?" for one mod, and for a mod that was switched off. He would like them to name the mods and say On or Off.
  • After Later, switching the same mod On and Off again inside one visit to the menu does not ask again; a row of the other tabs does (set_row tells the restart state of a change, mods_tick does not).
  • On the other tabs, "Takes effect after a restart" shows in the help only once the row is changed; he expected to see it before changing a row. That one is main's and gets its own pull request.

Still tried by nobody: a .pchtxt patch, a cheat file, a start with a changed order, the compact bar with Mods current, a patch into a relocated word.

A hand test of the parts that were still open: several mods at once, on Windows (the mod's author played it; RTX 3090, an adult save, a build of 5142c2d7, nothing changed on the branch). The mod of the earlier runs was put in as the eight folders it is developed in, instead of its one merged folder: four of them ship a size table, six ship an `.ips` file each for the same build id, and four game files are shipped by more than one of them. All eight steps passed; they are in the description under Tested, with a table of what has been tried on which system and how. - **Several size tables:** `4 tables of mods merged (90 entries), 15 entries raised`, 679 ms at the first start after the build and 69 ms at a later one; with one of the four mods off, `3 tables of mods merged, 14 entries raised`, 58 ms. - **Several patch files:** the build took all six (`exefs: 4935 bytes of main patched by 6 files`, the same bytes as the merged file), 2 min 16 s after the last build, and the mod plays as before. - **Off and On, with a restart:** a mod with a table, a patch and a file another mod also ships was switched Off, the game restarted from the question, played, switched On, restarted. Off, its file is served from the other mod and its table is left out; its patch stays built in and the log says so (`1 ExeFS patch files built into the game belong to mods that are off or gone`). - **Moving a mod** on the tab works; `longshot_mods.txt` was written by the game afterwards. One thing for mod authors who come from the emulators, which I ran into setting this up: there the first folder by name wins a file two mods ship, here the lower one in the list wins. A set of folders that relies on the emulators' order needs its order turned round (I wrote `longshot_mods.txt` for it). The README says which one wins; nothing to change in the code, but it may be worth a line for people moving a set of mods over. Three notes from the tester on the restart texts, none of them a fault of the merge or the patches. I will put them up as a follow-up on top of this one rather than change this branch after its Linux run: - The question and the gold line say "the mods" whatever changed: "Restart to apply the mods?" for one mod, and for a mod that was switched off. He would like them to name the mods and say On or Off. - After Later, switching the same mod On and Off again inside one visit to the menu does not ask again; a row of the other tabs does (`set_row` tells the restart state of a change, `mods_tick` does not). - On the other tabs, "Takes effect after a restart" shows in the help only once the row is changed; he expected to see it before changing a row. That one is main's and gets its own pull request. Still tried by nobody: a `.pchtxt` patch, a cheat file, a start with a changed order, the compact bar with Mods current, a patch into a relocated word.
Author
Contributor

A correction: I wrote the comment above before reading mooki's second comment (16:14), so "a start with a changed order" was wrong in my list of what nobody has tried. mooki moved the mod below a second RomFS mod and switched it Off on Linux, and both were kept over a quit and a start, with the log's line about the patch that stays built in. Thank you. The description's table and Tested list now have that run beside the hand test; its Linux column says what a windowless run could not try (the restart question).

Tried by nobody, corrected: a .pchtxt patch, a cheat file, the mouse on the tab, the compact bar with Mods current, a patch into a relocated word.

A correction: I wrote the comment above before reading mooki's second comment (16:14), so "a start with a changed order" was wrong in my list of what nobody has tried. mooki moved the mod below a second RomFS mod and switched it Off on Linux, and both were kept over a quit and a start, with the log's line about the patch that stays built in. Thank you. The description's table and Tested list now have that run beside the hand test; its Linux column says what a windowless run could not try (the restart question). Tried by nobody, corrected: a `.pchtxt` patch, a cheat file, the mouse on the tab, the compact bar with Mods current, a patch into a relocated word.
First-time contributor

Test request T-AK6A: mixed (the full Mods menu and persistence pass; broader mod features remain untested).

Hardware: NVIDIA RTX 5090, 32607 MiB, driver 617.42; Ryzen 7 9800X3D, 96 GiB RAM; Windows 11 build 26300. Built exact PR head 5142c2d7b9, v3.0.0-rc.1-15-g5142c2d7 / source 1d80fd67. This is a fresh matched API22 game and renderer, not an API21 game with a swapped DLL. Cached dependency IDs/hashes match the branch's locked Windows dependencies.

What I checked:

  • Built with the documented make_game.py route from a local supported dump, into a new isolated runtime/work folder. 641 files compiled, 0 failures, 567 s compiling; both modules linked and the build exited 0. Shader preparation recorded 1 failed program (uninvestigated), while all 25,344 noted and 9,925 compute pipelines were made. That is not an all-programs-green claim.
  • Six focused native Windows checks passed: cheat VM, ExeFS parser, Mods menu, size-table merge/routing, mod-size/order behavior, and file links. No independent whole-suite run for this test.
  • Used two authored inert text-only RomFS mods, Review Alpha and Review Beta, and a separately copied private test profile. No external mod package or game-derived fixture was downloaded.
  • First launch, headless Vulkan with the supported menu script and menu captures: opened the full Mods tab, switched Alpha from On to Off, saw the restart-needed notice, moved Alpha beneath Beta, and inspected the final list and saved state.
  • Quit normally and started the same matched runtime/profile again. The tab still showed Beta On above Alpha Off, and the restart-needed notice was gone. Both launches returned native exit 0 and guard exit 0. Audio, DLSS and frame generation were off. This is a functional check, not an FPS measurement.

First launch, Alpha Off:

PR134 first launch: Review Alpha Off with restart needed

After the normal restart, reordered state persisted:

PR134 second launch: Beta On above Alpha Off

Not tested: compact-menu mode (no supported scripted trigger), physical keyboard/mouse/controller, the in-menu Restart-now confirmation (manual normal quit/start used), sound, gameplay or file-conflict priority, real code patches in a full build, pchtxt mods, live cheats, two real resource-size tables, relocated-word patch handling, other GPUs/OSes, long play, or performance. The parser/VM/size-table checks above use authored fixtures; they do not replace those in-game cases. No visual clipping defect was established. Installed player/saves/settings remained untouched; all 23 protected-file hashes were unchanged after the runs.

Test request T-AK6A: mixed (the full Mods menu and persistence pass; broader mod features remain untested). Hardware: NVIDIA RTX 5090, 32607 MiB, driver 617.42; Ryzen 7 9800X3D, 96 GiB RAM; Windows 11 build 26300. Built exact PR head 5142c2d7b9adc782077830cb6d655186494e7633, v3.0.0-rc.1-15-g5142c2d7 / source 1d80fd67. This is a fresh matched API22 game and renderer, not an API21 game with a swapped DLL. Cached dependency IDs/hashes match the branch's locked Windows dependencies. What I checked: - Built with the documented make_game.py route from a local supported dump, into a new isolated runtime/work folder. 641 files compiled, 0 failures, 567 s compiling; both modules linked and the build exited 0. Shader preparation recorded 1 failed program (uninvestigated), while all 25,344 noted and 9,925 compute pipelines were made. That is not an all-programs-green claim. - Six focused native Windows checks passed: cheat VM, ExeFS parser, Mods menu, size-table merge/routing, mod-size/order behavior, and file links. No independent whole-suite run for this test. - Used two authored inert text-only RomFS mods, Review Alpha and Review Beta, and a separately copied private test profile. No external mod package or game-derived fixture was downloaded. - First launch, headless Vulkan with the supported menu script and menu captures: opened the full Mods tab, switched Alpha from On to Off, saw the restart-needed notice, moved Alpha beneath Beta, and inspected the final list and saved state. - Quit normally and started the same matched runtime/profile again. The tab still showed Beta On above Alpha Off, and the restart-needed notice was gone. Both launches returned native exit 0 and guard exit 0. Audio, DLSS and frame generation were off. This is a functional check, not an FPS measurement. First launch, Alpha Off: ![PR134 first launch: Review Alpha Off with restart needed](https://git.magicbean.dev/attachments/12a7178e-5d8a-4221-a33c-a47ce2a6bbd8) After the normal restart, reordered state persisted: ![PR134 second launch: Beta On above Alpha Off](https://git.magicbean.dev/attachments/ccf5366e-3a4e-41d3-9ba7-c842517384ce) Not tested: compact-menu mode (no supported scripted trigger), physical keyboard/mouse/controller, the in-menu Restart-now confirmation (manual normal quit/start used), sound, gameplay or file-conflict priority, real code patches in a full build, pchtxt mods, live cheats, two real resource-size tables, relocated-word patch handling, other GPUs/OSes, long play, or performance. The parser/VM/size-table checks above use authored fixtures; they do not replace those in-game cases. No visual clipping defect was established. Installed player/saves/settings remained untouched; all 23 protected-file hashes were unchanged after the runs.
Author
Contributor

For the record: Hookshot is back on its own git server with the history kept and its issues and pull requests gone, so your draft (GitHub's 2478) is no longer there. I have offered this same work to Hookshot as a draft pull request on current main there, your two commits under your name and credited in the description: https://git.notrexed.dev/Hookshot/Hookshot/pulls/3. Its only change against this branch is the port: Hookshot's names, and API version 23 for the Mods tab's functions because its main took 22. Say if you would rather carry it there yourself.

For the record: Hookshot is back on its own git server with the history kept and its issues and pull requests gone, so your draft (GitHub's 2478) is no longer there. I have offered this same work to Hookshot as a draft pull request on current main there, your two commits under your name and credited in the description: https://git.notrexed.dev/Hookshot/Hookshot/pulls/3. Its only change against this branch is the port: Hookshot's names, and API version 23 for the Mods tab's functions because its main took 22. Say if you would rather carry it there yourself.
Author
Contributor

A correction to the hand test's 'a mod Off whose patch is built in: works' (seen today on a Hookshot build of the same code, with the whole mod switched Off instead of one of its eight folders): the game runs, but not cleanly. The tester saw the tabs at the top of the game's own menu drawn wrong in that state. That is the half-off state the log names: the mod's RomFS files are no longer served, while its code patches stay built in until the next build. One of this mod's patches gives the menu's header a fourth tab, and the layout and text for it are RomFS files, so the patched code draws a header whose files are gone. Nothing in the merge or the patching is wrong, and the Mods tab does say 'Its ExeFS patches stay in the game until it is built again' for such a mod, but a player who switches a code mod Off gets half of it until they build again. A follow-up could say so in the restart question when the mod being switched off has patches built in; say if you want it here or in #305.

A correction to the hand test's 'a mod Off whose patch is built in: works' (seen today on a Hookshot build of the same code, with the whole mod switched Off instead of one of its eight folders): the game runs, but not cleanly. The tester saw the tabs at the top of the game's own menu drawn wrong in that state. That is the half-off state the log names: the mod's RomFS files are no longer served, while its code patches stay built in until the next build. One of this mod's patches gives the menu's header a fourth tab, and the layout and text for it are RomFS files, so the patched code draws a header whose files are gone. Nothing in the merge or the patching is wrong, and the Mods tab does say 'Its ExeFS patches stay in the game until it is built again' for such a mod, but a player who switches a code mod Off gets half of it until they build again. A follow-up could say so in the restart question when the mod being switched off has patches built in; say if you want it here or in #305.
Author
Contributor

A new link for the Hookshot offer: their server restored its old issues and pull requests this evening and the numbers moved, so pull request 3 there is gone. It is https://git.notrexed.dev/Hookshot/Hookshot/pulls/2900 now, built and played on Hookshot by hand since. Your draft is back there as their #2478, shown as by Ghost and no longer merging; 2900 says it supersedes it.

A new link for the Hookshot offer: their server restored its old issues and pull requests this evening and the numbers moved, so pull request 3 there is gone. It is https://git.notrexed.dev/Hookshot/Hookshot/pulls/2900 now, built and played on Hookshot by hand since. Your draft is back there as their #2478, shown as by Ghost and no longer merging; 2900 says it supersedes it.
Author
Contributor

Rebased onto main @ 98e2d95f, since it no longer merged: head 58b1e64d. Two conflicts, both small: restart_values in gpu/vk/vk_menu.cpp (main now reads a row's value through row_restart_value; the mods' slot is added after that loop as before) and the list of posix shims in tests/test_mods_size_table.py (main's comment kept, MoveFileExA added). vk_menu.cpp compiles; tests/run_all.py at this head: 519 passed, 34 skipped, 6 failed, the same six as main @ 98e2d95f on this PC (named in the description). Not built or played again after this rebase: the runs in the game, mooki's on Linux and monsterlag's on Windows are of 5142c2d7. Thanks, mooki, for the labels on this one and on #304 and #305.

Rebased onto main @ 98e2d95f, since it no longer merged: head 58b1e64d. Two conflicts, both small: `restart_values` in `gpu/vk/vk_menu.cpp` (main now reads a row's value through `row_restart_value`; the mods' slot is added after that loop as before) and the list of posix shims in `tests/test_mods_size_table.py` (main's comment kept, `MoveFileExA` added). `vk_menu.cpp` compiles; `tests/run_all.py` at this head: 519 passed, 34 skipped, 6 failed, the same six as main @ 98e2d95f on this PC (named in the description). Not built or played again after this rebase: the runs in the game, mooki's on Linux and monsterlag's on Windows are of 5142c2d7. Thanks, mooki, for the labels on this one and on #304 and #305.
snaildev99 force-pushed feat/mods-exefs-patches from 5142c2d7b9
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 3s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
to 58b1e64dfd
Some checks failed
PR description / Description follows the template (pull_request_target) Has been cancelled
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
2026-11-14 02:16:29 +00:00
Compare
First-time contributor

Edit: False behavior, ignore.

Found an offset issue while testing the ExeFS pchtxt support. This game’s main NSO has a .text file offset of 0x104, but the patcher assumed 0x100. That placed Auto Jump’s patch records four bytes late and caused a crash when jumping. Changed the build-time mapper to derive the NSO’s actual segment offsets. Testing confirmed Auto Jump works with the fix, without modifying the mod files.

Edit: False behavior, ignore. Found an offset issue while testing the ExeFS pchtxt support. This game’s main NSO has a .text file offset of 0x104, but the patcher assumed 0x100. That placed [Auto Jump’s](https://gamebanana.com/mods/739034) patch records four bytes late and caused a crash when jumping. Changed the build-time mapper to derive the NSO’s actual segment offsets. Testing confirmed Auto Jump works with the fix, without modifying the mod files.
Author
Contributor

@Serfrost thank you for trying a real .pchtxt mod: that case had been tried by nobody. I could not reproduce the fault on this branch, though, and I think 0x100 is right here. What I checked, offline (not in the game):

  • The file. Auto Jump's patch has @flag offset_shift 0x100 and three records, at 007F7574, 01EB4224 and 01EB4324. parse_pchtxt adds the shift and takes the header (NSO_HEADER, 0x100) off again, so the records land in main's image at exactly those offsets.
  • Where they land. In the stock game, 0x1EB4224 and 0x1EB4324 are each the first byte of a run of zero padding between two functions, right after the ret that ends the function before (28 bytes of padding each; the records are 20 and 28 bytes). The mod's readme says its code sits in "two unused gaps between the game's functions", so that fits. Four bytes earlier, each record would overwrite that ret.
  • The mod's own branch agrees. The word at 0x7F7574 is a b whose target, counted from 0x7F7574, is 0x1EB4324: the second gap. Counted from 0x7F7570 it would point four bytes short of it.
  • The convention. The emulators and Atmosphere patch a copy of the module laid out as a 0x100 header followed by the image as loaded, not the NSO file: an offset minus 0x100 is an offset in memory, wherever .text sits in the file. That main's .text starts at 0x104 in the file should not enter into it. The .ips mod this pull request was tested with is built on the same rule and plays on Linux and Windows.

So on this branch a mapper that follows the file's segment offsets would move Auto Jump's records four bytes early. Could you say which code you saw the crash on: this branch's head (58b1e64d), or the patcher of #367? And where the change you made lives, so I can look at it? If the crash when jumping is on this branch as it is, I would like the build's exefs_patches.txt line for the mod and the log around the crash: then it is something other than the offset, and worth finding.

@Serfrost thank you for trying a real `.pchtxt` mod: that case had been tried by nobody. I could not reproduce the fault on this branch, though, and I think 0x100 is right here. What I checked, offline (not in the game): - **The file.** Auto Jump's patch has `@flag offset_shift 0x100` and three records, at 007F7574, 01EB4224 and 01EB4324. `parse_pchtxt` adds the shift and takes the header (`NSO_HEADER`, 0x100) off again, so the records land in main's image at exactly those offsets. - **Where they land.** In the stock game, 0x1EB4224 and 0x1EB4324 are each the first byte of a run of zero padding between two functions, right after the `ret` that ends the function before (28 bytes of padding each; the records are 20 and 28 bytes). The mod's readme says its code sits in "two unused gaps between the game's functions", so that fits. Four bytes earlier, each record would overwrite that `ret`. - **The mod's own branch agrees.** The word at 0x7F7574 is a `b` whose target, counted from 0x7F7574, is 0x1EB4324: the second gap. Counted from 0x7F7570 it would point four bytes short of it. - **The convention.** The emulators and Atmosphere patch a copy of the module laid out as a 0x100 header followed by the image *as loaded*, not the NSO file: an offset minus 0x100 is an offset in memory, wherever `.text` sits in the file. That main's `.text` starts at 0x104 in the file should not enter into it. The `.ips` mod this pull request was tested with is built on the same rule and plays on Linux and Windows. So on this branch a mapper that follows the file's segment offsets would move Auto Jump's records four bytes early. Could you say which code you saw the crash on: this branch's head (58b1e64d), or the patcher of #367? And where the change you made lives, so I can look at it? If the crash when jumping is on this branch as it is, I would like the build's `exefs_patches.txt` line for the mod and the log around the crash: then it is something other than the offset, and worth finding.
snaildev99 force-pushed feat/mods-exefs-patches from 58b1e64dfd
Some checks failed
PR description / Description follows the template (pull_request_target) Has been cancelled
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
to 0f3a47dd9e
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 4s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
2026-11-14 16:28:34 +00:00
Compare
Author
Contributor

Rebased onto main @ 9c9d69ed, since it no longer merged: head 0f3a47dd. Three conflicts this time, and one of them changes a number reviewers should know:

  • API version 23. Main took version 22 for the text fields (take_text) while this was open, so the Mods tab's five functions now follow take_text in MkwVkHost as version 23: vk_api.h, the table in host/rx_backend.c (in the struct's order), can_mods() and tests/test_mods_menu.py. Nothing else about them changed.
  • vk_menu.cpp, the rows' keys. Main's new text field line (field_row) joins the block that this branch moved inside the Mods tab's else; that block is otherwise as it was.
  • tests/test_menu_text_field.py (main's new test). Its harness cuts restart_values out of the menu, and that function also fills the mods' slot here, so the harness declares the slot (none), can_mods and the host's mods_changed beside its pad_raw. One small commit of its own, the last on the branch.

tests/run_all.py at this head on Windows: 535 passed, 34 skipped, 5 failed, the same five as main @ 9c9d69ed on this PC (named in the description). vk_menu.cpp compiles alone with the build's flags. Not built or played after this rebase or the one before: the runs in the game, mooki's on Linux and monsterlag's on Windows are of 5142c2d7. The description has the new numbers.

Rebased onto main @ 9c9d69ed, since it no longer merged: head 0f3a47dd. Three conflicts this time, and one of them changes a number reviewers should know: - **API version 23.** Main took version 22 for the text fields (`take_text`) while this was open, so the Mods tab's five functions now follow `take_text` in `MkwVkHost` as version 23: `vk_api.h`, the table in `host/rx_backend.c` (in the struct's order), `can_mods()` and `tests/test_mods_menu.py`. Nothing else about them changed. - **`vk_menu.cpp`, the rows' keys.** Main's new text field line (`field_row`) joins the block that this branch moved inside the Mods tab's `else`; that block is otherwise as it was. - **`tests/test_menu_text_field.py` (main's new test).** Its harness cuts `restart_values` out of the menu, and that function also fills the mods' slot here, so the harness declares the slot (none), `can_mods` and the host's `mods_changed` beside its `pad_raw`. One small commit of its own, the last on the branch. `tests/run_all.py` at this head on Windows: 535 passed, 34 skipped, 5 failed, the same five as main @ 9c9d69ed on this PC (named in the description). `vk_menu.cpp` compiles alone with the build's flags. Not built or played after this rebase or the one before: the runs in the game, mooki's on Linux and monsterlag's on Windows are of 5142c2d7. The description has the new numbers.
First-time contributor

@snaildev99

@Serfrost thank you for trying a real .pchtxt mod ...

Thanks for checking the offsets and explaining where the patch records land. The 0x104 change was made by the coding assistant during the investigation; the mod’s original 0x100 flag should not have been changed. The assistant mistook the four-byte adjustment as something to put in the mod and treated a successful test as confirmation despite the guardrail meant to never edit mods. Your analysis indicates 0x100 is correct.

The packaged mod’s flag has been restored to 0x100; it now behaves as intended when used on this PR.

@snaildev99 > @Serfrost thank you for trying a real `.pchtxt` mod ... Thanks for checking the offsets and explaining where the patch records land. The 0x104 change was made by the coding assistant during the investigation; the mod’s original 0x100 flag should not have been changed. The assistant mistook the four-byte adjustment as something to put in the mod and treated a successful test as confirmation despite the guardrail meant to never edit mods. Your analysis indicates 0x100 is correct. The packaged mod’s flag has been restored to 0x100; it now behaves as intended when used on this PR.
Author
Contributor

Rebased onto main @ 82886e89, since it no longer merged: head 56fd6f8d. Two conflicts and one new commit:

  • README.md, Mods. Main's pointer to docs/mods.md (#93) is kept after this branch's ExeFS and cheats paragraphs.
  • vk_menu.cpp, the rows' keys. Main's new laptop row line joins the block this branch moved inside the Mods tab's else; the script that resolved it checks that every line the branch had there is still there.
  • docs/mods.md and its example (new commit, the last). #93 documents the loader as it is on main: ExeFS left out, one mod's size table, no Mods tab. This branch changes those three, so the document now says so: an exefs folder's patches belong to the build and are only reported at a start, the size table is merged at a start (one mod's table when that cannot be made), and longshot_mods.txt can change the order and the switches. Without that file the order is exactly the documented one (mod_cmp is untouched), so the example's expected files are unchanged. The one expectation I had to change is the log line: main logs 03-code-only: exefs patches are left out, this branch lists the mod as 03-code-only: 0 RomFS files (0 replace the game's, 0 added), ExeFS patches.

@mooki the example's checks run on Linux only, and I have no Linux here: I changed that one expected line by reading hos_mods_init, not by running it. Could you (or the Linux job) run python -I -B tests/test_mods_size_table.py at this head? If the wording of the document is not what you meant by the contract, say so and I will change it.

tests/run_all.py at this head on Windows: 534 passed, 34 skipped, 13 failed, the same thirteen as main @ 82886e89 on this PC in the same run (530 passed there). vk_menu.cpp compiles alone with the build's flags. Not built or played after this rebase.

Rebased onto main @ 82886e89, since it no longer merged: head 56fd6f8d. Two conflicts and one new commit: - **`README.md`, Mods.** Main's pointer to `docs/mods.md` (#93) is kept after this branch's ExeFS and cheats paragraphs. - **`vk_menu.cpp`, the rows' keys.** Main's new laptop row line joins the block this branch moved inside the Mods tab's `else`; the script that resolved it checks that every line the branch had there is still there. - **`docs/mods.md` and its example (new commit, the last).** #93 documents the loader as it is on main: ExeFS left out, one mod's size table, no Mods tab. This branch changes those three, so the document now says so: an `exefs` folder's patches belong to the build and are only reported at a start, the size table is merged at a start (one mod's table when that cannot be made), and `longshot_mods.txt` can change the order and the switches. **Without that file the order is exactly the documented one** (`mod_cmp` is untouched), so the example's expected files are unchanged. The one expectation I had to change is the log line: main logs `03-code-only: exefs patches are left out`, this branch lists the mod as `03-code-only: 0 RomFS files (0 replace the game's, 0 added), ExeFS patches`. @mooki the example's checks run on Linux only, and I have no Linux here: I changed that one expected line by reading `hos_mods_init`, not by running it. Could you (or the Linux job) run `python -I -B tests/test_mods_size_table.py` at this head? If the wording of the document is not what you meant by the contract, say so and I will change it. `tests/run_all.py` at this head on Windows: 534 passed, 34 skipped, 13 failed, the same thirteen as main @ 82886e89 on this PC in the same run (530 passed there). `vk_menu.cpp` compiles alone with the build's flags. Not built or played after this rebase.
Author
Contributor

@Serfrost thank you for following it up and for saying plainly what happened; that is useful to know for anyone else who tests this with an assistant.

So Auto Jump, unchanged, works on this pull request: that is the first real .pchtxt mod anybody has run on it. May I put it in the description's table of what has been tried? For that I would need the head you built (the branch has been rebased since; longshot_log.txt's first line names the source), your system (Windows or Linux, GPU), and what you saw in the game (running off a ledge jumps, I take it). The build's exefs_patches.txt line for the mod would be welcome too.

@Serfrost thank you for following it up and for saying plainly what happened; that is useful to know for anyone else who tests this with an assistant. So Auto Jump, unchanged, works on this pull request: that is the first real `.pchtxt` mod anybody has run on it. May I put it in the description's table of what has been tried? For that I would need the head you built (the branch has been rebased since; `longshot_log.txt`'s first line names the source), your system (Windows or Linux, GPU), and what you saw in the game (running off a ledge jumps, I take it). The build's `exefs_patches.txt` line for the mod would be welcome too.
snaildev99 force-pushed feat/mods-exefs-patches from 0f3a47dd9e
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 4s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
to 56fd6f8d82
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 9s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
2026-11-14 22:13:56 +00:00
Compare
Author
Contributor

This head (56fd6f8d, after the four rebases, API version 23) is now built and played on Windows, by hand, with a larger code mod than before.

  • The mod: v0.2.0 of the one this pull request has been tested with (https://github.com/snaildev99/OoT-Character-Select/releases/tag/v0.2.0): one folder, 5804 RomFS files, a size table and one .ips of seven code patches (7353 bytes of main changed; v0.1.3's was 4935).
  • The build: nx2/make_game.py into the test copy, 10 min 25 s (compiled in 7 min 55 s, 0 failed). The patch bytes changed, so the lift ran again: this is the time for a changed code mod on this PC (Ryzen, 24 threads), where a build with unchanged patches took about two minutes.
  • The start: the mod is listed with its ExeFS patch built into the game; the size table merged in 258 ms (1 table of 1222 entries, 15 raised).
  • In the game (Windows 11, RTX 3090, a child save; the mod's author): the Mods tab shows the mod On with nothing waiting for a build; the mod's page, a pick and the swap in the field, a character's own voice, a dress as cloth, the mod's new Customize mode (hooks that run every frame) and one of its new characters. He reports all of it working.

The description's Tested list has it as its second item. Still not run by anybody: the Linux part of tests/test_mods_size_table.py at this head (the example of #93; my question to @mooki above), and a cheat file. A .pchtxt mod is Serfrost's report above.

This head (56fd6f8d, after the four rebases, API version 23) is now built and played on Windows, by hand, with a larger code mod than before. - **The mod:** v0.2.0 of the one this pull request has been tested with (https://github.com/snaildev99/OoT-Character-Select/releases/tag/v0.2.0): one folder, 5804 RomFS files, a size table and one `.ips` of seven code patches (7353 bytes of main changed; v0.1.3's was 4935). - **The build:** `nx2/make_game.py` into the test copy, 10 min 25 s (compiled in 7 min 55 s, 0 failed). The patch bytes changed, so the lift ran again: this is the time for a changed code mod on this PC (Ryzen, 24 threads), where a build with unchanged patches took about two minutes. - **The start:** the mod is listed with its ExeFS patch built into the game; the size table merged in 258 ms (1 table of 1222 entries, 15 raised). - **In the game** (Windows 11, RTX 3090, a child save; the mod's author): the Mods tab shows the mod On with nothing waiting for a build; the mod's page, a pick and the swap in the field, a character's own voice, a dress as cloth, the mod's new Customize mode (hooks that run every frame) and one of its new characters. He reports all of it working. The description's Tested list has it as its second item. Still not run by anybody: the Linux part of `tests/test_mods_size_table.py` at this head (the example of #93; my question to @mooki above), and a cheat file. A `.pchtxt` mod is Serfrost's report above.
First-time contributor

@snaildev99 wrote in #134 (comment):

@Serfrost thank you for following it up and for saying plainly what happened..

I've refreshed the repository with current commits and pending PRs quite a few times now, including the latest version of this PR. I have not tested other exefs mods, as most require subsdk9 or other support to actually work in the game. Auto Jump works as intended. Sprint Speed +20% as listed in the log also works as intended, both mods being pchtxt.

[hos host] longshot Nightly 147 - Custom (source 5b33f6ba)
[hos host] run_id: 65bdcd3fe85074c46ee9c482f9383557
[hos host] oe: output resolution 2560x1440
[hos host] boot: widescreen: a 1.7778 screen is 16:9: the game's own picture
[hos host] boot: image 96 MB at 0x80000000, 9809 MB of memory for the game
[hos host] boot: 32678 MB of memory, 32678 MB of swap
[hos host] mods: Auto Jump: 0 RomFS files (0 replace the game's, 0 added), ExeFS patches
[hos host] mods: Gerudo Mirror Shield: 7 RomFS files (7 replace the game's, 0 added)
[hos host] mods: JP Navi Callouts: 71 RomFS files (71 replace the game's, 0 added)
[hos host] mods: Old Hookshot: 2 RomFS files (2 replace the game's, 0 added)
[hos host] mods: Play as Zelda: off (the Mods tab)
[hos host] mods: Sprint Speed +20%: 1 RomFS files (0 replace the game's, 1 added), ExeFS patches
[hos host] mods: Auto Jump: ExeFS auto-jump.pchtxt: built into the game
[hos host] mods: Sprint Speed +20%: ExeFS movement-speed.pchtxt: built into the game
[hos host] mods: resource size table: 2 tables of mods merged (1 entries), 1 entries raised for mod files (1 added files estimated), served from modcache, 34 ms
[hos host] mods: 6 mods from C:\Users\Sean\Desktop\LongshotBuilderOffline\game\mods: 91 RomFS entries

Device is Windows 11, 32GB DDR4, RTX4080 Driver ver. 595.79

No further problems to report other than hoping we can get an implementation for subsdk modding with exefs.

@snaildev99 wrote in https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/134#issuecomment-20917: > @Serfrost thank you for following it up and for saying plainly what happened.. I've refreshed the repository with current commits and pending PRs quite a few times now, including the latest version of this PR. I have not tested other exefs mods, [as most require subsdk9 or other support](https://gamebanana.com/mods/737865) to actually work in the game. Auto Jump works as intended. Sprint Speed +20% as listed in the log also works as intended, both mods being pchtxt. ``` [hos host] longshot Nightly 147 - Custom (source 5b33f6ba) [hos host] run_id: 65bdcd3fe85074c46ee9c482f9383557 [hos host] oe: output resolution 2560x1440 [hos host] boot: widescreen: a 1.7778 screen is 16:9: the game's own picture [hos host] boot: image 96 MB at 0x80000000, 9809 MB of memory for the game [hos host] boot: 32678 MB of memory, 32678 MB of swap [hos host] mods: Auto Jump: 0 RomFS files (0 replace the game's, 0 added), ExeFS patches [hos host] mods: Gerudo Mirror Shield: 7 RomFS files (7 replace the game's, 0 added) [hos host] mods: JP Navi Callouts: 71 RomFS files (71 replace the game's, 0 added) [hos host] mods: Old Hookshot: 2 RomFS files (2 replace the game's, 0 added) [hos host] mods: Play as Zelda: off (the Mods tab) [hos host] mods: Sprint Speed +20%: 1 RomFS files (0 replace the game's, 1 added), ExeFS patches [hos host] mods: Auto Jump: ExeFS auto-jump.pchtxt: built into the game [hos host] mods: Sprint Speed +20%: ExeFS movement-speed.pchtxt: built into the game [hos host] mods: resource size table: 2 tables of mods merged (1 entries), 1 entries raised for mod files (1 added files estimated), served from modcache, 34 ms [hos host] mods: 6 mods from C:\Users\Sean\Desktop\LongshotBuilderOffline\game\mods: 91 RomFS entries ``` Device is Windows 11, 32GB DDR4, RTX4080 Driver ver. 595.79 No further problems to report other than hoping we can get an implementation for subsdk modding with exefs.
Author
Contributor

@Serfrost thank you, that is exactly what the table needed. Two .pchtxt mods (Auto Jump and Sprint Speed +20%) on Windows 11 with an RTX 4080 are in the description now: a new item in Testing, the .pchtxt row of the table, and the Flags row. I wrote it as your build, a nightly with other pending pull requests merged in (the log's source 5b33f6ba is not a commit of this branch); say so if that reads wrong.

On mods that need subsdk9: this pull request patches main and adds no module, so those are outside it, and the description now says so under Not tested. It is a fair next step once this is in: a module of a mod's own would have to be placed, linked and lifted with the game's at build time, which is a change to the image builder and not to the patcher. I would rather not grow this pull request by it; an issue with one such mod named as the test case would be a good place to start.

@Serfrost thank you, that is exactly what the table needed. Two `.pchtxt` mods (Auto Jump and Sprint Speed +20%) on Windows 11 with an RTX 4080 are in the description now: a new item in Testing, the `.pchtxt` row of the table, and the Flags row. I wrote it as your build, a nightly with other pending pull requests merged in (the log's source 5b33f6ba is not a commit of this branch); say so if that reads wrong. On mods that need `subsdk9`: this pull request patches `main` and adds no module, so those are outside it, and the description now says so under Not tested. It is a fair next step once this is in: a module of a mod's own would have to be placed, linked and lifted with the game's at build time, which is a change to the image builder and not to the patcher. I would rather not grow this pull request by it; an issue with one such mod named as the test case would be a good place to start.
Author
Contributor

Two more rows of the "what has been tried" table are filled, both by hand on Windows 11 (RTX 3090) with the build of this head (56fd6f8d), no rebuild:

  • A cheat file works. One cheat written for the test, cheats/E0F3CCFAF50065AE.txt, six lines: a condition on a pointer in main's data (opcode 1), two loads along it (opcode 5), an add (opcode 7), a 4-byte store (opcode 6), the block's end. Tried on an emulator first, where it sets the rupee count. Here: cheats: E0F3CCFAF50065AE.txt in 1 mods: 1 cheats, 0 on; 1 data, 0 master, 0 headings, 0 not supported, the Mods tab lists it under its mod, Off; switched On, the count was the cheat's value as soon as the menu closed, without a start. Not tried: a cheat that writes code, {Master} codes.
  • A mod that is only a code module (a published mod of one file, exefs/subsdk7): listed as 0 RomFS files (0 replace the game's, 0 added), ExeFS patches, no note on the Mods tab, nothing of it happens in the game, no crash. That is what the code says should happen (only .ips and .pchtxt are read), but neither the log nor the tab tells the player why the mod does nothing.

On the second: about half of the code mods I have seen for this game ship such a module, some with a small .ips that only jumps into it. I read two of those .ips files: their stubs load a pointer the module would have filled in, find it empty and run the game's own instruction, so built in here they would do nothing rather than crash (read, not built). I would like to send a small follow-up once this is merged: a mod with subsdk* or main in its exefs folder gets a note on the Mods tab and a log line saying code modules are not supported. I have left it out of this pull request so the head that four people tested stays as it is; say so if you would rather have it here.

The description has both under Testing, the table, the Flags row for cheats and Reviewer notes.

Two more rows of the "what has been tried" table are filled, both by hand on Windows 11 (RTX 3090) with the build of this head (56fd6f8d), no rebuild: - **A cheat file works.** One cheat written for the test, `cheats/E0F3CCFAF50065AE.txt`, six lines: a condition on a pointer in main's data (opcode 1), two loads along it (opcode 5), an add (opcode 7), a 4-byte store (opcode 6), the block's end. Tried on an emulator first, where it sets the rupee count. Here: `cheats: E0F3CCFAF50065AE.txt in 1 mods: 1 cheats, 0 on; 1 data, 0 master, 0 headings, 0 not supported`, the Mods tab lists it under its mod, Off; switched On, the count was the cheat's value as soon as the menu closed, without a start. Not tried: a cheat that writes code, `{Master}` codes. - **A mod that is only a code module** (a published mod of one file, `exefs/subsdk7`): listed as `0 RomFS files (0 replace the game's, 0 added), ExeFS patches`, no note on the Mods tab, nothing of it happens in the game, no crash. That is what the code says should happen (only `.ips` and `.pchtxt` are read), but neither the log nor the tab tells the player why the mod does nothing. On the second: about half of the code mods I have seen for this game ship such a module, some with a small `.ips` that only jumps into it. I read two of those `.ips` files: their stubs load a pointer the module would have filled in, find it empty and run the game's own instruction, so built in here they would do nothing rather than crash (read, not built). I would like to send a small follow-up once this is merged: a mod with `subsdk*` or `main` in its `exefs` folder gets a note on the Mods tab and a log line saying code modules are not supported. I have left it out of this pull request so the head that four people tested stays as it is; say so if you would rather have it here. The description has both under Testing, the table, the Flags row for cheats and Reviewer notes.
Owner

This pull request now conflicts with main: gpu/vk/vk_api.h and host/rx_backend.c, touched on main by #729, #577, #95, #593; both define MKWVK_API_VERSION 23 now, so this one needs 24. Could you rebase it onto the current main? Thank you.

This pull request now conflicts with `main`: gpu/vk/vk_api.h and host/rx_backend.c, touched on `main` by https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/729, https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/577, https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/95, https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/593; both define `MKWVK_API_VERSION` 23 now, so this one needs 24. Could you rebase it onto the current `main`? Thank you.
snaildev99 force-pushed feat/mods-exefs-patches from 56fd6f8d82
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 9s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
to 4f8b975d6f
Some checks are pending
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
Guide screenshots / Guide images and local UI source review (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 3s
2026-11-15 18:48:44 +00:00
Compare
Author
Contributor

Rebased onto main @ 6a013efd, since it no longer merged: head 4f8b975d.

  • API version 24. Main took version 23 for the settings file's path (options_path) since the last rebase, so the Mods tab's five functions now follow it in MkwVkHost as version 24: vk_api.h, the table in host/rx_backend.c (in the struct's order), can_mods() and tests/test_mods_menu.py. These were the only two conflicts, and nothing else in the seven commits changed (git range-diff shows the first commit's version lines and the other six as equal).
  • Checks. The mod checks pass (the Mods tab, ExeFS patches, the cheat VM, the text field harness, the restart rows), gpu/vk/vk_menu.cpp compiles alone with the build's flags, and tests/run_all.py on Windows gives 516 passed, 21 failed, 74 skipped, the same twenty-one as main @ 6a013efd in the same run (512 passed there); the description names them.
  • Not built or played at this head. The head that was built and played, with the cheat and the code module rows, is 56fd6f8d.

A note for whoever merges second: #799 takes version 24 as well, so one of the two moves to 25.

Still open: a Linux run of python -I -B tests/test_mods_size_table.py (now at 4f8b975d), and a second look at the review from before the answers.

Rebased onto main @ 6a013efd, since it no longer merged: head 4f8b975d. - **API version 24.** Main took version 23 for the settings file's path (`options_path`) since the last rebase, so the Mods tab's five functions now follow it in `MkwVkHost` as version 24: `vk_api.h`, the table in `host/rx_backend.c` (in the struct's order), `can_mods()` and `tests/test_mods_menu.py`. These were the only two conflicts, and nothing else in the seven commits changed (`git range-diff` shows the first commit's version lines and the other six as equal). - **Checks.** The mod checks pass (the Mods tab, ExeFS patches, the cheat VM, the text field harness, the restart rows), `gpu/vk/vk_menu.cpp` compiles alone with the build's flags, and `tests/run_all.py` on Windows gives 516 passed, 21 failed, 74 skipped, the same twenty-one as main @ 6a013efd in the same run (512 passed there); the description names them. - **Not built or played at this head.** The head that was built and played, with the cheat and the code module rows, is 56fd6f8d. A note for whoever merges second: #799 takes version 24 as well, so one of the two moves to 25. Still open: a Linux run of `python -I -B tests/test_mods_size_table.py` (now at 4f8b975d), and a second look at the review from before the answers.
Some checks are pending
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
Guide screenshots / Guide images and local UI source review (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 3s
This pull request doesn't have enough approvals yet. 0 of 1 approvals granted.
Some workflows are waiting to be reviewed.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'feat/mods-exefs-patches:snaildev99-feat/mods-exefs-patches'
git switch 'snaildev99-feat/mods-exefs-patches'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
5 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!134
No description provided.