feat(fs): support an optional indexed RomFS image #2600

Open
Arkex-ai wants to merge 9 commits from Arkex-ai/feat/optional-packed-romfs into main
Arkex-ai commented 2026-11-12 01:03:21 +00:00 (Migrated from github.com)

Summary

A folder-backed RomFS needs a separate host file for every asset. This adds an optional read-only indexed image while preserving folder installs, mods and writable mounts. Related to #583, tracked in #1007; no loading-speed or memory improvement is claimed.

Technical changes

  • Add a streaming, copy-only packer with atomic publication and source-change checks, plus a bounded immutable index and positional native reads.
  • Preserve mod/prompt/overlay precedence and guest-range/directory locking. A null directory base now explicitly lists mod-only entries without probing a fictitious host folder.
  • Select the virtual native-font callback only for packed mode, before renderer initialization. Folder installs retain the original direct-file Reader and its size behavior; the 64 MiB virtual-input limit applies only to packed assets. Both main and translated-menu font callers support the image.
  • Use renderer API 22: retain main's API20 screen-size provider and API21 program-created callback, then append the RomFS reader to the host interface. The source regression executes the actual initializer, loader and program-created forwarder; it also rejects an older renderer ABI. Later interface changes must rebase/bump again. Builder/launcher auto-selection remains outside this opt-in CLI proposal, pending the maintainer decision on #1007.
  • Discover the base resource-table name in the selected image for prompt/mod merging, preserving main's first-by-name rule without consulting a stale loose root.

Validation

Current head 3bdde84f5c4dd742fdcee1310fa3cd6a85e1c240, on main 4cf785a0, with a clean merge-tree audit. 14 focused checks pass per OS. Three source mutations per OS are rejected at runtime: lost screen callback (exit4), lost program-created forwarding (exit5), accepted older renderer ABI (exit6). Packed table discovery also retains its old-caller stale-root failure control. New hosted CI is starting.

Matching native/game validation is pending, so this PR remains draft. The Windows attempt at 0624b8cc stopped before linking and is not accepted as a build or runtime result. Earlier native/game evidence below remains pinned to 329b2a93/72f107bf.

Earlier full source suites at bd12802f (base 3abfa0a0): Windows 473 passed/5 failed/36 skipped; Linux 488 passed/2 failed/24 skipped. Failures are retained; these are not full-green current-head suites. The old opening-ABI assertion is corrected upstream and passes here. Main's README failure is covered by #2857; Windows CRT fixture coordination is on #2767. Fresh full checks are still required.

Earlier-head CI (72f107bf) Result / run
Linux AppImage success
Game-free source regressions success
Windows builder success
PR title success
  • 12 focused checks per OS pass, covering actual filesystem/loader, packer, mounts/startup, mods, native fonts, UTF-8 and links. Two causal controls per OS fail on their intended runtime assertion: old folder-font selection and host probes with a null mod base.
  • Earlier 329b2a93 matching Windows native build passes (184.4 s) and prompts are regenerated (123.8 s). Verified guest generation retains 23c50087 provenance: existing generator modules are unchanged; only the standalone packer is added.
  • Folder and image-only runs load the copied Kokiri checkpoint, log FZLTHB merged into the zh-Hans font atlas and exit0; the source checkpoint is preserved. Image mode has no loose RomFS root. Captures show normal gameplay, not native-menu/per-glyph visual coverage. Individual runs.
  • Full source suites at 329b2a93: Windows465/3/35, with all three failures reproduced on exact main; Linux478/1/24, failing the unchanged control fixture's newest-save ordering assertion. Three isolated repetitions each on exact main and candidate pass; the full-suite failure remains recorded. No all-green local-suite claim.

Earlier source bdefcade established complete private asset/guest-read parity (186,583 files and 598 directories); 836692b2 established packed translated-font acquisition. These are earlier-head results. Current Windows coverage is early Kokiri and font acquisition, not campaign, all scripts, Linux gameplay, physical ultrawide, loading speed or memory savings. Game inputs, generated images and private runtime material are excluded.

## Summary A folder-backed RomFS needs a separate host file for every asset. This adds an optional read-only indexed image while preserving folder installs, mods and writable mounts. Related to #583, tracked in #1007; no loading-speed or memory improvement is claimed. ## Technical changes - Add a streaming, copy-only packer with atomic publication and source-change checks, plus a bounded immutable index and positional native reads. - Preserve mod/prompt/overlay precedence and guest-range/directory locking. A null directory base now explicitly lists mod-only entries without probing a fictitious host folder. - Select the virtual native-font callback only for packed mode, before renderer initialization. Folder installs retain the original direct-file Reader and its size behavior; the 64 MiB virtual-input limit applies only to packed assets. Both main and translated-menu font callers support the image. - Use renderer API **22**: retain main's API20 screen-size provider and API21 program-created callback, then append the RomFS reader to the host interface. The source regression executes the actual initializer, loader and program-created forwarder; it also rejects an older renderer ABI. Later interface changes must rebase/bump again. Builder/launcher auto-selection remains outside this opt-in CLI proposal, pending the maintainer decision on #1007. - Discover the base resource-table name in the selected image for prompt/mod merging, preserving main's first-by-name rule without consulting a stale loose root. ## Validation Current head **`3bdde84f5c4dd742fdcee1310fa3cd6a85e1c240`**, on main **`4cf785a0`**, with a clean merge-tree audit. **14 focused checks pass per OS**. Three source mutations per OS are rejected at runtime: lost screen callback (exit4), lost program-created forwarding (exit5), accepted older renderer ABI (exit6). Packed table discovery also retains its old-caller stale-root failure control. New hosted CI is starting. Matching native/game validation is pending, so this PR remains **draft**. The Windows attempt at `0624b8cc` stopped before linking and is not accepted as a build or runtime result. Earlier native/game evidence below remains pinned to `329b2a93`/`72f107bf`. Earlier full source suites at `bd12802f` (base `3abfa0a0`): Windows **473 passed/5 failed/36 skipped**; Linux **488 passed/2 failed/24 skipped**. Failures are retained; these are not full-green current-head suites. The old opening-ABI assertion is corrected upstream and passes here. Main's README failure is covered by #2857; Windows CRT fixture coordination is on #2767. Fresh full checks are still required. | Earlier-head CI (`72f107bf`) | Result / run | |---|---| | Linux AppImage | [success](https://github.com/NotRexed/Hookshot/actions/runs/43255902011) | | Game-free source regressions | [success](https://github.com/NotRexed/Hookshot/actions/runs/43255902830) | | Windows builder | [success](https://github.com/NotRexed/Hookshot/actions/runs/43255901629) | | PR title | [success](https://github.com/NotRexed/Hookshot/actions/runs/43255893112) | - **12 focused checks per OS pass**, covering actual filesystem/loader, packer, mounts/startup, mods, native fonts, UTF-8 and links. Two causal controls per OS fail on their intended runtime assertion: old folder-font selection and host probes with a null mod base. - Earlier `329b2a93` matching Windows native build passes (**184.4 s**) and prompts are regenerated (**123.8 s**). Verified guest generation retains `23c50087` provenance: existing generator modules are unchanged; only the standalone packer is added. - Folder and image-only runs load the copied Kokiri checkpoint, log **FZLTHB** merged into the zh-Hans font atlas and exit0; the source checkpoint is preserved. Image mode has no loose RomFS root. Captures show normal gameplay, **not native-menu/per-glyph visual coverage**. [Individual runs](https://github.com/Arkex-ai/Hookshot/blob/72f107bf3a5986553debf3415233e2e1cb129d13/docs/tested-configurations.md). - Full source suites at `329b2a93`: **Windows465/3/35**, with all three failures reproduced on exact main; **Linux478/1/24**, failing the unchanged control fixture's newest-save ordering assertion. Three isolated repetitions each on exact main and candidate pass; the full-suite failure remains recorded. No all-green local-suite claim. Earlier source `bdefcade` established complete private asset/guest-read parity (186,583 files and 598 directories); `836692b2` established packed translated-font acquisition. These are earlier-head results. Current Windows coverage is early Kokiri and font acquisition, not campaign, all scripts, Linux gameplay, physical ultrawide, loading speed or memory savings. Game inputs, generated images and private runtime material are excluded.
Owner

@Joey-Magnus reviewed on GitHub · 2026-11-12 14:33 UTC · original

