fix(build): a romfs without the game's heap definition is refused too, also from a .nup and for an update #863
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
beanstalk: approved @ 1697603
beanstalk: approved @ 2c5a62e
beanstalk: approved @ 4fd82b7
beanstalk: approved @ 64d6d09
beanstalk: approved @ 6f91fae
beanstalk: approved @ 70a78f9
beanstalk: approved @ 8015c9a
beanstalk: approved @ 8d361dc
beanstalk: approved @ 9d8356e
beanstalk: approved @ abf2227
beanstalk: approved @ d82f202
beanstalk: approved @ e5f1e40
beanstalk: approved @ ee8ab5f
ci: approved
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/macos
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
regression
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
ci: approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!863
Loading…
Reference in a new issue
No description provided.
Delete branch "llun/Longshot:fix/romfs-heapdef-check"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type: fix · Risk: low · Tested on: Windows 11, RTX 5080 (the builder; the crash reproduced in the game) · Needs: none
Why
Some players' games crash the moment they start, every time, with "Longshot closed unexpectedly during the start" and "An unexpected error". Their game was built from game files missing the romfs's
Systemfolder, which holds the file that tells the game how big to make its memory heaps. Without it the game sets its heaps up with no room in them, and the first file it loads crashes it. Rebuilding from the same files gives the same crash, and nothing tells the player that their dump is the problem.#534 refuses a chosen romfs folder without
Pack/ZsDic.pack.zsorSystem/Resource. That catches a folder with noSystemat all, but not one that hasSystem/Resourcewithout the heap definition. It also doesn't check a romfs unpacked from a.nup, or the one an update builds from.See #776, #689 (the same crash: guest lr
0x80c4afcc, "write 0x100000000000, guest 0"), and a v3.0.0-rc.4 player's report with the same backtrace.How to reproduce
Seen on: #776 (Windows 11, Ryzen 5 8600G, nightly-189), #689 (Windows 11, GTX 1050, nightly-171), a v3.0.0-rc.4 player (Windows 11, i9-13900H); reproduced on Windows 11, RTX 5080, main
25726217Settings and switches: defaults
Steps: build the game from a romfs folder without
System/HeapSize/Product.Nin_Ounce_NVN2.HeapDef.byml.zs(System/Resourcestill there), then start it.Expected: the builder refuses the folder before it builds and says which file is missing.
Actual: the build runs to the end. The game then crashes at boot with
exception 0xc0000005 (write 0x100000000000, guest 0), guest backtracelr 0x80c4afcc 0x83bb705c 0x83b7b47c 0x83b7a838 0x83b79e88 .... A romfs with the wholeSystemfolder missing crashes with the same backtrace and memory-touch addresses as #689's log.What changed
The heap definition joins the files a romfs must hold, and the check now runs wherever the build takes its romfs from: a chosen folder, a
.nup's fresh unpack, a.nupread before, and an update. A romfs that fails is refused before the build, with the file it lacks.nx2/make_game.py):ROMFS_NEEDEDlistsPack/ZsDic.pack.zs,System/Resourceand the heap definition. The game opensSystem/HeapSize/Product.Nin_Ounce_NVN2.HeapDef.byml.zs, thenDefault.; either one passes.romfs_missing()names the first thing missing, andcheck_romfs_folder()(#534's check for a chosen folder, same message) uses it.nx2/make_game.py):check_unpacked_romfs()gives the same check for the other three sources, with "Your .nup file ... does not hold all of the game's files (no ...): a game built from it could not start. Dump the game again and choose the complete dump in Longshot Builder."try, so a refused one is removed and the folder's earlier files and saves stay..nupread before, and an update (--update), check the game folder's ownromfs.tests/test_make_game_cache.py): the fixture's old game folder and its.nupextractions get stand-ins for the start files. New cases:.nupwhose unpack lacks the start files is refused, with the earlier files and saves kept and no.gameleft;.nupread before, and an update, whose romfs lacks the heap definition are refused.Measurements
Verdict: pass. The builder refuses a romfs without the heap definition at once, where main builds it into a game that crashes at boot; the new checks fail on main and pass here; a real, complete romfs still passes.
Hardware: Windows 11 Pro, RTX 5080, Builder tools v2.1.0-rc.3-44 (llvm-mingw clang 23.1.5)
Scene: none, the builder (no game run for the measurement)
Method: 2026-11-15.
nx2/make_game.py --romfs <copy> --exefs <game folder's .longshot_exefs> --out <scratch> --no-prepare-shaders, on a hard-linked copy of a complete romfs (186,583 files) with onlySystem/HeapSize/Product.Nin_Ounce_NVN2.HeapDef.byml.zsleft out; main's build was stopped after 90 s. The checks:tests/test_make_game_cache.pyandtests/run_all.py.How to reproduce: the command above, in a checkout of each commit
Baseline: the builder rows ran on main @
1c91dafbagainst this PR @ b7324bd8, before the rebases. Since then main brought in #561 (a test), #857 (the exe's name and icon, in a part ofnx2/make_game.pythis PR doesn't touch) and #346 (the renderer); the romfs check and where it runs are the same. The test rows ran again on the rebased commits.1c91dafb(tests:1310ecc6)be4de235)@@stage read, game folder made; stopped at 90 s)Systemromfs_missing()on a complete romfs unpacked from the.nupNone: passestest_make_game_cache.py: the three new refusal casestests/run_all.pytest_longshot_ctlfailed once, then passed twice: timing)Flags and settings
None.
Testing
Tested:
System(refused on both)..nup: it passes.25726217), the crash itself: a romfs without only the heap definition, and one without the wholeSystemfolder, both crash at boot with the players' backtrace. The whole romfs reaches the intro.tests/run_all.pyon Windows. On this PCtests/test_make_game_cache.pyfails on main too: the work folder's lock is left open on Windows (#804), and with noRX_VK_PGOthe renderer profile's clang probe hits the test's no-process guard. WithRX_VK_PGO=off, every assertion passes here (23 tests), and the errors left are the lock cleanup alone (main has 15 of them, this PR 18, three of them the new tests').Not tested:
.nupunpacked through the new check: a 25 GB extraction. The unpacked result was checked instead (above), and the path itself is covered by the test.test_make_game_cache.pyruns fully on the Linux checks.Must be tested: None.
Risks
What could break: a dump that holds neither heap definition, but that the game would start from, is now refused. The supported version opens
Product.and thenDefault., and crashes without both, as reproduced above.Turning it off: revert the commit.
Compatibility: nothing changes for a game that's already built. A new build, a
.nupread again or an update, from game files without the heap definition, is refused with the missing file named instead of building a game that can't start.Issues
See #776
See #689
Checklist
Everyone
type(scope): description) of the status line's type.main.python -I -B tests/run_all.pypasses (any failure that also fails onmainis named). Main's own 19 here: test_appimage_player_build, test_bake_portability, test_cpp_preflight, test_crash_dialog, test_env_on, test_fpx_sticky, test_lift_cache, 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_readback_late, test_spawn_emit, test_tested_configurations, test_vk_pgo, test_walk_key.third_party/untouched (a new folder with aPROVENANCE.mdinstead).When it applies
Assisted-by:/Co-Authored-By:trailers.mainand passing here.tests/that reads changed source updated in this PR, not weakened.🤖 Generated with Claude Code
WIP: fix(build): a romfs without the game's heap definition is refused too, also from a .nup and for an updateto fix(build): a romfs without the game's heap definition is refused too, also from a .nup and for an update7085be4422be4de2351dRebased onto main
1310ecc6(#561, #857, #346; #857 touches make_game.py elsewhere, the romfs check is the same). Suite again on both: 497 passed / 19 failed here, the same 19 as main's (main also had one timing failure of test_longshot_ctl, which passed on rerun); test_make_game_cache's assertions all pass.