Celeste plays on the Mac: profile 01002B30028F6000 (update 1.4.1.1, SDK 15.3.2)

- MonoGame for the Switch, its C# and the game's compiled to C++ by Sickhead's BRUTE into main; FMOD Studio through AudioOut; only main and sdk. No host change: the logos, the title, a new file and the prologue at 60 fps with music and sound, no red line, about 1.65 GB resident, flat over five minutes. Not yet tested on an iPhone
- The shaders are MonoGame effects (MGFX version 8): 13 in Content/Effects/*.xnb and SpriteBatch's built-in one in main. shader_split.py reads them (mgfx_effect), each effect parsed to its end; a shader's bytecode is u32 control length, the control section, u32 code length, the code area. 26 shaders (20 pixel, 6 vertex), every binding decodes. A pass may name no vertex shader and keep SpriteBatch's (10 of the 13 effects), so they pair as single-stage programs. Cuphead's and 13 Sentinels' tables split again unchanged
- NSP script: a provisional profile never got the sys_resource the survey lists, so the host answered 32 MB of system resource for every new game; write_profile writes it now (Celeste: 0, as its NPDM declares)
- Survey: XNB files and the MonoGame and FMOD markers (profile_guess.py), and a note on where a MonoGame game's shaders are
- Prewarm list recorded through boot, the title, a new file and the prologue
This commit is contained in:
seedds committed 2026-10-22 09:09:55 +08:00
1 parent 80b59c5bed
commit 00de15f095
16 files changed
+234 -7

No files matched your search

+1
View File
@@ -0,0 +1 @@
# generated
1 # generated
+3
View File
@@ -0,0 +1,3 @@
# chain b-b94f1911fe8f 2026-10-22T08:50:37+08:00
2b34d193d87075d5
da9c1ded21a21fb9
1 # chain b-b94f1911fe8f 2026-10-22T08:50:37+08:00
2 2b34d193d87075d5
3 da9c1ded21a21fb9
+22
View File
@@ -0,0 +1,22 @@
# chain b-b94f1911fe8f 2026-10-22T08:50:37+08:00
# shader key, then reg:bytes for each uniform slot whose reads stay inside its first bytes, and s<reg> for each storage slot it never writes (cb_extent.py)
02f3f795ba2726da 0:16
1e5020d03a2d979c 0:16
26279906b8e184aa 0:16
2b34d193d87075d5 0:16
4300f337c29f2945 0:64
437f5d6254ae9e09 0:80
47fe303ce0384f87 0:32
6ee6174a2bdfa00f 0:64
75ca72883d4f9455 0:64
7ca4d51104ee3176 0:32
b049e30271230d01 0:48
caba794cb0114b0f 0:16
d2bb00f61df70865 0:16
da9c1ded21a21fb9 0:16
dc7e0b051284c311 0:64
dfa65b269db8ee4a 0:80
e82e71820b9b2278 0:32
f35b1e72f6a7ab84 0:80
fcc4b0ea884dbeec 0:16
fe83a3898374a799 0:64
1 # chain b-b94f1911fe8f 2026-10-22T08:50:37+08:00
2 # shader key, then reg:bytes for each uniform slot whose reads stay inside its first bytes, and s<reg> for each storage slot it never writes (cb_extent.py)
3 02f3f795ba2726da 0:16
4 1e5020d03a2d979c 0:16
5 26279906b8e184aa 0:16
6 2b34d193d87075d5 0:16
7 4300f337c29f2945 0:64
8 437f5d6254ae9e09 0:80
9 47fe303ce0384f87 0:32
10 6ee6174a2bdfa00f 0:64
11 75ca72883d4f9455 0:64
12 7ca4d51104ee3176 0:32
13 b049e30271230d01 0:48
14 caba794cb0114b0f 0:16
15 d2bb00f61df70865 0:16
16 da9c1ded21a21fb9 0:16
17 dc7e0b051284c311 0:64
18 dfa65b269db8ee4a 0:80
19 e82e71820b9b2278 0:32
20 f35b1e72f6a7ab84 0:80
21 fcc4b0ea884dbeec 0:16
22 fe83a3898374a799 0:64
+2
View File
@@ -0,0 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 01002B30028F6000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "b94f1911fe8f"
+41
View File
@@ -0,0 +1,41 @@
# Game profile of Celeste (Switch, update 1.4.1.1 = v589824, the game shows v.1.4.1.0; base game v0 not extracted). Plays on the Mac (title, file select, the prologue at 60 fps with sound)
# Format: see dm/games/README.md
# MonoGame for the Switch (Microsoft.Xna.Framework over NVN), its C# and the game's compiled to C++ by Sickhead's BRUTE into main, FMOD Studio sound through AudioOut, SDK 15.3.2.
# The shaders are MonoGame effects (MGFX version 8): 13 in Content/Effects/*.xnb, and SpriteBatch's built-in effect in main (shader_split.py mgfx_effect). The provisional profile's guesses held: no key needed changing
# ---- Identity ----
title_id 01002B30028F6000
program_id 01002B30028F6000
# ---- App ----
# Game name: only a label for profile listings and the Mac window title; the iOS app name is the game name in the base NSP, and the bundle ID comes from the base game title ID (build_ipa.sh / NSP script)
name Celeste
ipa Celeste.ipa
app_category public.app-category.action-games
# ---- Versions ----
# only the merged 1.4.1.1 copy is extracted
build v589824 1.4.1.1 main=9382989af9636a1e02aeb3693094e87d sdk=8618895246b420329b77746b960a66ab
package v589824
shader_versions v589824
# ---- Files ----
modules main sdk
romfs_marker Content/FMOD/Desktop/music.bank
zip_store .bank:.obj:.png:.metallib
# ---- Memory ----
# The NPDM declares no system resource, so the game has no virtual address memory: it sets a heap of the application pool − used memory (3.11 GB) and allocates from it.
# The block is 256 MB in front + 256 MB alias region + 256 MB stack region + 3.25 GB heap region
mem_reserve 0x100000000
mem_alias 0x10000000
mem_stack 0x10000000
app_pool 0xCD500000
# the NPDM declares no system resource: IsVirtualAddressMemoryEnabled is false, as on Horizon
sys_resource 0x0
# the rewritten modules are larger than the default 64 MB of used memory (BRUTE's main alone is 78 MB with its bss), so the heap request plus the modules would exceed the pool
mem_used 0x6400000
# ---- Shader split (shader_split.py; MonoGame effects) ----
shader_groups /Content/=content
shader_note Celeste shaders
1 # Game profile of Celeste (Switch, update 1.4.1.1 = v589824, the game shows v.1.4.1.0; base game v0 not extracted). Plays on the Mac (title, file select, the prologue at 60 fps with sound)
2 # Format: see dm/games/README.md
3 # MonoGame for the Switch (Microsoft.Xna.Framework over NVN), its C# and the game's compiled to C++ by Sickhead's BRUTE into main, FMOD Studio sound through AudioOut, SDK 15.3.2.
4 # The shaders are MonoGame effects (MGFX version 8): 13 in Content/Effects/*.xnb, and SpriteBatch's built-in effect in main (shader_split.py mgfx_effect). The provisional profile's guesses held: no key needed changing
5 # ---- Identity ----
6 title_id 01002B30028F6000
7 program_id 01002B30028F6000
8 # ---- App ----
9 # Game name: only a label for profile listings and the Mac window title; the iOS app name is the game name in the base NSP, and the bundle ID comes from the base game title ID (build_ipa.sh / NSP script)
10 name Celeste
11 ipa Celeste.ipa
12 app_category public.app-category.action-games
13 # ---- Versions ----
14 # only the merged 1.4.1.1 copy is extracted
15 build v589824 1.4.1.1 main=9382989af9636a1e02aeb3693094e87d sdk=8618895246b420329b77746b960a66ab
16 package v589824
17 shader_versions v589824
18 # ---- Files ----
19 modules main sdk
20 romfs_marker Content/FMOD/Desktop/music.bank
21 zip_store .bank:.obj:.png:.metallib
22 # ---- Memory ----
23 # The NPDM declares no system resource, so the game has no virtual address memory: it sets a heap of the application pool − used memory (3.11 GB) and allocates from it.
24 # The block is 256 MB in front + 256 MB alias region + 256 MB stack region + 3.25 GB heap region
25 mem_reserve 0x100000000
26 mem_alias 0x10000000
27 mem_stack 0x10000000
28 app_pool 0xCD500000
29 # the NPDM declares no system resource: IsVirtualAddressMemoryEnabled is false, as on Horizon
30 sys_resource 0x0
31 # the rewritten modules are larger than the default 64 MB of used memory (BRUTE's main alone is 78 MB with its bss), so the heap request plus the modules would exceed the pool
32 mem_used 0x6400000
33 # ---- Shader split (shader_split.py; MonoGame effects) ----
34 shader_groups /Content/=content
35 shader_note Celeste shaders
@@ -0,0 +1,35 @@
# Prewarm seed list (shipped in the app, next to shader_map.tsv). Rows: S<TAB>shader key<TAB>stage / R<TAB>render pipeline key (pso_skey hex) / C<TAB>compute shader key.
# Recorded on the Mac with prewarm off: boot, the title, a new file and the prologue
S 4300f337c29f2945_b 0
S d94db26519ed226f_b 1
S 75ca72883d4f9455_b 0
S 48a153564c2f49a6_b 1
R 343330306633333763323966323934355f62000000000000643934646232363531396564323236665f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000004010000040100000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
R 343330306633333763323966323934355f62000000000000643934646232363531396564323236665f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000004010000040100000100000001000000ffffffff0000000028080a8204000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
R 373563613732383833643466393435355f62000000000000343861313533353634633266343961365f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000004010000040100000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
R 343330306633333763323966323934355f62000000000000643934646232363531396564323236665f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
S 6ee6174a2bdfa00f_b 0
S 2ba14ff19c1af5ca_b 1
S dc7e0b051284c311_b 0
S f35b1e72f6a7ab84_b 1
S 1e5020d03a2d979c_b 1
R 366565363137346132626466613030665f62000000000000326261313466663139633161663563615f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000004010000040100000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
R 646337653062303531323834633331315f62000000000000663335623165373266366137616238345f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000004010000040100000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000160c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
R 343330306633333763323966323934355f62000000000000316535303230643033613264393739635f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
R 343330306633333763323966323934355f62000000000000643934646232363531396564323236665f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000004010000040100000100000001000000ffffffff0000000028180a8604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
S fe83a3898374a799_b 0
S a76b7c0584cdce62_b 1
S 26279906b8e184aa_b 1
S 2b34d193d87075d5_b 1
S 47fe303ce0384f87_b 1
S 83611e59d27b73b5_b 1
R 373563613732383833643466393435355f62000000000000343861313533353634633266343961365f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000001008040212000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
R 373563613732383833643466393435355f62000000000000343861313533353634633266343961365f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000001008048216000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
R 666538336133383938333734613739395f62000000000000613736623763303538346364636536325f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff0000000028080a8204000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c000025100000161400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
R 343330306633333763323966323934355f62000000000000323632373939303662386531383461615f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
R 343330306633333763323966323934355f62000000000000326233346431393364383730373564355f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000001008028205000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
R 343330306633333763323966323934355f62000000000000343766653330336365303338346638375f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
R 343330306633333763323966323934355f62000000000000643934646232363531396564323236665f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000001004028204000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
R 343330306633333763323966323934355f62000000000000643934646232363531396564323236665f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000002808028204000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
R 343330306633333763323966323934355f62000000000000643934646232363531396564323236665f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff0000000028080a8204000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
R 343330306633333763323966323934355f62000000000000383336313165353964323762373362355f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000004010000040100000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
1 # Prewarm seed list (shipped in the app, next to shader_map.tsv). Rows: S<TAB>shader key<TAB>stage / R<TAB>render pipeline key (pso_skey hex) / C<TAB>compute shader key.
2 # Recorded on the Mac with prewarm off: boot, the title, a new file and the prologue
3 S 4300f337c29f2945_b 0
4 S d94db26519ed226f_b 1
5 S 75ca72883d4f9455_b 0
6 S 48a153564c2f49a6_b 1
7 R 343330306633333763323966323934355f62000000000000643934646232363531396564323236665f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000004010000040100000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
8 R 343330306633333763323966323934355f62000000000000643934646232363531396564323236665f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000004010000040100000100000001000000ffffffff0000000028080a8204000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
9 R 373563613732383833643466393435355f62000000000000343861313533353634633266343961365f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000004010000040100000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
10 R 343330306633333763323966323934355f62000000000000643934646232363531396564323236665f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11 S 6ee6174a2bdfa00f_b 0
12 S 2ba14ff19c1af5ca_b 1
13 S dc7e0b051284c311_b 0
14 S f35b1e72f6a7ab84_b 1
15 S 1e5020d03a2d979c_b 1
16 R 366565363137346132626466613030665f62000000000000326261313466663139633161663563615f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000004010000040100000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
17 R 646337653062303531323834633331315f62000000000000663335623165373266366137616238345f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000004010000040100000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000160c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
18 R 343330306633333763323966323934355f62000000000000316535303230643033613264393739635f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
19 R 343330306633333763323966323934355f62000000000000643934646232363531396564323236665f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000004010000040100000100000001000000ffffffff0000000028180a8604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
20 S fe83a3898374a799_b 0
21 S a76b7c0584cdce62_b 1
22 S 26279906b8e184aa_b 1
23 S 2b34d193d87075d5_b 1
24 S 47fe303ce0384f87_b 1
25 S 83611e59d27b73b5_b 1
26 R 373563613732383833643466393435355f62000000000000343861313533353634633266343961365f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000001008040212000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
27 R 373563613732383833643466393435355f62000000000000343861313533353634633266343961365f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000001008048216000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
28 R 666538336133383938333734613739395f62000000000000613736623763303538346364636536325f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff0000000028080a8204000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c000025100000161400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
29 R 343330306633333763323966323934355f62000000000000323632373939303662386531383461615f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
30 R 343330306633333763323966323934355f62000000000000326233346431393364383730373564355f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000001008028205000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
31 R 343330306633333763323966323934355f62000000000000343766653330336365303338346638375f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
32 R 343330306633333763323966323934355f62000000000000643934646232363531396564323236665f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000001004028204000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
33 R 343330306633333763323966323934355f62000000000000643934646232363531396564323236665f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff000000002808028204000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
34 R 343330306633333763323966323934355f62000000000000643934646232363531396564323236665f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000ffffffff0000000028080a8204000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
35 R 343330306633333763323966323934355f62000000000000383336313165353964323762373362355f620000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000004010000040100000100000001000000ffffffff000000001018048604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000250c00001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
@@ -0,0 +1 @@
# Answer table for translation queries (generated by dm/gfx/tools/shader_split.py, do not edit): key <TAB> fact JSON. This game has no PC version to pair with: only the thread group / shared memory of compute shaders (taken from the control section); other queries get the translator defaults
1 # Answer table for translation queries (generated by dm/gfx/tools/shader_split.py, do not edit): key <TAB> fact JSON. This game has no PC version to pair with: only the thread group / shared memory of compute shaders (taken from the control section); other queries get the translator defaults
+29
View File
@@ -0,0 +1,29 @@
# kind: evidence Celeste shaders (MonoGame MGFX in RomFS + main) → control section + code area keys (dm/gfx/tools/shader_split.py)
# column 5 records where each key first appears; this game has no PC version to pair with
package index stage dm_ctl_sha16 source
ps_content.bin 0 pixel caba794cb0114b0f /Content/Effects/Border.xnb#MGFX@0x8d#0
vs_content.bin 0 vertex 437f5d6254ae9e09 /Content/Effects/Border.xnb#MGFX@0x8d#1
ps_content.bin 1 pixel fcc4b0ea884dbeec /Content/Effects/ColorGrade.xnb#MGFX@0x8d#0
ps_content.bin 2 pixel 83611e59d27b73b5 /Content/Effects/ColorGrade.xnb#MGFX@0x8d#1
ps_content.bin 3 pixel e82e71820b9b2278 /Content/Effects/Distort.xnb#MGFX@0x8d#0
ps_content.bin 4 pixel 47fe303ce0384f87 /Content/Effects/Distort.xnb#MGFX@0x8d#1
ps_content.bin 5 pixel 2b34d193d87075d5 /Content/Effects/Dither.xnb#MGFX@0x8d#0
ps_content.bin 6 pixel da9c1ded21a21fb9 /Content/Effects/Dither.xnb#MGFX@0x8d#1
ps_content.bin 7 pixel 361d69acb38e0d2c /Content/Effects/Dust.xnb#MGFX@0x8d#0
ps_content.bin 8 pixel 26279906b8e184aa /Content/Effects/GaussianBlur.xnb#MGFX@0x8d#0
ps_content.bin 9 pixel 1e5020d03a2d979c /Content/Effects/GaussianBlur.xnb#MGFX@0x8d#1
ps_content.bin 10 pixel d2bb00f61df70865 /Content/Effects/GaussianBlur.xnb#MGFX@0x8d#2
ps_content.bin 11 pixel 7ca4d51104ee3176 /Content/Effects/Glitch.xnb#MGFX@0x8d#0
ps_content.bin 12 pixel a76b7c0584cdce62 /Content/Effects/Lighting.xnb#MGFX@0x8d#0
vs_content.bin 1 vertex fe83a3898374a799 /Content/Effects/Lighting.xnb#MGFX@0x8d#1
ps_content.bin 13 pixel b049e30271230d01 /Content/Effects/MagicGlow.xnb#MGFX@0x8d#0
ps_content.bin 14 pixel 02f3f795ba2726da /Content/Effects/Mirrors.xnb#MGFX@0x8d#0
ps_content.bin 15 pixel f35b1e72f6a7ab84 /Content/Effects/MountainRender.xnb#MGFX@0x8d#0
vs_content.bin 2 vertex dc7e0b051284c311 /Content/Effects/MountainRender.xnb#MGFX@0x8d#1
ps_content.bin 16 pixel dfa65b269db8ee4a /Content/Effects/MountainRender.xnb#MGFX@0x8d#2
ps_content.bin 17 pixel 48a153564c2f49a6 /Content/Effects/Primitive.xnb#MGFX@0x8d#0
vs_content.bin 3 vertex 75ca72883d4f9455 /Content/Effects/Primitive.xnb#MGFX@0x8d#1
ps_content.bin 18 pixel 2ba14ff19c1af5ca /Content/Effects/Texture.xnb#MGFX@0x8d#0
vs_content.bin 4 vertex 6ee6174a2bdfa00f /Content/Effects/Texture.xnb#MGFX@0x8d#1
ps_main.bin 0 pixel d94db26519ed226f main#MGFX@0x49b9358#0
vs_main.bin 0 vertex 4300f337c29f2945 main#MGFX@0x49b9358#1
1 # kind: evidence Celeste shaders (MonoGame MGFX in RomFS + main) → control section + code area keys (dm/gfx/tools/shader_split.py)
2 # column 5 records where each key first appears; this game has no PC version to pair with
3 package index stage dm_ctl_sha16 source
4 ps_content.bin 0 pixel caba794cb0114b0f /Content/Effects/Border.xnb#MGFX@0x8d#0
5 vs_content.bin 0 vertex 437f5d6254ae9e09 /Content/Effects/Border.xnb#MGFX@0x8d#1
6 ps_content.bin 1 pixel fcc4b0ea884dbeec /Content/Effects/ColorGrade.xnb#MGFX@0x8d#0
7 ps_content.bin 2 pixel 83611e59d27b73b5 /Content/Effects/ColorGrade.xnb#MGFX@0x8d#1
8 ps_content.bin 3 pixel e82e71820b9b2278 /Content/Effects/Distort.xnb#MGFX@0x8d#0
9 ps_content.bin 4 pixel 47fe303ce0384f87 /Content/Effects/Distort.xnb#MGFX@0x8d#1
10 ps_content.bin 5 pixel 2b34d193d87075d5 /Content/Effects/Dither.xnb#MGFX@0x8d#0
11 ps_content.bin 6 pixel da9c1ded21a21fb9 /Content/Effects/Dither.xnb#MGFX@0x8d#1
12 ps_content.bin 7 pixel 361d69acb38e0d2c /Content/Effects/Dust.xnb#MGFX@0x8d#0
13 ps_content.bin 8 pixel 26279906b8e184aa /Content/Effects/GaussianBlur.xnb#MGFX@0x8d#0
14 ps_content.bin 9 pixel 1e5020d03a2d979c /Content/Effects/GaussianBlur.xnb#MGFX@0x8d#1
15 ps_content.bin 10 pixel d2bb00f61df70865 /Content/Effects/GaussianBlur.xnb#MGFX@0x8d#2
16 ps_content.bin 11 pixel 7ca4d51104ee3176 /Content/Effects/Glitch.xnb#MGFX@0x8d#0
17 ps_content.bin 12 pixel a76b7c0584cdce62 /Content/Effects/Lighting.xnb#MGFX@0x8d#0
18 vs_content.bin 1 vertex fe83a3898374a799 /Content/Effects/Lighting.xnb#MGFX@0x8d#1
19 ps_content.bin 13 pixel b049e30271230d01 /Content/Effects/MagicGlow.xnb#MGFX@0x8d#0
20 ps_content.bin 14 pixel 02f3f795ba2726da /Content/Effects/Mirrors.xnb#MGFX@0x8d#0
21 ps_content.bin 15 pixel f35b1e72f6a7ab84 /Content/Effects/MountainRender.xnb#MGFX@0x8d#0
22 vs_content.bin 2 vertex dc7e0b051284c311 /Content/Effects/MountainRender.xnb#MGFX@0x8d#1
23 ps_content.bin 16 pixel dfa65b269db8ee4a /Content/Effects/MountainRender.xnb#MGFX@0x8d#2
24 ps_content.bin 17 pixel 48a153564c2f49a6 /Content/Effects/Primitive.xnb#MGFX@0x8d#0
25 vs_content.bin 3 vertex 75ca72883d4f9455 /Content/Effects/Primitive.xnb#MGFX@0x8d#1
26 ps_content.bin 18 pixel 2ba14ff19c1af5ca /Content/Effects/Texture.xnb#MGFX@0x8d#0
27 vs_content.bin 4 vertex 6ee6174a2bdfa00f /Content/Effects/Texture.xnb#MGFX@0x8d#1
28 ps_main.bin 0 pixel d94db26519ed226f main#MGFX@0x49b9358#0
29 vs_main.bin 0 vertex 4300f337c29f2945 main#MGFX@0x49b9358#1
@@ -0,0 +1,2 @@
# kind: evidence control-section key conflict rulings: empty
dm_ctl_sha16 chosen_pc class candidates records
1 # kind: evidence control-section key conflict rulings: empty
2 dm_ctl_sha16 chosen_pc class candidates records
@@ -0,0 +1,7 @@
# kind: evidence vertex / pixel shader pairs within BNSH variations (generated by dm/gfx/tools/shader_split.py, do not edit): vertex key <TAB> pixel key
4300f337c29f2945 a76b7c0584cdce62
437f5d6254ae9e09 a76b7c0584cdce62
6ee6174a2bdfa00f a76b7c0584cdce62
75ca72883d4f9455 a76b7c0584cdce62
dc7e0b051284c311 a76b7c0584cdce62
fe83a3898374a799 a76b7c0584cdce62
1 # kind: evidence vertex / pixel shader pairs within BNSH variations (generated by dm/gfx/tools/shader_split.py, do not edit): vertex key <TAB> pixel key
2 4300f337c29f2945 a76b7c0584cdce62
3 437f5d6254ae9e09 a76b7c0584cdce62
4 6ee6174a2bdfa00f a76b7c0584cdce62
5 75ca72883d4f9455 a76b7c0584cdce62
6 dc7e0b051284c311 a76b7c0584cdce62
7 fe83a3898374a799 a76b7c0584cdce62
+2 -1
View File
@@ -74,7 +74,8 @@ These are not profile keys: the tools read them from the game files each time, b
- **Unreal paks.** An Unreal Engine game keeps its shaders in the shader code archives of its paks (`*.ushaderbytecode`; a pak is Unreal's when its footer has Unreal's magic, `uepak.py` `unreal_paks`), each shader Oodle-compressed, which only the game's own Oodle decodes. The NSP script (`decode_ue_shaders`) takes the archives out of the paks with `dm/gfx/tools/uepak.py` (the pak index is AES-encrypted with a key it finds in `main`), boots the game in the Mac host with `DM_OODLE_JOB` (`dm/host/hle_oodle.c`: the game's `OodleLZ_Decompress`, found as the one function that references the library's own name string, decodes every shader right before `nnMain`, then the host exits) and writes the decoded archives to `<version dir>/ue_shaders`, where `shader_split.py` reads them (`DM_UE_SHADERS` overrides; without them it stops). Octopath Traveler 0: 10,596 shaders in 2 s. - **Unreal paks.** An Unreal Engine game keeps its shaders in the shader code archives of its paks (`*.ushaderbytecode`; a pak is Unreal's when its footer has Unreal's magic, `uepak.py` `unreal_paks`), each shader Oodle-compressed, which only the game's own Oodle decodes. The NSP script (`decode_ue_shaders`) takes the archives out of the paks with `dm/gfx/tools/uepak.py` (the pak index is AES-encrypted with a key it finds in `main`), boots the game in the Mac host with `DM_OODLE_JOB` (`dm/host/hle_oodle.c`: the game's `OodleLZ_Decompress`, found as the one function that references the library's own name string, decodes every shader right before `nnMain`, then the host exits) and writes the decoded archives to `<version dir>/ue_shaders`, where `shader_split.py` reads them (`DM_UE_SHADERS` overrides; without them it stops). Octopath Traveler 0: 10,596 shaders in 2 s.
- **Trinity packs.** Game Freak's Trinity engine keeps nearly every file in Oodle-compressed packs (`arc/data.trpfs`, files starting `ONEPACK`; Pokémon Legends: Z-A). `shader_split.py` decodes them with the game's own `OodleLZ_Decompress`, run by the Mac host in its tool mode (`dm/host/hle_oodle.c`, `DM_OODLE_SERVE`). The split needs `DM_OODLE_HOST` (the Mac host) and `DM_OODLE_MODULES` (the rewritten modules); the NSP script builds the host and sets both when the split runs. - **Trinity packs.** Game Freak's Trinity engine keeps nearly every file in Oodle-compressed packs (`arc/data.trpfs`, files starting `ONEPACK`; Pokémon Legends: Z-A). `shader_split.py` decodes them with the game's own `OodleLZ_Decompress`, run by the Mac host in its tool mode (`dm/host/hle_oodle.c`, `DM_OODLE_SERVE`). The split needs `DM_OODLE_HOST` (the Mac host) and `DM_OODLE_MODULES` (the rewritten modules); the NSP script builds the host and sets both when the split runs.
- **GLSLC outputs in `main` and loose files.** `shader_split.py` reads GLSLC compiler outputs (magic `0x19866891`, each one checked whole) in every game's `main`: 13 Sentinels and Unicorn Overlord keep all their shaders there, the KTGL games some; Unity's player has 11 of its own beside the game's shaders in its bundles (Engage, Ace Attorney Investigations Collection). It reads them as well in any RomFS file no other reader takes: FINAL FANTASY TACTICS - The Ivalice Chronicles keeps 257 in its technique files (`resources_nx/release/master/shader/*.tec`), Absolum (FNA3D) 163 in `shaders.nx.bin`. - **GLSLC outputs in `main` and loose files.** `shader_split.py` reads GLSLC compiler outputs (magic `0x19866891`, each one checked whole) in every game's `main`: 13 Sentinels and Unicorn Overlord keep all their shaders there, the KTGL games some; Unity's player has 11 of its own beside the game's shaders in its bundles (Engage, Ace Attorney Investigations Collection). It reads them as well in any RomFS file no other reader takes: FINAL FANTASY TACTICS - The Ivalice Chronicles keeps 257 in its technique files (`resources_nx/release/master/shader/*.tec`), Absolum (FNA3D) 163 in `shaders.nx.bin`.
- **Single-stage programs.** A GLSLC output or an Unreal shader record is a program of one stage, combined with others per draw, so its real vertex / pixel pairs exist only at run time. `shader_split.py` pairs each such vertex shader with a cover of pixel shaders that together read every input any pixel shader reads, and `dmsh_build.py`'s `vs_pad` makes it link with any pixel shader (13 Sentinels, Unicorn Overlord, the KTGL games, Octopath Traveler 0). BNSH variations, Unity programs and the MSHD and SDF files pair their own shaders. - **MonoGame effects.** `shader_split.py` reads MonoGame's MGFX effects wherever they are (`mgfx_effect`): Celeste keeps 13 in its XNB files (`Content/Effects/*.xnb`) and SpriteBatch's built-in one is in `main`.
- **Single-stage programs.** A GLSLC output, an Unreal shader record or a MonoGame effect's shader is a program of one stage, combined with others per draw (a MonoGame pass may name no vertex shader and keep SpriteBatch's), so its real vertex / pixel pairs exist only at run time. `shader_split.py` pairs each such vertex shader with a cover of pixel shaders that together read every input any pixel shader reads, and `dmsh_build.py`'s `vs_pad` makes it link with any pixel shader (13 Sentinels, Unicorn Overlord, the KTGL games, Octopath Traveler 0, Celeste). BNSH variations, Unity programs and the MSHD and SDF files pair their own shaders.
- **NVN driver tables.** The driver's constant segments have one layout in every game (`dmsh_build.py`, filled by `nvn_mtl.m`): c2 holds the handles bound with `nvnCommandBufferBindTexture` at `0x20 + 8 × slot`, images at `0x120 + 8 × slot`, separate textures at `0x168 + 8 × slot` and separate samplers at `0x568 + 8 × slot`; c0 the storage buffer tables (vertex 0x110, compute 0x310, pixel 0x510). Separate texture binds are texture slots 128 + n of the host's table (`NVN_SEPTEX_SLOT0`), so a game may bind both kinds in one frame (Monster Hunter Rise: combined slot 0 beside separate slots 0-4), and a combined slot past 31 (Hades II binds slot 34) lies in the image table, where a bound texture wins over an unbound image. The translator (`dmsh_xlate/Program.cs`) numbers slots the same way when it looks up per-slot texture / image types in `shader_facts.tsv`. - **NVN driver tables.** The driver's constant segments have one layout in every game (`dmsh_build.py`, filled by `nvn_mtl.m`): c2 holds the handles bound with `nvnCommandBufferBindTexture` at `0x20 + 8 × slot`, images at `0x120 + 8 × slot`, separate textures at `0x168 + 8 × slot` and separate samplers at `0x568 + 8 × slot`; c0 the storage buffer tables (vertex 0x110, compute 0x310, pixel 0x510). Separate texture binds are texture slots 128 + n of the host's table (`NVN_SEPTEX_SLOT0`), so a game may bind both kinds in one frame (Monster Hunter Rise: combined slot 0 beside separate slots 0-4), and a combined slot past 31 (Hades II binds slot 34) lies in the image table, where a bound texture wins over an unbound image. The translator (`dmsh_xlate/Program.cs`) numbers slots the same way when it looks up per-slot texture / image types in `shader_facts.tsv`.
## Adding a game ## Adding a game
+3 -2
View File
@@ -17,11 +17,12 @@ sys.path.insert(0, str(HERE.parent / 'tools')); import nso_syms # dynamic symb
MAGICS = [(b'UnityFS\0', 'UnityFS'), (b'UnityWeb', 'UnityWeb'), (b'UnityRaw', 'UnityRaw'), (b'BNSH', 'BNSH'), (b'BAHS', 'BAHS'), (b'SARC', 'SARC'), (b'Yaz0', 'Yaz0'), MAGICS = [(b'UnityFS\0', 'UnityFS'), (b'UnityWeb', 'UnityWeb'), (b'UnityRaw', 'UnityRaw'), (b'BNSH', 'BNSH'), (b'BAHS', 'BAHS'), (b'SARC', 'SARC'), (b'Yaz0', 'Yaz0'),
(bytes.fromhex('28b52ffd'), 'zstd'), (b'FRES', 'bfres'), (b'BNTX', 'BNTX'), (b'BWAV', 'BWAV'), (b'FSAR', 'BFSAR'), (b'CPK ', 'CRI CPK'), (b'CRID', 'CRI USM'), (bytes.fromhex('28b52ffd'), 'zstd'), (b'FRES', 'bfres'), (b'BNTX', 'BNTX'), (b'BWAV', 'BWAV'), (b'FSAR', 'BFSAR'), (b'CPK ', 'CRI CPK'), (b'CRID', 'CRI USM'),
(b'@UTF', 'CRI UTF'), (b'AKPK', 'Wwise pck'), (b'BKHD', 'Wwise bnk'), (b'FSB5', 'FMOD'), (b'\x89PNG', 'PNG'), (b'RIFF', 'RIFF'), (b'OggS', 'Ogg'), (b'PK\x03\x04', 'zip'), (b'NSO0', 'NSO'), (b'@UTF', 'CRI UTF'), (b'AKPK', 'Wwise pck'), (b'BKHD', 'Wwise bnk'), (b'FSB5', 'FMOD'), (b'\x89PNG', 'PNG'), (b'RIFF', 'RIFF'), (b'OggS', 'Ogg'), (b'PK\x03\x04', 'zip'), (b'NSO0', 'NSO'),
(bytes.fromhex('03022307'), 'SPIR-V'), (b'ONEPACK\0', 'Trinity pack'), (b'ARC\0', 'MT Framework ARC'), (b'SCNE', 'Bezel Engine archive')] (bytes.fromhex('03022307'), 'SPIR-V'), (b'ONEPACK\0', 'Trinity pack'), (b'ARC\0', 'MT Framework ARC'), (b'SCNE', 'Bezel Engine archive'), (b'XNB', 'XNB')]
PACKED = {'UnityFS', 'UnityWeb', 'Yaz0', 'zstd', 'BWAV', 'CRI CPK', 'CRI USM', 'Wwise pck', 'FMOD', 'PNG', 'Ogg', 'zip', 'Trinity pack', 'MT Framework ARC', 'Bezel Engine archive'} # file headers of compressed / media formats (zip_store) PACKED = {'UnityFS', 'UnityWeb', 'Yaz0', 'zstd', 'BWAV', 'CRI CPK', 'CRI USM', 'Wwise pck', 'FMOD', 'PNG', 'Ogg', 'zip', 'Trinity pack', 'MT Framework ARC', 'Bezel Engine archive'} # file headers of compressed / media formats (zip_store)
# IL2CPP by the metadata file every IL2CPP player opens: Unity 2022's main (Ace Attorney Investigations Collection) has no il2cpp_ API names, Engage's 2020.3 has both # IL2CPP by the metadata file every IL2CPP player opens: Unity 2022's main (Ace Attorney Investigations Collection) has no il2cpp_ API names, Engage's 2020.3 has both
MARKERS = [(b'nvnBootstrapLoader', 'NVN'), (b'vkCreateInstance', 'Vulkan'), (b'eglGetProcAddress', 'EGL / OpenGL'), (b'global-metadata.dat', 'Unity IL2CPP'), (b'UnityEngine', 'Unity'), MARKERS = [(b'nvnBootstrapLoader', 'NVN'), (b'vkCreateInstance', 'Vulkan'), (b'eglGetProcAddress', 'EGL / OpenGL'), (b'global-metadata.dat', 'Unity IL2CPP'), (b'UnityEngine', 'Unity'),
(b'sead::', 'Nintendo sead'), (b'agl::', 'Nintendo agl'), (b'gsys::', 'Nintendo gsys'), (b'Unreal', 'Unreal'), (b'UE4', 'Unreal 4'), (b'Havok', 'Havok'), (b'CRIWARE', 'CRIWARE'), (b'Wwise', 'Wwise')] (b'sead::', 'Nintendo sead'), (b'agl::', 'Nintendo agl'), (b'gsys::', 'Nintendo gsys'), (b'Unreal', 'Unreal'), (b'UE4', 'Unreal 4'), (b'Havok', 'Havok'), (b'CRIWARE', 'CRIWARE'), (b'Wwise', 'Wwise'),
(b'MonoGame.Framework', 'MonoGame'), (b'FMOD mixer', 'FMOD')]
GLSLC_MAGIC = b'\x91\x68\x86\x19' # GLSLC compiler outputs (shader_split.py glslc_shaders) GLSLC_MAGIC = b'\x91\x68\x86\x19' # GLSLC compiler outputs (shader_split.py glslc_shaders)
APP_POOL = 0xCD500000 # the application memory pool of a 4 GB Switch (3285 MiB), what every profile but Engage's reports APP_POOL = 0xCD500000 # the application memory pool of a 4 GB Switch (3285 MiB), what every profile but Engage's reports
MiB = 1 << 20 MiB = 1 << 20
+75 -3
View File
@@ -22,6 +22,7 @@
# · Trinity packs (Game Freak, Pokémon Legends: Z-A): BNSH inside the files of arc/data.trpfs (Oodle Mermaid, decoded by the game's own decoder in the Mac host), see trpfs_jobs and trinity_shaders # · Trinity packs (Game Freak, Pokémon Legends: Z-A): BNSH inside the files of arc/data.trpfs (Oodle Mermaid, decoded by the game's own decoder in the Mac host), see trpfs_jobs and trinity_shaders
# · SPK (Capcom's MT Framework, The Great Ace Attorney Chronicles): the shader package inside an ARC archive (zlib entries), code area + control section per shader, see mtarc_members and mtspk_shaders # · SPK (Capcom's MT Framework, The Great Ace Attorney Chronicles): the shader package inside an ARC archive (zlib entries), code area + control section per shader, see mtarc_members and mtspk_shaders
# · BEA (NintendoWare Bezel Engine archives, Big Brain Academy: Brain vs. Brain): BNSH in the .bfsha entries of the archives (zstd entries), see bea_members # · BEA (NintendoWare Bezel Engine archives, Big Brain Academy: Brain vs. Brain): BNSH in the .bfsha entries of the archives (zstd entries), see bea_members
# · MGFX (MonoGame effects, Celeste): the effects in its XNB files and SpriteBatch's in main, each shader a control section and a code area, see mgfx_effect
# Code area: 0x30 header + (graphics) 0x50 SPH + instructions; header magic 0x12345678 for graphics, 0x12345679 for compute. The control section starts with 0x98761234, +0x704 = code area length, # Code area: 0x30 header + (graphics) 0x50 SPH + instructions; header magic 0x12345678 for graphics, 0x12345679 for compute. The control section starts with 0x98761234, +0x704 = code area length,
# +0x700 = start of the c1 constant segment within the code area; for compute +0x748 thread group xyz (u32 ×3), +0x754 shared memory bytes (checked one by one against PC reflection on D2R's 22 compute shaders). # +0x700 = start of the c1 constant segment within the code area; for compute +0x748 thread group xyz (u32 ×3), +0x754 shared memory bytes (checked one by one against PC reflection on D2R's 22 compute shaders).
# Stage: BNSH uses the stage slot; BAHS uses bits 10..13 of SPH word 0 (1 vertex 2 hull 3 domain 4 geometry 5 pixel), and a compute header means compute. # Stage: BNSH uses the stage slot; BAHS uses bits 10..13 of SPH word 0 (1 vertex 2 hull 3 domain 4 geometry 5 pixel), and a compute header means compute.
@@ -362,6 +363,75 @@ def bea_members(d, label):
if len(data) != usz: red(f'{label}/{path}: {len(data):#x} bytes, the entry says {usz:#x}') if len(data) != usz: red(f'{label}/{path}: {len(data):#x} bytes, the entry says {usz:#x}')
yield path, data yield path, data
class _Rd: # a little-endian reader over bytes, with .NET's BinaryReader strings (7-bit encoded length + UTF-8)
def __init__(s, d, p): s.d, s.p = d, p
def u8(s): s.p += 1; return s.d[s.p - 1]
def un(s, f): v = struct.unpack_from(f, s.d, s.p); s.p += struct.calcsize(f); return v[0] if len(v) == 1 else v
def raw(s, n): s.p += n; return s.d[s.p - n:s.p]
def str(s):
n = sh = 0
while True:
b = s.u8(); n |= (b & 0x7f) << sh; sh += 7
if b < 0x80: return s.raw(n)
MGFX = b'MGFX'
def mgfx_effect(d, p, label):
"""A MonoGame effect at d[p:] (Celeste: MonoGame for the Switch, its C# compiled to C++ by Sickhead's BRUTE; 13 in Content/Effects/*.xnb, whose XNB 'S' files are read by EffectReader,
and SpriteBatch's in main): 'MGFX', u8 version 8, u8 profile, u32 effect key; the constant buffers {name, i16 size, u8 count, {u8 parameter, u16 offset} each}; the shaders {u8 vertex (1) or
pixel (0), u32 length, the bytecode, u8 sampler count, {u8 type, u8 texture slot, u8 sampler slot, u8 has state (then 20 bytes of state), name, u8 parameter} each, u8 count + constant buffer
indices, u8 count + attributes {name, u8 usage, u8 index, i16 location}}; the bytecode is u32 control length, the control section, u32 code length, the code area. Then the parameters and the
techniques and passes, read to the end so that a misread shows: a pass names its vertex and pixel shader by index, 255 for none. → [(label, stage, ctl, code)], or None when the header is not
version 8's or its first shader is not an NVN one (four bytes 'MGFX' anywhere in a main)"""
if d[p + 4:p + 5] != b'\x08': return None
r = _Rd(d, p + 10)
try:
for _ in range(r.u8()): r.str(); r.raw(2); r.raw(3 * r.u8())
if d[r.p + 10:r.p + 14] != CTL_MAGIC: return None # shader count, then the first shader's stage, length and control length
except IndexError: return None
def no_annotations():
if r.u8(): red(f'{label} MGFX @{p:#x}: annotations (MonoGame writes none)')
out = []
for i in range(r.u8()):
vs, n = r.u8(), r.un('<I'); bc = r.raw(n)
nctl = struct.unpack_from('<I', bc, 0)[0]; ncode = struct.unpack_from('<I', bc, 4 + nctl)[0] if 8 + nctl <= n else -1
if vs > 1 or 8 + nctl + ncode != n: red(f'{label} MGFX @{p:#x} shader {i}: {n:#x} bytes, not a control section and a code area')
ctl, code = bc[4:4 + nctl], bc[8 + nctl:]
st = SPH_STAGE.get((struct.unpack_from('<I', code, 0x30)[0] >> 10) & 0xf) if code[:4] == GFX_MAGIC else None
why = check(label, st, ctl, code) if st == ('ps', 'vs')[vs] else f'the SPH stage is {st}, the effect says {("pixel", "vertex")[vs]}'
if why: red(f'{label} MGFX @{p:#x} shader {i}: {why}')
out.append((f'{label}#MGFX@{p:#x}#{i}', st, ctl, code))
for _ in range(r.u8()):
r.raw(3)
if r.u8(): r.raw(20) # address u / v / w, border colour, filter, max anisotropy, max mip level, mip LOD bias
r.str(); r.u8()
r.raw(r.u8())
for _ in range(r.u8()): r.str(); r.raw(4)
def params(): # {u8 class, u8 type, name, semantic, annotations, u8 rows, u8 columns, elements, struct members, the value when it has neither} each → how many
n = r.u8()
for _ in range(n):
typ = r.raw(2)[1]; r.str(); r.str(); no_annotations()
rows, cols = r.u8(), r.u8()
if params() + params(): continue
if typ in (1, 2, 3): r.raw(4 * rows * cols) # bool, int32, single
elif typ == 4: r.str()
return n
params()
for _ in range(r.u8()): # techniques
r.str(); no_annotations()
for _ in range(r.u8()): # passes: name, annotations, vertex / pixel shader index, then the blend, depth-stencil and rasterizer states when present
r.str(); no_annotations(); v, f = r.u8(), r.u8()
if v != 255 and v >= len(out) or f != 255 and f >= len(out): red(f'{label} MGFX @{p:#x}: a pass names shader {v} / {f} of {len(out)}')
for n in (18, 25, 12):
if r.u8(): r.raw(n)
if r.p > len(d) or label.endswith('.xnb') and r.p != len(d): red(f'{label} MGFX @{p:#x}: the effect ends at {r.p:#x}, the file at {len(d):#x}')
return out
def mgfx_shaders(d, label):
out = []
for m in re.finditer(MGFX, d): out += mgfx_effect(d, m.start(), label) or []
if label.endswith('.xnb') and b'EffectReader' in d[:0x200] and not out: red(f'{label}: an XNB effect without an MGFX version 8 effect of NVN shaders')
return out
ONEPACK = b'ONEPACK\0' ONEPACK = b'ONEPACK\0'
def fb_field(d, t, i): def fb_field(d, t, i):
"""FlatBuffers: absolute offset of field i of the table at t, None when the field is absent""" """FlatBuffers: absolute offset of field i of the table at t, None when the field is absent"""
@@ -570,6 +640,7 @@ def scan_blob(d, label, out):
for m in re.finditer(b'BNSH\0\0\0\0', d): for m in re.finditer(b'BNSH\0\0\0\0', d):
size = struct.unpack_from('<I', d, m.start() + 0x1c)[0] size = struct.unpack_from('<I', d, m.start() + 0x1c)[0]
if m.start() + size <= len(d) and d[m.start() + 0x60:m.start() + 0x64] == b'grsc': out.extend(bnsh_shaders(d, m.start(), label)) if m.start() + size <= len(d) and d[m.start() + 0x60:m.start() + 0x64] == b'grsc': out.extend(bnsh_shaders(d, m.start(), label))
if MGFX in d: out.extend(mgfx_shaders(d, label))
def group(label): def group(label):
"""source group (second half of the package file name)""" """source group (second half of the package file name)"""
@@ -582,7 +653,8 @@ def ps_inputs(code):
"""Generic attributes a pixel shader reads: SPH words 6–13 are the input map, 8 bits for each of the 32 attributes (Ryujinx ShaderHeader.ImapTypes), non-zero = read; the translated MSL inputs are exactly user(locn<attribute>)""" """Generic attributes a pixel shader reads: SPH words 6–13 are the input map, 8 bits for each of the 32 attributes (Ryujinx ShaderHeader.ImapTypes), non-zero = read; the translated MSL inputs are exactly user(locn<attribute>)"""
return {i for i in range(32) if struct.unpack_from('<I', code, 0x30 + 4 * (6 + (i >> 2)))[0] >> ((i & 3) * 8) & 0xff} return {i for i in range(32) if struct.unpack_from('<I', code, 0x30 + 4 * (6 + (i >> 2)))[0] >> ((i & 3) * 8) & 0xff}
SINGLE_STAGE = ('#GLSLC@', '#UE', '#MTSPK@') # sources whose every shader is a program of one stage: GLSLC outputs, Unreal's shader records, MT Framework's shader package (BNSH variations, Unity programs, MSHD and SDF pair theirs) SINGLE_STAGE = ('#GLSLC@', '#UE', '#MTSPK@', '#MGFX@') # sources whose every shader is a program of one stage: GLSLC outputs, Unreal's shader records, MT Framework's shader package (BNSH variations, Unity programs, MSHD and SDF pair theirs);
# and MonoGame's effects, whose passes may name no vertex shader and keep the one bound (Celeste: 10 of its 13 effects draw through SpriteBatch's vertex shader, which is in main)
def separable_pairs(keys): def separable_pairs(keys):
"""Single-stage programs (13 Sentinels: each GLSLC output becomes a program with one stage, and BindProgram's stage bits combine vertex and pixel programs per draw; Unreal combines its shaders per draw """Single-stage programs (13 Sentinels: each GLSLC output becomes a program with one stage, and BindProgram's stage bits combine vertex and pixel programs per draw; Unreal combines its shaders per draw
from the pipeline state) have their real pairs only at draw time. vs_pad uses only the union of the locations the paired pixel shaders read, so every single-stage vertex shader is paired with from the pipeline state) have their real pairs only at draw time. vs_pad uses only the union of the locations the paired pixel shaders read, so every single-stage vertex shader is paired with
@@ -663,7 +735,7 @@ def scan_file(args):
import unityfs import unityfs
try: unity_shaders(d, '/' + rel, found, trees if not TREES else dict(TREES)) try: unity_shaders(d, '/' + rel, found, trees if not TREES else dict(TREES))
except unityfs.NoTree: return rel, {}, set(), 'defer' except unityfs.NoTree: return rel, {}, set(), 'defer'
elif b'BNSH\0\0\0\0' not in d and b'BAHS' not in d and d[:4] != b'MSHD' and not (d[:4] == b'SARC' and b'Yaz0' in d) and b'\x91\x68\x86\x19' not in d: return rel, {}, set(), {} # a SARC may hold Yaz0 members elif b'BNSH\0\0\0\0' not in d and b'BAHS' not in d and d[:4] != b'MSHD' and not (d[:4] == b'SARC' and b'Yaz0' in d) and b'\x91\x68\x86\x19' not in d and MGFX not in d: return rel, {}, set(), {} # a SARC may hold Yaz0 members
else: else:
scan_blob(d, '/' + rel, found) scan_blob(d, '/' + rel, found)
# whole GLSLC outputs in a file no other reader takes (FINAL FANTASY TACTICS - The Ivalice Chronicles: its engine's technique files, resources_nx/…/shader/*.tec, 261 outputs beside # whole GLSLC outputs in a file no other reader takes (FINAL FANTASY TACTICS - The Ivalice Chronicles: its engine's technique files, resources_nx/…/shader/*.tec, 261 outputs beside
@@ -774,7 +846,7 @@ def main():
with open(mapout, 'w') as f: with open(mapout, 'w') as f:
kinds = ' / '.join(k for k, hit in (('BNSH / BAHS', any('#BNSH@' in r[4] or '#BAHS@' in r[4] for r in rows)), ('Unity Shader', any('#var' in r[4] and '#BNSH@' not in r[4] and '#MSHD#' not in r[4] and '#SDF#' not in r[4] for r in rows)), kinds = ' / '.join(k for k, hit in (('BNSH / BAHS', any('#BNSH@' in r[4] or '#BAHS@' in r[4] for r in rows)), ('Unity Shader', any('#var' in r[4] and '#BNSH@' not in r[4] and '#MSHD#' not in r[4] and '#SDF#' not in r[4] for r in rows)),
('MSHD', any('#MSHD#' in r[4] for r in rows)), ('GLSLC', any('#GLSLC@' in r[4] for r in rows)), ('RE Engine SDF', any('#SDF#' in r[4] for r in rows)), ('MSHD', any('#MSHD#' in r[4] for r in rows)), ('GLSLC', any('#GLSLC@' in r[4] for r in rows)), ('RE Engine SDF', any('#SDF#' in r[4] for r in rows)),
('MT Framework SPK', any('#MTSPK@' in r[4] for r in rows)), ('MT Framework SPK', any('#MTSPK@' in r[4] for r in rows)), ('MonoGame MGFX', any('#MGFX@' in r[4] for r in rows)),
('Unreal Engine shader code archives', any('#UE' in r[4] for r in rows))) if hit) ('Unreal Engine shader code archives', any('#UE' in r[4] for r in rows))) if hit)
f.write(f'# kind: evidence {NOTE} ({kinds} in RomFS + main) → control section + code area keys (dm/gfx/tools/shader_split.py)\n') f.write(f'# kind: evidence {NOTE} ({kinds} in RomFS + main) → control section + code area keys (dm/gfx/tools/shader_split.py)\n')
f.write('# column 5 records where each key first appears; this game has no PC version to pair with\n') f.write('# column 5 records where each key first appears; this game has no PC version to pair with\n')
+1
View File
@@ -21,6 +21,7 @@ Do not stop at the obvious archive. In Mario the main `bfsha` held 4,888 of the
- **BNSH in Trinity packs** (Pokémon Legends: Z-A, Game Freak's engine): every shader is a BNSH inside the Oodle-compressed files of `arc/data.trpfs` ([01](01-extract.md)). For any game with Trinity packs (`ONEPACK` files), `trpfs_jobs` / `trinity_shaders` in `shader_split.py` name the packs from `data.trpfd`, decode every file with the game's own Oodle in the Mac host (`DM_OODLE_HOST` / `DM_OODLE_MODULES`, set by the NSP script) and scan it like a loose file. 28,603 shaders (11,633 vertex, 16,845 pixel, 125 compute) and 26,051 variation pairs: the material shaders in `system_resource/gfx2/shader/NX64/env_ikkaku` (`material/standard.bnsh`, `material/transparent.bnsh`, `system/light.bnsh`, and the `special/imgui.trsha` pack with the engine's system shaders), particle effects (`.ptcl` / `.trtml` in `ik_effectdata`, 19,842 of the shaders), UI layouts (`__ArchiveShader.bnsh` in the `.truiv` packs of `uidata`) and the 40 BNSH in `main`. The layout archives' control sections are 104 KB long, and their stage descriptors run 456 bytes past the control's own length (control +0x20), with bytes that differ between copies of one shader; `same_shader` compares copies only up to the control's own length. - **BNSH in Trinity packs** (Pokémon Legends: Z-A, Game Freak's engine): every shader is a BNSH inside the Oodle-compressed files of `arc/data.trpfs` ([01](01-extract.md)). For any game with Trinity packs (`ONEPACK` files), `trpfs_jobs` / `trinity_shaders` in `shader_split.py` name the packs from `data.trpfd`, decode every file with the game's own Oodle in the Mac host (`DM_OODLE_HOST` / `DM_OODLE_MODULES`, set by the NSP script) and scan it like a loose file. 28,603 shaders (11,633 vertex, 16,845 pixel, 125 compute) and 26,051 variation pairs: the material shaders in `system_resource/gfx2/shader/NX64/env_ikkaku` (`material/standard.bnsh`, `material/transparent.bnsh`, `system/light.bnsh`, and the `special/imgui.trsha` pack with the engine's system shaders), particle effects (`.ptcl` / `.trtml` in `ik_effectdata`, 19,842 of the shaders), UI layouts (`__ArchiveShader.bnsh` in the `.truiv` packs of `uidata`) and the 40 BNSH in `main`. The layout archives' control sections are 104 KB long, and their stage descriptors run 456 bytes past the control's own length (control +0x20), with bytes that differ between copies of one shader; `same_shader` compares copies only up to the control's own length.
- **BNSH in Bezel Engine archives** (Big Brain Academy: Brain vs. Brain, NintendoWare Bezel Engine 1.56): the shaders are BNSH in the `.bfsha` entries of the `.bea` archives (zstd entries, [01](01-extract.md)), most of them the engine's own in `System.nx.bea` (`_BezelSystemResources/<system>/shader/*.bfsha`: PBR shadow maps 846, post effects 770, …), the rest in the layouts' `__ArchiveShader.bnsh` and the per-archive shader packs (`.bnbshpk`), plus one BNSH in `main`. `shader_split.py` scans each archive entry as a file of its own (`bea_members`): 2,125 shaders in about 90 s. - **BNSH in Bezel Engine archives** (Big Brain Academy: Brain vs. Brain, NintendoWare Bezel Engine 1.56): the shaders are BNSH in the `.bfsha` entries of the `.bea` archives (zstd entries, [01](01-extract.md)), most of them the engine's own in `System.nx.bea` (`_BezelSystemResources/<system>/shader/*.bfsha`: PBR shadow maps 846, post effects 770, …), the rest in the layouts' `__ArchiveShader.bnsh` and the per-archive shader packs (`.bnbshpk`), plus one BNSH in `main`. `shader_split.py` scans each archive entry as a file of its own (`bea_members`): 2,125 shaders in about 90 s.
- **SPK in an MT Framework ARC** (The Great Ace Attorney Chronicles, Capcom's MT Framework): the engine's one shader package, `sc\NX_64\root` (`SPK\0`, type hash `0x02358e1a`) in `nativeNXx64/sa/NX_64/root.arc` ([01](01-extract.md)). After the package's tables every shader is a code area followed directly by its control section, back to back to the end of the file: 230 vertex shaders, then 230 pixel shaders. Each control section names its code length (+0x704), so the code is the bytes right before it, and every block must start where the previous one ended (`mtspk_shaders`). The tables that say which vertex and pixel shaders the engine combines are not read: the vertex shaders are paired with the single-stage cover (one pixel shader reads every input any of them reads). `CustomShaderPackage.mfx` (`MFX\0`, the effect descriptions) holds no shader code, and neither `main` nor any other ARC entry does. All 460 build, with Mario's binding layout; every binding decodes. - **SPK in an MT Framework ARC** (The Great Ace Attorney Chronicles, Capcom's MT Framework): the engine's one shader package, `sc\NX_64\root` (`SPK\0`, type hash `0x02358e1a`) in `nativeNXx64/sa/NX_64/root.arc` ([01](01-extract.md)). After the package's tables every shader is a code area followed directly by its control section, back to back to the end of the file: 230 vertex shaders, then 230 pixel shaders. Each control section names its code length (+0x704), so the code is the bytes right before it, and every block must start where the previous one ended (`mtspk_shaders`). The tables that say which vertex and pixel shaders the engine combines are not read: the vertex shaders are paired with the single-stage cover (one pixel shader reads every input any of them reads). `CustomShaderPackage.mfx` (`MFX\0`, the effect descriptions) holds no shader code, and neither `main` nor any other ARC entry does. All 460 build, with Mario's binding layout; every binding decodes.
- **MonoGame effects** (Celeste: MonoGame for the Switch, its C# compiled to C++ by Sickhead's BRUTE): the 13 XNB files in `Content/Effects` (XNB platform `S`, uncompressed, read by `EffectReader`) each hold one MGFX effect, and `main` holds SpriteBatch's built-in one; no other file and no GLSLC output. MGFX version 8 is MonoGame's own layout: `MGFX`, u8 version, u8 profile, u32 effect key, the constant buffers, then each shader as u8 vertex (1) / pixel (0), u32 length, the bytecode (u32 control length, the control section, u32 code length, the code area: GLSLC's output without its header), the samplers, the constant buffer indices and an attribute list (as MonoGame's OpenGL backend writes it), then the parameters and the techniques with their passes (`mgfx_effect`). The reader parses every effect to its end, which checks the layout; in `main`, four bytes `MGFX` count only when version 8 and a control section follow. A pass names its vertex and pixel shader by index, 255 for none, and 10 of the 13 effects have no vertex shader: SpriteBatch binds its own and the effect's pass only replaces the pixel shader. Vertex and pixel shaders therefore combine across effects, and they are paired as single-stage programs. 26 shaders (20 pixel, 6 vertex), all with Mario's binding layout; every binding decodes.
- **BAHS** (agl `sharcb`, engine utility shaders): code region at header +0x1c (size +0x18). Binary entries in the table area are 16 bytes `{code offset, 0, control size (0x880/0x88c; 0x848 in BAHS version 9, Super Mario 3D World), signed offset from this entry to the control section}`. Control sections are shared between programs, so pair by entry, never by searching for code near a control. - **BAHS** (agl `sharcb`, engine utility shaders): code region at header +0x1c (size +0x18). Binary entries in the table area are 16 bytes `{code offset, 0, control size (0x880/0x88c; 0x848 in BAHS version 9, Super Mario 3D World), signed offset from this entry to the control section}`. Control sections are shared between programs, so pair by entry, never by searching for code near a control.
- **Shorter control sections (SDK 10, Super Mario 3D World + Bowser's Fury)**: the control section stores its own length at +0x20. Mario's, Engage's, Dread's and 13 Sentinels' are all at least 0x880 bytes; 3D World has 2,888 of 0x850 bytes (its ShaderData bfsha and six layouts) and 0x848 in BAHS version 9, each followed directly by other data. The key therefore hashes the first `min(0x880, control +0x20)` bytes (`ctl_len` in `shader_split.py`, `keys_hash` in `hle_nvn.c`); the other games' tables came out byte-identical. - **Shorter control sections (SDK 10, Super Mario 3D World + Bowser's Fury)**: the control section stores its own length at +0x20. Mario's, Engage's, Dread's and 13 Sentinels' are all at least 0x880 bytes; 3D World has 2,888 of 0x850 bytes (its ShaderData bfsha and six layouts) and 0x848 in BAHS version 9, each followed directly by other data. The key therefore hashes the first `min(0x880, control +0x20)` bytes (`ctl_len` in `shader_split.py`, `keys_hash` in `hle_nvn.c`); the other games' tables came out byte-identical.
- **Compute code without a header (SDK 10)**: 3D World's particle compute shaders start their instructions at code +0 (like D2R), not after a `0x12345679` header. `shader_split.py` accepts both; `dmsh_build.py` strips only a header that is there. - **Compute code without a header (SDK 10)**: 3D World's particle compute shaders start their instructions at code +0 (like D2R), not after a `0x12345679` header. `shader_split.py` accepts both; `dmsh_build.py` strips only a header that is there.
+8
View File
@@ -423,6 +423,14 @@ The game's own work and its frame rate stay as they are; the host avoids work of
- **Input:** controller only (stick or D-pad to move, A redo, B undo, X reset, + menu): it stays docked at 1920x1080 with the 2D pad layout. - **Input:** controller only (stick or D-pad to move, A redo, B undo, X reset, + menu): it stays docked at 1920x1080 with the 2D pad layout.
- **Mac input:** a fresh save solves Intro 1 with `DM_KEYS="f700:A,f1000:A,f1300:A,f1600:A,f2400:Right,f2450:Down,f2500:Down,f2550:Down,f2600:Down,f3000:Up*3/30,f3090:Right*3/30,f3180:Down*2/30,f3240:Up*3/30,f3330:Left*4/30"` (English, the title, into Intro 1 from the hub at about frame 2,700, solved and on to Intro 2 at about frame 3,500). - **Mac input:** a fresh save solves Intro 1 with `DM_KEYS="f700:A,f1000:A,f1300:A,f1600:A,f2400:Right,f2450:Down,f2500:Down,f2550:Down,f2600:Down,f3000:Up*3/30,f3090:Right*3/30,f3180:Down*2/30,f3240:Up*3/30,f3330:Left*4/30"` (English, the title, into Intro 1 from the hub at about frame 2,700, solved and on to Intro 2 at about frame 3,500).
**Celeste (`01002B30028F6000`)**
- **Status:** brought up on the Mac with no host change: the logos, the title, a new file and the prologue (walking, jumping, the camera following Madeline), at 60 fps with music and sound, no red line; about 1.6–1.7 GB resident, flat over five minutes. Not yet tested on an iPhone.
- **Engine:** MonoGame for the Switch: MonoGame's and the game's C# compiled to C++ by Sickhead's BRUTE into `main` (78 MB with its bss), FMOD Studio (its banks in `Content/FMOD/Desktop`, sound through AudioOut), SDK 15.3.2, NVN, update 1.4.1.1 (v589824; the title shows v.1.4.1.0). Only `main` and `sdk`.
- **Shaders:** 26 (20 pixel, 6 vertex) in MonoGame effects: the 13 XNB files in `Content/Effects` and SpriteBatch's built-in effect in `main`, which `shader_split.py` now reads (`mgfx_effect`, [04](agents/04-graphics.md)). Every binding decodes.
- **Memory:** no system resource (`sys_resource 0`), so no virtual address memory: the game sets a 3.11 GB heap (pool − used memory) in the 4 GB layout. The rewritten modules need 100 MB of used memory (`mem_used`).
- **Input:** controller only; the game maps its buttons by position, so B and X jump. The virtual gamepad starts in the 2D layout.
- **Mac input:** a fresh save reaches the prologue with `DM_KEYS="f1850:A,f2200:A,f2600:A,f3000:A"` (the title, Climb, a new file, Begin; control of Madeline from about frame 5,000); then `f5100:LRight~1.0,f5400:LRight+X~0.3` walks to the first ledge and jumps onto it.
## Tools (`dm/tools`) ## Tools (`dm/tools`)
| Tool | What it does | | Tool | What it does |
+2 -1
View File
@@ -457,6 +457,7 @@ def survey(nsp_dir, app, fam, keys, repo, py, env):
if rom['mags']['Trinity pack']: notes.append('Trinity packs (Game Freak, arc/data.trpfs with its index data.trpfd): the shaders are BNSH inside Oodle Mermaid streams; shader_split.py decodes them with the game\'s own OodleLZ_Decompress in the Mac host, which dm/host/hle_oodle.c finds by the library\'s name string') if rom['mags']['Trinity pack']: notes.append('Trinity packs (Game Freak, arc/data.trpfs with its index data.trpfd): the shaders are BNSH inside Oodle Mermaid streams; shader_split.py decodes them with the game\'s own OodleLZ_Decompress in the Mac host, which dm/host/hle_oodle.c finds by the library\'s name string')
elif rom['mags']['MT Framework ARC']: notes.append('MT Framework ARC archives (Capcom, The Great Ace Attorney Chronicles): the shaders are in a shader package (SPK) inside one of them, which shader_split.py reads (mtarc_members, mtspk_shaders)') elif rom['mags']['MT Framework ARC']: notes.append('MT Framework ARC archives (Capcom, The Great Ace Attorney Chronicles): the shaders are in a shader package (SPK) inside one of them, which shader_split.py reads (mtarc_members, mtspk_shaders)')
elif rom['mags']['Bezel Engine archive']: notes.append('NintendoWare Bezel Engine archives (.bea, magic SCNE; Big Brain Academy: Brain vs. Brain): every entry is a zstd frame, and the shaders are BNSH in their .bfsha entries, which shader_split.py reads (bea_members)') elif rom['mags']['Bezel Engine archive']: notes.append('NintendoWare Bezel Engine archives (.bea, magic SCNE; Big Brain Academy: Brain vs. Brain): every entry is a zstd frame, and the shaders are BNSH in their .bfsha entries, which shader_split.py reads (bea_members)')
elif rom['mags']['XNB'] and 'MonoGame' in F['markers']: notes.append('MonoGame (XNB content): the shaders are MonoGame effects (MGFX version 8, NVN control sections and code areas) in the XNB files EffectReader reads, and SpriteBatch\'s in main, which shader_split.py reads (mgfx_effect; Celeste). A compressed XNB (LZX / LZ4) is not read')
elif not (rom['mags']['BNSH'] or rom['mags']['BAHS']) and not rom['mags']['UnityFS'] and not vulkan and not F['glslc']: elif not (rom['mags']['BNSH'] or rom['mags']['BAHS']) and not rom['mags']['UnityFS'] and not vulkan and not F['glslc']:
notes.append('no BNSH / BAHS at the top level: the shaders are inside compressed or other containers or in main, and must be located first') notes.append('no BNSH / BAHS at the top level: the shaders are inside compressed or other containers or in main, and must be located first')
major = {v.split('_')[0] for v in F['sdkver']} major = {v.split('_')[0] for v in F['sdkver']}
@@ -524,7 +525,7 @@ def write_profile(pdir, app, f):
'# ---- App ----', f'name{T}{name}', f'ipa{T}{ipa_name(name, app)}', f'app_category{T}public.app-category.games', *key('pad_layout'), *key('op_mode'), '', '# ---- App ----', f'name{T}{name}', f'ipa{T}{ipa_name(name, app)}', f'app_category{T}public.app-category.games', *key('pad_layout'), *key('op_mode'), '',
'# ---- Versions ----', T.join(['build', f['version'], f['display']] + [f'{m}={F["mods"][m]["bid"]}' for m in mods]), f'package{T}{f["version"]}', f'shader_versions{T}{f["version"]}', '', '# ---- Versions ----', T.join(['build', f['version'], f['display']] + [f'{m}={F["mods"][m]["bid"]}' for m in mods]), f'package{T}{f["version"]}', f'shader_versions{T}{f["version"]}', '',
'# ---- Files ----', *key('modules'), f'romfs_marker{T}{R["biggest"][1]}', f'zip_store{T}{":".join(store + [".metallib"])}', '', '# ---- Files ----', *key('modules'), f'romfs_marker{T}{R["biggest"][1]}', f'zip_store{T}{":".join(store + [".metallib"])}', '',
'# ---- Memory ----', *key('mem_reserve'), *key('mem_alias'), *key('mem_stack'), *key('app_pool'), '', '# ---- Memory ----', *key('mem_reserve'), *key('mem_alias'), *key('mem_stack'), *key('app_pool'), *key('sys_resource'), '',
'# ---- Shader split ----', *key('shader_groups', " ".join(groups) or "/=romfs"), f'shader_note{T}{name} shaders (provisional profile)'] '# ---- Shader split ----', *key('shader_groups', " ".join(groups) or "/=romfs"), f'shader_note{T}{name} shaders (provisional profile)']
(pdir / 'game.tsv').write_text('\n'.join(L) + '\n', encoding='utf-8') (pdir / 'game.tsv').write_text('\n'.join(L) + '\n', encoding='utf-8')
for n, text in EMPTY_TABLES.items(): (pdir / n).write_text(text) for n, text in EMPTY_TABLES.items(): (pdir / n).write_text(text)