Skip to content

Super Mario Bros. Wonder Nintendo Switch 2 Edition (Program 1): boot, New Game, Continue and correct rendering on the Mac - #1639

Open
BrentRosen wants to merge 2 commits into
seedds:mainfrom
BrentRosen:wonder-switch2-edition
Open

BrentRosen wants to merge 2 commits into
seedds:mainfrom
BrentRosen:wonder-switch2-edition

Conversation

@BrentRosen

Copy link
Copy Markdown
Contributor

Summary

Replaces #1637, closed only to drop a stale area label (an unrelated Donkey Kong Bananza documentation change was removed from the branch).

Super Mario Bros. Wonder – Nintendo Switch 2 Edition + Meetup in Bellabel Park runs on the Mac from a raw dump: the Switch 2 program 040015100B514001 (update v327680, display 1.2.1, main build ec47e6f8846d2a7b6892e2682845c725) beside the original program, with the Bellabel DLC. This PR collects everything that needed to change for it to boot, start a New Game, continue an existing save and play, and render correctly.

The 120 fps work done on the same build is not included.

What was required

1. Reading the dump (program 1, shared files, DLC)

The unpacked update puts the companion in updates/327680/program1/, with the original program's files beside it, and the DLC under nested base/update snapshots.

  • scripts/pack_dump.py: program_dir / default_program select program 1 when a complete Switch 2 companion is present (--program 0 picks the primary). dlc_romfs finds the DLC RomFS inside nested snapshots. dlc_index ignores the platform byte, because the Switch 2 companion (04…) and its Switch 1 DLC (01…) differ there. control/control.nacp is kept.
  • pack_version / pack_dlc take raw=True for Mac apps and link the RomFS and DLC directories rather than packing images.
  • scripts/build_ipa_from_nsp.py: dump program selection, the companion's shared RomFS surveyed beside its own, and DM_ROMFS_PROGRAM_0 passed through. Companions are Mac only, because iOS indexed RomFS bundling is not implemented.

2. Two RomFS at once

The companion mounts its own RomFS as content and the primary's as conorig through MountRom(name, index).

  • hle_fs.c: the indexed overload reads DM_ROMFS_PROGRAM_<index>. Index 0 with no mapping keeps the old single-RomFS behaviour, and any other unmapped index now fails instead of silently mounting the wrong files.
  • dm/mac/pack_app.py --romfs-program <n>\t<dir> copies it into Resources/romfs_programs/<n>; launcher.c sets the variables and accepts a marker found there.

