diff --git a/AGENTS.md b/AGENTS.md index 321829d..0935f15 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # Notes for agents -- This repository runs Nintendo Switch games natively on Apple platforms: the game's own code runs, rewritten only where Apple's kernel requires it. Thirty-two games are playable on the iPhone: Super Mario Bros. Wonder (update 1.2.1, profile `010015100B514000`), Fire Emblem Engage (update 2.0.0, `0100A6301214E000`), Metroid Dread (update 2.1.0, `010093801237C000`), 13 Sentinels: Aegis Rim (update 1.10, `01003FC01670C000`), Super Mario 3D World + Bowser's Fury (update 1.2.2, `010028600EBDA000`), The Legend of Zelda: Breath of the Wild (update 1.9.0, SDK 20, `01007EF00011E000`), The Legend of Zelda: Link's Awakening (update 1.1.0, SDK 8, `01006BB00C6F0000`) Super Mario Odyssey (update 1.4.1, SDK 7, `0100000000010000`), Hades (update 1.0.38233, SDK 9, `0100535012974000`), Hyrule Warriors: Age of Calamity (update 1.3.0, SDK 9.6, `01002B00111A2000`), Fire Emblem: Three Houses (update 1.2.0, SDK 6.5, `010055D009F78000`), Super Mario Galaxy (update 1.3.1, SDK 18, `010099C022B96000`) The Legend of Zelda: Tears of the Kingdom (update 1.4.3, SDK 20, `0100F2C0115B6000`) Monster Hunter Rise (update 16.0.2, SDK 11.4.3, `0100B04011742000`), Ace Attorney Investigations Collection (update 1.0.0.2, SDK 17.5.3, `010005501E68C000`), Hades II (base game, SDK 19.3.9, `0100A00019DE0000`), Unicorn Overlord (update 1.05, SDK 16.2.4, `010069401ADB8000`), Rhythm Heaven Groove (base game 1.0.0, SDK 21.4.0, `0100D9F01D474000`), Octopath Traveler 0 (update 1.0.5, SDK 19.3.13, `01005270232F2000`), Pokémon Legends: Z-A (update 2.0.2, SDK 19.3.11, `0100F43008C44000`), Cuphead (update 1.3.7, SDK 5.5.0, `0100A5C00D162000`), Super Mario Galaxy 2 (update 1.4.0, SDK 18.3.5, `0100FD8022DAA000`), FINAL FANTASY TACTICS - The Ivalice Chronicles (update 1.4.0, SDK 19.3.9, `010038B015560000`), Absolum (update 1.1.1, SDK 18.3.2, `0100A83022A22000`), Captain Toad: Treasure Tracker (update 1.4.2, SDK 8.2.0, `01009BF0072D4000`) and Brain Memory Training Academy (base game 1.0.0, SDK 16.2.4, `0100F5D01DA3A000`), The Great Ace Attorney Chronicles (base game 1.0.0, SDK 10.4.1, `010036E00FB20000`), STEINS;GATE RE:BOOT (update 1.0.3, SDK 20.5.17, `01008BC024FDE000`), Big Brain Academy: Brain vs. Brain (update 1.2.0, SDK 11.4.3, `0100620012D6E000`), Patrick's Parabox (update 1.0.1, SDK 13.3.7, `0100E44016C22000`), Celeste (update 1.4.1.1, SDK 15.3.2, `01002B30028F6000`) and DRAGON QUEST XI S: Echoes of an Elusive Age – Definitive Edition (update 1.0.4, SDK 7.3.2, `01006C300E9F0000`). Hades renders with Vulkan, which the host implements on Metal (`dm/gfx/vk/hle_vk.m`; `main` imports Vulkan, not NVN). Age of Calamity is Koei Tecmo's KTGL engine: files in RDB archives, shaders as GLSLC outputs (`shader_split.py` `idrk_shaders`). Three Houses is KTGL too; 9 of its shaders exist only as compiles of the game's own GLSLC at run time, captured once on the Mac (its `main` holds GLSL sources and imports `glslcCompile`). Breath of the Wild and Tears of the Kingdom run at 60 fps through a frame step the host decides in whole vsyncs (`patch_nso.py` `FRAME_STEP`, `nvn_fstep.h`, profile keys `fps_target` and `frame_step`); Tears of the Kingdom has a 5 GB guest layout. Monster Hunter Rise is Capcom's RE Engine: files in KPKA PAK archives (`shader_split.py` `kpka_jobs`, shaders in SDF files), the base game an XCI, its GPU heap a 64 GB sparse virtual pool (`hle_nvn.c` `vpool_*`, a 4 KB page table the replay reads textures through); it runs at 60 fps (30 on the Switch). Super Mario Galaxy is the Wii game on NERD's Hagi layer (PowerPC code recompiled into `main`), renders with Vulkan, and generates its shaders at run time, which the NSP script captures on the Mac (no SPIR-V file in its RomFS); its pointer is the touch screen in handheld mode (profile key `op_mode`). Ace Attorney Investigations Collection is Unity 2022 IL2CPP: its shaders keep their parameters as blob entries of their own (`shader_split.py` `unity_code_blobs`), and its C# file API goes through the SDK's C library, whose path checks the host answers (`hle_fs.c` `IsPathMounted` and the length limits). Hades II, unlike Hades, renders with NVN; its shaders bind textures at fixed slots up to 34 (slot 34's handle lies in the driver's image table, `dmsh_build.py`), and the two long lines through Melinoë while the staff's Omega attack charges are the game's own aim lines, not a rendering bug. Unicorn Overlord is Vanillaware's engine like 13 Sentinels (shaders compiled into `main`); both need the profile key `alloc_big_align`, which aligns large allocations to 64 KB so the ring heaps the engine cuts from a memory pool fit. Rhythm Heaven Groove is Nintendo's own 2D engine on SDK 21, the newest SDK so far (its DSP command list has no magic, `hle_audren.c`); it plays handheld with Boost (profile keys `op_mode handheld`, `perf_mode boost`), which skips its TV latency calibration and keeps its 1920x1080 draw size. Octopath Traveler 0 is Unreal Engine 5: everything in one pak with an AES-encrypted index whose key is found in `main` (`dm/gfx/tools/uepak.py`), its shaders Oodle-compressed in the pak's shader code archives and decoded once on the Mac by the game's own Oodle (any game with Unreal paks, `DM_OODLE_JOB`), its allocator replaced by the host's because Unreal's reserves 37 GB of address space (`patch_nso.py` cut, `hle_uemalloc.c`), its address region capped by the game's own argument (profile key `game_args`), and the particle shaders that pick a texture slot at run time bound by slot (profile key `tex_dyn_slots`); it runs at 60 fps (30 on the Switch) because the host writes Unreal's `t.MaxFPS` and `rhi.SyncInterval` in game memory (`hle_fpslock.c`, profile key `fps_target`). Pokémon Legends: Z-A is Game Freak's Trinity engine: everything in the packs of `arc/data.trpfs` (FlatBuffers, every file Oodle-compressed), which the shader split decodes with the game's own decoder in the Mac host's tool mode (`shader_split.py` `trinity_shaders`, `hle_oodle.c` `DM_OODLE_SERVE`); its 12 GB StandardAllocator is cut to 4 GB to fit a 5 GB guest layout, and the host refuses maps past the pool as Horizon does (profile key `mem_map_limit`); its characters are skinned with transform feedback (`dmsh_build.py` `vs_tf`, `nvn_mtl.m` `tf_draw`), and its occlusion queries, which decide how characters are animated, need the draw's base instance in the driver segment (c0 + 0x644, `encode_args`). Cuphead is Unity 2017.4 IL2CPP on SDK 5.5.0, the oldest SDK so far: Unity 2017's serialized and Shader formats are read by `unityfs.py` / `shader_split.py`, its rewritten modules (80 MB) need the profile key `mem_used` above the default 64 MB, or its `SetHeapSize` fails, and its NVN pool allocator breaks on a zero-size block, so the host never reports a ZCull storage size of 0 (`hle_nvn.c` `zcull_size`). Super Mario Galaxy 2 is Galaxy's Hagi layer with Galaxy's `sdk` build; its movie player reserves 384 MB of virtual address memory, which fits only because a rewriter cut on that `sdk` build shrinks the SDK's address-region node area to 62 MB (`patch_nso.py`; the profile's alias region is 512 MB). FINAL FANTASY TACTICS - The Ivalice Chronicles is Square Enix's engine on NVN: its shaders are GLSLC outputs in loose technique files, which `shader_split.py` reads in any RomFS file no other reader takes; its UI textures are written by the CPU into uncached pools (profile keys `tex_flush_upload`, `tex_dirty_upload`); and two job-pool workers share one auto-clear event, so a build cut makes a worker woken in another's place pass the wake on (`patch_nso.py`). Absolum is FNA (XNA 4 in C#) on .NET NativeAOT with SDL3 and FNA3D's NVN driver: its `main` starts its text with 1.7 MB of data the rewriter leaves alone (`nso_code.py`), its shaders are GLSLC outputs in `shaders.nx.bin`, its picture is flipped by a viewport swizzle the host applies, and everything lives in virtual address memory: malloc's 40 GB StandardAllocator is cut to 2.5 GB and the SDK's node area and large-region alignment are cut (`patch_nso.py`), and the .NET GC's region range is 1.5 GB (profile key `game_env`), to fit a 5 GB guest layout. Captain Toad: Treasure Tracker is Nintendo EPD Tokyo's engine (the `al` library over sead / agl, as 3D World and Odyssey) and needed no host change: it plays handheld because its pointer is the touch screen there (the Joy-Con gyro docked; profile key `op_mode`), and agl's post-effect program that samples its own attachment reads a snapshot (profile key `feedback_snap`). Brain Memory Training Academy is Unity 2022.3 IL2CPP with no bundles: its serialized files carry no type trees and nothing lends it a `Shader` tree, so `unityfs.py` has Unity 2022.3's `Shader` layout written out (`KNOWN_TREES`), and like Ace Attorney Investigations Collection it needs the SDK's address-region node area cut on its `sdk` build (`patch_nso.py`). The Great Ace Attorney Chronicles is Capcom's MT Framework: files in ARC archives of zlib entries, its 460 shaders in one shader package inside one of them (`shader_split.py` `mtspk_shaders`); it copies its textures into a CPU-cached pool after creating them and never flushes (its `main` has no cache maintenance at all), so it needs `tex_flush_upload` and `tex_dirty_upload` like FINAL FANTASY TACTICS. STEINS;GATE RE:BOOT is M2's ADV engine (E-mote characters, PSB archives, H.264 movies through the SDK's movie library): it needed `nn::audio::SetAudioOutVolume` (`hle_audio.c`), and because it decodes every picture into one reused region of a pool that lives for good, the storage-sharing texture table now drops an entry whose storage another texture takes over (`nvn_mtl.m` `alias_take_over`); it draws 1280x720 in both modes by its own choice. Big Brain Academy: Brain vs. Brain is NintendoWare Bezel Engine 1.56: nearly all of RomFS is in Bezel Engine archives of zstd entries, its shaders in their `.bfsha` entries (`shader_split.py` `bea_members`); its `nninitStartup` sets a heap or a 60 GB virtual address region by `IsVirtualAddressMemoryEnabled`, so the host reports the NPDM's system resource (profile key `sys_resource`, 0 here); its activity previews loop the movie decoder with Flush and Start from four threads (`hle_movie.c`); and it plays handheld with Boost (profile keys `op_mode handheld`, `perf_mode boost`), because only handheld does it read the touch screen. Patrick's Parabox is Unity 2020.3 IL2CPP with no bundles: `unityfs.py` has Unity 2020.3's `Shader` layout written out (`KNOWN_TREES`), it needs the SDK's address-region node area cut on its `sdk` build like Brain Memory Training Academy (`patch_nso.py`), and Unity's player hands the shared fonts straight to FreeType, so a game that imports no `ScalableFontEngineHelper::Decode` gets the TTF itself (`hle_sdk16.c` `font_skip`). Celeste is MonoGame for the Switch, its C# compiled to C++ by Sickhead's BRUTE: its shaders are MonoGame effects (MGFX) in its XNB files and in `main` (`shader_split.py` `mgfx_effect`), and it needed no host change. DRAGON QUEST XI S is Unreal Engine 4.18: legacy paks with LZ5 blocks, decoded on the Mac by the game's own LZ5 (`uepak.py`, `hle_oodle.c`), its shaders inline in the cooked packages (`shader_split.py` `ue_legacy_jobs`), and the slice-writing geometry shader of its colour-grading LUT folded into the vertex shader (`dmsh_build.py` `gs_slice`, `nvn_mtl.m` `prog_fold_gs`); Square Enix's sound driver never waits on the renderer event, so audio catch-up frames wait for queue space (`hle_audren.c`); its movie player plays only once every track holds a decoded output, so audio movie decoders keep 32 (`hle_movie.c`); and its Unreal region is capped at 4064 MB (profile key `game_args`, MB up to 4095) with the node area and large-region alignment cut on its SDK 7.3.2 build (`patch_nso.py`) for a 5 GB guest layout. [README.md](README.md) is the player-facing page (games, packing, playing); [docs/development.md](docs/development.md) has the internals, the script's options, building by hand, prewarm lists and per-game notes. +- This repository runs Nintendo Switch games natively on Apple platforms: the game's own code runs, rewritten only where Apple's kernel requires it. Thirty-two games are playable on the iPhone: Super Mario Bros. Wonder (update 1.2.1, profile `010015100B514000`), Fire Emblem Engage (update 2.0.0, `0100A6301214E000`), Metroid Dread (update 2.1.0, `010093801237C000`), 13 Sentinels: Aegis Rim (update 1.10, `01003FC01670C000`), Super Mario 3D World + Bowser's Fury (update 1.2.2, `010028600EBDA000`), The Legend of Zelda: Breath of the Wild (update 1.9.0, SDK 20, `01007EF00011E000`), The Legend of Zelda: Link's Awakening (update 1.1.0, SDK 8, `01006BB00C6F0000`) Super Mario Odyssey (update 1.4.1, SDK 7, `0100000000010000`), Hades (update 1.0.38233, SDK 9, `0100535012974000`), Hyrule Warriors: Age of Calamity (update 1.3.0, SDK 9.6, `01002B00111A2000`), Fire Emblem: Three Houses (update 1.2.0, SDK 6.5, `010055D009F78000`), Super Mario Galaxy (update 1.3.1, SDK 18, `010099C022B96000`) The Legend of Zelda: Tears of the Kingdom (update 1.4.3, SDK 20, `0100F2C0115B6000`) Monster Hunter Rise (update 16.0.2, SDK 11.4.3, `0100B04011742000`), Ace Attorney Investigations Collection (update 1.0.0.2, SDK 17.5.3, `010005501E68C000`), Hades II (base game, SDK 19.3.9, `0100A00019DE0000`), Unicorn Overlord (update 1.05, SDK 16.2.4, `010069401ADB8000`), Rhythm Heaven Groove (base game 1.0.0, SDK 21.4.0, `0100D9F01D474000`), Octopath Traveler 0 (update 1.0.5, SDK 19.3.13, `01005270232F2000`), Pokémon Legends: Z-A (update 2.0.2, SDK 19.3.11, `0100F43008C44000`), Cuphead (update 1.3.7, SDK 5.5.0, `0100A5C00D162000`), Super Mario Galaxy 2 (update 1.4.0, SDK 18.3.5, `0100FD8022DAA000`), FINAL FANTASY TACTICS - The Ivalice Chronicles (update 1.4.0, SDK 19.3.9, `010038B015560000`), Absolum (update 1.1.1, SDK 18.3.2, `0100A83022A22000`), Captain Toad: Treasure Tracker (update 1.4.2, SDK 8.2.0, `01009BF0072D4000`) and Brain Memory Training Academy (base game 1.0.0, SDK 16.2.4, `0100F5D01DA3A000`), The Great Ace Attorney Chronicles (base game 1.0.0, SDK 10.4.1, `010036E00FB20000`), STEINS;GATE RE:BOOT (update 1.0.3, SDK 20.5.17, `01008BC024FDE000`), Big Brain Academy: Brain vs. Brain (update 1.2.0, SDK 11.4.3, `0100620012D6E000`), Patrick's Parabox (update 1.0.1, SDK 13.3.7, `0100E44016C22000`), Celeste (update 1.4.1.1, SDK 15.3.2, `01002B30028F6000`) and DRAGON QUEST XI S: Echoes of an Elusive Age – Definitive Edition (update 1.0.4, SDK 7.3.2, `01006C300E9F0000`). Hades renders with Vulkan, which the host implements on Metal (`dm/gfx/vk/hle_vk.m`; `main` imports Vulkan, not NVN). Age of Calamity is Koei Tecmo's KTGL engine: files in RDB archives, shaders as GLSLC outputs (`shader_split.py` `idrk_shaders`). Three Houses is KTGL too; 9 of its shaders exist only as compiles of the game's own GLSLC at run time, captured once on the Mac (its `main` holds GLSL sources and imports `glslcCompile`). Breath of the Wild and Tears of the Kingdom run at 60 fps through a frame step the host decides in whole vsyncs (`patch_nso.py` `FRAME_STEP`, `nvn_fstep.h`, profile keys `fps_target` and `frame_step`); Tears of the Kingdom has a 5 GB guest layout. Monster Hunter Rise is Capcom's RE Engine: files in KPKA PAK archives (`shader_split.py` `kpka_jobs`, shaders in SDF files), the base game an XCI, its GPU heap a 64 GB sparse virtual pool (`hle_nvn.c` `vpool_*`, a 4 KB page table the replay reads textures through); it runs at 60 fps (30 on the Switch). Super Mario Galaxy is the Wii game on NERD's Hagi layer (PowerPC code recompiled into `main`), renders with Vulkan, and generates its shaders at run time, which the NSP script captures on the Mac (no SPIR-V file in its RomFS); its pointer is the touch screen in handheld mode (profile key `op_mode`). Ace Attorney Investigations Collection is Unity 2022 IL2CPP: its shaders keep their parameters as blob entries of their own (`shader_split.py` `unity_code_blobs`), and its C# file API goes through the SDK's C library, whose path checks the host answers (`hle_fs.c` `IsPathMounted` and the length limits). Hades II, unlike Hades, renders with NVN; its shaders bind textures at fixed slots up to 34 (slot 34's handle lies in the driver's image table, `dmsh_build.py`), and the two long lines through Melinoë while the staff's Omega attack charges are the game's own aim lines, not a rendering bug. Unicorn Overlord is Vanillaware's engine like 13 Sentinels (shaders compiled into `main`); both need the profile key `alloc_big_align`, which aligns large allocations to 64 KB so the ring heaps the engine cuts from a memory pool fit. Rhythm Heaven Groove is Nintendo's own 2D engine on SDK 21, the newest SDK so far (its DSP command list has no magic, `hle_audren.c`); it plays handheld with Boost (profile keys `op_mode handheld`, `perf_mode boost`), which skips its TV latency calibration and keeps its 1920x1080 draw size. Octopath Traveler 0 is Unreal Engine 5: everything in one pak with an AES-encrypted index whose key is found in `main` (`dm/gfx/tools/uepak.py`), its shaders Oodle-compressed in the pak's shader code archives and decoded once on the Mac by the game's own Oodle (any game with Unreal paks, `DM_OODLE_JOB`), its allocator replaced by the host's because Unreal's reserves 37 GB of address space (`patch_nso.py` cut, `hle_uemalloc.c`), its address region capped by the game's own argument (profile key `game_args`), and the particle shaders that pick a texture slot at run time bound by slot (profile key `tex_dyn_slots`); it runs at 60 fps (30 on the Switch) because the host writes Unreal's `t.MaxFPS` and `rhi.SyncInterval` in game memory (`hle_fpslock.c`, profile key `fps_target`). Pokémon Legends: Z-A is Game Freak's Trinity engine: everything in the packs of `arc/data.trpfs` (FlatBuffers, every file Oodle-compressed), which the shader split decodes with the game's own decoder in the Mac host's tool mode (`shader_split.py` `trinity_shaders`, `hle_oodle.c` `DM_OODLE_SERVE`); its 12 GB StandardAllocator is cut to 4 GB to fit a 5 GB guest layout, and the host refuses maps past the pool as Horizon does (profile key `mem_map_limit`); its characters are skinned with transform feedback (`dmsh_build.py` `vs_tf`, `nvn_mtl.m` `tf_draw`), and its occlusion queries, which decide how characters are animated, need the draw's base instance in the driver segment (c0 + 0x644, `encode_args`). Cuphead is Unity 2017.4 IL2CPP on SDK 5.5.0, the oldest SDK so far: Unity 2017's serialized and Shader formats are read by `unityfs.py` / `shader_split.py`, its rewritten modules (80 MB) need the profile key `mem_used` above the default 64 MB, or its `SetHeapSize` fails, and its NVN pool allocator breaks on a zero-size block, so the host never reports a ZCull storage size of 0 (`hle_nvn.c` `zcull_size`). Super Mario Galaxy 2 is Galaxy's Hagi layer with Galaxy's `sdk` build; its movie player reserves 384 MB of virtual address memory, which fits only because a rewriter cut on that `sdk` build shrinks the SDK's address-region node area to 62 MB (`patch_nso.py`; the profile's alias region is 512 MB). FINAL FANTASY TACTICS - The Ivalice Chronicles is Square Enix's engine on NVN: its shaders are GLSLC outputs in loose technique files, which `shader_split.py` reads in any RomFS file no other reader takes; its UI textures are written by the CPU into uncached pools (profile keys `tex_flush_upload`, `tex_dirty_upload`); and two job-pool workers share one auto-clear event, so a build cut makes a worker woken in another's place pass the wake on (`patch_nso.py`). Absolum is FNA (XNA 4 in C#) on .NET NativeAOT with SDL3 and FNA3D's NVN driver: its `main` starts its text with 1.7 MB of data the rewriter leaves alone (`nso_code.py`), its shaders are GLSLC outputs in `shaders.nx.bin`, its picture is flipped by a viewport swizzle the host applies, and everything lives in virtual address memory: malloc's 40 GB StandardAllocator is cut to 2.5 GB and the SDK's node area and large-region alignment are cut (`patch_nso.py`), and the .NET GC's region range is 1.5 GB (profile key `game_env`), to fit a 5 GB guest layout. Captain Toad: Treasure Tracker is Nintendo EPD Tokyo's engine (the `al` library over sead / agl, as 3D World and Odyssey) and needed no host change: it plays handheld because its pointer is the touch screen there (the Joy-Con gyro docked; profile key `op_mode`), and agl's post-effect program that samples its own attachment reads a snapshot (profile key `feedback_snap`). Brain Memory Training Academy is Unity 2022.3 IL2CPP with no bundles: its serialized files carry no type trees and nothing lends it a `Shader` tree, so `unityfs.py` has Unity 2022.3's `Shader` layout written out (`KNOWN_TREES`), and like Ace Attorney Investigations Collection it needs the SDK's address-region node area cut on its `sdk` build (`patch_nso.py`). The Great Ace Attorney Chronicles is Capcom's MT Framework: files in ARC archives of zlib entries, its 460 shaders in one shader package inside one of them (`shader_split.py` `mtspk_shaders`); it copies its textures into a CPU-cached pool after creating them and never flushes (its `main` has no cache maintenance at all), so it needs `tex_flush_upload` and `tex_dirty_upload` like FINAL FANTASY TACTICS. STEINS;GATE RE:BOOT is M2's ADV engine (E-mote characters, PSB archives, H.264 movies through the SDK's movie library): it needed `nn::audio::SetAudioOutVolume` (`hle_audio.c`), and because it decodes every picture into one reused region of a pool that lives for good, the storage-sharing texture table now drops an entry whose storage another texture takes over (`nvn_mtl.m` `alias_take_over`); it draws 1280x720 in both modes by its own choice. Big Brain Academy: Brain vs. Brain is NintendoWare Bezel Engine 1.56: nearly all of RomFS is in Bezel Engine archives of zstd entries, its shaders in their `.bfsha` entries (`shader_split.py` `bea_members`); its `nninitStartup` sets a heap or a 60 GB virtual address region by `IsVirtualAddressMemoryEnabled`, so the host reports the NPDM's system resource (profile key `sys_resource`, 0 here); its activity previews loop the movie decoder with Flush and Start from four threads (`hle_movie.c`); and it plays handheld with Boost (profile keys `op_mode handheld`, `perf_mode boost`), because only handheld does it read the touch screen. Patrick's Parabox is Unity 2020.3 IL2CPP with no bundles: `unityfs.py` has Unity 2020.3's `Shader` layout written out (`KNOWN_TREES`), it needs the SDK's address-region node area cut on its `sdk` build like Brain Memory Training Academy (`patch_nso.py`), and Unity's player hands the shared fonts straight to FreeType, so a game that imports no `ScalableFontEngineHelper::Decode` gets the TTF itself (`hle_sdk16.c` `font_skip`). Celeste is MonoGame for the Switch, its C# compiled to C++ by Sickhead's BRUTE: its shaders are MonoGame effects (MGFX) in its XNB files and in `main` (`shader_split.py` `mgfx_effect`), and it needed no host change. DRAGON QUEST XI S is Unreal Engine 4.18: legacy paks with LZ5 blocks, decoded on the Mac by the game's own LZ5 (`uepak.py`, `hle_oodle.c`), its shaders inline in the cooked packages (`shader_split.py` `ue_legacy_jobs`), and the slice-writing geometry shader of its colour-grading LUT folded into the vertex shader (`dmsh_build.py` `gs_slice`, `nvn_mtl.m` `prog_fold_gs`); Square Enix's sound driver never waits on the renderer event, so audio catch-up frames wait for queue space (`hle_audren.c`); its movie player plays only once every track holds a decoded output, so audio movie decoders keep 32 (`hle_movie.c`); and its Unreal region is capped at 4064 MB (profile key `game_args`, MB up to 4095) with the node area and large-region alignment cut on its SDK 7.3.2 build (`patch_nso.py`) for a 5 GB guest layout; it runs at 60 fps (30 on the Switch) because the host writes Unreal's `t.MaxFPS` and `nvn.SyncInterval` in game memory (`hle_fpslock.c`, profile key `fps_target`). [README.md](README.md) is the player-facing page (games, packing, playing); [docs/development.md](docs/development.md) has the internals, the script's options, building by hand, prewarm lists and per-game notes. - Each game's constants and shader tables form a game profile in `dm/games//`, where `` is the base game's title ID: `game.tsv` plus the tables and `dmsh_chain.h`. Mario's profile is `010015100B514000`. Tools pick the profile with `DM_GAME`; `make -C dm/host` and `build_ipa.sh` pick it with `GAME`. There is no default game: set it every time (the tools stop and list the profiles otherwise). Put per-game values in the profile, not in code ([dm/games/README.md](dm/games/README.md)). - Prefer general optimizations and fixes to game-specific rules. Performance work (frame pacing, power, caches, load times) counts here as much as correctness. First look for a change that holds for every game: Horizon / NVN / Vulkan semantics, or what the binary, its SDK version, its imports or its files show. Use a game-specific rule (a profile key, a rewriter cut, a build-ID gate, a per-game branch in the host) only when it is absolutely necessary, that is, when no general rule can do it. - Per-build knowledge is keyed by the NSO build ID: rewriter cuts in `dm/tools/patch_nso.py`, the profile's `build` rows (the builds the NSP script accepts, and their display versions), and host behaviour that uses fixed module addresses checks `module_t.build_id` (from `fixups.tsv`; `dm_main_build()`). When a new game build appears, carry addresses over with `dm/tools/nso_fnmap.py` and check them by disassembly; never let fixed addresses run on a build they were not found in. diff --git a/README.md b/README.md index 64eae28..8155959 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Needs iOS / iPadOS 26 or later on an iPhone 15 Pro or newer (A17 Pro chip or lat | ![Captain Toad: Treasure Tracker](docs/images/captain-toad-treasure-tracker.jpg)
Captain Toad: Treasure Tracker
IPA ~2 GB | ![Brain Memory Training Academy](docs/images/brain-memory-training-academy.jpg)
Brain Memory Training Academy
IPA ~0.1 GB | | ![The Great Ace Attorney Chronicles](docs/images/the-great-ace-attorney-chronicles.jpg)
The Great Ace Attorney Chronicles
IPA ~4 GB | ![STEINS;GATE RE:BOOT](docs/images/steins-gate-reboot.jpg)
STEINS;GATE RE:BOOT
IPA ~6 GB | | ![Big Brain Academy: Brain vs. Brain](docs/images/big-brain-academy-brain-vs-brain.jpg)
Big Brain Academy: Brain vs. Brain
IPA ~0.8 GB | ![Patrick's Parabox](docs/images/patricks-parabox.jpg)
Patrick's Parabox
IPA ~0.2 GB | -| ![Celeste](docs/images/celeste.jpg)
Celeste
IPA ~0.9 GB | ![DRAGON QUEST XI S: Echoes of an Elusive Age – Definitive Edition](docs/images/dragon-quest-xi-s.jpg)
DRAGON QUEST XI S: Echoes of an Elusive Age – Definitive Edition
IPA ~15 GB | +| ![Celeste](docs/images/celeste.jpg)
Celeste
IPA ~0.9 GB | ![DRAGON QUEST XI S: Echoes of an Elusive Age – Definitive Edition](docs/images/dragon-quest-xi-s.jpg)
DRAGON QUEST XI S: Echoes of an Elusive Age – Definitive Edition
IPA ~15 GB · 60 fps (30 on Switch) with update 1.0.4 | ## Pack an IPA diff --git a/dm/games/01006C300E9F0000/game.tsv b/dm/games/01006C300E9F0000/game.tsv index f62ad96..c780e7a 100644 --- a/dm/games/01006C300E9F0000/game.tsv +++ b/dm/games/01006C300E9F0000/game.tsv @@ -22,6 +22,12 @@ modules main subsdk0 sdk romfs_marker JackGame/Content/Paks/pakchunk1-Switch.pak zip_store .pak:.webm:.metallib +# ---- Graphics ---- +# 60 fps (30 on the Switch): the game's config caps Unreal at 30 (FrameRateLimit=30 in DefaultGameUserSettings.ini, which sets t.MaxFPS, and nvn.SyncInterval=2 in SwitchEngine.ini, the present +# interval 2 it asks for); the host writes t.MaxFPS 60 and nvn.SyncInterval 1 into its console variables (hle_fpslock.c), so the game asks for present interval 1 itself. Unreal steps the game by +# the time that really passed. DM_FPS_TARGET=30 (Documents/env.txt on the iPhone) keeps the game's 30 and saves power +fps_target 60 + # ---- Input ---- # a 3D game: D-pad, right stick (camera) and L3 / R3 on the iPhone's virtual gamepad pad_layout full diff --git a/docs/development.md b/docs/development.md index 709aaa3..8063022 100644 --- a/docs/development.md +++ b/docs/development.md @@ -437,7 +437,7 @@ The game's own work and its frame rate stay as they are; the host avoids work of - **Files and shaders:** legacy version 4 paks whose blocks are LZ5-compressed, decoded on the Mac with the game's own `LZ5_decompress_safe` (`uepak.py`, `DM_OODLE_SERVE` after `nninitStartup`, since the decoder uses the game's malloc). The 38,892 shaders are inline in the cooked packages and in `GlobalShaderCache-GLSL_SWITCH.bin`, each a zlib stream of an `LSLG` record (`shader_split.py` `ue_legacy_jobs`). The colour-grading LUT's slice-writing geometry shader is folded into its vertex shader's layer entry points (`prog_fold_gs`); the other 22 geometry shaders do not build ([04](agents/04-graphics.md)). - **Sound and movies:** SEAD refills its voices without waiting on the renderer event, so catch-up frames wait for queue space. The movie player registers its decoder events before filling them (read at signal time), plays only when every track holds a decoded output (audio decoders keep 32), and after a skip sends an end-of-stream packet for every input it gets (none is handed out after end-of-stream) ([03](agents/03-bringup.md)). - **Memory:** Unreal's one address region is capped at 4064 MB (`game_args`, MB up to 4095); two cuts on the `sdk` build (node area 62 MB, 32 MB alignment for regions of 1 GB and more) put it 64 MB into Engage's 5 GB block. Its cube-map array (510 faces) needed NVN's layer-face count ([04](agents/04-graphics.md)). -- **Frame rate:** 30 fps, as on the Switch; `hle_fpslock.c` has this build's `t.MaxFPS` and `nvn.SyncInterval`, unused without `fps_target` (60 fps not tried). +- **Frame rate:** 60 fps (30 on the Switch). The profile's `fps_target` writes 60 into this build's `t.MaxFPS` and 1 into `nvn.SyncInterval` (`hle_fpslock.c`, both threads' copies; the game's config sets 30 and 2); `DM_FPS_TARGET=30` (`Documents/env.txt` on the iPhone) keeps the game's 30 and saves power. On the Mac (M4 Pro) the field holds 60 fps (frame time p95 17 ms, GPU 6.6 ms p50 / 9.7 ms p95) at normal speed, since Unreal steps the game by the time that really passed, with sound intact. The game's dynamic resolution budgets are for 30 fps (`r.DynamicResolution.*TimeBudget` 28–35 ms in `SwitchEngine.ini`), so it does not lower the resolution to reach 60: where a frame needs more than 16.7 ms, frames are dropped instead. - **Mac input:** with the save of an earlier run (the name entered), `DM_KEYS="20:A*150/3,250:Plus*4/10,480:B,483:LUp~4"` skips the prologue movie at about 270 s and walks on Cobblestone Tor at about 480 s. ## Tools (`dm/tools`)