I merged this into current main (it merges cleanly) and ran the RomFS, game font, vcore_fs_open and fs tests on Linux. All 12 pass. The reader in host/romfs_pack.h holds up. Names are capped at 0x300 bytes and validated. Entries must be sorted and have a parent directory, and file data has to be contiguous and inside the file. read_at still checks the guest range before romfs_pack_read. Before it goes in:

  • MKWVK_API_VERSION 20 is also claimed by #2604, #2317, #2261 and #2566. Whichever lands second has to move to 21.
  • The description claims no loading or memory gain. Nothing passes --romfs-pack yet either (the Builder and launcher don't know about it), so this is a second filesystem path behind a command-line flag. Whether that's wanted is a maintainer call on #1007.
  • In folder mode, the menu fonts now go through host->romfs_read too, with a new 64 MB cap in Reader::load (vk_game_font.h). That changes things for every player, not only image users.
  • OpenDirectory passes "<" as base_dir to hos_mods_dir_extras, so it relies on "<\name" never existing. That works, but a NULL base_dir handled in mods.c would be clearer.
  • The new docs/worklist.md row (0.4) sits above the 0.6 row, but the table is sorted by score.
> [@Joey-Magnus](https://github.com/Joey-Magnus) reviewed on GitHub · 2026-11-12 14:33 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2600#pullrequestreview-5876220356) I merged this into current main (it merges cleanly) and ran the RomFS, game font, vcore_fs_open and fs tests on Linux. All 12 pass. The reader in host/romfs_pack.h holds up. Names are capped at 0x300 bytes and validated. Entries must be sorted and have a parent directory, and file data has to be contiguous and inside the file. read_at still checks the guest range before romfs_pack_read. Before it goes in: - MKWVK_API_VERSION 20 is also claimed by #2604, #2317, #2261 and #2566. Whichever lands second has to move to 21. - The description claims no loading or memory gain. Nothing passes --romfs-pack yet either (the Builder and launcher don't know about it), so this is a second filesystem path behind a command-line flag. Whether that's wanted is a maintainer call on #1007. - In folder mode, the menu fonts now go through host->romfs_read too, with a new 64 MB cap in Reader::load (vk_game_font.h). That changes things for every player, not only image users. - OpenDirectory passes "<" as base_dir to hos_mods_dir_extras, so it relies on "<\name" never existing. That works, but a NULL base_dir handled in mods.c would be clearer. - The new docs/worklist.md row (0.4) sits above the 0.6 row, but the table is sorted by score. <!-- github-sync:r:5876220356 -->
Owner

@Arkex-ai commented on GitHub · 2026-11-12 16:11 UTC · original

Thanks @Joey-Magnus. Updated at 72f107bf on main 51da0464:

  • Folder fonts now retain the legacy Reader path; the virtual callback and its 64 MiB input cap are packed-mode only. The actual loader regression catches the old unconditional selection.
  • hos_mods_dir_extras accepts a null base explicitly. Its real-loader test checks zero filesystem probes for that case and retains filtering for a real loose base; both causal controls are rejected on Windows/Linux.
  • The worklist row is sorted. API20 still sits on main's19, with the later-merge bump/rebase gate retained. No Builder/launcher default is added; the acceptance question remains with the maintainers on #1007.

Twelve focused checks pass per OS. Matching native build/current prompts and both folder/image-only Kokiri runs pass, with FZLTHB acquisition logged and the source checkpoint preserved. The new captures do not establish native-menu glyph coverage. Full-suite failures and the isolated base controls are recorded in the description; new exact-head CI is starting.

> [@Arkex-ai](https://github.com/Arkex-ai) commented on GitHub · 2026-11-12 16:11 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2600#issuecomment-6828966680) Thanks @Joey-Magnus. Updated at `72f107bf` on main `51da0464`: - Folder fonts now retain the legacy Reader path; the virtual callback and its 64 MiB input cap are packed-mode only. The actual loader regression catches the old unconditional selection. - `hos_mods_dir_extras` accepts a null base explicitly. Its real-loader test checks zero filesystem probes for that case and retains filtering for a real loose base; both causal controls are rejected on Windows/Linux. - The worklist row is sorted. API20 still sits on main's19, with the later-merge bump/rebase gate retained. No Builder/launcher default is added; the acceptance question remains with the maintainers on #1007. Twelve focused checks pass per OS. Matching native build/current prompts and both folder/image-only Kokiri runs pass, with FZLTHB acquisition logged and the source checkpoint preserved. The new captures do not establish native-menu glyph coverage. Full-suite failures and the isolated base controls are recorded in the description; new exact-head CI is starting. <!-- github-sync:c:6828966680 -->
Commenting is not possible because the repository is archived.
No description provided.