New profile 010005501E68C000 (update 1.0.0.2, Unity 2022.3.17f1 IL2CPP, SDK 17.5.3; the base game an XCI): - Rewriter: two IL2CPP thunks end in an indirect tail call, ldr x18, [x0, #0x18]; ...; br x18 (main+0x1d271bc, +0x204ebec), which asserted as an unknown jump table. When neither table form is found, the last write of x18 is a plain load and the br ends the function, it becomes x16 = guest x18; br x16 (tail_call / site_br_call). Tried only where the rewriter used to stop, so modules it accepted before come out byte-identical. - sdk cut: the AddressRegionManager node area 1 GB -> 62 MB on this build (sdk+0x139468 / +0x1394fc), as for Engage; StandardAllocator::Initialize aborted in nninitStartup without it. - Shaders (unityfs.py, shader_split.py): Unity 2022 keeps each program's parameters as blob entries of their own (m_ParameterBlobIndices; the programs are m_PlayerSubPrograms[tier][].m_BlobIndex), and subprogram version 202012090 (from 2021.2) has no local keyword list. The player's files borrow the unitybuiltinshaders bundle's Shader type tree. 4,951 shaders, 4,950 build (one geometry shader); Engage's layout of driver segments decodes every binding. UnityPy's type trees for 2020.3.18 and 2021.3 have neither field, so Engage reads every entry as before. - hle_fs.c: nnmusl (open / stat / opendir) checks every path with nn::fs::IsPathMounted, GetPathLengthMax, GetUtf16PathLengthMax, GetEntryNameLengthMax and GetUtf16EntryNameLengthMax, which the SDK answered from its empty mount table: File.Exists failed with no nn::fs call, Addressables never found settings.json and the game crashed on a null asset (main+0x31100ec). Answered from the host's mount table with Horizon's 0x300-byte limits. - hle_time.c: clock_gettime(CLOCK_REALTIME) reads the source nntimeStartup selects (sdk+0x8e414), nn::time::GetStandardUserSystemClockCurrentTimespec, which asked the time service; system_clock::now() then called std::terminate after the first dialog. It and the adjustable clock's getter are the host clock now. - Memory: Engage's 5 GB block with a 1 GB application pool. StandardAllocator commits 0.19 GB; with a 2.5 GB pool its reservation and Unity's own pools reached the end of the 4.375 GB alias region after 140 s (probes on VammManager::AllocateAddressRegion / FreeAddressRegion). 25 minutes now peak at +2.81 GB. On the Mac: first-boot settings, title, Investigations 1's Episode 1 into the first investigation, 60 fps, FMOD sound, 25 minutes with no red line, 1.55 GB resident and 1.25 GB of Metal allocations, flat. IPA AceAttorneyInvestigations.ipa (7.0 GB) built from HEAD + these changes; awaits an iPhone test. Also: UNIMPL stubs log once (the seen counter was a byte and wrapped every 256 calls); build_ipa.sh quotes FONT_PY (a Python path with spaces broke the font step); profile_guess.py marks IL2CPP by global-metadata.dat (Unity 2022's main has no il2cpp_ names). Not needed in the end: pull requests #15 (this main's string table is intact) and #16 (nninitStartup sizes no heap with virtual address memory). Much of the bring-up was found first by armyja (GitHub issues #17, #18, #19, pull requests #15, #16). Co-authored-by: armyja <546323405@qq.com>
nxnative
Play your own Nintendo Switch games natively on iPhone and iPad. Bring your game (its NSP files) and your prod.keys; one script turns them into an IPA that you sideload.
It is not an emulator: the game's own code runs directly on the Apple chip, with Switch graphics rebuilt on Metal. Nothing from any game is in this repository, and the project is not affiliated with Nintendo or any game publisher.
Acknowledgements
This project is built on other people's work:
- d2r-mobile (MIT) developed this native-execution approach, for Diablo II: Resurrected. nxnative (first called smbw-mobile) started as a copy of its generic parts: the NSO rewriter, the host that stands in for Horizon, the NVN-on-Metal layer and the iOS app shell. Fixes go both ways.
- Ryujinx (MIT): its shader translator (
Ryujinx.Graphics.Shader,Ryujinx.CommonandSpv.Generator1.3.2, indm/third_party/ryujinxwith a few fixes listed in its README) turns the games' Maxwell shaders into SPIR-V. Its emulation of the kernel and system services is also a reference when the host's behaviour is in doubt. - Atmosphère: its kernel, mesosphère, is the reference for Horizon's synchronization semantics (address arbiter, condition variables).
- yuzu: Maxwell instruction encodings for the shader reference tool, and a cross-check for kernel behaviour.
- LibHac: reads NSPs and layers updates over the base game in
dm/tools/nx_extract. hactool did the first extractions. - SPIRV-Cross: SPIR-V to Metal Shading Language.
- deko3d (zlib): its texture layout size algorithm is ported in
dm/gfx/nvn/nvn_tex.c. - libvpx (VP9 movies), stb_vorbis (movie sound), libdeflate, the Vulkan headers and NVIDIA FLIP ship in
dm/third_partywith their licenses (list). - Capstone, lz4, fontTools and XcodeGen are used by the build.
Games
All of these are tested on an iPhone and playable:
| Game | IPA | Notes |
|---|---|---|
| Super Mario Bros. Wonder | ~4 GB | |
| Fire Emblem Engage | ~15 GB | Upgraded to 60 fps (30 fps on Switch) |
| Metroid Dread | ~5 GB | |
| 13 Sentinels: Aegis Rim | ~5 GB | |
| Super Mario 3D World + Bowser's Fury | ~3 GB | |
| The Legend of Zelda: Breath of the Wild | ~16 GB | Upgraded to 60 fps (30 fps on Switch) |
| The Legend of Zelda: Link's Awakening | ~6 GB | |
| Super Mario Odyssey | ~6 GB | |
| Hades | ~4 GB | |
| Hyrule Warriors: Age of Calamity | ~15 GB | Upgraded to 60 fps (30 fps on Switch) |
| Fire Emblem: Three Houses | ~13 GB | Upgraded to 60 fps (30 fps on Switch) |
| Super Mario Galaxy | ~3 GB | |
| The Legend of Zelda: Tears of the Kingdom | ~17 GB | Upgraded to 60 fps (30 fps on Switch) |
| Monster Hunter Rise | ~22 GB | Upgraded to 60 fps (30 fps on Switch) |
Needs iOS / iPadOS 26 or later on an iPhone 15 Pro or newer (A17 Pro chip or later) or an iPad with an M1 chip or later: the games' textures are BC-compressed, and older iPhones can't read that format. Other games aren't supported yet. For them the script writes a survey and still tries its best to pack an IPA, guessing the game's settings; such an IPA may not start.
Pack an IPA
1. Get nxnative and its tools (once, on a Mac with Xcode and Homebrew):
git clone https://github.com/seedds/nxnative.git && cd nxnative
brew install xcodegen zstd spirv-cross
brew install --cask dotnet-sdk
python3 -m pip install lz4 capstone fonttools
2. Put one game's NSPs in a folder, base game (an NSP, or an XCI cartridge dump) and update (and any DLC):
~/Games/MarioWonder/
Super Mario Bros. Wonder [010015100B514000][v0].nsp
Super Mario Bros. Wonder [010015100B514800][v327680].nsp
3. Run the script:
python3 scripts/build_ipa_from_nsp.py ~/Games/MarioWonder -k ~/keys/prod.keys
The IPA appears in the same folder (here ~/Games/MarioWonder/SMBWMobile.ipa). The first build takes 30–60 minutes, later ones about 10. You need free disk space of about twice the NSPs' size plus 10 GB. Options such as -o (output path) and --app-name are listed by -h.
4. Install the IPA with your sideloading tool (AltStore, Sideloadly, LiveContainer, …). The app gets the game's name, icon and version (the update's version, for example 1.2.1 for Mario Wonder; the base game's when there is no update).
Play
Touch controls. Tap 2D / 3D at the top to switch layouts; the app remembers your choice. Wonder, Engage, Dread, 13 Sentinels and Link's Awakening start in 2D, the other games in 3D. In Super Mario Galaxy, touches outside the controls move the pointer. A connected game controller hides the touch controls and keeps every button.
- Game language is English by default. For Chinese, add the line
DM_LANG=zh-HanstoDocuments/env.txt. - Saves and logs are in the Files app, under the app's name:
save/andlog.txt. When something goes wrong, sendlog.txt(the previous run islog.prev.txt). - Short stutters the first time a new scene appears are normal: the app compiles graphics for it once and remembers them for the next launch.
- Reinstalling: some sideloading tools give a reinstalled app a new data folder. Copy
save/andpso_user.tsvover from the old one to keep your progress. - Settings without rebuilding: put lines like
DM_OPMODE=0(handheld mode: lower resolution, higher frame rate) inDocuments/env.txt.DM_FPS_TARGET=30keeps Breath of the Wild and Tears of the Kingdom at their original 30 fps, which saves battery.
Known issues
- No amiibo and no online features.
- Fire Emblem Engage: after switching away from the app and back, some sounds may stay silent until you restart it.
- Fire Emblem: Three Houses: small square patches on the ground where decals land.
- Super Mario Galaxy: effects drawn as points or lines come out one pixel wide.
For developers
How it works, the script's options, running games on the Mac, adding a game, and per-game notes: docs/development.md. In-depth bring-up and debugging notes: docs/agents.
License
MIT (see LICENSE). Third-party code keeps its own license; see dm/third_party.













