The NVN driver's constant segments have the same layout in every game, so the
nvn_* profile keys are gone. Combined textures are at c2 0x20 + 8 r (a slot past
31 shares the image table; Hades II binds 34), images at 0x120, separate textures
at 0x168 (now always the host's slots 128 + n, as Monster Hunter Rise had them),
samplers at 0x568, and the storage buffer tables at c0 0x110 / 0x310 / 0x510.
dmsh_build.py decodes with that layout, the replay fills both texture tables,
and the translator numbers slots the same way (rebuilt; its SPIR-V and
reflection are byte-identical on 1,491 sample shaders, lock updated).
The shader_* switches and graphics are gone too; the build reads them from the
files (profile_guess.py shader_sources):
- Vulkan when main imports Vulkan and not NVN; its capture when the RomFS has
no SPIR-V.
- A GLSLC capture when main holds GLSL sources and imports glslcCompile.
- An Oodle decode for Unreal paks, and the tool-mode decoder for Trinity packs.
- GLSLC outputs from every main.
- A cover pairing for each single-stage vertex shader (GLSLC, Unreal).
build_ipa.sh takes GFXAPI from the NSP script.
Also removed: the last D2R pieces (the embedded-shader path in dmsh_build.py,
dmsh_facts.py --freeze / --check, maxwell_refs.py, ref/hle_hid_d2r.c).
Chain: every profile's dmsh_chain.h is regenerated. The c1_full lists are
carried over: the change touches no c1 read, and the rebuilt Octopath
Traveler 0, Hades II, Unicorn Overlord and Rhythm Heaven Groove gave the same
lists, identical tables and the same reflection apart from separate texture
slots + 128.
Still to do:
- Run the chain procedure for the other NVN profiles.
- Engage's and Three Houses' shader tables need a new split: Engage gains
Unity's 11 GLSLC shaders in main, and Three Houses' 17 BNSH vertex shaders
lose their cover pairs.
- Ace Attorney's new tables are in, but its libraries are not rebuilt.
- The loader reads the SDK version from the sdk module's NintendoSDK_nnSdk string
(dm_sdk_major). SDK 10 and older report NVN API 53.313 and point the shared-font
address at the TTF itself; later SDKs 55.15 and the BFTTF header. That is what
every profile set, so device_api and shared_font leave the five that had them
(Odyssey, 3D World, Link's Awakening, Age of Calamity, Three Houses) and
profile_guess.py no longer guesses them; a profile can still override.
- DM_OODLE_JOB finds OodleLZ_Decompress as the one function in main that takes
the address of the library's own name string, with its start from
.eh_frame_hdr, instead of an address per main build: Octopath Traveler 0
main+0x4f77e10 and Pokemon Legends: Z-A main+0x1a9c9e0. Octopath's 10,596
shaders decode byte-identical to before.
- Rows that only restated defaults are gone (pad_layout simple, graphics nvn,
present_interval game), and the NSP script writes no default rows into
provisional profiles.
No chain version changes. All 19 profiles build, the host tests pass; Mac boots
of 3D World (SDK 10.4.0: API 53.313) and Engage (SDK 12.3.7) read the version.
A 26-minute run from a fresh save goes through character creation and the story opening into free play with no red
line; the game saves four times and a later boot continues from the saves. Not yet run on an iPhone.
- Dynamic texture slots (profile key tex_dyn_slots, on for this game only): 61 vertex shaders (particles) pick one of
several c2 table slots at run time, which the translator cannot trace to one slot (Ryujinx's 32-entry fallback
arrays, <stage>_<t|tb|s>_cb0_40000000). The reflection reads them as NVN_RB_TEXD / NVN_RB_SAMPD arrays, the replay
writes slot indices into that shader's c2 tables and binds slot k at element k. They used not to load; the castle
scene's snow is back. Host only: no chain version changes.
- Image atomics: SPIRV-Cross's MSL 2.3 emulation binds a device atomic pointer (<image>_atomic) beside the image, which
the build reads as a second image of the slot, so the replay put a texture resource ID where an address belongs.
NVN_RB_IMGA binds the storage of a buffer or linear image there (a buffer texture is a view of its pool); a
block-linear image's atomics go to the dummy buffer (logged). 2,084 shaders in this game, at least ten run.
- Buffer textures at an offset below Metal's reported 16-byte alignment are created when the offset is a multiple of
the element size (measured on an M6: views at offsets 4, 8 and 12 read the right elements); they were dummies.
- The first occlusion-query reset inside a pass without a visibility buffer splits the pass, so it is counted.
- A buffer texture in a slot a shader reads as another type (a cube) reads 0 instead of undefined data.
- Service calls: nn::fs::GetSaveDataSizeMax (SIGSEGV at the first save, about 13 minutes in), GetCacheStorageSize,
GetFreeSpaceSize, account::PushOpenUsers, web::ShowOfflineHtmlPage, oe::ExecuteProgram, found by listing main's
unhooked imports.
- The engine is Unreal Engine 5 (its paged global distance field shaders and LowLevelTasks), not 4.26 / 4.27; the 16
compute shaders with 3D image atomics are that global distance field, which the mobile renderer does not dispatch.
Tears of the Kingdom boots the same with the new host (no shader libraries there, so the new bindings are not
exercised); the dm/gfx unit tests give the same results as before the change.
Octopath Traveler 0 1.0.5 (Unreal Engine 4.26 / 4.27 mobile renderer, SDK 19.3.13) plays from boot through character
creation into the story on the Mac (15 minutes with the iPhone memory layout, no crash); not yet run on an iPhone.
- Shaders: the 10,324 are in the two shader code archives of the game's pak, each one Oodle-compressed. uepak.py reads
the version 11 pak (AES-256 index; the key is the window of main that decrypts it, never printed or written; AES
through CommonCrypto) and the archives; the Mac host decodes them with the game's own OodleLZ_Decompress
(DM_OODLE_JOB, hle_oodle.c, build-gated) and shader_split.py reads the decoded LSLG records (profile key
shader_ue_decode; the NSP script's decode_ue_shaders stage). Bindings measured: Engage's separate texture layout.
- Memory: Unreal's nninitStartup reserves a 62 GB address region and its FMallocBinned3 37 GB of it. A cut skips the
allocator's constructor and the host puts its own FMalloc vtable on a malloc zone in the object (hle_uemalloc.c);
the region is capped at 2 GB with the game's own -SwitchVirtualAddressSpaceSize (new profile key game_args, also
for the iOS app) and SDK 19's node area is cut to 62 MB as Engage's, for a 3.75 GB guest block.
- Host: game arguments are set before nninitStartup, as on hardware; an undefined weak import nothing defines binds
to 0, as rtld binds it (Tears of the Kingdom boots the same with the old and new hosts: its weak glslc* imports
now resolve to 0); nvnCommandBufferFenceSync (OP_FENCE_SYNC), the texture / sampler pool getters,
vi::GetLatestFrameNumber / ListFrameHistory; DM_ALIAS_WINDOWS for sizing fixed-window allocators.
- Rewriter: a byte jump table whose add sits 17 instructions before its br (jump_bytes_far, tried last).
- build_ipa.sh quotes FONT_PY (a python path with spaces failed the font step).
Open: 61 vertex shaders pick a texture slot at run time (dynamic handle), 16 compute shaders with 3D image atomics
do not build. The profile (dm/games/01005270232F2000) holds the shader tables, translation chain b-ba76ba138e78.