Reuse unchanged IPA entries through the packing index and APFS clones, and keep Mac app and derived-profile shader outputs while their inputs match. Preserve main's shader regression checks and .NET discovery. Count uncompressed bytes in streamed files so a size change aborts the pack without replacing the previous IPA or index. Cover both deflated and stored streams with growth and shrinkage regression tests, and record the cause and fix in the packing reference. Co-authored-by: Suraj Mandal <surajmandalcell@gmail.com>
3.4 KiB
3.4 KiB
Packing
Build tools
Homebrew .NET is on PATH but apphosts cannot find the runtime
- Symptom: the first extraction stops with
nx_extract exit code 131and.NET location: Not found, even thoughdotnetis onPATH. The shader translator can fail the same way. - Cause: standalone .NET apphosts search runtime roots and registered installations, not
PATH(.NET runtime discovery). Homebrew's formula installs the runtime underlibexecand itsbin/dotnetcan be a wrapper that setsDOTNET_ROOTonly for its own child (Homebrew caveat). Resolving that wrapper's symlink givesbin, not the runtime root. - Fix:
scripts/build_ipa_from_nsp.pyconfigure_dotnetderives the root from theMicrosoft.NETCore.Appentry indotnet --list-runtimes, using the build's environment, before any extraction or survey. It exportsDOTNET_ROOTboth to that environment and toos.environ, for tools launched with an explicit environment and those that inherit it. Caller-supplied roots, including architecture-specific overrides, retain precedence. A failed or malformed probe leaves the environment unchanged rather than guessing the wrapper's directory. - Found on: Balatro, first extraction with Homebrew .NET (PR #284). Applies to every game's build on that installation setup.
IPA archives
A streamed file changes size and the IPA fails its CRC check
- Symptom: packing succeeds, but
unzip -tor Python'sZipFile.testzip()reports a bad CRC for a large deflated file. - Cause:
pack_ipa.pywrote the uncompressed size collected before reading. Its streaming check compared the output size only for stored files, so a compressible file that grew or shrank still published an archive with the old size. - Fix: count uncompressed bytes as they are read and compare them with the listed size before completing either streamed method. A mismatch stops the pack and removes the partial archive, preserving the previous IPA and its reuse index. Regression tests cover growth and shrinkage with both deflate and store.
- Found on: synthetic IPA inputs while reviewing PR #279; no game files required.
Compressed inputs
NSZ decompression rejects --keys
- Symptom:
decompress NSZstops immediately withnsz: error: unrecognized arguments: --keys …. - Cause:
nsz4.6.1 has no--keysargument. It loadskeys.txtbesidesys.argv[0], or falls back to~/.switch/prod.keys, while importing its modules, before parsing arguments (4.6.1 key loader). Newer upstream code supports--keys; stagingkeys.txtalso supports 4.6.1. - Fix:
scripts/build_ipa_from_nsp.pydecompress_nszlinks the file selected by-kaskeys.txtin the conversion's temporary folder and runsnsz -Dthere, withsys.argv[0]set tonszbefore import. Input and output paths are absolute. The temporary folder and link are removed on success or failure; the user's default keys are untouched. - Found on: Super Mario Odyssey, converting its v0 NSZ. Applies to NSZ and XCZ inputs for every game.