Commit Graph
12 Commits
Author SHA1 Message Date
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 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 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 5c7ac33752 App name and bundle ID come from the base NSP
- App name = the game name in the base NSP's control data (control.nacp, saved by nx_extract), read by the new dm/tools/nacp.py that the NSP script and build_ipa.sh share; Engage → "Fire Emblem Engage", Mario → "Super Mario Bros. Wonder"
- Bundle ID = org.nxnative.<base game title ID in lowercase>, e.g. org.nxnative.0100a6301214e000: unique per game and independent of language or version. A new bundle ID installs as a separate app, so existing saves (Documents/save) have to be copied over once
- Overrides: the NSP script's APP_NAME / BUNDLE_ID settings (--app-name / --bundle-id), build_ipa.sh's DM_DISPLAY_NAME / DM_BUNDLE_ID. With no readable name the NSP script stops and says to re-extract
- Profiles drop app_name / bundle_id; a plain name key is only a label for game.py list, error messages and the Mac window title
- Verified: the built IPA's Info.plist has CFBundleDisplayName "Fire Emblem Engage" and CFBundleIdentifier org.nxnative.0100a6301214e000
2026-10-15 14:34:13 +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 1ce6af4cc5 build_ipa_from_nsp.py: compare only data rows of the shader tables
- The check that the split shader tables match the game profile compared whole files, so a header comment written by an older shader_split.py stopped the build
- Fire Emblem Engage hit this: shader_map.tsv differs only in its first comment line (checked against a fresh split of the extracted 2.0.0 files: 9,270 data rows identical); rewriting the header instead would change the chain version and force a shader-library rebuild
2026-10-15 10:25:57 +08:00
seedds 3a6c719f4c App icon from the game's own icon (English)
- nx_extract saves the game icon from the control data as icon.jpg
  next to control.nacp (256x256 JPEG; American English first, then
  British English, then the first other language).
- build_ipa.sh turns icon.jpg from beside the RomFS (or DM_ICON) into a
  1024x1024 single-size app icon in Gen/Assets.xcassets and sets
  ASSETCATALOG_COMPILER_APPICON_NAME; project.yml lists the catalog as
  optional. Without an icon the build is as before (blank icon).
- build_ipa_from_nsp.py passes the extracted icon (packed version
  first, then the base game) and says so when an older extraction has
  none.
- Checked: nx_extract on the Engage NSPs saves the English icon; both
  IPAs built with icons (Assets.car, AppIcon60x60@2x.png,
  AppIcon76x76@2x~ipad.png, CFBundleIcons in Info.plist). README notes
  it.
2026-10-15 09:17:45 +08:00
seedds ef9aa76e9b build_ipa_from_nsp.py: the IPA goes to the NSP folder by default
- With OUT_IPA / -o empty, the IPA is written to the NSP folder
  (<the profile's ipa>, e.g. FEEngage.ipa), next to the NSPs and
  <id>_build/, instead of ~/Desktop.
- The script prints the output IPA and its work folder at the start;
  the extracted game files are in <id>_build/extract/ in the NSP
  folder (deleted after the IPA is built unless --keep-extracted).
- README and the script header say so. Checked with a run on the
  Engage NSP folder (default and -o) up to the free-space check.
2026-10-15 08:42:11 +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