Skip to content
NX Native

Xenoblade Chronicles 2

Other engines0100E95004038000
Xenoblade Chronicles 2 feature illustration: original artwork for the nxnative notes
Title ID
0100E95004038000
Tested build
2.1.0
Nintendo SDK
3.5.0
Engine
Monolith's engine
Graphics API
NVN

Build this game

Keep [0100E95004038000] in your file names, put the base game, update and DLC in one folder and run the script. Follow the build guide, then the sideloading guide. Needs an iPhone 15 Pro or newer, or an M1 iPad or later.

Technical notes

The project's own record of bringing this game up: what its profile needs and why, what was found and what is still open.

Base title ID 0100E95004038000, update 2.1.0 (v983040), SDK 3.5.0, Monolith's engine, NVN. Modules: main, subsdk0 (NintendoSDK_movie), sdk. The build IDs are in game.tsv.

Current status

Opening gameplay tested on an Apple-silicon Mac (2026-10-27). The game boots, mounts all six supplied add-on content entries, renders its title and menus, completes King Crustip and reaches Argentum in Chapter One. Field tutorials, walking and the menu work. The general queue raster-origin fix restores the lighting and geometry without a game-specific rule or rewriter cut.

All 15,572 shaders build for Mac and iOS. The unsigned IPA has been rebuilt with this fix: 18,331,894,202 bytes (17.07 GiB), 78,482 ZIP entries, all ZIP CRCs valid, with all shader libraries and six DLC mount entries. It has no code signature or provisioning profile. iOS runtime testing, DLC gameplay and later areas remain open. System → Save and a restart into Argentum were exercised during bring-up; the save was created with the isolated winding diagnostic and reloaded with the production path. The final fixed host also loads it and walks in Argentum.

Profile and memory

The NPDM declares no system resource. Startup sets a 0xC9400000-byte heap inside the profile's 4 GB guest reservation. The application pool remains 0xCD500000, with 256 MB each for alias memory and stacks. No game-specific rewriter cuts are used. pad_layout full supplies both sticks and the D-pad.

Files and shaders

The merged base/update RomFS contains 28,376 files (13.05 GB), including 154 WebM movies and bf2.arh / bf2.ard. The ARH1 index names 40,733 packed assets. The existing Monolith archive reader decodes its XBC1-compressed HCPS programs. The complete split, including the supplied DLC, finds 15,572 keys: 4,726 vertex, 10,830 pixel, three geometry and 13 compute, with 12,028 variation pairs. Six stages come from the standalone SLCT at main+0x9ce0e0; the other 15,566 come from assets. Of those keys, 2,834 first appear in DLC assets, 12,732 in the base/update assets and six in main. Full Mac and iOS compilation passes, including all three geometry shaders. DLC gameplay remains unvalidated.

Rendering

Resolved, hard-edged dark boxes in the indirect lighting (GitHub issue #643): indoors and under decks a world-space box was much darker than the scene, darker with the time of day (Goldmouth Return Port), and elsewhere a lighter translucent rectangle. The indirect-lighting pass (shd_lgt.wishp var4.3, 468bd9c28161136f / 93d0e7a7c14cdfb0) reads a per-pixel list of up to four light probes from a 640×360 RG32Uint target (slot 7): the index bytes in x, then the probes' boxes from fp_c6 and their cubes from the cube-map array at slot 5. The probe boxes build that list with logic operation OR (lo=7): the writers (shd_lgt.wishp var3: ef07fd3d5046164f, aa9a0923524b7e46, 2717a0aa47361bbd, d95874c161deb77d, f24b58784a33adea) put their index into the next free byte and set a bit in an R32Uint image, and every other fragment writes 0. The host drew them with COPY, so the list was replaced fragment by fragment. Hand-patching the five writers to OR into their target removed the boxes; the general fix is Logic operations through a library of its own (4,245 _bl libraries for this game, 101 MB on the Mac). The reporter's lead, 3 of 21 64×64 cube-map arrays with few non-zero bytes, counts guest memory the CPU wrote and does not bear on it.

Resolved, stretched geometry streaks (GitHub issue #643): for a frame, a character's mesh stretched into a wedge or sheet reaching another character (the Nopon by Bonbon in the Argentum Bazaar, the pink Nopon and the diving-suit NPCs at Goldmouth Return Port), and other characters missing in that frame; about one frame in a hundred near the crates at the spawn. Skinning runs as vertex-only draws (shd_post.wishp var122: fa456dcb6b05c2ce, 0cf988e8a8ea2aee, bce3419b07cb2f3d, 3fb8ebc13eb37ad7) that write blended bone matrices, 48 bytes per slot at gl_VertexIndex, from slot 0 of one 384,000-byte storage buffer, which the character's draws read; each is preceded by ORDER_PRIMITIVES (bits 0x1) and followed by 0x21. One frame had 91 such draws, 43 in the same Metal pass as the previous character's draws. Fix: Ordering barriers before a draw that writes; with it, 104 a frame take a barrier inside the pass, none ends it, and no streak showed in 288 frames at the spawn. The cb_extent.tsv lead of the report is not it: the 256 shaders it counts are the c1_full.tsv rows, whose gl_VertexIndex reads go to the shader's own constant table (a full 64 KiB each), and the slot-1 extent it names is for reads at fixed indices.

Resolved, dark and inside-out geometry: the game creates its queue before switching the device from the default lower-left origin to upper-left (boot31properties). Mario Wonder sets upper-left before queue creation (wonder33order). The native SDK's DeviceSetWindowOriginMode at sdk+0x2bfac0 only stores the mode. Its queue initializer at sdk+0x2c54ec calls 0x2dba3c at 0x2c594c, which calls 0x2db3b0 at 0x2dc2ec. That helper emits SET_WINDOW_ORIGIN at 0x2db670–0x2db6a0: the lower-left device state selects 0x801004eb, upper-left selects 0x800004eb. This helper has no caller outside queue initialization. Later viewport construction at 0x2d5ca0 reads the device's current mode. These SDK offsets refer to build 867b345248124ef3d70e3202e221e978.

The general queue raster-origin fix preserves this distinction without a game rule or SDK-version gate. boot34queue loads Chapter One and walks in Argentum with correct geometry and lighting. The earlier global winding experiment was rejected because it broke Mario Wonder; the queue-state fix passes Wonder's full smoke route.

Before the fix, indirect-lighting pair 468bd9c28161136f / 93d0e7a7c14cdfb0 writes nothing. Its depth test is off, stencil comparison is ALWAYS, culling is BACK and front-face winding is CW. Native post-vertex capture gives (-1,-1,0.5,1), (3,-1,0.5,1), (-1,3,0.5,1); the byte indices are 00 01 02. Those vertices also occur unchanged at main+0x9d3c30 in build f77f1559371c0ec6d50e78774ac59d95. The material buffers and reflection cubes contain valid images. Highlighting the five direct-lighting material shaders reaches their expected geometry; highlighting the indirect-lighting draw does not. A native D16 depth-equality fixture passes tags 1 through 8, ruling out that earlier hypothesis.

The engine initializes all seven reusable polygon states to CW at main+0x6ff6a4 in that build. The SDK's original polygon binder and window-origin command encoding were compared with Mario Wonder's SDK 16 driver: both encode the same front-face and upper/lower-left origin rules. The difference is initialization order, rather than SDK-version semantics.

Resolved, title logo: the game deliberately hides IMG_Logo when its save buffer does not begin with 0x0300a15b (main+0x489140 through 0x489180 in the build above). A fresh save buffer begins with zero. Forcing visibility in an isolated diagnostic draws the 800×312 BC3 logo correctly; no such patch is retained. More decisively, boot28save creates bf2savefile.sav through System → Save after reaching Argentum, and boot29reload uses unmodified game modules and the production rendering path: its frame 600 shows the logo and Chapter One's blue-sky title, and frame 1800 is back in Argentum. The absent fresh-save logo is not a texture-loading or language-selection defect. One startup layout shader, da21866ffe3a9d7a, reads a 3D texture through a declared 2D slot; its relation to a visible defect is not established.

Validation

  • Python tool suite: 292 tests pass, 16 skipped. The complete Metal graphics suite passes. The queue-origin fixture passes 389 checks in each encoding mode, including actual culling, fragment facing and attachment coverage. All 178 iOS source compile checks pass.

  • boot34queue validates saved-game loading and walking with the fix. After refreshing the standard NSP Mac build and all 15,572 Mac libraries, boot38packagedmac repeats loading into Argentum with that build's host. boot37play starts fresh with real audio, completes the opening battle, plays Chapter One's cutscene, skips it through the game's confirmation, advances the field tutorials and walks in Argentum (field37.png). It runs for fifteen minutes without a guest crash. A separate twenty-minute windowed/audio run (boot36play) remains in the battle tutorial. The fifteen-minute run reports fifteen audio underruns during concurrent validation/build work; its timing is not used as a performance measurement.

  • Full local regression report: 0100E95004038000-work/regress-harness/reports/20261027-161811-check/report.md. No shaders or interfaces change. Mario Wonder and Hades smoke routes pass. Xenoblade 2's screenshots show the corrected lighting, but this sweep's timing and frame-deadline checks fail while other validation builds run. The isolated repeat, 0100E95004038000-work/regress-harness/reports/20261027-174408-check/report.md, passes both games' frame deadlines without new red lines: Xenoblade 2 stays at 30.1 fps (frame p99 33.46 → 33.42 ms), Wonder at 59.7 fps (19.21 → 17.31 ms). Xenoblade 2 is labelled REGRESSION only for the intended screenshot change; Wonder's image passes unchanged. Balatro is outside the harness's GL chain support. Bayonetta 2/3 and OCTOPATH TRAVELER I/II lack package files required by the baseline shader build; Yoshi's Crafted World has no profile in the baseline revision. These are unchecked.

  • Current, #424 controls (2026-10-27): the unchanged 1963fab7 host reproduces the startup texture warning described above and peak footprints of 1,522 and 2,070 MB, versus the first baseline's 564 MB and the patched run's 1,274 MB. A byte-identical HEAD copy also misses both scheduled captures. These flags are not specific to the analyzer fixes. The 65-second startup route reaches different workloads between runs, so its timing difference is inconclusive. An alternating fresh-save idle-title benchmark, two runs per host with the same shaders, also has unstable timing: p99 134.51/712.67 ms on HEAD and 117.44/180.31 ms patched. This does not establish a patch-induced slowdown or a stable performance baseline. Reports are in the NSP work folder's issue424-controls/ and issue424-idle-benchmark/results-valid/; the earlier idle benchmark with an incorrect shader-cache path is marked invalid.

Controller motion review

Current (PR #351, 2026-10-27): the full regression comparison of 6d1e07b with f978e28 reaches the tutorial on both hosts, with unchanged shaders and no new red lines, but flags windowed frame timing. The candidate repeatedly logs one-second display-link stalls; the baseline also stalls and misses both requested screenshots. Its windowed visual comparison is unavailable.

Two cached-archive A/B repeats in reverse order reproduce the large spikes on both hosts, with nearly identical median frame rates. A second two-run pair using direct presentation (DM_PRESENT_LINK=0) has matching 33.24 ms median frame times and overlapping p99 ranges. These controls do not reproduce a consistent slowdown from the gyro change. Windowed performance remains open for a controlled foreground run; see silent display links. DM_WINDOW=0 still creates a window: the Mac front end checks whether the variable exists.

The original flagged report is nx-harness/reports/pr351-review/ on the game drive. The cached-archive controls are in this game's 0100E95004038000-work/regress/pr351-windowed-ab/ and 0100E95004038000-work/regress/pr351-direct-ab/; their public tables are in PR #351.

Integration with main

Rebased onto 75065df (2026-10-27), whose shader chain is 157e1b96c8da. The complete Metal suite, all 389 queue-origin checks in both encoding modes, 347 Python tool tests (16 skipped) and all 178 iOS source compile checks pass on the integrated code. The Python app-bundle fixture needs access to macOS's icon tools outside the sandbox.

All 15,572 Mac shaders rebuild with that chain. boot40main uses the refreshed host, libraries and merged RomFS.img, loads the saved game into Argentum and exercises walking; its frame 1500 shows the correctly lit field.

The final rebase includes 349abd0, with the same shader chain. Repeated checks pass: 389 queue-origin checks in each encoding mode, 73 pipeline archive checks, 79 prewarm checks on each Metal path, 108 affected Python tests and all 178 iOS compile checks. boot41latestmain loads Argentum and walks with that host, with no crash or new red lines. The impact report is in 0100E95004038000-work/regress-harness/main-integration-impact/.

The delivered IPA above retains the gameplay-tested 9bdef355 base plus this queue-origin fix, with shader chain 7fbca6dd477d; it was not replaced during this integration. The regression reports above also refer to that base.

Mac development files

The supplied folder is /Volumes/S790/nsp/Xenoblade Chronicles 2/. Extraction, rewritten modules and shader builds live in 0100E95004038000_build/; binary analysis, bounded runs, saves and logs live in 0100E95004038000-work/. run/boot.py accepts a log name, duration and environment assignments. boot01.log records the initial startup and clearing frames; it stops because that diagnostic launch did not yet have a shader map configured.

Found on this game

These notes are republished from the nxnative repository under its MIT license (license and attribution). The original, which may be newer, is the game's notes on GitHub. This site hosts no game files.

More Other engines games