Builder: pre-#456 unpacked romfs retains outer .nup Zstd frame on damaged .zs files, bypassing damaged_game_files() on --update (v2.0.12–v2.1.0) #2361
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot#2361
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Summary
Game directories whose
romfswas unpacked from the incomplete 23.3 GB.nup(25,020,741,061bytes) before PR #456 (2026-11-08) and later updated to v2.0.12–v2.1.0 viaHookshotBuilder/make_game.py --updatestill exhibit invisible walls and floors in the Water Temple (#1259), Great Fairy Fountain (#867, #1700), and Ganon's Stage (#1002), even thoughstand_in_damaged_files()(#1308, #1373) ran and wrote.hookshot_romfs_checked.Root Cause
Pre-#456
nup.pywrote the outer.nupZstd frame directly toromfs/Tex/*.bntx.zs:In the 23.3 GB
.nup, the 3 damaged.zsmembers are stored with outer Zstd compression (codec == 1,stored < size), and their inner decompressed payloads begin with0x4000zero bytes (00 00 00 00) instead of a Zstd frame header:Tex/Rock_WaterTemple_Gate_M_01_Nrm.bntx.zs(size=452972,stored=444818)Tex/CmnTex_Map_Soil_FairyFountain_Floor_D_Nrm.bntx.zs(size=216015,stored=203753)Tex/RBake_Scene_GanonStage_G_2_1_d0_t0.bntx.zs(size=1901341,stored=1889157)Before PR #456,
Nup.is_packed(r, name)did not checkr[5] == 1(codec == 1); for.zsfiles whereframe_content_size(head) == r[2], it fell back to:Because the inner payload of those 3 damaged members begins with
00 00 00 00,inner_is_zstd(r)returnedFalse. The old unpacker therefore treated all 3 members as uncompressed raw extents (packed = False) and copiedsizeraw bytes from offsetoffin the.nup—consisting of the still-compressed outer.nupZstd frame (storedbytes starting with28 B5 2F FD) plussize - storedtrailing bytes from the next extent—directly toromfs/Tex/*.bntx.zson disk, and wrote.hookshot_romfs.--updatein v2.0.12–v2.1.0 skips re-unpackingromfsanddamaged_game_files(romfs)only checks the first 4 bytes (28 B5 2F FD):When an affected install is updated in v2.0.12–v2.1.0:
make_game.pysees.hookshot_romfsmatchesunpacked by this builder: <nup_name> 25020741061, so it skipsunpack_nup_dir(nup, "romfs", ...)(meaning neither PR #456'scodec == 1unpack nor PR #1885'sHeadSinknor PR #2019's extraction digest check runs).stand_in_damaged_files()runsnup.damaged_game_files(romfs), which opens each.zsfile on disk and checks onlyzstd_start(f.read(4))(28 B5 2F FDor skippable frame magic)..nupZstd frame (28 B5 2F FD),zstd_start()returnsTruefor all 3:Rock_WaterTemple_Gate_M_01_Nrm.bntx.zs(452,972bytes on disk, starts with28 b5 2f fd a0 6c e9 06...): Zstd-decompresses to452,972bytes of00 00 00 00...(unused_data=8154).CmnTex_Map_Soil_FairyFountain_Floor_D_Nrm.bntx.zs(216,015bytes on disk, starts with28 b5 2f fd a0 cf 4b 03...): Zstd-decompresses to216,015bytes of00 00 00 00...(unused_data=12262).RBake_Scene_GanonStage_G_2_1_d0_t0.bntx.zs(1,901,341bytes on disk, starts with28 b5 2f fd a0 1d 03 1d...): Zstd-decompresses to1,901,341bytes of00 00 00 00...(unused_data=12184).stand_in_damaged()replaces 0 files and writes.hookshot_romfs_checked, causing subsequent updates (such as v2.1.0) to skipstand_in_damaged_files()immediately (check_files: 0.0s).hookshot.exedecompresses the outer.nupZstd frame on disk into00 00 00 00...instead of aBNTXtexture, so the affected walls and floors fail to render.Suggested Fix
.zsfiles indamaged_game_files(romfs)(orstand_in_damaged_files()):.zsfiles on disk (or when checking an existingromfswithout.hookshot_romfs_heads), either decompress the first few bytes of the Zstd stream to verify the inner header is not00 00 00 00(e.g. validBNTXmagic for.bntx.zsor non-zero header, and/or check forunused_data), or when--nupis provided, cross-check against the.nupTOC SHA-256 /codec == 1damaged extents..hookshot_romfs_checkedmarker version/format:.hookshot_romfs_checkedmatching.hookshot_romfs, bumping the marker string (similar to the versioned marker proposed in #2096) will ensure already-updated game folders re-run the check once and replace the 3 files with their healthy stand-ins (Rock_WaterTemple_Gate_P_01_Nrm.bntx.zs,CmnTex_Map_Sand_FairyFountain_Sand_A_Nrm.bntx.zs,RBake_Scene_GanonStage_G_2_2_d0_t0.bntx.zs).Thanks for the report. Reports of the same problem are now collected in #2505, which lists this one with your details and links back here, so one fix closes them together and the open list stays readable. Follow #2505 for the fix, and add new details there (or here). If this turns out to be a separate problem it will be reopened. Pull request #2391 works on this.
github-sync referenced this issue2026-11-12 06:22:54 +00:00