Skip to content

fix(mods): merge a mod's resource size table with the button prompts' instead of switching the icons off - #2270

Merged
hckrman101 merged 1 commit into
NotRexed:mainfrom
mooki000:mods-size-table-merge
Nov 11, 2026
Merged

hckrman101 merged 1 commit into
NotRexed:mainfrom
mooki000:mods-size-table-merge

Conversation

@mooki000

@mooki000 mooki000 commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

While a mod that ships its own resource size table is loaded, the button icons in every text go back to the Switch's,
whatever controller and Button prompts setting the player has. Every randomizer seed made by
Clawshot ships one, and Clawshot lists this as a known issue ("While a seed is
loaded, button icons in text show Switch buttons instead of your controller's").

Fixes #2329.

Why it happens

The game reads one size table. The button prompts' fonts are larger than the game's, so Hookshot serves the game's table
with the font's entry raised (prompts/rsizetable.zs). A mod that makes files bigger has to ship the table with its
own entries raised (#1325), and the mod's file wins, as any mod file does. host/prompts.c then saw a mod table and
turned the prompts' font off (prompts: a mod has the resource size table: the game's own icons in texts), because the
larger font would not fit the mod's entry. The textures and layouts stayed the controller's, so the pause menu showed
the controller's pictures and Switch letters side by side.

What changed

  • host/prompts.c, host/mods.c: when the size table the game opens is a mod's file, it is served with the prompts'
    entries merged into it: each file gets the larger of the two entries, entries only one side has are kept, sorted as
    the game's (merge_restbl). The result is written once per run as raw zstd blocks to prompts/rsizetable.mod.zs, as
    the font already is, and the prompts' font is served as without a mod. Any other mod file is served as before.
  • Two mods with tables: the loader's rule stays, the table of the mod that sorts last is the base; only the prompts'
    entries are merged into it.
  • A table that cannot be merged (not the game's format, other name fields, entries out of order, a zstd frame that does
    not give its size) is served as it is, with the game's icons, as before; the log says why.
  • The log: mods: System/Resource/ResourceSizeTable.Product.111.Nin_Ounce_NVN2.rsizetable.zs <- <file> (with the button prompts' sizes merged in) and prompts: <file>: the button prompts' sizes merged into the mod's resource size table (1 entries raised or added).
  • nx2/build.py: zstd's decoder (third_party/zstd, already compiled into hookshot_vk) is compiled into the runtime
    too, from one zstd_jobs() both use. The readme's Mods section says what happens to a mod's table.

How it was tested

  • tests/test_mods_size_table.py (new): merge_restbl cut out of prompts.c on made-up tables against an independent
    model, and seven tables it must refuse; then, on Linux, mods.c and prompts.c as they are over real folders: no
    mod, a mod with a table, a mod without a table that grows a file, two mods with tables, a table that cannot be merged,
    no prompts table, a mod with its own font, and with zstandard a level-19 frame and one without its size. It fails
    on main.

  • python -I -B tests/run_all.py on Linux, before a rebase onto current main (the new test and the two tests this change edits were run again after it and pass): 361 passed, 10 skipped; the failures are the two that fail on main on
    this machine (test_updater_asset_digest.py: no .NET 8 packs offline; test_wayland_libdecor_hint.py: a clang 23
    warning) and test_bake_mimo.py, whose shader-recompiler compile failed with an empty error at load 134 and passes
    run alone (103 s).

  • In the game (RTX 4090, NVIDIA 615.78.08, Arch Linux, headless; Button prompts set to PlayStation 5, as a run
    without a controller needs; a Clawshot seed as a mod; a save in Kokiri Forest, the pause menu's Basic Controls page):

    • before (main): the DualSense picture next to Switch icons in the texts (ZL, minus, A, B, X, Y, "Select (A) Back
      (B)"), and prompts: a mod has the resource size table: the game's own icons in texts in the log;
    • after: PlayStation icons in every text (L2, the Create button, circle, cross, triangle, square), the same as the
      same build without the mod; the log says (with the button prompts' sizes merged in) after the table's line and
      prompts: ps5 icons, 16 buttons on this pad's controls. The seed's Kokiri Forest and Hyrule Field placement files
      were read from the mod and the forest loaded. The table the game wrote has all 19 entries the seed raised and the
      font's entry raised, and equals the merge computed separately in Python.

    Before and after: the Basic Controls page with a Clawshot seed loaded, Button prompts PlayStation 5

Not tested

Windows: host/prompts.c and host/mods.c compile for x86_64-w64-mingw32 with -Wall -Wextra and no warning, but
were not built into a Windows game or run there; the folder part of the test runs on Linux only (it uses host/posix's
file calls), the merge part on both. AMD and Intel: nothing here touches the GPU. Real controllers: the face was chosen
with the Button prompts setting, as a run without a controller needs.

Found while helping with Clawshot's known issue; I'll tell its maintainer about this pull request.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com

… instead of switching the icons off

A RomFS mod that grows game files ships its own resource size table (every Clawshot
randomizer seed does). The loader served it and host/prompts.c then switched the
prompts' font off, since the faces' fonts are larger than the game's entry: with a
seed loaded, texts showed the Switch's button icons instead of the controller's.

Now the table that wins (the last-sorted mod's, as for any file) is served with the
prompts' entries merged into it: each file gets the larger of the two entries, entries
of one side are kept, sorted as the game's. The merged table is written as raw zstd
blocks to prompts/rsizetable.mod.zs. A table it cannot merge (not a RESTBL, other name
fields, out of order, a zstd frame without its size) is served as it is and the
icons stay the game's, as before. host/prompts.c reads the tables with zstd's decoder
(third_party/zstd), now compiled into the runtime too.

tests/test_mods_size_table.py: the merge on made-up tables, and mods.c with
prompts.c over folders (no mod, a mod with a table, a mod that grows a file without
one, two mods, unmergeable tables, no prompts' table, a mod with its own font).

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@hckrman101 hckrman101 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. I read the merge and the precedence carefully.

Precedence: a mod's table still wins as a file (hos_mods_romfs keeps e->host), and the prompts' entries are only raised into it: max of the two sizes per file, entries only one side has are kept, sorted as the game's. The merge refuses anything that is not the game's format (header, name-field size, byte-length check, out-of-order parts, a name field that differs), and then the mod's table is served as it is with the game's icons, as before. The mod's version and name field are kept. Both parts are bounded (name field <= 1024, frame <= 64 MB, output buffer sized for the worst case). The ordering is right for both the CRC part and the name part. The non-table keys return 0 when a mod has the file, so a mod's own font/textures/layouts still win. Merged once per run, and the state machine with from guards the second-table case.

Nothing game-derived is added: the new test builds made-up tables, there is no binary in the diff, and the zstd change is the existing vendored decoder reused (PROVENANCE updated).

Two small nits, not blocking:

  • build() picks the mod table with hos_mods_find, which returns the first match in hash order, while hos_mods_romfs passes the path actually opened. If a mod ever carried two files ending in .rsizetable.zs (another product number besides 111), the merge could be made from the one the game never opens, and the one it opens is then served unmerged next to the larger font, which the game drops. Unlikely, but picking the key hos_mods_romfs gets for the real table name would remove it.
  • Only the Conventional Commit title check has run on this head so far; the rest of CI did not show up when I looked, so worth a glance that the full suite runs before merging. The author reports the Linux run and the in-game before/after screenshots, and says Windows was compiled for mingw but not run.

No prior comments or reviews on the PR to answer.

@hckrman101
hckrman101 merged commit 9a3eb90 into NotRexed:main Nov 11, 2026
2 checks passed
LNDF added a commit to LNDF/Hookshot that referenced this pull request Nov 11, 2026
Conflicts resolved:
- MKWVK_API_VERSION 18: upstream's version 17 is prompts_live (NotRexed#2366); the
  data folder's MkwVkHost.data_dir follows it as version 18.
- host/hos_update.c: "Skip this version" stays in the data folder; upstream's
  call order (NotRexed#2283, the check after the saves' place is known) is kept, with
  hos_data_init having chosen that place first. tests/test_linux_update_skip.py
  builds the start with hos_data.c instead of the removed saves_dir.
- host/prompts.c: upstream's live prompts and mod size tables (NotRexed#2366, NotRexed#2270),
  with the generated files (and the new rsizetable.mod.zs) in cache/prompts;
  made_dir makes cache/ too.
- builder/linux/play-diagnostics.sh: upstream's socket stderr (NotRexed#2279), with
  hookshot_startup.log in logs/.
- The program cache moved to vk_progcache.cpp (NotRexed#2323): its path is cache/.
- hookshot_driver_faults.txt (NotRexed#1823) goes to cache/ and is moved there.
- env_on/env_value_off (NotRexed#2042), env_override (vk_menu.cpp), the prepare
  watchdog (vk_main.cpp), audio and docs merged with the data folder paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DUDUYG added a commit to DUDUYG/Hookshot that referenced this pull request Nov 11, 2026
…cache.cpp (NotRexed#2323), zstd's jobs to zstd_jobs (NotRexed#2270)

- vk_progcache.cpp: the cache read keeps each stage's code (stage_code) and a refused record leaves nothing to destroy;
  program_cache_borrow lets go of the layouts alone. vk_progcache.h no longer declares make_module.
- vk_shader.cpp: seed_variant_of's destroys of a failed or already made variant's modules (NotRexed#2358) are gone with the modules.
- nx2/build.py: zstd_jobs(encoder=True) builds the encoder for the Vulkan back end, without ZSTD_MULTITHREAD; zstd_headers()
  is the users' dependency (vk_stage_code.cpp). The runtime's decoder-only zstd is unchanged.
- tests: test_seed_variant_cleanup (NotRexed#2358) checks what the cache keeps instead of module lifetimes; test_recorder_sync's
  BOUNDARY_OK has no vk_shader.cpp entry left and vk_progcache.cpp's names its layouts; test_shader_stamp_cache knows
  zstd_headers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mooki000

Copy link
Copy Markdown
Contributor Author

@hckrman101 thanks for the review here. #2592 now selects the mod table by the game's own RomFS table name instead of hash order, and made-up two-version tables prove both font-first and table-first requests with GCC and Clang (both fail on main). Your other point was CI visibility: the follow-up records the local suite separately and still needs the full GitHub regression checks approved and green, not just the title check.

mooki000 added a commit to mooki000/Hookshot that referenced this pull request Nov 12, 2026
Follow hckrman101's review of NotRexed#2270. A mod can ship several versions'
size tables; do not seed the prompts' one-time merge from the first
match in hash order. Use the game's RomFS table name, selected as the
prompt-assets builder does, and look up the winning mod file exactly.
Keep the merge and mod precedence unchanged.

Made-up two-version tables cover both font-first and table-first opens.
The branch passes with GCC and Clang; both orders fail on main. No game
file is used by these fixtures.

Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra <noreply@openai.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A mod with its own resource size table (every Clawshot seed) switches the button icons in text back to the Switch's

2 participants