Commit Graph
49 Commits
Author SHA1 Message Date
seeddsandJeffrey Wang 64c17c26d4 patch_nso: offset-table jumps whose entry is loaded into another register
Cuphead (Unity 2017.4) main+0x1bb872c loads the table entry into x17 and only
the add writes x18: cmp w17,#7; ldrsw x17,[x15,x17,lsl #2]; add x18,x17,x15;
br x18. jump_offsets now takes the loaded register from the add; the walk
from the index back to the bounds check is unchanged.

Ported from GitHub pull request #1 onto the current jump_offsets (the PR's
other two shapes already pass since Link's Awakening's subsdk work). All 35
rewritable modules of the 9 extracted titles give byte-identical output.

Co-authored-by: Jeffrey Wang <11900676+armyja@users.noreply.github.com>
2026-10-18 08:59:19 +08:00
seedds 9b1d22d81f Super Mario Odyssey playable on the Mac: H.264 movies, sticks, HUD; all shaders compiled with preserved invariance
- Movies: H.264 decoded with VideoToolbox (dm/host/hle_movie_avc.c). This SDK's MP4 demuxer passes the parameter sets as the "avcc" record; pictures leave in presentation order through a 4-deep queue (41 of Odyssey's 44 movies have B-frames), and an end of stream drains the queue. The hint videos in Odyssey's tutorial boxes now play (they were black). VP9 unchanged (Engage: 2,816 packets in, 2,816 frames out).
- Controller input: GetNpadStates fills every requested history entry with the current state under consecutive sampling numbers. Odyssey reads all 16 entries whatever count comes back and polls every slot each frame; with only the newest written, neither stick did anything in play while buttons and menus worked (its first hint, "Look Around", waited forever). DM_KEYS takes RLeft / RUp / RRight / RDown.
- Odyssey's HUD counters were solid white cards: the profile lists its 26 layout shaders (main's BNSH at 0x132c000) under alpha_from_red, as for Breath of the Wild.
- -fpreserve-invariance on every Metal compile (dmsh_build.py MAC_METAL, ios_metal.py): Odyssey draws a depth pre-pass with alRenderDepthOnly, then the G-buffer with other vertex shaders and depth test less-or-equal; Metal gave the identical position math slightly different depths and those pixels kept stale content (Cap Kingdom's ground in 32x32 blocks on every fourth frame). Checked by recompiling the frame's 78 vertex shaders with the flag: four consecutive frames identical, no blocks. All eight chain versions change; Mac libraries and c1_full.tsv are being rebuilt and follow.
- DM_NVN_TEXSNAP works again (its entries lacked the layer field the dump loop reads) and names its files <path>_s<pass index>_<serial>_<level>.png.
- Smoke runs of the seven playable games on the Mac after these changes: only their known red lines.
2026-10-18 08:22:56 +08:00
seedds e39da25d93 No-BC GPUs: Breath of the Wild no longer aborts at start (GitHub issue #4)
The game encodes one 32x32 BC4 texture on the GPU, drawing its blocks through an RGBA16UI view. On a GPU with BC that attachment was already left out (Metal cannot attach a compressed texture or make an uncompressed view of one). Without BC the texture is stored decompressed (R8), the check on the Metal format missed it, and the host asked Metal for an R8Unorm -> RGBA16Uint view, which aborts the process (iPhone 14 Pro / A16; DM_TEST_NO_BC=1 reproduces it on the Mac).

attach, the pipeline prediction and the sampling views now decide by the NVN format: a BC texture is never a render target and its raw blocks are not sampled through an uncompressed view, on any GPU (red line once each). Checked on the Mac with DM_TEST_NO_BC=1: Breath of the Wild runs with the same two red lines as with BC; smoke runs of the other games unchanged.

Comments and dm/gfx/tex/README.md no longer point at d2r-mobile's offline ASTC pack (dm/tools/astc_pack.py), which is not in this repository; the memory cost of no BC (about 3.9 GB instead of 2.8 GB in Breath of the Wild) is noted in 04-graphics.md.
2026-10-18 02:45:47 +08:00
seedds 65deb2f03d Vertex outputs start at the hardware's (0, 0, 0, 1): Link's Awakening's outdoor sunlight fixed; IPA version is the game's display version
- dmsh_build.py vs_init: every float4 vertex output is set to (0, 0, 0, 1) before the translated body, as unwritten components read on the hardware. The translator's MSL started from all zeros; Link's Awakening's surface shaders divide a world position by its w, which the vertex shader never writes, so every sun-shadow compare value was infinite and the whole outdoor scene was lit as if in shadow (dim, no shadows from fences, house or trees). All seven chain versions change.
- Mac libraries and c1_full.tsv rebuilt for Link's Awakening, Engage, 13 Sentinels and Mario Wonder (Breath of the Wild, Super Mario 3D World and Metroid Dread follow).
- DM_NVN_TEXRAW dumps the depth plane of a Depth32Float_Stencil8 texture (shadow maps).
- The IPA's version (CFBundleShortVersionString / CFBundleVersion) is the packed game's display version: the update's when an update is packed, else the base game's (nacp.py --version, DM_APP_VERSION in build_ipa.sh, project.yml; the NSP script prints it).
- README: Link's Awakening screenshot outdoors in Mabe Village.
2026-10-18 01:44:42 +08:00
seedds d035e37c37 Depth mode per game at run time: Link's Awakening's outdoor blur fixed; README screenshots of Engage, 13 Sentinels and Link's Awakening
- NVN's depth mode is runtime state: Mario, Metroid Dread, Super Mario 3D
  World and Breath of the Wild keep near = -w (GL); Link's Awakening,
  13 Sentinels and Engage set near = 0. The translator rewrote z for GL
  in every vertex shader, so near = 0 games had all depths in 0.5..1 and
  depth-to-distance effects were wrong: Link's Awakening's depth of field
  blurred the whole screen outdoors.
- The translator no longer rewrites z; dmsh_build.py (vs_depth) writes two
  entry points into every vertex library, main0 with the GL rewrite (the
  same arithmetic as before) and main0_z without; the host picks one from
  the mode the game sets (mtl_set_depth_zero, shader_load). No per-game
  setting; a red line if the mode changes after vertex shaders loaded.
  Metal function constants were measured at about 2.4 ms of
  specialization per vertex shader, so two entry points instead.
- New translator dll (Ryujinx dlls unchanged), lock and chain versions for
  every profile; Mac libraries and c1_full.tsv rebuilt for Link's
  Awakening, Engage, 13 Sentinels and Mario Wonder (Breath of the Wild,
  Super Mario 3D World and Metroid Dread follow).
- Checked on the Mac: Link's Awakening outdoors (from a save in Mabe
  Village) has a sharp middle band again; Mario Wonder's title renders
  the same as before (differences only in animation).
- README gallery: Engage, 13 Sentinels and Link's Awakening recaptured
  with this build.
2026-10-18 00:02:28 +08:00
seedds f25ca5e670 Link's Awakening: physics crash at the switch to 30 fps fixed; README screenshots recaptured on the Mac
- Link's Awakening 1.1.0 main: the per-world PhysX completion task destroys
  itself in its release (vtable back to the base, mTm = 0). At 30 fps the
  game steps physics twice per frame; a worker descheduled between the run's
  unlock and the release let the main thread rebuild the task first, and
  the late destructor wiped the new one, so the next completion crashed in
  removeReference (main+0x10ccf28). A per-build cut makes that release a
  ret. Unfixed, every Mac run with the CPU loaded crashed; fixed, runs with
  138-257 switches to 30 fps and walks into and out of the house are clean.
- Crash reports print what each register points at ([dm] *xN), so an
  object's vtable names its class.
- RED_ONCE's flag array was one slot short.
- README gallery: Super Mario Bros. Wonder, Super Mario 3D World, Breath of
  the Wild and Metroid Dread recaptured with the current build; no "(Mac)"
  captions.
- docs: the crash in the bring-up catalogue; debugging recipe for racy
  crashes (CPU load, trace gates, a ring printed at the crash).
2026-10-17 23:27:32 +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 ab5c8ff1f4 Link's Awakening: game sound, doorway lighting tiles and missing foliage fixed
- Sound: SDK 8 numbers the DSP commands differently and its DeviceSink is
  0x10, not 0x13, so every rendered frame played silence (only the movies
  had sound). capture() now takes the one 0x148-byte command with 1-6
  inputs as the sink when the expected ID is missing.
- Lighting: the light-culling compute shader reads its workgroup count
  from NVN's driver segment c0 (+0x200). dmsh_build's drv_reads scan only
  matches fragment-shader names, so compute and vertex shaders got an
  all-zero c0: every tile row wrote row 0, and the doorway step flickered
  with white 64-pixel tiles. A declared driver-segment binding now counts
  as read, and compute c0 carries the dispatch size (0x200) and the
  uniform buffer table (0x210 + 16 r). BotW and SM3DW render as before.
- Foliage: the shadow / depth pre-pass pipeline 147c829e79fb322f failed
  (uint4 inputs fed 16-bit normalized formats), so with depth test EQUAL
  the palm tree and flowers never drew. New profile file
  shader_facts_manual.tsv (merged by shader_split.py) declares those
  inputs float; Link's Awakening's chain is now ccb18ba743bf.
2026-10-17 15:34:02 +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 1a8a5f5e94 README becomes a player-facing page: tested games, packing in four steps, playing; developer material moves to docs/development.md 2026-10-16 22:13:20 +08:00
seedds 96df78d81f DM_NVN_KEYDUMP logs every input of one fragment shader's draws on a device; PASSDUMP shows sampler LOD clamps and texture swizzles 2026-10-16 19:45:56 +08:00
seedds 4a5582ad8c Breath of the Wild holds 60 fps docked on the Mac: the replay hands its command buffer to the GPU every 64 draws; SDK 20 audio rendering goes through the host's locked path 2026-10-16 18:59:40 +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 145cb36b7f Directory listing skips per-entry getxattr and .holes probes; Engage's first frame at 1.8 s on the Mac
- Engage lists all of RomFS (216,554 entries) before its first frame. ReadDirectory called stat, getxattr (guest name) and a failing fopen("<file>.holes") for every entry, about 5 s on the Mac and 10 s on an iPhone (device log)
- guest_name reads the xattr only for .dm-case-* physical names: set_guest_name writes it only when the physical name differs from the guest name, which only those collision names do
- Directories take their type from d_type; files use fstatat and the .holes probe uses openat, both relative to the directory fd instead of resolving the full path again
- First frame 5.4 → 1.8 s on the Mac (three runs: 2.1 / 1.8 / 1.8 s under another app's load), opening movie at 7.4 s; Engage save → battle run clean, DLC still mounted; t_fs 68/68, t_fs_case 51/51; Mario regression t83 clean
2026-10-15 14:34:13 +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 dd3dbec548 DLC NSPs in the folder are packed and unlocked (Engage Expansion Pass)
- Engage's two DLC NSPs (…F001, …F002, 121 KB each) hold one empty marker file each (e0, h0); the content itself is already in update 2.0.0 (Fell Xenologue maps fld_e001–e006, the DLC Emblem room, Patch0–3). The host used to report no DLC and had no MountAddOnContent
- Host: aoc::CountAddOnContent / ListAddOnContent report the numbered folders under DM_AOC_DIR (index = title ID − base − 0x1000; hle_misc.c); fs::MountAddOnContent / QueryMountAddOnContentCacheSize mount DM_AOC_DIR/<index> read-only, a missing index answers fs 2-1002 (hle_fs.c). D2R keeps its fixed {1} without DM_AOC_DIR; DM_AOC_OWNED=0 still reports none
- Packing: nx_extract --aoc <dlc.nsp> <dir> writes a DLC's data NCA RomFS; build_ipa_from_nsp.py extracts every DLC NSP to <id>_build/aoc/<index>/ instead of skipping them; build_ipa.sh copies AOC (default $W/data/aoc) to <App>/aoc; the iOS app sets DM_AOC_DIR when it exists
- Verified on the Mac: Engage mounts e0 / h0 and DLCManager.RefreshHasList marks both contents owned (probe main+0x29f3c88: 2 of 2 with the folders, 0 without); nx_extract --aoc matches hactool; the script step tested on the user's NSP folder
- README games row and a bring-up catalogue entry
2026-10-15 13:52:12 +08:00
seedds 7a5788be63 RomFS lookups use a per-directory name index; Engage boots in 6 s instead of 21
- path_component matched each path component by scanning the whole parent directory and calling getxattr on every entry (case-sensitive guest names on case-insensitive APFS). Engage's RomFS has 216,554 entries, two Wwise bank directories of 38,000; walking RomFS at boot gave 17 s of black screen on the Mac and 32 s on an iPhone, and every audio file opened in play cost tens of milliseconds under the global path lock
- Read-only mounts (RomFS and overlays, a save that fell back to read-only) now build a sorted guest → physical name index per directory at its first lookup (dir_index_get, about 8 MB for all of Engage); writable mounts keep the exact scan
- Engage on the Mac: first frame 21.5 → 5.7 s, opening movie starts at 11 s instead of 49 s; prologue save → battle → combat run clean
- t_fs 68/68, t_fs_case 51/51 (its Makefile rule lacked $(GINC) and the test lacked g_mod / g_nmod, so it no longer built); Mario regression t81 clean; iOS builds
- docs/agents/03-bringup.md: catalogue entry for a silent black screen at boot
2026-10-15 12:57:06 +08:00
seedds 820738dc23 Engage: eShop and other system applet calls no longer crash
- Title menu → 新增内容 crashed on the iPhone: nn::ec::ShowShopAddOnContentList(ApplicationId, UserHandle) starts the shop applet through applet::CreateLibraryApplet, the host has no applet proxy (oe::Initialize is a no-op), and the SDK read through a null pointer at sdk+0x38660. Only D2R's no-argument overload was hooked
- All 18 nn::ec::ShowShop* overloads Engage imports return void, as if the player closed the shop at once (hle_misc.c); the title menu stays and 继续游戏 still works (Mac)
- Also hooked the other applet launchers Engage imports: friends::ShowFriendList / ShowBlockedUserList / ShowReceivedFriendRequestList / ShowMethodsOfSendingFriendRequest and web::ShowLobbyPage return failure, aoc::ShowAddOnContentLostError and err::ShowUnacceptable{AddOnContent,Application}VersionError are logged
- docs/agents/03-bringup.md: catalogue entry on finding unhooked Show… overloads
2026-10-15 12:56:57 +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 a1e388fbc8 Audio: per-output logs, Mac background round trip, no catch-up burst after suspend
- Each AudioOut is logged as audio #N: the open with its guest call site, Start / Stop / Close (first 32 per output), and the 2000-callback stats line per output. An Engage phone log had two outputs after a background round trip, one fed and one starved; the combined stats read as "every other callback underruns"
- DM_OE_SCRIPT gains bg=1 / bg=0, the same sequence as the iOS lifecycle callbacks (focus, audio take-over, guest clock freeze, graphics suspend); DM_AUDIO_MUTE=1 silences the real Core Audio path for such tests on the Mac
- The silent sink no longer makes up time the process was suspended (it pulled seconds' worth at once and logged hundreds of underruns)
- Host tests t_audio, t_audio_spsc and t_oe build again (game profile include, stubs) and pass
- docs/agents/03-bringup.md: several outputs, background round trip on the Mac
2026-10-15 10:16:17 +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 6c0a14dcb8 Host: saves survive a full disk; free thread blocks; iOS memory headroom
Ported from d2r-mobile (b94670c79, 2eb575877, 704147218), adapted:
- Save transaction: if the work directory cannot be set up at mount
  (leftovers not removable, clone fails, e.g. a full disk) the host
  now mounts the committed saves read-only and tells the front end
  instead of exiting; a reclone after a refused commit clones beside
  the work directory and swaps it in atomically, and on failure keeps
  it tainted for the next try; a failed rename rollback refuses the
  transaction instead of exiting. Creating an existing file or folder
  on a read-only mount answers "already exists", and file / directory
  calls ignore the null handle a failed open leaves. Test switch
  DM_TEST_FS_FAILAT; tests/t_fs.c section 6 (68 of 68 pass). An Engage
  run with the mount clone failing loads its autosave read-only and
  plays on.
- iOS front end: front_save_failed shows a bilingual notice (it only
  logged before).
- thread.c: each guest thread's TLS block, thread-local region and
  thread object (9.5 KB) are freed at thread exit through a key of
  their own (the guest overwrites the TLS / TLR key slots).
  tests/t_thread_leak.c: 600 blocks leaked before, none now.
- Memory line on iOS: os_proc_available_memory, how far the app is
  from being killed for memory.
- The host test Makefile gets the game profile include path; t_fs
  stubs the module table. Docs: 03-bringup memory line fields,
  05-debugging save failures. Mario regression t78 and the iOS compile
  are clean.
2026-10-15 08:12:10 +08:00
seedds 6788f74019 NVN: dirty-page tracker race; graphics unit tests build and run again
- Ported from d2r-mobile ef9b1ff13, adapted to our shared-page ranges:
  setting a page read-only marked it PROT before mprotect landed, so a
  concurrent unprotect-before-write could set it back to NONE and then
  the mprotect made it read-only anyway. A game thread writing it then
  spun forever in the fault handler, and a host system call into it
  failed with EFAULT. Now NONE -> BUSY -> mprotect -> PROT (and back),
  waiters re-check after BUSY clears, and the wait is capped at 2 s
  with a red line. Engage's movie decoder writes frames from a host
  thread while replay protects pages; this is a likely cause of the
  old early stalls.
- tests/t_dirty_race.c (ported) reproduces it deterministically: on
  the old tracker read() into the page fails with EFAULT; it passes
  now, and t_dirty_slots still passes.
- The graphics unit tests had stopped linking (prog_key moved to
  hle_nvn.c) and some crashed (hand-built textures lacked the texture
  magic replay now checks; t_nvn used a function that has since been
  implemented as its unimplemented example and hard-coded 523 names).
  Fixed: t_nvn, t_fuse_check, t_zskip, t_xv, t_ub, t_pfv, t_present_crop,
  t_rt_depth_ro, t_rt_range_shape, the dirty tests and others pass.
  Still failing, the same before this change: t_reorder_chain (30 of
  155), t_defer_chain (1 of 59), t_mip_extent (1 of 303), t_rt_range,
  t_rt_range_ro; t_refl needs D2R shader samples.
- Mario regression t77, an Engage movie / title / demo run (0 missed
  dirty pages) and the iOS compile are clean.
2026-10-15 07:59:35 +08:00
seedds 12a20d08ff NVN: evict a finalized pool's shared textures; more memory readouts
- The storage-sharing texture table (g_alias, keyed by pool object,
  offset and description) never evicted. Engage frees memory pools on
  scene changes and builds new ones, so the old pools' render targets
  and other private textures stayed for good, and linear textures built
  on a pool buffer kept the whole buffer alive. A reused pool object
  address could also hand an old texture to a new pool. Finalizing a
  pool now evicts its entries (mtl_alias_drop_pool) through deferred
  release.
- With 21b3bc3, Engage's 850 s idle demo stays at 1.2 to 1.4 GB of GPU
  memory (5.4 GB before both fixes) and ends at 2.5 GB resident instead
  of 3.6 GB; in the prologue battle GPU memory is 1.07 GB at frame
  11100 (2.38 GB before).
- The memory report line adds the host objects held by serial, the
  finalized pool buffers still alive, and the sharing table size with
  its evictions.
- Mario regression t72 (it does finalize pools: 32 evictions; same
  scene, 60 fps, GPU memory unchanged), Engage demo scenes and combat
  are clean; the iOS project compiles. Docs: 04-graphics entry.
2026-10-15 05:40:02 +08:00
seedds 85bed8087b Engage: fit the guest block in 5 GB; the early stalls look fixed
- Measured over a 12-minute run: StandardAllocator commits only 0.58 GB
  of the 3.14 GB it reserves, Unity's own regions 0.97 GB. The profile
  now reports a 2.5 GB pool (StandardAllocator about 2.44 GB) and uses
  a 5 GB block with a 4.375 GB alias region, the total proven on a
  phone. The old 6 GB block was iOS's single-reservation limit and left
  Metal no address space. Reserved address space peaks at 3.47 of
  4.31 GB; a 670 s battle-script run is clean.
- The early stalls (one run in four) have not come back in 12 runs
  since the real thread pause for IL2CPP's GC; the docs mark them
  probably fixed and keep the sampling recipe.
- Docs: sizing entry with the commit split and how to measure it,
  profile notes, README Engage row (it reaches the battle map; the
  scripted presses end on the save screen, not in combat).
2026-10-15 03:01:15 +08:00
seedds fd56427caf Docs: the Engage battle crash entry in 04-graphics is fixed
- The prologue battle crash is marked fixed, with a pointer to the
  address-space entry in 03-bringup (missed in fe9bab9).
- The early stalls get their own open entry, with the sampling recipe
  and the new stderr-lock line from the crash handler.
2026-10-15 02:08:40 +08:00
seedds 3ab0ea16cd Host: crash report never waits forever on stderr; Engage demo crash fixed
- crash.c: before printing a crash or time-limit report, the handler
  tries the stderr lock for 1 s. If another thread holds it, it writes
  one line (signal, pc / lr as module offsets) with write() and exits,
  instead of hanging with the process and leaving an empty log. This
  is for the intermittent early stalls, where even the SIGALRM report
  never appeared.
- Engage's idle attract-demo crash (about 220 s) was the same alias
  address-space shortage as the battle crash: the particle index
  buffers got no mapping past 2 MB. With the node-area cut (fe9bab9) an
  850 s idle run is clean, with no corrupted texture objects; with the
  old SDK the same run dies at 159 s in Unity's heap allocator.
- Docs: the 04-graphics entry is marked fixed with the cause; 03-bringup
  and the README Engage row updated. Mario regression t65 clean; the
  iOS project compiles.
2026-10-15 02:05:23 +08:00
seedds 4ab6d84190 Engage: shrink the SDK's address-region node area; the battle crash is gone
- The crash about 6 minutes into the prologue battle (null write at
  main+0x9a4664) was the alias region running out of address space, not
  memory: Unity's low-level virtual allocator got no region from
  AllocateAddressRegion and returned 0. The SDK's AddressRegionManager
  keeps the first 1 GB of the alias region as its node area and aligns
  each region by size; after StandardAllocator's 3.14 GB block only about
  1.2 GB was left for Unity, and it was full and fragmented.
- patch_nso.py: a host-only cut for Engage's sdk build (56ec2474…)
  shrinks the node area from 0x3fe00000 to 0x3e00000 (62 MB, about 1.35
  million nodes). Both constructor movs change together.
- A 12-minute scripted run now plays through the battle without a crash;
  reserved address space levels off at 4.17 of 5.31 GB.
- hle_svc.c: the alias-mapped total counts pages with a bitmap, so maps
  of already-mapped pages and oversized unmaps no longer skew it (it went
  negative). Engage really maps about 1.5 GB, not 0.5 GB.
- Docs: bring-up catalogue entry with the measuring recipe, profile
  memory notes, README Engage row. Mario regression t64 clean; the iOS
  project compiles.
2026-10-15 01:35:34 +08:00
seedds 72aa5ca40b Host: real thread pause for IL2CPP's GC; Engage reaches its first battle
- SetThreadActivity and GetThreadContext3 were placeholders that only
  reported success. IL2CPP's garbage collector pauses each thread and
  reads its registers for roots, so threads kept running and roots held
  in registers were missed. A thread inside an SVC now counts as paused
  at once with the gate frame as its context (thr_svc_enter / leave
  around every gate SVC; it waits at the SVC exit while paused). A
  thread in guest code is stopped with SIGUSR2 and parks in the handler.
  The context is Horizon's 0x320-byte ThreadContext.
- nn::pctl::IsFreeCommunicationAvailable answers "not available" like
  the other free-communication calls; the SDK aborted in sm when Engage
  entered its first battle map.
- Memory log lines: heap growth, alias-region high-water mark and the
  total mapped in the alias region (one line per 256 MB).
- Engage profile: a 6 GB reservation (5.375 GB alias region, 128 MB
  heap region), iOS's single-reservation limit without the extended
  virtual addressing entitlement. Engage never calls SetHeapSize; its
  mappings reach +5.26 GB.
- Engage on the Mac: with scripted input and DM_KBD answering the name
  keyboard, a new game reaches the prologue cutscene and first battle
  map. Open: a heap-allocator crash a few minutes into that battle, and
  occasional early stalls (docs/agents/04-graphics.md).
- Docs: catalogue entries, README rows. Mario 1.2.1 regression (120 s
  scripted) clean; the iOS project compiles.
2026-10-15 00:40:49 +08:00
seedds d47f89986b Host: crash gate, NVN texture snapshots, address arbiter fix
- crash.c: only the first crashing thread prints its state; later ones
  wait in pause(), and a 15 s alarm ends the process if the dump stalls.
  Unity's job threads crash together, and the dumps used to interleave
  and leave the process hung.
- NVN render target, texture copy and clear records now carry a copy of
  each texture object (REC_SNAP / tex_snap) instead of the game's
  pointer, as NVN commands do not depend on the API object once
  recorded. Replay rejects objects without the texture magic and sizes
  beyond Metal's limits with a red line (a 1869367126-high texture used
  to abort in Metal's validation).
- SignalToAddress type 2 with count <= 0 and waiters present now
  subtracts 2 (mesosphere, Ryujinx and yuzu agree); it subtracted 1.
- Engage on the Mac: scripted input reaches the main menu and a new
  game's appearance choice. The idle attract demo still crashes after
  about 220 s (Unity's particle geometry job overruns its index buffer);
  findings are in docs/agents/04-graphics.md.
- Docs: catalogue entries, README row. Mario 1.2.1 regression (120 s
  scripted) clean: only the known SetRenderEnableConditional red line.
2026-10-14 23:15:44 +08:00
seedds 53a1273cd1 Host movie decoder: Fire Emblem Engage reaches its title screen
- hle_movie.c implements movie::Decoder (the SDK's MediaCodec wrapper)
  through hooks on every vtable method: DecoderEvents signalled with
  the SDK's SignalEvent, input / output index queues, Buffer and
  MediaData filled with the SDK's own setters, one NV12 frame per
  packet into the buffer Unity allocates (w x h x 1.5); packets wait
  while every output slot is taken. Frames are black for now (no VP9
  decoding yet); the size comes from Configure or the VP9 header.
- Engage on the Mac: loading screen, the 94 s opening movie, then the
  title screen and, left idle, the 3D attract demo. Not yet: input,
  a crash about 221 s into the demo, an unmapped texture format, iOS.
- Docs: catalogue entries (movie protocol, shader keys, CPU-written
  textures, NVNprogram size), Engage graphics notes, README row.
2026-10-14 20:29:27 +08:00
seedds 41ac235637 Unity shaders: UnityFS reader, Engage shader tables and NVN layout
- New dm/gfx/tools/unityfs.py: UnityFS bundles (LZ4 / LZMA blocks),
  serialized files (formats 19..22), objects read by their type trees
  (Unity's common string table comes from the game's own main), and a
  Shader object's per-platform compressed subprograms. The player's
  own files have no type trees and borrow the bundles' Shader tree of
  the same type hash.
- shader_split.py reads Unity Shader objects in a second pass: each
  Switch subprogram is one NVN program (0x60-byte header of per-stage
  offset / control size / code size, then control + code per stage);
  a program's vertex / pixel stages form a pair. Engage: 9,269
  shaders, 4,634 pairs. Mario's tables and package are byte-identical.
- Engage profile: shader groups engine / assets, measured NVN layout
  (texture handles 0x168 + 8 r, 128 slots; separate samplers 0x568),
  regenerated tables, chain a295a8035b7b. 9,268 of 9,269 compile (one
  geometry shader; Metal has none).
- dmsh_build.py: the translator's built-in bases only matter with
  per-slot facts, so the guard checks that; c2 reads count the sampler
  table and 16-byte rows overlapping a table as fillable.
- game.py: DM_GAME_NVN_SAMP_BASE. NSP script survey note updated.
- Mario chain is now 271056163ea2 (Mac and iOS libraries rebuilt,
  16,266 of 16,268 as before; c1_full regenerated).
2026-10-14 20:29:26 +08:00
seedds 779e5097f3 Host and rewriter: fixes for Unity / SDK 12 titles (Engage bring-up)
- patch_nso.py: three more x18 forms from IL2CPP code: an offset jump
  table (targets read at rewrite time), blr x18 (original return
  address kept for C++ unwinding) and an sp write from x18 (alloca).
  Mario's rewrite output stays byte-identical.
- loader.c: symbols resolve globally in load order, also for a
  module's references to its own exports (local / non-default
  visibility excepted). Engage's main overrides operator new / delete;
  sdk bound its own copies and freed Unity's blocks into the SDK heap.
- hle_svc.c: QueryMemory reports each module's Horizon layout (code,
  read-only, data, patch area) and other host memory as free, which
  the SDK's C++ unwinder (nn::ro::detail::GetExceptionInfo) needs.
- New takeovers: nn::fs::IsMounted, nn::ro (hle_ro.c), prepo
  Initialize, SDK 12 audio device names and OpenDefaultAudioOut, an
  SDK 12 hid block (single GetNpadState, modes, touch, gestures,
  six-axis details, colors, applets), movie::Decoder with no impl
  (hle_movie.c) and failing nvdrv opens (hle_nvdrv.c), and NVN getters
  / no-ops Engage calls. nvn.h: TextureGetMemoryPool returns a pointer.
- Makefile: a game stamp, so changing GAME rebuilds the host.
- docs/agents/03-bringup.md: catalogue entries for all of the above.
- Mario 1.2.1 scripted run still reaches the opening course with the
  same single red line.
2026-10-14 18:09:30 +08:00
seedds 90875c73bd NVN: honour render-target view formats; present sRGB swapchains raw
- attach(): a render-target view that changes the format is attached
  as a Metal view of that format (kept until the submission completes),
  so pipeline formats follow it. Mario's last pass writes the RGBA8
  swapchain texture through an RGBA8_SRGB view; with the base texture
  attached, linear values were stored unencoded and the whole picture
  was darker: on the Mac and the iPhone every channel equalled the
  Switch value decoded from sRGB once more (0.55 -> 0.26). After the
  fix the Mac title frame matches a Switch screenshot (mean RGB
  0.556 / 0.621 / 0.633 vs 0.557 / 0.621 / 0.633).
- present_raw_src(): an sRGB-typed swapchain texture is sampled through
  a non-sRGB view, so the stored bytes reach the screen as on the
  Switch's scan-out (no change for Mario, whose swapchain is RGBA8).
  t_present_crop gets an sRGB case (34/34; fails without the fix).
- docs/agents/04-graphics.md and the mapping docs: the rule and how it
  was measured.
2026-10-14 18:09:30 +08:00
seedds 527fa29007 Remove the [DEBUG-flk1] instrumentation
- hle_nvn.c: drop the command memory callback / grant / caller
  logging, the overflow details, the +0x10 write log, the one-off
  NVN table dump, and the DM_DBG_GRANTS, DM_DBG_CALLSITES,
  DM_DBG_CMDMEM_SHRINK and DM_DBG_OLD_USED switches.
- nvn_mtl.m: drop the per-draw NaN probe (DM_DBG_NANPROBE), the
  colour target statistics (DM_DBG_TEXSTAT) and the PASSDUMP
  completion timing.
- Kept as regular diagnostics: the audio callback line every 2000
  callbacks (frames per callback, queued blocks, underruns) and
  DM_DBG_AU_FRAMES (docs/agents/03-bringup.md, crackling on iOS);
  DM_NVN_SHOT up to 4096 frames (the 05-debugging burst recipe).
- Mac 1.2.1 scripted run reaches the opening course with the same
  single red line as before.
2026-10-14 13:02:16 +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 874001eaee Sound, button icons, fountain water: CPU audio renderer, extension font, ordered copies
- Audio: run the sdk's own audio renderer on the CPU in manual mode (hle_audren.c),
  feed its DeviceSink output to the AudioOut sink; catch-up render on back-to-back
  updates fixes the ApplyLightLimiterEffect crash at the title screen
- Button icons: make_ext_font.py generates a stand-in Nintendo extension font
  (A/B/X/Y, L/R/ZL/ZR/SL/SR, +/-, D-pad, stick arrows); served as shared font type 5;
  the iOS bundle now carries both shared fonts
- Fountain panels: Metal 4 does not order copies inside one encoder; add barriers
  when ranges overlap, so the GPU wave simulation no longer reads a stale uniform
- Zero-fill new colour textures; scissored zero clears; uniform-update snapshots
- Docs: graphics ordering, audio, diagnostics, intermittent-glitch method
2026-10-13 13:16:42 +08:00
seedds b8d8164639 iOS app: unsigned all-in-one IPA with a standard-controller gamepad
- dm/ios: lean app shell (main.m boot/env/log, front_ios.m front.h + 16:9 Metal view,
  DmGamepad.m touch gamepad in Nintendo positions + GameController), xcodegen project,
  tools/build_ipa.sh (embedded game code, iOS shader libraries, RomFS in the bundle)
- Guest address space back to 5 GB (heap 1.625 GB; the game asks for 1.47 GB)
- README / agent guide: iOS build steps and address-space sizing
2026-10-13 09:20:43 +08:00
seedds 53642a6f9c Opening course playable: integer vertex inputs for skinned models, sky colour ramps
- dmsh_build.py: vertex shaders whose inputs are only used as integer bit casts are
  re-translated with uint inputs (529 shaders); bone indices were converted to floats and
  every character was stretched off screen
- Host: Metal vertex formats follow the shader input type (signedness swap, 32-bit float
  reinterpretation), mismatches reported
- LINEAR_RENDER_TARGET textures in CPU-visible pools are zero-copy too (sky colour ramps)
- Pass dump lists all vertex attributes; docs and README updated
2026-10-13 08:56:30 +08:00
seedds 4d8ac53681 Into the opening course: resource heap slack, compute start offset, CPU-written text
- patch_nso.py: Mario main cut gives every resource heap +16 KB; Model/NpcCommonerA.bfres
  came up 0x70 bytes short of its size-table budget while loading Course900
- dmsh_build.py: compute instructions start at code area +0x100, not +0x30; the misaligned
  decode turned 12 particle / post-effect compute shaders into endless loops (GPU hang)
- Linear textures in CPU-visible pools are built on the pool buffer, so the CPU-rasterized
  glyph cache reaches the GPU (menu and dialogue text)
- Host command-memory segments grow on demand; RGB10A2SN vertex format; instanced DrawArrays
- DM_PROBE: thread IDs, register-relative dumps, hit filters, ExpHeap walker
- dm/tools/ghidra/Decompile.java; agent guides and README updated
2026-10-13 08:13:48 +08:00
seedds 02321677e7 Input past the title screen; virtual pools; probes without a debugger
- DM_KEYS scripted controller input ("seconds:button[+button][~hold]")
- NVN virtual memory pools aliased onto their physical pool when every
  MapVirtual request shares one base (Mario's 128 MB pool); previously
  MapVirtual failed and the game treated the pool as unusable
- DM_PROBE ported from d2r-mobile (host/probe.c): brk probes with
  register / backtrace output, plus DM_PROBE_X2 object dumps; used
  because lldb launches now wait on a macOS authorization prompt
- diagnostics: DM_NVN_VLOG (pools, MapVirtual), DM_NVN_CBLOG (command
  memory grants and callbacks), DM_NVN_ALLOCPROF
- docs: new failure patterns and the probe recipe

Open: starting a new game crashes while loading the opening course
(model resource heap overflow for NpcCommonerA.bfres).
2026-10-13 01:26:39 +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