3. SDK 21 services

  • hle_audio.c: GetVirtualOutputDeviceProperties and the event-taking OpenOutputStream overload without flags. Without them the opening movie after New Game connects to auddmg:u and aborts with 0xa99. (This overlaps SDK 21 virtual output device properties and event-only stream open (Wonder Edition movie) #1561.)
  • hle_sdk16.c: pl::IsRunningOnOunce follows the loaded SDK instead of always reporting a Switch 1. GetBuiltInFontData returns the plain TTF independently of the older Decode convention, and RequestLoadNxFont prepares the fonts.

4. Frame pacing: the title held 30 fps

nvnWindowAcquireTexture blocked for the vsync, so sead's interval controller counted that sleep as CPU work and held interval 2. Acquire now publishes a fence that its consumer resolves (nvnSyncWait, nvnQueueWaitSync, nvnCommandBufferWaitSync, through sync_acquire_wait and mtl_present_ready). The game's own controller then held 59.9 fps instead of 29.7. sync_t grows to 32 bytes and the capture layout follows. Snapshot-ring and deterministic modes keep the synchronous path.

5. Shaders

  • dmsh_pch.py: -fbracket-depth=4096. Two agl pfx compute shaders nest past clang's default of 256. This raises only a parser limit, and the byte-identity tests still hold.
  • The false BAHS descriptor this build hit (agl_common@0x35428, compute control + vertex SPH, "Ampere instruction 481 at 0x0") is already fixed on main (control-span check). This PR adds test_split_bahs_control.py as a second regression case and lists Wonder's companion under that doc section.
  • Result: the full Mac shader build produces all 30,630 valid shaders, with none failing or lost.

6. New Game / Continue crash (#1580): resource heap allowance

Without this, a fresh save crashes about 3 s into New Game:

[RED] BufferInitialize: pool has no storage (pool 0x0 flags 0 offset 0 size 0x270)
CRASH sig=11 addr=0x4 pc=h_nvnMemoryPoolGetFlags+0
  • Cause: the resource loader (main+0x130460) sizes each ExpHeap as resource-size-table value + max(alignment, 0x20) + 8 and creates it at main+0x131e00 (constructor main+0x2464d0). Model/NpcCommonerA.bfres gets 0x8d03c8 bytes. Its 0x5424 MatBlock + DisplayList request rounds to a 0x6000 GPU chunk, which leaves 0x3a8 bytes for a 0x418 descriptor.
  • Fix: dm/games/010015100B514000/patches.py registers one cut for this build: main+0x130514 adds w0,w8,#8 → adds w0,w8,#4,lsl #12 (+16 KB per resource heap). The Switch 1 builds carried the same allowance before their command records were compacted.
  • Result: with the original DLC and no resource-size-table overlay, a fresh save plays the Flower Kingdom opening (NPCs, Yoshi, the castle scene), and a copied save continues into a course.
  • Not fixed: this is a heap allowance, not the native NVN2 byte-accounting correction NVN2: oversized model scratch recordings exhaust BFRES resource heaps (Wonder Switch 2 Edition) #1580 asks for, so NVN2: oversized model scratch recordings exhaust BFRES resource heaps (Wonder Switch 2 Edition) #1580 should stay open. The BINDING entry records the cut as build → host, blocked on that.

7. Rendering: conditional clears

  • Overworld blue tint (profile setting, not code): the companion's profile needs render_cond on. Without it, ignored conditional clears wipe the half-resolution depth and occlusion targets between their full-screen draws, and flowerbeds and pipes turn blue. With it on, pass dumps show the repeated clears skipped, and the map has its lavender flowerbeds, white pipes and bright lighting.
  • Lighting pulsing in courses with DM_NVN_ENC_THREAD=1:
    • Cause: a timestamp report's 16-byte record was written by the submission's completion handler. With Metal encoding on its own thread, that write landed after the game had already set the counter word to 1 for a later frame's conditional clear. The next check read 0 / 0 and skipped that frame's lighting blocks: in 24 of 90 course frames, four blocks were skipped and the count alternated 22/18.
    • Fix: the counter word, always 0, is now stored at replay (OP_REPORT_COUNTER), and the completion handler adds only the time (post_run).
    • Result: 1 of 90 frames had one block skipped, and the count held at 21 a frame. With the encode thread off, the pattern didn't occur before or after the change.

Related upstream work

These open PRs cover parts of the same ground; this PR is the set that worked end to end on one build:

The companion's provisional profile is generated outside the repository, as for other unregistered builds; it needs render_cond on.

Testing

Apple M5 MacBook Pro, from the packed .app with isolated saves:

  • Fresh-save New Game through the opening scene, and a copied save continued into a course.
  • No null-pool crash with the original DLC.
  • Overworld colours checked against a reference screenshot. Course lighting checked with 90/120-frame pass dumps and screenshot bursts.

Unit tests on this branch:

  • t_nvn: 254 checks, 0 failed.
  • t_present_rescue: 82 checks, 0 failed.
  • t_fs: 150 checks, 0 failed.
  • t_shared_font: 144 checks, 0 failed.
  • t_output_stream: passes.
  • Python: test_docs, test_nsp_dump, test_shader_pch, test_split_bahs_control and test_split_bahs_entries pass. test_nsp_mac_app has one error, SeenShaders.test_shader_watch_counts_the_list (ShaderWatch.__init__ signature), which fails the same way on unmodified main.
  • make GAME=010015100B514000 dmhost builds.

Not tested: iOS (unsupported for companions), individual Bellabel Park modes, long play sessions, and the Switch 1 builds (their profile is unchanged).

🤖 Generated with Claude Code

BrentRosen and others added 2 commits November 7, 2026 02:30
…n the Mac from a raw dump

What the raw Switch 2 companion (040015100B514001, main ec47e6f8..., 1.2.1) needed:
- Dumps: select program 1 from updates/<v>/program1 by default, keep raw RomFS and DLC
  directories for Mac apps, index Switch 2 DLC across the platform byte, keep control.nacp
- Indexed RomFS: MountRom(name, index) reads DM_ROMFS_PROGRAM_<n> (the primary's files as
  conorig); pack_app.py --romfs-program and the launcher set it from romfs_programs/<n>
- SDK 21: GetVirtualOutputDeviceProperties and the event/no-flags OpenOutputStream (the
  opening movie aborted on auddmg:u); pl::IsRunningOnOunce follows the SDK; built-in fonts
  are the plain TTF; RequestLoadNxFont
- NVN: acquire fences resolved by their consumer instead of in AcquireTexture, so sead's
  interval controller no longer counts the vsync sleep as CPU work (title 29.7 -> 59.9 fps)
- Shaders: -fbracket-depth=4096 for deeply nested agl pfx compute shaders; regression test
  for control words read as a BAHS descriptor (the fix itself is already on main)
- New Game crash (seedds#1580): +16 KB resource heap cut for this build (patches.py)
- render_cond: a timestamp report's counter word is stored at replay; with the encode
  thread, the completion handler's late write skipped lighting blocks (pulsing lighting)
- Docs, generated pages and tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tracked-files filter in docs_patches.py, its test, the packing.md section and the
Donkey Kong Bananza README entry came from separate Donkey Kong Bananza work, not the
Wonder Switch 2 Edition bring-up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added host The Horizon host: SVCs, services, threads, memory (dm/host) nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests) rewriter The NSO rewriter and per-build patches packing Building an IPA or a Mac app: the NSP script, extraction, the shader split, the packers mac The Mac front end and app (dm/mac, front_mac.m) tools Tools and scripts (dm/tools, scripts) docs Documentation game: 010015100B514000 Super Mario Bros. Wonder labels Nov 7, 2026
ishtiaq156 pushed a commit to ishtiaq156/nxnative that referenced this pull request Nov 7, 2026
Keep missing Program 0 RomFS fail-closed by default. For an extracted companion, permit an explicitly supplied Mac original-program image only when its enclosing original app Info.plist names the original application ID (or a raw folder NPDM names it). Validate the complete DMROMFS1 index before staging; preserve a portable image in the bundle, the launcher marker lookup, and read-only indexed HLE mount.

Extends the credited BrentRosen seedds#1639 indexed RomFS integration at 5c24653 without its unrelated resource-heap workaround. Original/selected identity, Control, signing precedence and iOS remain unchanged.
ishtiaq156 pushed a commit to ishtiaq156/nxnative that referenced this pull request Nov 7, 2026
Keep missing Program 0 RomFS fail-closed by default. For an extracted companion, permit an explicitly supplied Mac original-program image only when its enclosing original app Info.plist names the original application ID (or a raw folder NPDM names it). Validate the complete DMROMFS1 index before staging; preserve a portable image in the bundle, the launcher marker lookup, and read-only indexed HLE mount.

Extends the credited BrentRosen seedds#1639 indexed RomFS integration at 5c24653 without its unrelated resource-heap workaround. Original/selected identity, Control, signing precedence and iOS remain unchanged.

(cherry picked from commit 6cddb72)
ishtiaq156 added a commit to ishtiaq156/nxnative that referenced this pull request Nov 7, 2026
Reject an output app nested inside the signed source app or an indexed source directory. Protect user-provided images under the packer cleanup/swap (.partial/.old) and cache key (.key/.key.partial) paths before invalidating keys, deleting staging, copying, signing or replacing the destination. Extend the existing lexical/resolved symlink containment guard; external original-image sources remain supported. Four synthetic red/green regression methods cover nested signed-app mutation, staged and HFS+ fallback cleanup, directory recursion and key input destruction. Preserve BrentRosen seedds#1639 feature attribution and exclude title-specific heap workarounds.
ishtiaq156 added a commit to ishtiaq156/nxnative that referenced this pull request Nov 7, 2026
Reject an output app nested inside the signed source app or an indexed source directory. Protect user-provided images under the packer cleanup/swap (.partial/.old) and cache key (.key/.key.partial) paths before invalidating keys, deleting staging, copying, signing or replacing the destination. Extend the existing lexical/resolved symlink containment guard; external original-image sources remain supported. Four synthetic red/green regression methods cover nested signed-app mutation, staged and HFS+ fallback cleanup, directory recursion and key input destruction. Preserve BrentRosen seedds#1639 feature attribution and exclude title-specific heap workarounds.
ishtiaq156 added a commit to ishtiaq156/nxnative that referenced this pull request Nov 7, 2026
Allow a leaf symlink within a replaced .app to select a truly external original RomFS image: indexed_romfs resolves the target before swap. Keep the resolved-target and signed-source containment checks, so real source files inside outputs, reverse symlinks, nested signed apps and reserved cleanup paths still fail closed. Reject symlinked .app.partial staging directories and aliased symlink/hardlink .key.partial files before any mutation; write the key via detached exclusive temp creation to prevent source image truncation. Four disposable synthetic red/green methods and 114 Mac/NSP focused tests passed. Preserve the credited BrentRosen seedds#1639 generic indexed feature and exclude the unrelated +16 KiB RESTBL workaround.
ishtiaq156 added a commit to ishtiaq156/nxnative that referenced this pull request Nov 7, 2026
Allow a leaf symlink within a replaced .app to select a truly external original RomFS image: indexed_romfs resolves the target before swap. Keep the resolved-target and signed-source containment checks, so real source files inside outputs, reverse symlinks, nested signed apps and reserved cleanup paths still fail closed. Reject symlinked .app.partial staging directories and aliased symlink/hardlink .key.partial files before any mutation; write the key via detached exclusive temp creation to prevent source image truncation. Four disposable synthetic red/green methods and 114 Mac/NSP focused tests passed. Preserve the credited BrentRosen seedds#1639 generic indexed feature and exclude the unrelated +16 KiB RESTBL workaround.
ishtiaq156 added a commit to ishtiaq156/nxnative that referenced this pull request Nov 7, 2026
…source

Do not resolve a destination .app leaf symlink to a signed original app when an indexed external RomFS is supplied through a safe in-output leaf source link: otherwise an apparently separate output replaces the original app and embedded RomFS. Reject that source/output relationship before cache, staging, signing or replacement. Keep symlinked parent volumes and legitimate leaf source links inside a real destination app supported. Synthetic destructive red, three focused green and 116/116 Mac/NSP stand-in tests; no real app/image or game touched. Preserve BrentRosen seedds#1639 feature credit and exclude title-specific RESTBL workarounds.
ishtiaq156 added a commit to ishtiaq156/nxnative that referenced this pull request Nov 7, 2026
…source

Do not resolve a destination .app leaf symlink to a signed original app when an indexed external RomFS is supplied through a safe in-output leaf source link: otherwise an apparently separate output replaces the original app and embedded RomFS. Reject that source/output relationship before cache, staging, signing or replacement. Keep symlinked parent volumes and legitimate leaf source links inside a real destination app supported. Synthetic destructive red, three focused green and 116/116 Mac/NSP stand-in tests; no real app/image or game touched. Preserve BrentRosen seedds#1639 feature credit and exclude title-specific RESTBL workarounds.
FifteenPen added a commit to FifteenPen/nxnative that referenced this pull request Nov 13, 2026
Kirby and the Forgotten Land's Nintendo Switch 2 Edition (program 04004D300C5AE001, v65536, SDK 21) asks for its built-in fonts at boot with nn::pl::GetBuiltInFontDataSet, the list form of GetBuiltInFontData. Unhooked, the SDK connects to pl:u, which the host does not serve, and aborts.

The SDK's own version (sdk+0x4fc780 in that build) takes at most 6 types, sends them to pl:u as one byte each, then fills *count entries of 0x18 bytes in order and returns whether the service answered. The takeover fills each type as the GetBuiltInFontData takeover does, with the same stand-in fonts, in the order asked, and leaves out a type with no stand-in font. The Switch 1 overload (BuiltInFontTypeNx) is not hooked: no game at hand imports it.

t_shared_font checks the entries, their order, the limit of 6 and the case with no fonts. The game reached play past frame 1800 on a Mac build of main plus seedds#1639 (Program 1 packing) and this change. services.md's entry on SDK 21's built-in fonts covers the new call.
seedds added a commit that referenced this pull request Nov 14, 2026
Kirby and the Forgotten Land's Nintendo Switch 2 Edition (program 04004D300C5AE001, v65536, SDK 21) asks for its built-in fonts at boot with nn::pl::GetBuiltInFontDataSet, the list form of GetBuiltInFontData. Unhooked, the SDK connects to pl:u, which the host does not serve, and aborts.

The SDK's own versions (Splatoon Raiders' sdk+0x59a3c0 for Switch 2 types, sdk+0x59a720 for Switch 1 types; sdk+0x4fc780 in Kirby's) take at most 6 or 7 types, send them to pl:u as one byte each, fill *count entries of 0x18 bytes in order and return whether the fonts are loaded; a failed request aborts. The Switch 2 overload has the service write *count whatever the answer, the Switch 1 overload writes it only when the fonts are loaded. Both takeovers share one helper that fills each type as the GetBuiltInFontData takeover does, with the same stand-in fonts, in the order asked, and leaves out a type with no stand-in font.

t_shared_font checks the entries, their order, both limits and the case with no fonts (146 checks). The game reached play past frame 1800 on a Mac build of main plus #1639 (Program 1 packing) and the Switch 2 overload's takeover. services.md's entry on SDK 21's built-in fonts covers both overloads.

Co-authored-by: FifteenPen <354289956+FifteenPen@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation game: 010015100B514000 Super Mario Bros. Wonder host The Horizon host: SVCs, services, threads, memory (dm/host) mac The Mac front end and app (dm/mac, front_mac.m) nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests) packing Building an IPA or a Mac app: the NSP script, extraction, the shader split, the packers rewriter The NSO rewriter and per-build patches tools Tools and scripts (dm/tools, scripts)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant