28 Commits
Author SHA1 Message Date
Erick Sanchez 427474d98c Keep full upstream updates and every game surface synchronized 2026-10-26 20:06:32 -06:00
seeddsandv0lumehi 6f58a7d4f6 Add cross-game regression harness and prevent false passes (#328)
Compare revision shader builds and pipeline interfaces across local games,
and run available smoke routes with crash, hang, timing and image checks.
Add the developer loop and share the measuring lock with bench.py.

Keep all local games in static checks and honor --no-smoke. Include shader
bytes and fallback pairs in cache keys, recheck interfaces on reuse, and
reuse smoke baselines only for matching inputs and run settings. Detect
failed prebuilds, missing or intermittently bad screenshots, and Vulkan
build failures even when no MSL was emitted. Keep game-derived files and
writable profile copies inside each game's NSP work folder.

Validation: tool tests and the lint ratchet pass. Full shader rebuilds and
gameplay comparisons remain unverified; the 26 discovered Mac builds have
no smoke routes in this checkout.

Co-authored-by: v0lumehi <aljic.zijad@gmail.com>
2026-10-27 07:18:30 +08:00
seedds 16061e0399 Rule: fix a bug at its root cause, not around it. AGENTS.md says to find why a bug happens (the faulting instruction and its decompilation, the bad value and what wrote it, the semantics the host or translator gets wrong) and fix it there; a change that only hides the symptom (stub, skipped call, clamp, retry, sleep or timeout, cache flush, profile key that turns a feature off) is a workaround, used only when the cause lies where it cannot be fixed (the game's own code, changed deliberately by a rewriter cut, or a platform limit) or the user asks for a stopgap, and then recorded as open with the cause or what is unknown. docs/agents/README.md has the same as a ground rule. Checked: dm/tools/tests/test_docs.py passes (AGENTS.md 1,419 words) 2026-10-26 09:42:57 +08:00
seedds 3a993fa3fe Subsystem references and a symptom index: the failure catalogue of docs/agents/03-bringup.md and the per-title material of docs/agents/04-graphics.md (section 1's container list, section 6 and its "Seen in <game>" subsections) move into docs/reference/, one heading per mechanism: rewriter.md, memory.md, threads.md, services.md, movies.md, audio.md, frame-rate.md, nvn-objects.md, rendering.md (with the Vulkan and OpenGL layers), shaders.md and game-state.md, indexed by docs/reference/README.md. Each entry gives the symptom, the cause, the fix (file and function), the games it was found on (linked to their notes in dm/games/<id>/README.md) and any check, caveat or later change; one mechanism met on several games is one entry. docs/agents/symptoms.md lists every entry's symptom (an assertion, a [RED] line, an abort or crash site, a visible result) with a link to it, 350 lines, generated from the Symptom bullets. 03 keeps the loop and reading the log, and its Failure catalogue section now points to the references, the index and where a new finding goes; 04 keeps sections 1-5 and 7 (the container list is shaders.md's Containers by engine), and section 6 says where each kind of finding is. Facts in the old sections about one game only were checked against that game's note, where they already were. Statements the code no longer matches are marked as history beside the current value (movie output slots: MD_NOUT 64, MD_NOUT_AAC 32, MD_NOUT_VIDEO 16; APP_POOL is the profile key app_pool; Engage's layout is 5 / 4.375 GB; inline uniform updates are deferred by default; scaled copies are drawn), and Tears of the Kingdom joins the node-area entry's build list (issue #70). The game notes, docs/agents/README.md, 02 and 05 link to the exact entries, and so do the comments in nvn_mtl.m (nvnTextureCompare) and hle_audio.c (the RemoteIO buffer) and the provisional-profile note in profile_guess.py. 03 goes from 29,600 to 470 words, 04 from 34,400 to 3,500. Checked: every backticked span, module address, issue / PR number and 16-digit hash of AGENTS.md, development.md, dm/games/README.md and docs/agents/01-05 at 6a06877 (4,600 items) still appears in the repository's Markdown (the same five file patterns as before appear as the full names they stood for), every relative link and heading anchor in the repository's Markdown resolves, and the tool tests pass (the four that need capstone or lz4 in the T7 venv) 2026-10-25 12:31:58 +08:00
seedds 79d82f7b64 Remove the D2R code and the legacy frame step
D2R (Diablo II Resurrected) has no profile and its code could not run here: the
host-call svcs its cuts emitted only died, the touch-run-v4 rule set needed a
missing dm/PRODUCT, and the host files its hooks called were never in this
repository. Removed with it:
- rewriter: D2R cuts, observation gates and parameter slots, the touch-run-v4
  rule set and private gates, the boot list, the cut scope column and the
  KRES / KASPECT key slots (fixups.tsv keeps the four thread slots; no
  params.tsv any more); dm/tools/product.sh
- host: D2R save edits, add-on content, render-scale controller, time()
  takeover and Terror Zone clock, crash decoding at D2R offsets, host-call
  registry and stubs, i18n, UI-screen, shell-app loader, zero-stripped
  archives, boot profiling hooks with no implementation; D2R headers, tests
  and scripts; dm/ios/Sources/dm_touch_controls; dm/third_party/libdeflate
- graphics: runtime extrapolation, world freeze, iPad UI shift, shop drag
  image, brightness LUT, visual ruler, bloom / FXAA shader hashes, DM_GFXUSE,
  the eco-fps setting, the loading-screen gate; shader_map.py,
  nometal_run.py, nvn_slots.py, nvn_slots.tsv and the PC-texture samples

The legacy frame step (frame_step legacy, DM_FRAME_STEP) is retired: the
Breath of the Wild and Tears of the Kingdom stubs lose their mode test.
Breath of the Wild's interval-controller cuts stay: the framework keeps the
controller's interval while the game's VFR override holds.

patch_nso.py output is byte-identical for all 15 games on this machine except
the two frame-step stubs; all 18 profiles build; the host tests pass (their
Makefile now builds the general tests only).
2026-10-21 02:09:49 +08:00
seedds c9f6683742 Octopath Traveler 0 plays into free play on the Mac: dynamic texture slots, image atomics, save calls
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.
2026-10-21 01:06:04 +08:00
seedds 476b1135b4 Octopath Traveler 0 boots on the Mac: Unreal pak and Oodle shader decode, the host's allocator for Unreal
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.
2026-10-20 23:01:20 +08:00
seeddsandarmyja e437e80ab6 Ace Attorney Investigations Collection boots on the Mac: Unity 2022 shaders, the C library's file checks and clock
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>
2026-10-20 19:55:55 +08:00
seedds 6563bb1990 NSP script: provisional profiles read their switches off the game files; a short heap check
dm/games/profile_guess.py decides the profile keys a game's files show, each written with its reason:
graphics vulkan and the modules to load (a subsdk only when main takes something from it the host does
not supply), shader_vk_capture / shader_main_glslc / shader_separable / shader_glslc_capture, the memory
partition (giant bss, no heap, virtual address memory), the SDK 8 / 10 font and NVN API, Hagi's handheld
pointer. --check compares the guess with every profile in dm/games: all 13 agree on these keys.

After the rewrite the script sets mem_used from the rewritten modules (Age of Calamity's 72 MB), writes
the empty shader tables and chain header a provisional Vulkan game lacked, and boots a game with virtual
address memory that sets a heap until its heap request to size the heap region (about 7 s; Tears of the
Kingdom gets its 0x98000000 alias region). A minute's boot could not tell which games need render_cond,
tex_flush_upload or core_preempt, so they stay with the developer.

--provisional builds a game that has a profile from a guessed one (DM_GAMES_FIRST in game.py and the host
Makefile, whose profile header is now rewritten only when it changes); --no-heap-check skips the check.
A capture run whose host stops early now says so. hle_svc.c logs a heap request the pool check refuses.
2026-10-19 18:50:50 +08:00
seedds c5955d42a6 English only: no Chinese left in code, scripts, logs, tables or docs
- Log messages, script output, test labels, Makefile errors and comments are English. Red log lines start with "[RED] " (was 【红】).
- Shader map stage column: vertex / pixel / compute / geometry / hull / domain (generators, dmsh_build / dmsh_facts and nvn_mtl.m stage_of follow).
- The NSP script's regexes match the new shader_split.py / dmsh_build.py summary lines; patch_nso cut scopes are all / host-only.
- Host UI text table (dm_i18n_tab.h) is English only; dm_lang_zh() removed.
- Text that matches game data (t_swkbd.c, t_textbox.c) is written as \x escapes.
- Shader chain: the chain files lose their bilingual comments, so every profile has a new chain version. dmsh_xlate.dll rebuilt from the translated Program.cs (SPIR-V byte-identical on 4,500 shaders of Wonder, TOTK and Age of Calamity), lock and dmsh_chain.h headers regenerated. c1_full.tsv restamped for Dread, 13 Sentinels and Wonder; the other profiles follow when their rebuild finishes.
- AGENTS.md and docs/agents: the language rule now covers logs; quoted log lines updated.
2026-10-19 15:34:57 +08:00
seedds c162b95a93 Super Mario Galaxy: profile 010099C022B96000 (1.3.1, SDK 18); plays on the Mac
The Wii game on NERD's Hagi layer (PowerPC code recompiled into main), rendering
with Vulkan through Hades' layer. Title screen, file creation and the storybook
prologue into the Star Festival at about 58 fps on the Mac; the IPA awaits an
iPhone test.

- Vulkan layer (hle_vk.m): push constants, descriptor arrays, dual-source
  blending variants, copies inside a render pass (cut and resume with deferred
  store actions), linear images over their memory, timestamp and occlusion
  queries, pipeline caches, vkCmdDraw, image copies and read-backs, USCALED /
  SSCALED and three-component vertex formats, viewport and scissor kept across
  passes, the INHERIT swapchain transform, the vi layer crop at present.
- vk_shaders.py: ArrayStride on struct members, push constants, array counts,
  `_d` dual-source variants, extra SPIR-V input dirs; non-vert/frag stages
  skipped with a red line.
- Run-time SPIR-V capture: DM_VK_CAPTURE in the layer, profile key
  shader_vk_capture and vk_capture.tsv (3,089 keys), and the NSP script's
  Vulkan shader capture stage.
- Handheld pointer: profile key op_mode, touch hooks fill touches only in
  handheld mode, InitializeTouchScreen(int,int), DM_KEYS taps, iOS gamepad
  forwards untaken touches to the game.
- SleepThread under 20 us takes the yield back-off (Hagi's 100 ns poll kept a
  core at 100%).
- nx_extract / NSP script: --allow-damaged for NSPs whose files fail the NCA
  hash check.
2026-10-19 14:35:31 +08:00
seedds 8b21049d2a Age of Calamity and Three Houses play on the Mac; indirect NVN draws; Dread GPU leak fixed
Hyrule Warriors: Age of Calamity (01002B00111A2000, 1.3.0) reaches its first
battle on the Mac:
- shader_split.py reads Koei Tecmo's RDB / IDRK archives (G2S `_S2G` / `_DOK`).
- Profile key mem_used (DM_GAME_MEM_USED) for svc GetInfo UsedMem; the default
  stays 0x4000000 for every other game.
- patch_nso.py: jump tables indexed by x30 / x29 (capstone names them lr / fp);
  a per-build cut for AoC's KTSL sound heaps.
- NVN: the compute program is bound apart from the graphics stages (cprog);
  nvnTexturePoolRegisterImage; DrawArraysIndirect; reflection c1 on the heap.
- oe copyright-frame calls and the hardware multi-stream Opus decoder answered.

Fire Emblem: Three Houses (010055D009F78000, 1.2.0, SDK 6.5, KTGL) plays its
prologue on the Mac:
- shader_split.py reads LINKDATA / SPKG with KT chunked zlib and keeps GLSLC
  outputs with 0x848 / 0x850 controls and headerless compute; 9 run-time GLSLC
  compiles are captured once with DM_GLSLC_CAPTURE (hle_glslc.c, profile key
  shader_glslc_capture).
- NVN: submissions serialized across the two queues; GetCommandMemoryUsed counts
  from AddCommandMemory; per-pool texture / sampler ID ranges; depth clamp
  replayed; integer vertex inputs fed half / unorm / snorm / packed formats get
  float32 bits converted on the CPU (also fixes AoC's flat soldiers).
- SDK 6: OpenPreselectedUser returns a Result; the audio renderer's DSP command
  header layout. DM_KEYS steps run in time order.

NVN indirect paths: quads and triangle fans from DrawArraysIndirect, and
DrawElementsIndirect, go through a GPU compute conversion into indexed indirect
draws; DispatchComputeIndirect is implemented (AoC's battle effects; red line
88 gone).

Metroid Dread GPU leak: defer_drain() runs in its own autorelease pool. Run on
the guest submit thread, whose implicit pool never drains, it kept every
retired object (and its upload staging block) alive; under CPU load Dread
climbed to the 24 GB watchdog. The committed tree had the same leak; it is also
behind Breath of the Wild's memory growth in issue #9.

NVN pass dump diagnostics (TOTK): TEXSNAP copies the attachments after the pass
on the Metal 4 stream; DM_NVN_DEPTHOVR=<fs key prefixes> forces depth ALWAYS;
D lines show the depth range and logic op; recorded clears are logged.

Docs: game notes, bring-up and graphics catalogue entries, the README's list of
games being ported. .gitignore: __pycache__/.

Checked on the Mac with the 9-game smoke test (Wonder, Engage, 13 Sentinels,
3D World, Breath of the Wild, Dread, Link's Awakening, Odyssey, Hades): known
red lines only, screenshots match the earlier runs, Dread flat at 1769 MB to
frame 24300. iPhone builds of the new games are untested.
2026-10-18 20:45:16 +08:00
seedds 696d6a68dc README: English only; iPhone 15 Pro or newer; Hades being ported
- README.md: the Chinese half is removed, and so is its link at the top.
- Requirement: an iPhone 15 Pro or newer (A17 Pro or later), or an M1 or later iPad, because the games'
  textures are BC-compressed.
- Hades is listed as being ported, next to Super Mario Odyssey.
- The third-party list names the Vulkan headers instead of bcdec.
- The Super Mario 3D World magenta-squares known issue is gone: fixed and confirmed on an iPhone
  (feedback_snap).
- AGENTS.md and docs/agents/README.md: everything, the README included, is in English. AGENTS.md also
  notes that Hades plays on the Mac through the Vulkan layer.
2026-10-18 10:29:42 +08:00
seedds 7fdf6dccbe README screenshots of every game; NSP script packs unknown games with a provisional profile; Link's Awakening stencil copy and outdoor shaders
- README: a gallery with one picture per game (docs/images): iPhone shots
  with the virtual gamepad for Mario Wonder, 3D World, BotW and Link's
  Awakening; Mac shots (captioned) for Engage, Metroid Dread and 13
  Sentinels. Link's Awakening is now listed as in testing on an iPhone.
- scripts/build_ipa_from_nsp.py: a game with no profile is surveyed, then
  gets a provisional profile guessed from the survey in
  <NSP folder>/<id>_build/profile/<id>/ (outside the repo, reused on the
  next run) and a best-effort IPA; --survey-only keeps the old behaviour.
  game.py searches DM_GAMES_DIR after dm/games; the host Makefile reaches
  such a profile through a symlink; build_ipa.sh finds it through game.py.
  zip_store also stores files whose headers say they are compressed.
- copy_t2t_draw: depth-stencil copies now carry the stencil ([[stencil]]
  export from an X32_Stencil8 view). Link's Awakening's occlusion pass
  tests stencil bit 1 of its quarter-size depth copy, whose stencil was
  the new texture's undefined content.
- Link's Awakening: four outdoor shaders with the foliage shader's input
  loop failed on the iPhone (Half2 / normalized formats); listed in
  shader_facts_manual.tsv. Chain c4995bb3161c.
2026-10-17 21:04:33 +08:00
seedds b89c050e18 BotW: cream cloud boxes behind HUD prompts fixed (layout captures read alpha from red in all 26 layout shaders); work folders move to the NSP folder
The prompt mask is a layout capture (panes tagged CaptureOn / A8): an R8
render target mapped (R, 0, 0, 1) holding the blurred alpha in red. Besides
the black shadow (e6a88e12), each prompt bar draws a cream glow (aa209f57)
whose alpha is min(cloud alpha, capture alpha); with only the shadow shader
listed, the glow filled each key's rectangle with the Kumo64 cloud.
alpha_from_red now lists every pixel-shader variant of the layout library
(main's BNSH at 0x1d83000) and tex_alpha_red only converts render targets.

Game work folders now live in <NSP folder>/<id>-work instead of the home
folder: AGENTS.md and the docs say so, and build_ipa.sh requires W and
writes the IPA next to the work folder by default.
2026-10-17 11:34:58 +08:00
seedds e734ced7d9 Zelda Link's Awakening runs on the Mac (profile 01006BB00C6F0000, 1.1.0, SDK 8); BotW HUD prompt boxes fixed
BotW: the button-prompt shadow samples an R8 blur whose swizzle (R,0,0,1)
made alpha 1, so every prompt got a solid box. New profile key
alpha_from_red takes alpha from red for the named shader.

Link's Awakening:
- NVN lower-left window origin (negative viewport height, winding flip,
  flipped present and raw screenshots)
- texture copies with format change, scaling, mirroring and depth
  (copy_t2t_draw); SetCopyImageStride; nvnWindowGetPresentInterval
- oe::PopNotificationMessage, vi SetLayerCrop / DestroyLayer /
  CloseDisplay / Finalize, audren P_SIZE 0x40 for SDK 8, hid style-set
  update event signalled once on bind
- Vorbis movie audio through stb_vorbis (dm/third_party/stb_vorbis)
- patch_nso jump tables: other index registers, mov copies, masked copies
2026-10-17 08:28:44 +08:00
seedds b68ac089a3 SM3DW magenta blocks fixed (same-pass feedback snapshot); English default language; English-only comments, bilingual README
- 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).
2026-10-17 00:41:10 +08:00
seedds abcfdf9215 The Legend of Zelda: Breath of the Wild (1.9.0) reaches the Shrine of Resurrection on the Mac, with a 60 fps cut; deferred texture binds read at execution
- Profile 01007EF00011E000 (SDK 20.5.22, NVN 55, main + subsdk0 + sdk): 19,592 shaders, all translated (chain bd3979ea9b3d).
- Loader applies DT_RELR relative relocations (SDK 20; main has 223,994); nvn_slots_got.py and DM_LOADHASH read them too.
- 60 fps: five rewriter cuts lower the GameFrameworkNx dynamic-interval floor from 2 to 1; ksys::VFR scales the
  logic time step by the interval (ratio 0.5 at 60 fps, falls back to 30 with ratio 1 when frames run long).
- Black 3D scene: nvnCommandBufferBindTexturesDeferred reads the handles at execution (bh_fresh); the engine
  records once and fills the handles later, so the lighting composite sampled texture 0.
- A texture attached at two colour slots is attached once (TBDR tile stores raced, albedo kept scattered tiles).
- Cube textures are 6-layer 2D arrays so tex_fit can bind cube-array views (1,512 shaders sample cube arrays).
- NVN functions other than command recording run on the per-thread host stack (Metal compiled on a small guest stack).
- Address-to-pool lookups use the last hit plus a sorted index (1,760 pools): submit time 21 ms -> 7 ms per frame.
- SDK 20: ARUID hook, audio renderer layout (Span GenerateCommand, new offsets, DeviceSink ID 0x14), nfp events,
  VR mode, seven-six-axis sensor, steady clock, launch parameter, stereo vision permission.
- Compressed render targets (BC4 via an RGBA16UI view) are skipped with a red line instead of a Metal abort.
- DM_KEYS takes frame numbers (f2500:A); TEXDUMP saves 2D-array layers; PASSDUMP prints skipped passes.
- NSP script skips empty NSP files and keeps one copy per DLC.
2026-10-16 16:07:15 +08:00
seedds 68cfe6c47e Super Mario 3D World + Bowser's Fury (1.2.2) reaches World 1-1 and Lake Lapcat on the Mac; textures sampled with another type get compatible views
- Profile 010028600EBDA000 (SDK 10.4.0, NVN API 53): 11,080 shaders from Yaz0 SARCs (BNSH, BAHS v9, effects, main).
- Shader key hashes min(0x880, control +0x20) bytes (0x850 / 0x848 controls); other games' tables unchanged.
- Profile keys device_api (NVN API version the game checks) and shared_font (ttf / bfttf at the shared-font address).
- File system hooks run on a per-thread host stack (HLE_FORCE_BIG, hle_bigstack.c): the save thread has 8 KB.
- Washed-out scenes: a one-layer 2D array sampled as texture2d read one value on Metal; tex_fit binds 2D / array / cube views.
- Unwritten colour outputs get a zero write mask; NVN 1D textures are one-row 2D.
- Hooks: InlineKeyboard, settings LanguageCode, ListAllUsers, oe perf / album, nifm SDK 10, prepo session, vi indirect image size;
  nvnQueueAcquireTexture, SamplerCompare, debug groups, TextureGetSwizzle / GetStorageClass.
- NSP script builds nx_extract in a temp folder (an apostrophe in the path broke MSBuild silently); DM_KEYS left stick.
2026-10-16 13:04:49 +08:00
seedds 550db7dc5a 13 Sentinels: Aegis Rim (1.10) reaches its first battle on the Mac; Mario holds interval-2 frames for two display ticks
- New profile 01003FC01670C000: sdk and subsdk0 are byte-identical to Metroid Dread's; data is six CRI CPKs; the 751 shaders are GLSLC outputs compiled into main (shader_split.py: shader_main_glslc), built as single-stage programs and paired per draw (shader_separable: every vertex shader is paired with a cover of the pixel shader inputs, so vs_pad lets any pair link)
- Host hooks for SDK 11 calls that reach a service: hid::GetMouseState, fs::CleanDirectoryRecursively, fs::EnableGlobalFileDataCache, oe::SetUserInactivityDetectionTimeExtendedUnsafe, time::ToPosixTime, audio::GetAudioOutName
- StandardAllocator::Allocate(size): on this build only, blocks of 64 KB or more are 64 KB aligned; the engine cuts three ring heaps out of a GPU pool at 64 KB alignment relative to its GPU address, and with the host's 4 KB-aligned address the third heap kept base 0 (null write when the prologue enters its first battle)
- NVN: triangle fans and indexed quads, vertex streams 14 and 15 on Metal slots 14 / 15 (Metal assertion buffer index 31), TextureBuilderSetPackagedTextureData copied into the storage at TextureInitialize, InsertDebugMarker as a no-op
- Mario 1.2.1 sets present interval 2 for single frames after a slow one and advances its time step by the interval; frames were shown for one tick and the game jumped ahead on the iPhone. The display link now keeps a frame for its whole period, and the 30 fps target waits until interval 2 has held for a second (Engage, which asks for 1 with its 60 fps cut, is unchanged). Test switch DM_TEST_PRESENT_LAG
- Survey notes: extra modules are supported, GLSLC outputs in main are counted
- Regression: Mario 1.2.1, Engage and Metroid Dread runs show only their known red lines; t_nvn 63/63; t_present_crop / t_mip_extent build again (t_mip_extent's one failure is the same at a9c56fd)
2026-10-15 23:41:10 +08:00
seedds 753dee6400 Metroid Dread (2.1.0) reaches gameplay on the Mac at 60 fps, cutscenes at 30
New profile 010093801237C000 (MercurySteam engine, SDK 11.4.3). A new game
runs through the title, the story recap and the opening cutscene into ZDR,
with movies and music. An unsigned IPA builds from the NSP folder; it has
not been tested on a device yet.

Rewriter and loader
- main has a 1.8 GB bss (the engine's static heap): when an appended patch
  area would start beyond 112 MB, it goes 32 MB before the module base
  (negative PATCH_OFF, IMAGE_SIZE / FILE_IMAGE_LEN). Mac loader, iOS
  embedding (no bss in the executable) and QueryMemory handle both layouts;
  Mario's and Engage's output is byte-identical.
- Observation gates may sit right after a bl as well as at function entries.
- Used memory counts that bss, so SetHeapSize asks for 1.38 GB, not the pool.

Host
- Hooks: oe::FinishStartupLogo, fs::CommitSaveData / DeleteDirectory (SDK 11
  names), hid::detail::SendVibrationValues (vibration graph flush).
- Guest paths resolve "." and ".." (Atk stream paths use ../../).
- Movies: the SDK's own demuxer runs natively; the decoder supports the
  native-texture mode (RGBA into the client's NVNtexture, sync signalled),
  the D1 destructor, copied client events, MediaCodec-like Flush / Stop, and
  an asynchronous input signal after Start.
- Same-core preemption approximation (profile key core_preempt, Dread only)
  plus gates that wait for the movie player's request ring: the game frees
  objects its higher-priority threads still use, relying on them preempting
  the main thread. 12 of 12 runs clean, before about half crashed.
- SleepThread is precise (macOS timer slack made 33 ms sleeps 36.4 ms): the
  game's 30 Hz sound thread drives cutscene frames, now 33.3 ms. Mario's
  frame-time p95 19.7 -> 18.4 ms.
- DM_KEYS takes <key>*<count>/<interval>.

Graphics
- BindProgram binds per stage: Dread's depth prepass unbinds only the
  fragment stage, which used to drop every prepass draw and leave scenes
  black. Composite programs give a vertex-only pipeline.
- WriteTexels writes regions in place (the map writes one texel per call);
  t_nvn checks 15 regions byte for byte.
- Pass dumps print Z lines for dropped draws.
- shader_split.py reads MSHD containers (1,010 shaders, 659 pairs).

Regression: Mario reaches frame 6900 with only the known red line; Engage
plays its opening movie and battle cutscene.
2026-10-15 21:30:44 +08:00
seedds 7f0cc98cdc Renamed nxnative; there is no default game
- The repository is now nxnative (first called smbw-mobile): a general NSP → native app converter, not a Mario port. README title and history lines updated; each game keeps its own app name and bundle ID (Mario's org.smbwmobile.app stays, so installed apps keep their saves)
- No default game: game.load() without DM_GAME, make -C dm/host / dm/gfx/tests without GAME, and build_ipa.sh without GAME stop and list the profiles (title ID and app name). The host Makefile still lets clean and a bare make (libvpx only) through; host unit tests only need GAME when they use the profile
- The NSP script is unchanged in use: it picks the profile from the NSPs' title ID
- Docs (README, AGENTS.md, profile and gfx READMEs, docs/agents) describe the required GAME / DM_GAME. dmsh_chain.py / dmsh_build.py comments still mention the old default: they are part of the chain hash
2026-10-15 14:01:11 +08:00
seedds 4d0e1a11cb Game profiles are named after the base game's title ID
- dm/games/smbw → dm/games/010015100B514000 (Mario, still the default everywhere), dm/games/fe_engage → dm/games/0100A6301214E000; GAME= / DM_GAME= take the title ID, and game.py lists the profiles (ID and app name) when given an unknown name
- Defaults updated in game.py, the host / host tests / gfx tests Makefiles and build_ipa.sh; workspaces follow as ~/<title ID>-work and NSP builds as <NSP folder>/<title ID>_build/
- Both chain versions are unchanged (Mario 271056163ea2, Engage a295a8035b7b: the hash covers table contents and keys, not the profile name). dmsh_chain.py and dmsh_build.py keep "default smbw" in their comments on purpose, since their source is part of the chain hash and editing them would force a full shader rebuild
- dmsh_chain.h headers regenerated (comment only); README, AGENTS.md, profile READMEs, gfx READMEs and docs/agents use the IDs
- Mario regression t82 clean; Engage boots and mounts its DLC under the new name; t_fs 68/68, t_fs_case 51/51, t_oe 47/47, t_nvn 61/61; iOS builds
2026-10-15 13:52:22 +08:00
seedds 328095a622 README rewritten: acknowledgements, packing an IPA with examples, games
- Acknowledgements first: d2r-mobile, Ryujinx, Atmosphere, yuzu, LibHac / hactool, SPIRV-Cross, deko3d and the vendored libraries
- Pack an IPA: requirements, NSP folder layout, terminal examples for Mario and Fire Emblem Engage (version, output, app name, keep-extracted, diagnostic build), Spyder use, what the script does
- Games table lists the two games that pack today and what each NSP folder needs
- Install and play, how it works, porting another game, building by hand, prewarm lists, game notes, tools, license
- AGENTS.md mentions Engage; docs/agents links d2r-mobile on GitHub
2026-10-15 10:27:35 +08:00
seedds 97fb028917 scripts/: build_ipa_from_nsp.py moves here and runs from the CLI too
- Scripts the user runs now live in scripts/ at the repository root;
  build_ipa_from_nsp.py moves there from dm/ios/tools (history kept).
  AGENTS.md records the convention.
- It still runs in Spyder (F5, settings block, prompts for an empty
  NSP folder or prod.keys). From a terminal it now takes options that
  override the settings: <NSP folder>, -k prod.keys, -p version,
  -o out.ipa, --app-name, --keep-extracted, --env-file (-h for help).
  Without keyboard input a missing value is an error instead of a hung
  prompt. Exit codes 0 / 1 / 130 in a terminal; inside IPython (Spyder)
  it does not call sys.exit, so the console stays clean.
- The repository root is found by walking up to dm/tools/nx_extract
  rather than a fixed depth.
- Checked: -h from another directory; error paths (missing folder, no
  terminal input, empty folder, unregistered version) exit 1 with a
  clear message; a CLI run on the Engage NSP folder identifies the game,
  skips DLC, picks base + update, passes the tool check and stops at
  the free-space check; IPython %run under the Spyder interpreter
  (prompts, -h, an error) keeps the console alive.
- README, dm/games READMEs and docs/agents point at the new path.
2026-10-15 08:38:58 +08:00
seedds af80109761 Docs: a general Switch-on-Apple project, one supported game
- README: presented as Switch games running natively on Apple
  platforms, with Mario as the one supported game. A games table
  (Mario playable, Fire Emblem Engage surveyed), component status,
  game profiles and how to add a game, the NSP script, generic
  step-by-step build commands (profile, workspace and version as
  variables), and a Mario section for the per-game notes (1.2.1).
- AGENTS.md: profiles, DM_GAME / GAME, workspaces, build-ID gating,
  the chain rule per profile.
- docs/agents: one repository with one profile per title; survey
  mode in 02-survey, including the compressed NACP title area.
- dm/gfx README (+ zh-CN): table paths in the profile.
2026-10-14 11:15:55 +08:00
seedds 73b1722248 Docs: iPhone fixes, update 1.2.1, prewarm lists; drop host README
- README: status for v0 / 1.2.1 on the iPhone, update 1.2.1 build
  steps, diagnostic env files, finishing the IPA by hand when Xcode
  uses a custom build location, app data folders after a reinstall,
  prewarm lists (pso_user.tsv / pso_manifest.tsv).
- AGENTS.md: per-build knowledge keyed by build ID, shader_pairs.tsv on
  the chain, unsigned IPAs only.
- docs/agents: update extraction and offset porting (01), the
  command-buffer byte, command memory, iOS audio, update imports and
  build-gated D2R code (03), vertex output padding and iOS math mode
  (04), on-device diagnostics and the offline link check (05).
- dm/gfx README / nvn_metal_mapping (en + zh-CN): pairs table, several
  game versions, prewarm lists, DM_PSO_USER, command buffer notes;
  corrected shader key, build script and 8-bit index lines.
- dm/host/README(.zh-CN).md removed: an index of d2r-mobile's D2R
  features; most files it listed no longer exist.
2026-10-14 01:21:04 +08:00
seedds 16d3820a72 Mario Wonder renders its title screen on the Mac host
Shaders
- shader_split.py (new): scan RomFS and main for every BNSH and agl BAHS
  container (bfsha, particle effects, layouts, agl/gsys packs, main):
  12,811 shaders, map + compute facts from the control section
- key = sha256(control + code): many shaders share a control section
- NVN 55 binding layout: bound textures at 0x20 + 8*slot, images 0x120
- bindless textures read from uniform buffers (tex_ubo / samp_ubo)
- GL depth mode: translator remaps z' = z/2 + w/2 in vertex shaders
- compute code areas carry a 0x30 header (magic 0x12345679)

Replay
- 8-bit index buffers widened to 16-bit
- texture swizzle through Metal swizzled views
- non-packaged textures shared by storage (fixes a ~13 MB/frame leak
  from transient render targets rebuilt every frame)
- view_t packed to 16 bytes (SDK 16 NVNtextureView; fixes a stack smash)
- TBDR optimizations off by default until they know bindless readers

Host
- alias region 2.875 GB for the 1.75 GB VirtualAddressMemory reservation
- ideal core -2 resolves to the process default core
- shared font served with a BFTTF "decoded" header
- crash reports scan the stack and dump the popped frame

Docs: docs/agents (guides for porting other titles), AGENTS.md
2026-10-13 00:54:49 +08:00