- Magenta 16x16 blocks in Super Mario 3D World 1-1 (iPhone only): the composite shader 66a71a3081b91b9a renders into the RGB10A2 image while sampling it in the same pass. That is undefined in Metal: the M4 Pro reads the old content, the A20 Pro sometimes returns whole 16x16 blocks as (1,0,1). Found with the new device self-check DM_NVN_MAGSCAN (blocks first appear on that attachment, and still appear with the suspected particle dropped). fb_prepare ends the pass, copies the attachment level into a snapshot and binds the snapshot for that draw, only for shaders in the new profile key feedback_snap (doing it for every shader cost BotW 16.6 -> 36.6 ms). DM_NVN_FB_SNAP=0 / all / <keys> overrides. - The game language now defaults to English (nn::oe::GetDesiredLanguage returns en-US; DM_LANG=zh-Hans in Documents/env.txt for Chinese). - Code comments, docstrings and docs are English only; the zh-CN doc twins are removed. README.md has the English text followed by the same text in Chinese. Files the shader chain version hashes keep their bilingual comments (editing them changes every game's chain version).
Porting a Switch title: agent guide
These pages are for an agent asked to analyse a Switch NSP and port it with the native-execution approach of this repository and d2r-mobile: the game's own aarch64 code runs natively on Apple hardware, a rewriter patches only the instructions Apple's kernel cannot run, the host replaces Horizon system services, and NVN is reimplemented on Metal.
Everything here was learned on Diablo II: Resurrected (SDK ~12, NVN 53, the original target of d2r-mobile) and Super Mario Bros. Wonder (SDK 16.2.0, NVN 55, this repository; its update 1.2.1 uses SDK 16.2.12), then extended by Fire Emblem Engage (Unity, SDK 12.3.7), Metroid Dread (MercurySteam's engine, SDK 11.4.3) 13 Sentinels: Aegis Rim (Vanillaware's engine, SDK 11.4.3, the same sdk and movie module as Dread) Super Mario 3D World + Bowser's Fury (Nintendo EPD's sead / agl engine, SDK 10.4.0, NVN API 53) and The Legend of Zelda: Breath of the Wild (EPD's sead / ksys engine, SDK 20.5.22, DT_RELR relocations, update 1.9.0). Where titles differed, each is given; the next title will differ again, so treat every number below as "measure it, don't assume it".
| Page | Covers |
|---|---|
| 01-extract.md | Keys, NSP → NCA → ExeFS / RomFS, update NSPs, the workspace layout |
| 02-survey.md | Static survey of a new title before touching the host: SDK version, imports vs. host hooks, SVCs, instructions to rewrite, NVN surface, data containers, Ghidra / REA setup |
| 03-bringup.md | Getting the host to boot the game: the run loop, how to read the log, and a catalogue of every failure pattern met so far with its root cause and fix |
| 04-graphics.md | NVN on Metal for a new title: object sizes, shader containers and keys, binding layouts, bindless textures, depth mode, index sizes, swizzle, transient render targets, the diagnostic switches |
| 05-debugging.md | Recipes: crash reports, lldb scripts, Ghidra headless decompilation, objdump, memory growth, render-pass and texture dumps, on-device diagnostics, offline pipeline link checks |
Ground rules
- Bring your own files. Keys, NSPs and anything extracted from them stay in the user's workspace (
~/<title>-work), never in the repository, never in a commit, never printed. Print key names and lengths only. - Measure, then change. Each fix in this repository started from a concrete observation (a crash address and its decompilation, a census over all shaders, a memory profile). Write the evidence into the code comment next to the fix, the way the existing code does; the next title will need it.
- One repository, one profile per title. nxnative (first called smbw-mobile) started as a copy of d2r-mobile's generic parts. Title- or SDK-specific constants live in the game profile
dm/games/<id>/(dm/games/README.md). That covers program ID, known builds, modules, address-space partition, NVN driver tables, the shader tables and the app identity. The code reads the profile throughgame_profile.h(host) ordm/games/game.py(tools). Behaviour that fits only one title's code is gated by that title's build ID (dm_main_build()), never left on for every title. Some things are still built into the translator: the texture / sampler handle bases and the depth mode (see 04).dmsh_build.pyrefuses a profile whose bases differ from the translator's. - Write code comments in English, and keep the
【红】prefix for red log lines that mark something unimplemented or wrong. The README's Chinese half is the only Chinese text. - Test where it runs. The Mac host (
dm/host/dmhost) is the development loop; the iOS app reuses the same host and graphics code. Never claim something works on iOS without running it there.
Checklist for a new title
-
Run
scripts/build_ipa_from_nsp.pyon the title's NSP folder. With no profile for its title ID, it writes<NSP folder>/<title ID>_survey/survey.md:- modules and build IDs, SDK version and components, engine and graphics API markers;
- RomFS containers;
- the rewrite-site census and imports vs. host hooks.
It extracts only the code modules plus a RomFS list.
-
Extract base (and update, if in scope) — 01. Finish the survey — 02. Write down: SDK version, NVN API version, module list, imports the host lacks, SVCs, instruction census, NVN loader pattern, shader containers.
-
Add a game profile
dm/games/<title ID>/game.tsv, named after the base game's title ID (start from010015100B514000/game.tsv: identity,buildrows,modules; keep the memory / NVN values until they are measured) and build the host for it withmake -C dm/host GAME=<id>. -
Run the rewriter (
DM_RULESET=native) and check its counts againstinsn_census.py. -
Boot on the Mac and iterate on the failure catalogue in 03 until the game reaches its frame loop.
-
Extract and translate the shaders, find the binding layout by census, and get the first frame — 04.
-
Check memory over a minute of running (
footprint, the host's memory report line); fix growth before moving on. -
Input, audio, then the iOS bundle (d2r-mobile's
dm/ios, RomFS inside the app). -
Test on a phone: an unsigned IPA the user sideloads, switches through
env.txt, results throughDocuments/log.txt(05). Several bugs only showed there (the command-buffer byte and the audio buffer size in 03, pipeline link failures in later levels in 04). -
For a game update: extract it over the base game, diff the imports, carry the per-build addresses over, and rebuild the shader tables for both versions (01).