DRAGON QUEST XI S plays on the Mac: profile 01006C300E9F0000 (update 1.0.4, SDK 7.3.2)

New profile for Square Enix's Unreal Engine 4.18 game: from the opening movie and the title
through the prologue into free play at Cobblestone Tor, 30 fps with sound and saves, no red
line. An IPA builds from the NSP folder; it has not run on an iPhone yet.

- Files and shaders: Unreal 4.18 legacy (version 4) paks with LZ5 blocks, decoded on the Mac
  with the game's own LZ5_decompress_safe (uepak.py, hle_oodle.c serving after nninitStartup);
  38,892 shaders inline in the cooked packages as zlib-compressed LSLG records (shader_split.py
  ue_legacy_jobs, ue_inline_shaders; the NSP script and profile_guess know both pak kinds).
- Memory: Unreal's address region capped at 4064 MB (game_args, MB up to 4095); SDK 7.3.2 cuts
  in patch_nso.py (node area 62 MB, 32 MB alignment for large regions; the build Odyssey shares,
  which boots unchanged) for Engage's 5 GB block. Rewriter: a jump table whose bounds check sits
  right before the load (check_before_copy).
- Host, for every game: audio catch-up frames wait for queue space (Square Enix's sound driver
  never waits on the renderer event: 11,000 updates a second before); movie decoder events read
  at signal time as the SDK does, audio decoders keep 32 outputs (the player plays only once
  every track holds one: the opening movie never started in half the boots), and no input after
  an end-of-stream packet until Flush / Stop (a skipped movie mapped 2.25 GB of end-of-stream
  samples); ec shop accessors, friends, auto-sleep, album, file data cache and movie hooks.
- Graphics, for every game: a geometry shader that only picks the layer (Unreal's
  FWriteToSliceGS, the colour-grading LUT) folded into the vertex shader's layer entry points
  (dmsh_build.py gs_slice / vs_layer_entry, nvn_mtl.m prog_fold_gs); cube-map array depth counted
  in layer-faces in the Metal texture, the upload and the guest layout (t_nvn checks it);
  nvnTextureGetTextureAddress and the command memory size / free getters.
- Shader chain changed (dmsh_build.py): DQ11 rebuilt for Mac and iOS; every other profile's
  dmsh_chain.h rewritten with its c1_full / cb_extent lists carried over (no c1 or uniform read
  touched). Mac checks on this host: Big Brain Academy (movie loops, 23 catch-up renders as
  before), Monster Hunter Rise (movies), Super Mario Odyssey (boot on the cut sdk).
This commit is contained in:
seedds authored and MacMini committed 2026-10-22 09:32:16 +08:00
1 parent 0beed52f4a
commit df0820eb68
116 files changed
+83114 -168

No files matched your search

+1 -1
View File
@@ -1,3 +1,3 @@
# chain b-61eff511fce3 2026-10-21T21:45:38+08:00
# chain b-664c5edfc04a 2026-10-21T21:45:38+08:00 (list carried over from b-61eff511fce3: that chain change did not touch any c1 read)
6405f3e55facc7c9
d9097b8aba3a7080
1 # chain b-61eff511fce3 2026-10-21T21:45:38+08:00 # chain b-664c5edfc04a 2026-10-21T21:45:38+08:00 (list carried over from b-61eff511fce3: that chain change did not touch any c1 read)
2 6405f3e55facc7c9 6405f3e55facc7c9
3 d9097b8aba3a7080 d9097b8aba3a7080
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-61eff511fce3 2026-10-21T21:45:49+08:00
# chain b-664c5edfc04a 2026-10-21T21:45:49+08:00 (list carried over from b-61eff511fce3: that chain change did not touch any uniform read)
# 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)
00008cfe4fc1d31b 5:304 6:1856 7:64 12:16
0004d9615081f2dd 5:304 6:1856 7:64
Can't render this file because it is too large.
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 0100000000010000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "61eff511fce3"
#define DMSH_CHAIN "664c5edfc04a"
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-b3fcfcb9ddcc 2026-10-21T23:23:14+08:00 (list carried over from b-6c7fd55d331c: that chain change did not touch any c1 read)
# chain b-5010ee7f8a0e 2026-10-21T23:23:14+08:00 (list carried over from b-b3fcfcb9ddcc: that chain change did not touch any c1 read)
0ffd2c92087e4b54
69d3868c8d828fe6
8a06e3dda01235c1
1 # chain b-b3fcfcb9ddcc 2026-10-21T23:23:14+08:00 (list carried over from b-6c7fd55d331c: that chain change did not touch any c1 read) # chain b-5010ee7f8a0e 2026-10-21T23:23:14+08:00 (list carried over from b-b3fcfcb9ddcc: that chain change did not touch any c1 read)
2 0ffd2c92087e4b54 0ffd2c92087e4b54
3 69d3868c8d828fe6 69d3868c8d828fe6
4 8a06e3dda01235c1 8a06e3dda01235c1
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 010005501E68C000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "b3fcfcb9ddcc"
#define DMSH_CHAIN "5010ee7f8a0e"
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-1a20936f730c 2026-10-21T23:25:42+08:00
# chain b-8e946feb9199 2026-10-21T23:25:42+08:00 (list carried over from b-1a20936f730c: that chain change did not touch any c1 read)
35cbc686dd8461b8
4872bec9a5567f8b
54a618792b2d5f0f
1 # chain b-1a20936f730c 2026-10-21T23:25:42+08:00 # chain b-8e946feb9199 2026-10-21T23:25:42+08:00 (list carried over from b-1a20936f730c: that chain change did not touch any c1 read)
2 35cbc686dd8461b8
3 4872bec9a5567f8b
4 54a618792b2d5f0f
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-1a20936f730c 2026-10-21T23:25:49+08:00
# chain b-8e946feb9199 2026-10-21T23:25:49+08:00 (list carried over from b-1a20936f730c: that chain change did not touch any uniform read)
# 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)
000429e5b56f54be 0:304 2:528 3:704
0011e7066f8b040e 0:144 1:224 2:112
Can't render this file because it is too large.
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 010015100B514000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "1a20936f730c"
#define DMSH_CHAIN "8e946feb9199"
+1 -1
View File
@@ -1,3 +1,3 @@
# chain b-7f751dfec0ce 2026-10-21T23:27:25+08:00
# chain b-9237eda03d2b 2026-10-21T23:27:25+08:00 (list carried over from b-7f751dfec0ce: that chain change did not touch any c1 read)
25c5829f891f9b2c
976839d3ba92c8f3
1 # chain b-7f751dfec0ce 2026-10-21T23:27:25+08:00 # chain b-9237eda03d2b 2026-10-21T23:27:25+08:00 (list carried over from b-7f751dfec0ce: that chain change did not touch any c1 read)
2 25c5829f891f9b2c 25c5829f891f9b2c
3 976839d3ba92c8f3 976839d3ba92c8f3
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-7f751dfec0ce 2026-10-21T23:27:30+08:00
# chain b-9237eda03d2b 2026-10-21T23:27:30+08:00 (list carried over from b-7f751dfec0ce: that chain change did not touch any uniform read)
# 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)
000064cdb6305b93 5:304 7:64 12:320
00021722b48824b8 5:304 6:1872 7:112
1 # chain b-7f751dfec0ce 2026-10-21T23:27:30+08:00 # chain b-9237eda03d2b 2026-10-21T23:27:30+08:00 (list carried over from b-7f751dfec0ce: that chain change did not touch any uniform read)
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 000064cdb6305b93
4 00021722b48824b8
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 010028600EBDA000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "7f751dfec0ce"
#define DMSH_CHAIN "9237eda03d2b"
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-8200d93ca492 2026-10-21T23:44:26+08:00
# chain b-6d8251f74fab 2026-10-21T23:44:26+08:00 (list carried over from b-8200d93ca492: that chain change did not touch any c1 read)
0002d193f6c52811
0010e810186cdc91
001299ca51e856c2
1 # chain b-8200d93ca492 2026-10-21T23:44:26+08:00 # chain b-6d8251f74fab 2026-10-21T23:44:26+08:00 (list carried over from b-8200d93ca492: that chain change did not touch any c1 read)
2 0002d193f6c52811
3 0010e810186cdc91
4 001299ca51e856c2
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-8200d93ca492 2026-10-21T23:45:10+08:00
# chain b-6d8251f74fab 2026-10-21T23:45:10+08:00 (list carried over from b-8200d93ca492: that chain change did not touch any uniform read)
# 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)
0002d193f6c52811 0:1872
00088e149f8757ca 0:1872
Can't render this file because it is too large.
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 01002B00111A2000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "8200d93ca492"
#define DMSH_CHAIN "6d8251f74fab"
+1 -1
View File
@@ -1,3 +1,3 @@
# chain b-b94f1911fe8f 2026-10-22T08:50:37+08:00
# chain b-e9a6b54a7284 2026-10-22T08:50:37+08:00 (list carried over from b-b94f1911fe8f: that chain change did not touch any c1 read)
2b34d193d87075d5
da9c1ded21a21fb9
1 # chain b-b94f1911fe8f 2026-10-22T08:50:37+08:00 # chain b-e9a6b54a7284 2026-10-22T08:50:37+08:00 (list carried over from b-b94f1911fe8f: that chain change did not touch any c1 read)
2 2b34d193d87075d5 2b34d193d87075d5
3 da9c1ded21a21fb9 da9c1ded21a21fb9
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-b94f1911fe8f 2026-10-22T08:50:37+08:00
# chain b-e9a6b54a7284 2026-10-22T08:50:37+08:00 (list carried over from b-b94f1911fe8f: that chain change did not touch any uniform read)
# 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
1 # chain b-b94f1911fe8f 2026-10-22T08:50:37+08:00 # chain b-e9a6b54a7284 2026-10-22T08:50:37+08:00 (list carried over from b-b94f1911fe8f: that chain change did not touch any uniform read)
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
4 1e5020d03a2d979c
+1 -1
View File
@@ -1,2 +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"
#define DMSH_CHAIN "e9a6b54a7284"
+1 -1
View File
@@ -1 +1 @@
# chain b-85e8c98ac176 2026-10-22T00:21:36+08:00 (list carried over from b-adfdb13db222: that chain change did not touch any c1 read)
# chain b-f64ba2b71fff 2026-10-22T00:21:36+08:00 (list carried over from b-85e8c98ac176: that chain change did not touch any c1 read)
1 # chain b-85e8c98ac176 2026-10-22T00:21:36+08:00 (list carried over from b-adfdb13db222: that chain change did not touch any c1 read) # chain b-f64ba2b71fff 2026-10-22T00:21:36+08:00 (list carried over from b-85e8c98ac176: that chain change did not touch any c1 read)
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-85e8c98ac176 2026-10-22T00:21:36+08:00 (list carried over from b-adfdb13db222: that chain change did not touch any uniform read)
# chain b-f64ba2b71fff 2026-10-22T00:21:36+08:00 (list carried over from b-85e8c98ac176: that chain change did not touch any uniform read)
# 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)
01705ebebe3e302b 0:64 1:16 2:304
020b72102b82b310 0:64
1 # chain b-85e8c98ac176 2026-10-22T00:21:36+08:00 (list carried over from b-adfdb13db222: that chain change did not touch any uniform read) # chain b-f64ba2b71fff 2026-10-22T00:21:36+08:00 (list carried over from b-85e8c98ac176: that chain change did not touch any uniform read)
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 01705ebebe3e302b
4 020b72102b82b310
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 010036E00FB20000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "85e8c98ac176"
#define DMSH_CHAIN "f64ba2b71fff"
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-7820419c3539 2026-10-21T23:27:35+08:00
# chain b-bb5763db4c8d 2026-10-21T23:27:35+08:00 (list carried over from b-7820419c3539: that chain change did not touch any c1 read)
1b5e5c3e653702d2
1bfeb381d95d18c6
280094f212199d77
1 # chain b-7820419c3539 2026-10-21T23:27:35+08:00 # chain b-bb5763db4c8d 2026-10-21T23:27:35+08:00 (list carried over from b-7820419c3539: that chain change did not touch any c1 read)
2 1b5e5c3e653702d2
3 1bfeb381d95d18c6
4 280094f212199d77
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-7820419c3539 2026-10-21T23:27:35+08:00
# chain b-bb5763db4c8d 2026-10-21T23:27:35+08:00 (list carried over from b-7820419c3539: that chain change did not touch any uniform read)
# 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)
01058b659b699495 0:288
0117c03be720951e 0:560
1 # chain b-7820419c3539 2026-10-21T23:27:35+08:00 # chain b-bb5763db4c8d 2026-10-21T23:27:35+08:00 (list carried over from b-7820419c3539: that chain change did not touch any uniform read)
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 01058b659b699495
4 0117c03be720951e
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 010038B015560000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "7820419c3539"
#define DMSH_CHAIN "bb5763db4c8d"
+1 -1
View File
@@ -1 +1 @@
# chain b-d3964897130f 2026-10-21T23:27:41+08:00
# chain b-c5e13b82944d 2026-10-21T23:27:41+08:00 (list carried over from b-d3964897130f: that chain change did not touch any c1 read)
1 # chain b-d3964897130f 2026-10-21T23:27:41+08:00 # chain b-c5e13b82944d 2026-10-21T23:27:41+08:00 (list carried over from b-d3964897130f: that chain change did not touch any c1 read)
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-d3964897130f 2026-10-21T23:27:41+08:00
# chain b-c5e13b82944d 2026-10-21T23:27:41+08:00 (list carried over from b-d3964897130f: that chain change did not touch any uniform read)
# 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)
0040f948baa144f4 0:1904
00870f86a49cf3b9 0:1904 12:64
1 # chain b-d3964897130f 2026-10-21T23:27:41+08:00 # chain b-c5e13b82944d 2026-10-21T23:27:41+08:00 (list carried over from b-d3964897130f: that chain change did not touch any uniform read)
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 0040f948baa144f4
4 00870f86a49cf3b9
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 01003FC01670C000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "d3964897130f"
#define DMSH_CHAIN "c5e13b82944d"
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-3824e28513d2 2026-10-21T23:23:14+08:00 (list carried over from b-2eb33bdda057: that chain change did not touch any c1 read)
# chain b-572a12a37414 2026-10-21T23:23:14+08:00 (list carried over from b-3824e28513d2: that chain change did not touch any c1 read)
006a737523e70720
00da789b4ca18c03
00e5f2d3b5851fab
1 # chain b-3824e28513d2 2026-10-21T23:23:14+08:00 (list carried over from b-2eb33bdda057: that chain change did not touch any c1 read) # chain b-572a12a37414 2026-10-21T23:23:14+08:00 (list carried over from b-3824e28513d2: that chain change did not touch any c1 read)
2 006a737523e70720
3 00da789b4ca18c03
4 00e5f2d3b5851fab
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 01005270232F2000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "3824e28513d2"
#define DMSH_CHAIN "572a12a37414"
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 0100535012974000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "4e619a4691df"
#define DMSH_CHAIN "810851ccb224"
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-58aa432ce3c0 2026-10-22T08:26:19+08:00
# chain b-7de5c5304538 2026-10-22T08:26:19+08:00 (list carried over from b-58aa432ce3c0: that chain change did not touch any c1 read)
1a2b65cd05ff557c
21c7ff0d9591a637
33e4340e5bcbc5c6
1 # chain b-58aa432ce3c0 2026-10-22T08:26:19+08:00 # chain b-7de5c5304538 2026-10-22T08:26:19+08:00 (list carried over from b-58aa432ce3c0: that chain change did not touch any c1 read)
2 1a2b65cd05ff557c
3 21c7ff0d9591a637
4 33e4340e5bcbc5c6
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-58aa432ce3c0 2026-10-22T08:26:22+08:00
# chain b-7de5c5304538 2026-10-22T08:26:22+08:00 (list carried over from b-58aa432ce3c0: that chain change did not touch any uniform read)
# 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)
00388ae860457bd9 0:160
00a24b2c0ac95e17 0:32
1 # chain b-58aa432ce3c0 2026-10-22T08:26:22+08:00 # chain b-7de5c5304538 2026-10-22T08:26:22+08:00 (list carried over from b-58aa432ce3c0: that chain change did not touch any uniform read)
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 00388ae860457bd9
4 00a24b2c0ac95e17
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 010055D009F78000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "58aa432ce3c0"
#define DMSH_CHAIN "7de5c5304538"
+1 -1
View File
@@ -1,3 +1,3 @@
# chain b-cb0b0057e8d7 2026-10-22T00:45:45+08:00
# chain b-34a53de6ceff 2026-10-22T00:45:45+08:00 (list carried over from b-cb0b0057e8d7: that chain change did not touch any c1 read)
3a24a7c1351c6492
5c116cabc36aeae3
1 # chain b-cb0b0057e8d7 2026-10-22T00:45:45+08:00 # chain b-34a53de6ceff 2026-10-22T00:45:45+08:00 (list carried over from b-cb0b0057e8d7: that chain change did not touch any c1 read)
2 3a24a7c1351c6492 3a24a7c1351c6492
3 5c116cabc36aeae3 5c116cabc36aeae3
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-cb0b0057e8d7 2026-10-22T00:45:51+08:00
# chain b-34a53de6ceff 2026-10-22T00:45:51+08:00 (list carried over from b-cb0b0057e8d7: that chain change did not touch any uniform read)
# 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)
003b9b8d92206318 1:1616 2:48 4:832 6:624 s0
005efd060ad46799 1:1616 4:832 6:592 s0
1 # chain b-cb0b0057e8d7 2026-10-22T00:45:51+08:00 # chain b-34a53de6ceff 2026-10-22T00:45:51+08:00 (list carried over from b-cb0b0057e8d7: that chain change did not touch any uniform read)
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 003b9b8d92206318
4 005efd060ad46799
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 0100620012D6E000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "cb0b0057e8d7"
#define DMSH_CHAIN "34a53de6ceff"
+1 -1
View File
@@ -1 +1 @@
# chain b-d605d84674ef 2026-10-21T23:23:14+08:00 (list carried over from b-5f5ac45ebe51: that chain change did not touch any c1 read)
# chain b-70bd2bcb3c30 2026-10-21T23:23:14+08:00 (list carried over from b-d605d84674ef: that chain change did not touch any c1 read)
1 # chain b-d605d84674ef 2026-10-21T23:23:14+08:00 (list carried over from b-5f5ac45ebe51: that chain change did not touch any c1 read) # chain b-70bd2bcb3c30 2026-10-21T23:23:14+08:00 (list carried over from b-d605d84674ef: that chain change did not touch any c1 read)
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 010069401ADB8000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "d605d84674ef"
#define DMSH_CHAIN "70bd2bcb3c30"
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-f9b2bbcfc576 2026-10-21T23:29:29+08:00
# chain b-b6964c355af2 2026-10-21T23:29:29+08:00 (list carried over from b-f9b2bbcfc576: that chain change did not touch any c1 read)
26c5fd7adbc12cc0
67f23f4ad5925e06
96a3e288731b2433
1 # chain b-f9b2bbcfc576 2026-10-21T23:29:29+08:00 # chain b-b6964c355af2 2026-10-21T23:29:29+08:00 (list carried over from b-f9b2bbcfc576: that chain change did not touch any c1 read)
2 26c5fd7adbc12cc0 26c5fd7adbc12cc0
3 67f23f4ad5925e06 67f23f4ad5925e06
4 96a3e288731b2433 96a3e288731b2433
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-f9b2bbcfc576 2026-10-21T23:29:36+08:00
# chain b-b6964c355af2 2026-10-21T23:29:36+08:00 (list carried over from b-f9b2bbcfc576: that chain change did not touch any uniform read)
# 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)
00008ab75998b65a 0:16 2:16 3:16 5:16 6:16
000be95364b95b90 0:416 1:880 4:816 6:16 s0 s1
1 # chain b-f9b2bbcfc576 2026-10-21T23:29:36+08:00 # chain b-b6964c355af2 2026-10-21T23:29:36+08:00 (list carried over from b-f9b2bbcfc576: that chain change did not touch any uniform read)
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 00008ab75998b65a
4 000be95364b95b90
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 01006BB00C6F0000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "f9b2bbcfc576"
#define DMSH_CHAIN "b6964c355af2"
+1
View File
@@ -0,0 +1 @@
# generated
1 # generated
+12
View File
@@ -0,0 +1,12 @@
# chain b-3882e2d3295f 2026-10-22T08:09:34+08:00
026f2479adc13489
10b8a90504eb68ad
2a0fa3cfc6a8391f
2f7bfe0d86ff032c
71ca7603bf39e49d
72f28e309fc62a57
9eb8165e902fb288
cd02a70fbc8fca77
d924aa64f19fdcc7
ecf3101594e73bb5
f6f6a178466b083b
1 # chain b-3882e2d3295f 2026-10-22T08:09:34+08:00
2 026f2479adc13489
3 10b8a90504eb68ad
4 2a0fa3cfc6a8391f
5 2f7bfe0d86ff032c
6 71ca7603bf39e49d
7 72f28e309fc62a57
8 9eb8165e902fb288
9 cd02a70fbc8fca77
10 d924aa64f19fdcc7
11 ecf3101594e73bb5
12 f6f6a178466b083b
File diff suppressed because it is too large. Load diff
+2
View File
@@ -0,0 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 01006C300E9F0000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "3882e2d3295f"
+44
View File
@@ -0,0 +1,44 @@
# DRAGON QUEST XI S: Echoes of an Elusive Age – Definitive Edition: Square Enix, Unreal Engine 4.18 (legacy version 4 paks, LZ5-compressed), SDK 7.3.2, NVN, Square Enix's own sound driver
# (SQEX::Sd, "SEAD") on the audio renderer, nn::movie for the movies (WebM: VP9 + Vorbis); update 1.0.4.
# Started from the NSP script's provisional profile (dm/games/profile_guess.py) and brought up on the Mac (docs/agents)
# ---- Identity ----
title_id 01006C300E9F0000
program_id 01006C300E9F0000
# ---- App ----
name DRAGON QUEST XI S: Echoes of an Elusive Age – Definitive Edition
ipa DragonQuestXIS.ipa
app_category public.app-category.role-playing-games
# ---- Versions ----
build v262144 1.0.4 main=1719aabfa5eae42b132b65770b871288 subsdk0=93ff8252d975adf07230cb86c83301c5 sdk=fcf93a41b08c5193a1410d262602dc62
package v262144
shader_versions v262144
# ---- Files ----
# subsdk0 is NintendoSDK_movie (main imports from it)
modules main subsdk0 sdk
romfs_marker JackGame/Content/Paks/pakchunk1-Switch.pak
zip_store .pak:.webm:.metallib
# ---- Input ----
# a 3D game: D-pad, right stick (camera) and L3 / R3 on the iPhone's virtual gamepad
pad_layout full
# ---- Memory ----
# Unreal Engine 4.18: nninitStartup takes one address region for the whole engine (16 GB by default), which a NumberLineAllocator (main+0x177c4d0) hands out; the game's argument
# -SwitchVirtualAddressSpaceSize= caps it, in MB up to 4095 (main+0x177c440: atoi, and a larger number is taken as bytes; 4224 gave a 4 KB region and a crash in the allocator's
# Compaction at boot). Two cuts on the SDK 7.3.2 build (dm/tools/patch_nso.py) put it 64 MB into the alias region: the node area 62 MB instead of 1 GB, and regions of 1 GB and more
# 32 MB aligned instead of 1 GB. Engage's 5 GB block: 256 MB in front + 4.375 GB alias region + 256 MB stack region + 128 MB heap region (the game sets no heap)
game_args -SwitchVirtualAddressSpaceSize=4064
mem_reserve 0x140000000
mem_alias 0x118000000
mem_stack 0x10000000
app_pool 0xCD500000
# ---- Shader split ----
# The engine's global shaders (Engine/GlobalShaderCache-GLSL_SWITCH.bin), the engine's own materials and the game's. Unreal Engine 4.18 keeps every shader inline in the cooked packages,
# zlib-compressed, in paks whose blocks are LZ5-compressed; the NSP script decodes them on the Mac with the game's own LZ5 (uepak.py, shader_split.py ue_legacy_jobs, DM_OODLE_SERVE)
shader_groups GlobalShaderCache=global #Engine/=engine #JackGame/=game
shader_note DRAGON QUEST XI S shaders
Can't render this file because it contains an unexpected character in line 2 and column 14.
@@ -0,0 +1,2 @@
# 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.
# Provisional profile: empty until recorded on the Mac (DM_PSO_MANIFEST=off DM_PSO_RECORD).
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 # Provisional profile: empty until recorded on the Mac (DM_PSO_MANIFEST=off DM_PSO_RECORD).
+235
View File
@@ -0,0 +1,235 @@
# 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
0087a5d5dd676b36 {"local":[64,1,1],"shared":1024}
00cd7fcfb2a63c1f {"local":[16,16,1],"shared":0}
00e98680af2c4258 {"local":[8,8,1],"shared":0}
0174eccbefad6070 {"local":[8,8,1],"shared":0}
0191c9971497582d {"local":[32,1,1],"shared":0}
0206d7c4b15b8ad8 {"local":[8,8,1],"shared":0}
020a84e294bcf6df {"local":[16,16,1],"shared":0}
02b7665e9ec8a4d0 {"local":[128,1,1],"shared":9456}
06f56a825a47e7f9 {"local":[8,8,1],"shared":0}
06f807e73f6a8d74 {"local":[8,8,1],"shared":0}
09df0d8130305a26 {"local":[8,8,1],"shared":0}
0bf2633c1b7057b4 {"local":[8,8,1],"shared":0}
0d3ec32c42e6ed02 {"local":[8,8,1],"shared":0}
0f99f0f4da4e4be7 {"local":[8,8,1],"shared":0}
1017b494e294dea0 {"local":[16,1,1],"shared":256}
1276bc78d78f29be {"local":[8,8,1],"shared":0}
1521aaeceb1e9e42 {"local":[32,1,1],"shared":0}
15a22c9583ff17a9 {"local":[8,8,1],"shared":0}
16bac67a232d8cbf {"local":[8,8,1],"shared":0}
17fe7944e4fe5caa {"local":[1,1,1],"shared":0}
18cb6ead3db49f3a {"local":[8,8,1],"shared":0}
1a201a9c485b6af9 {"local":[8,8,1],"shared":0}
1a6a483674bc54d4 {"local":[8,8,1],"shared":0}
1a93171daf4e5510 {"local":[8,8,1],"shared":0}
1baa65a1b85d099d {"local":[8,8,1],"shared":0}
1cfa4d51e0415c32 {"local":[8,1,1],"shared":512}
1dac1d09f64aaf12 {"local":[8,8,1],"shared":0}
1def7905c1a1ec5d {"local":[8,8,1],"shared":0}
1fbacc910596f9dd {"local":[8,8,1],"shared":0}
1fe65bf4fbaebabf {"local":[8,8,1],"shared":0}
213fcea0f568dcd0 {"local":[16,16,1],"shared":0}
229b9c0e8d76c2ec {"local":[64,1,1],"shared":0}
22d45abf1403c03d {"local":[32,1,1],"shared":0}
23b6d6dd2e9cf667 {"local":[8,8,1],"shared":0}
23c9f5b12579a86a {"local":[8,8,1],"shared":0}
23f28dd141833f58 {"local":[8,8,1],"shared":0}
25d3868b5f1bbb60 {"local":[8,8,1],"shared":0}
26171286f4f8101a {"local":[8,8,1],"shared":0}
26524270fd28eaf7 {"local":[8,8,1],"shared":0}
276f5ef9c397a9bd {"local":[8,8,1],"shared":0}
286df466c6e844ba {"local":[8,8,1],"shared":0}
2a0fa3cfc6a8391f {"local":[8,8,8],"shared":10240}
2a3f279629cbb560 {"local":[8,8,1],"shared":0}
2ad2f6a3d6ea38b4 {"local":[64,1,1],"shared":2048}
2b039c6219e44cfd {"local":[8,8,1],"shared":0}
2bb29df48f1a28cf {"local":[8,8,1],"shared":0}
2d1cce707faf1f0d {"local":[8,8,1],"shared":0}
2ed397351be2d894 {"local":[8,4,1],"shared":8192}
2f13c284faba7bc9 {"local":[8,8,1],"shared":0}
2f4e7e3e31df5526 {"local":[8,8,1],"shared":0}
30535e7c4ddb9e99 {"local":[512,1,1],"shared":32768}
31df2a5d71e61795 {"local":[8,1,1],"shared":512}
34d4f369e8c0800c {"local":[1024,1,1],"shared":16384}
364e3ca611e4ae15 {"local":[8,8,1],"shared":0}
3789652335105905 {"local":[64,1,1],"shared":2048}
3793b51acdbf53ee {"local":[8,8,1],"shared":0}
3ac970be7ecc64a4 {"local":[8,8,1],"shared":0}
3b31cd3b491a9e47 {"local":[8,8,1],"shared":0}
3bbe07a610ccd913 {"local":[512,1,1],"shared":32768}
3e3355a9288015d4 {"local":[8,8,1],"shared":0}
3e59a0da5337a2c7 {"local":[4,4,4],"shared":0}
3ff7ccafbf0ba9d0 {"local":[8,8,1],"shared":0}
402a3e04dc45ed9b {"local":[8,8,1],"shared":0}
410a7bc63ed2e690 {"local":[8,8,1],"shared":0}
41ed91d4d3903e41 {"local":[6,32,1],"shared":0}
4244de8aba8beef4 {"local":[16,1,1],"shared":256}
4303d8560a6ec306 {"local":[8,8,1],"shared":0}
438e22346896f7f9 {"local":[8,8,1],"shared":0}
4474fe9f55495405 {"local":[8,8,1],"shared":0}
458e3dae42a42dc4 {"local":[8,8,1],"shared":0}
473493742308c589 {"local":[8,8,1],"shared":0}
48056ac64b2f69a9 {"local":[8,8,8],"shared":0}
482c5f716661f590 {"local":[256,1,1],"shared":8192}
49863c5255195788 {"local":[8,8,1],"shared":0}
4b6f4083c2ed23d8 {"local":[8,8,1],"shared":0}
4c6a69805cfa9062 {"local":[16,16,1],"shared":0}
4cc3791edf16d929 {"local":[8,8,1],"shared":0}
4e49b65f714378ba {"local":[64,1,1],"shared":1024}
4ec07436d9fe4656 {"local":[16,16,1],"shared":14352}
4f612b423e75ad97 {"local":[8,8,1],"shared":0}
4faf4f12b6fdd2f5 {"local":[1,1,1],"shared":32}
50163eec216dd927 {"local":[32,1,1],"shared":0}
50694f117cbaa9db {"local":[8,8,1],"shared":0}
52110e6df7486606 {"local":[64,1,1],"shared":0}
55a8e21043017d1d {"local":[8,8,1],"shared":0}
56f3380466745edc {"local":[8,8,1],"shared":0}
57971461d6b20a35 {"local":[64,1,1],"shared":4976}
595367eebfda0ab8 {"local":[1024,1,1],"shared":16384}
598237c5f38bedbe {"local":[8,8,1],"shared":0}
5ade564d7586fd08 {"local":[8,8,1],"shared":0}
5b095cc820726ada {"local":[8,8,1],"shared":0}
5ba044a0d6b6ab10 {"local":[4,1,1],"shared":128}
5bdf05f6d1c85041 {"local":[8,8,1],"shared":0}
5c1f592b6cef8892 {"local":[64,1,1],"shared":1024}
5cde7967933904d5 {"local":[8,8,1],"shared":0}
5eae74ce832b2243 {"local":[8,8,1],"shared":0}
5f0e1b5bfc0006e9 {"local":[8,8,1],"shared":0}
5f2d68d3e9d72e0d {"local":[8,8,1],"shared":0}
5fc89c14c5f1690e {"local":[8,8,1],"shared":0}
606ff0f0afa5651d {"local":[8,8,1],"shared":0}
608c6a21735d2c77 {"local":[8,8,1],"shared":0}
61d2f4e918fad5a0 {"local":[8,8,1],"shared":0}
63c519bbdc30ee56 {"local":[16,16,1],"shared":0}
64c5aecbb8471caa {"local":[1024,1,1],"shared":16384}
6640c49b56dd7b24 {"local":[8,8,1],"shared":0}
681487593c1a73c1 {"local":[8,8,1],"shared":0}
6a0b9343b1f18419 {"local":[8,8,1],"shared":0}
6aabc4fa397b3910 {"local":[64,1,1],"shared":0}
6d8fe5292af40ba9 {"local":[8,8,1],"shared":0}
6ec2365e8d3a144a {"local":[8,8,1],"shared":0}
6ecf0b19a7fe0864 {"local":[16,16,1],"shared":0}
6fbf746d8b388d63 {"local":[1,1,1],"shared":0}
7189d29d3a02f1fb {"local":[512,1,1],"shared":0}
723efd79f853a7ad {"local":[8,8,1],"shared":0}
75bec860c88aee41 {"local":[64,1,1],"shared":4096}
75ce37340dc04b55 {"local":[4,4,4],"shared":0}
762fc5511501896a {"local":[8,8,1],"shared":0}
763a3bb664156f24 {"local":[128,1,1],"shared":8848}
7dd5219d5c107de3 {"local":[8,8,1],"shared":0}
7e653cac19398e7b {"local":[8,8,1],"shared":0}
7ecb76d8c4749807 {"local":[8,8,1],"shared":0}
7fbc1cfbc7c33d4f {"local":[8,8,1],"shared":0}
811a41951a8efca4 {"local":[8,8,1],"shared":0}
82370579fdf301ba {"local":[8,8,1],"shared":0}
84d22bce0f4fa3b6 {"local":[8,8,1],"shared":0}
877aa6235a76c9df {"local":[256,1,1],"shared":8192}
88043a45452cd943 {"local":[8,8,1],"shared":0}
88e56e843a6de111 {"local":[8,8,1],"shared":0}
89482c7a4425cdb6 {"local":[8,8,1],"shared":0}
89bebaf83cefc192 {"local":[256,1,1],"shared":8192}
89fac3751d1724e7 {"local":[16,1,1],"shared":256}
8c0675ac7a5e136e {"local":[512,1,1],"shared":32768}
8eefb81a9e8090ca {"local":[16,16,1],"shared":0}
8f30d5c626ab731d {"local":[8,8,1],"shared":0}
8f9e3746ee724775 {"local":[8,8,1],"shared":0}
90654df14691d66a {"local":[4,1,1],"shared":128}
911feb0b8cdc9040 {"local":[8,8,1],"shared":0}
91c6229a3469bc0f {"local":[8,8,1],"shared":0}
93e0c57694e95f2c {"local":[8,8,1],"shared":0}
9419cf0bbfccc804 {"local":[1,1,1],"shared":32}
9458208e1e8a730a {"local":[8,8,1],"shared":0}
9611b00b14d63618 {"local":[8,8,1],"shared":0}
9653e41ec35bf868 {"local":[8,8,1],"shared":0}
987c00ea411d729b {"local":[8,8,1],"shared":0}
991dc8911faa2061 {"local":[8,8,1],"shared":0}
9a9f40ebf61ae513 {"local":[8,8,1],"shared":0}
9b3cf668853c5d10 {"local":[8,8,1],"shared":0}
9dd9ca710eb84614 {"local":[4,4,4],"shared":0}
9eb8165e902fb288 {"local":[8,8,8],"shared":10240}
9f0492cfb80b95b7 {"local":[8,8,1],"shared":0}
9fa1479144834fae {"local":[8,8,1],"shared":0}
a00a3b7d9513bfca {"local":[1,1,1],"shared":16}
a194a4ff7a4c43df {"local":[8,8,1],"shared":0}
a25961bbd8435d49 {"local":[8,8,1],"shared":0}
a439b8c52e151e9f {"local":[8,8,1],"shared":0}
a603ce5b905f1832 {"local":[64,1,1],"shared":4096}
a667cfd7cb934fa7 {"local":[1024,1,1],"shared":28672}
a6d4a5775677e7b5 {"local":[16,16,1],"shared":4096}
a9b1db3b23e39c6d {"local":[8,8,1],"shared":0}
aac4a89b41226971 {"local":[16,16,1],"shared":0}
abec545c60079af0 {"local":[1,1,1],"shared":32}
b099cf70b7c89163 {"local":[4,4,4],"shared":0}
b185669da2842455 {"local":[1,1,1],"shared":16}
b2677af6513015fe {"local":[8,8,1],"shared":0}
b33527917f94b50a {"local":[16,16,1],"shared":0}
b4c50cde2ad481c5 {"local":[32,1,1],"shared":0}
b4f32c9e9553f026 {"local":[16,16,1],"shared":0}
b743642e83a5eb20 {"local":[8,8,1],"shared":0}
ba4b78b2eb32a0c5 {"local":[8,8,1],"shared":0}
ba9db841905a59bc {"local":[8,8,1],"shared":0}
bac444387a3907f5 {"local":[8,8,1],"shared":0}
bbad198ecc4e3a82 {"local":[8,8,1],"shared":0}
be23d0d42f23c3b1 {"local":[8,8,1],"shared":0}
be7dd4ca65114b4b {"local":[1,1,1],"shared":16}
bf89306798d13ab4 {"local":[8,8,1],"shared":0}
c0ea710708fedcb4 {"local":[8,8,1],"shared":0}
c128acdb547d891f {"local":[16,16,1],"shared":0}
c188b8506f4903f6 {"local":[8,8,1],"shared":0}
c41a2f1950012ace {"local":[16,16,1],"shared":0}
c570f7c5014a2ea7 {"local":[8,8,1],"shared":0}
c614b3eab375c142 {"local":[32,1,1],"shared":0}
c64f9b1252ad23be {"local":[4,1,1],"shared":128}
c727ba4792a83294 {"local":[64,1,1],"shared":2048}
c87ac30163739922 {"local":[8,8,1],"shared":0}
c9abbb797a9b8bd1 {"local":[8,8,1],"shared":0}
ca094b31657cc32e {"local":[8,8,1],"shared":0}
cabf3373febcb31f {"local":[1,1,1],"shared":64}
cb7d4c4bd7157182 {"local":[8,8,1],"shared":0}
cb8f2307777a6dd9 {"local":[8,8,1],"shared":0}
cc4b51aac6aa6dbe {"local":[8,8,1],"shared":0}
cd02a70fbc8fca77 {"local":[8,8,8],"shared":10240}
cd2a012db40785d4 {"local":[8,8,1],"shared":0}
cd85fa2a55796767 {"local":[8,8,1],"shared":0}
cf729f57b7a60886 {"local":[16,16,1],"shared":0}
d4549959cd4ba008 {"local":[16,16,1],"shared":0}
d4946ab01838dee1 {"local":[8,8,1],"shared":0}
d55307353a2015cf {"local":[64,1,1],"shared":0}
d553c084a3a88967 {"local":[8,8,1],"shared":0}
d924aa64f19fdcc7 {"local":[8,8,8],"shared":10240}
d92fce3c937362f3 {"local":[8,8,1],"shared":0}
dafa4cfbcf82172c {"local":[128,1,1],"shared":0}
db585cbb7ecd1b62 {"local":[8,8,1],"shared":0}
db5b4c6c9c07e27b {"local":[8,8,1],"shared":0}
dbc69288f07b414e {"local":[16,16,1],"shared":10256}
dbdb1feaf34f4cec {"local":[8,8,1],"shared":0}
dc466f844976c769 {"local":[1,1,1],"shared":64}
dcb9510423fc8375 {"local":[8,8,1],"shared":0}
dd52d629b2136fc4 {"local":[1,1,1],"shared":64}
e020ddc9b407b888 {"local":[8,8,1],"shared":0}
e1176df8fdf33ca0 {"local":[64,1,1],"shared":2048}
e1ea606230e38d87 {"local":[16,16,1],"shared":0}
e36afc7974151310 {"local":[8,8,1],"shared":0}
e4a5aeb290e809bc {"local":[8,1,1],"shared":512}
e7156fc94f765163 {"local":[8,8,1],"shared":0}
e71cf56a236b9fa0 {"local":[8,8,1],"shared":0}
e9865d660417cd00 {"local":[8,8,1],"shared":0}
ebab98ce568df297 {"local":[8,8,1],"shared":0}
ecf3101594e73bb5 {"local":[8,8,8],"shared":10240}
ee02a56b07e945a8 {"local":[8,8,1],"shared":0}
f0d032c8ad18ae89 {"local":[8,8,1],"shared":0}
f11beed3dae863dc {"local":[8,8,1],"shared":0}
f3c93b8fc4681195 {"local":[8,8,1],"shared":0}
f41325b313b6c8ca {"local":[4,4,4],"shared":0}
f4fb6b44d265f4ee {"local":[1,32,1],"shared":0}
f54e88673752a76c {"local":[8,8,1],"shared":0}
f5dbba4e70bb07ff {"local":[4,4,4],"shared":0}
f68890332940fc65 {"local":[8,8,1],"shared":0}
f890db367b9622ea {"local":[8,8,1],"shared":0}
f8a2f2a54101ea32 {"local":[6,32,1],"shared":0}
f8a6006c4c9a445c {"local":[16,16,1],"shared":0}
fb84b2ab912c5386 {"local":[64,1,1],"shared":4096}
fdb487db6e313c01 {"local":[8,8,1],"shared":0}
fdf5ee7d3ee9ca9f {"local":[16,16,1],"shared":0}
fe95a416fa63fcd1 {"local":[8,8,1],"shared":0}
Can't render this file because it contains an unexpected character in line 2 and column 19.
File diff suppressed because it is too large. Load diff
@@ -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
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -1,3 +1,3 @@
# chain b-15547f29143a 2026-10-21T23:06:35+08:00
# chain b-9f3544a56cf1 2026-10-21T23:06:35+08:00 (list carried over from b-15547f29143a: that chain change did not touch any c1 read)
21fe759b8114941c
60e2041ed375cae1
1 # chain b-15547f29143a 2026-10-21T23:06:35+08:00 # chain b-9f3544a56cf1 2026-10-21T23:06:35+08:00 (list carried over from b-15547f29143a: that chain change did not touch any c1 read)
2 21fe759b8114941c 21fe759b8114941c
3 60e2041ed375cae1 60e2041ed375cae1
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-15547f29143a 2026-10-21T23:06:45+08:00
# chain b-9f3544a56cf1 2026-10-21T23:06:45+08:00 (list carried over from b-15547f29143a: that chain change did not touch any uniform read)
# 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)
0002f3adb60ad088 5:304 6:1792 7:112 10:64 13:32
0006cbc2ca63e38b 6:1520 12:432
Can't render this file because it is too large.
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 01007EF00011E000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "15547f29143a"
#define DMSH_CHAIN "9f3544a56cf1"
+1 -1
View File
@@ -1 +1 @@
# chain b-d6930bbe1baa 2026-10-22T00:21:36+08:00 (list carried over from b-cf4190f31748: that chain change did not touch any c1 read)
# chain b-834c0b77a894 2026-10-22T00:21:36+08:00 (list carried over from b-d6930bbe1baa: that chain change did not touch any c1 read)
1 # chain b-d6930bbe1baa 2026-10-22T00:21:36+08:00 (list carried over from b-cf4190f31748: that chain change did not touch any c1 read) # chain b-834c0b77a894 2026-10-22T00:21:36+08:00 (list carried over from b-d6930bbe1baa: that chain change did not touch any c1 read)
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-d6930bbe1baa 2026-10-22T00:21:36+08:00 (list carried over from b-cf4190f31748: that chain change did not touch any uniform read)
# chain b-834c0b77a894 2026-10-22T00:21:36+08:00 (list carried over from b-d6930bbe1baa: that chain change did not touch any uniform read)
# 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)
00298d8d603fb418 0:16
00ba482babf36d17 0:16
1 # chain b-d6930bbe1baa 2026-10-22T00:21:36+08:00 (list carried over from b-cf4190f31748: that chain change did not touch any uniform read) # chain b-834c0b77a894 2026-10-22T00:21:36+08:00 (list carried over from b-d6930bbe1baa: that chain change did not touch any uniform read)
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 00298d8d603fb418
4 00ba482babf36d17
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 01008BC024FDE000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "d6930bbe1baa"
#define DMSH_CHAIN "834c0b77a894"
+1 -1
View File
@@ -1,3 +1,3 @@
# chain b-1a9b40cf13d7 2026-10-21T23:23:22+08:00
# chain b-ada2f5482c93 2026-10-21T23:23:22+08:00 (list carried over from b-1a9b40cf13d7: that chain change did not touch any c1 read)
57eb9f0bae677756
f826f2b59cfa39a8
1 # chain b-1a9b40cf13d7 2026-10-21T23:23:22+08:00 # chain b-ada2f5482c93 2026-10-21T23:23:22+08:00 (list carried over from b-1a9b40cf13d7: that chain change did not touch any c1 read)
2 57eb9f0bae677756 57eb9f0bae677756
3 f826f2b59cfa39a8 f826f2b59cfa39a8
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-1a9b40cf13d7 2026-10-21T23:23:23+08:00
# chain b-ada2f5482c93 2026-10-21T23:23:23+08:00 (list carried over from b-1a9b40cf13d7: that chain change did not touch any uniform read)
# 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)
003b38fe9b3627ba 0:192 2:64
00500b94cf88443d 0:1168
1 # chain b-1a9b40cf13d7 2026-10-21T23:23:23+08:00 # chain b-ada2f5482c93 2026-10-21T23:23:23+08:00 (list carried over from b-1a9b40cf13d7: that chain change did not touch any uniform read)
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 003b38fe9b3627ba
4 00500b94cf88443d
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 010093801237C000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "1a9b40cf13d7"
#define DMSH_CHAIN "ada2f5482c93"
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 010099C022B96000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "6c7c083b2aa7"
#define DMSH_CHAIN "68309d2d7bdd"
+1 -1
View File
@@ -1,3 +1,3 @@
# chain b-2542626d576f 2026-10-21T23:46:07+08:00 (list carried over from b-3f938d83f0c7: that chain change did not touch any c1 read)
# chain b-b6b52a84ad2c 2026-10-21T23:46:07+08:00 (list carried over from b-2542626d576f: that chain change did not touch any c1 read)
01a847b848df5e33
5fb90d2733898803
1 # chain b-2542626d576f 2026-10-21T23:46:07+08:00 (list carried over from b-3f938d83f0c7: that chain change did not touch any c1 read) # chain b-b6b52a84ad2c 2026-10-21T23:46:07+08:00 (list carried over from b-2542626d576f: that chain change did not touch any c1 read)
2 01a847b848df5e33 01a847b848df5e33
3 5fb90d2733898803 5fb90d2733898803
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-2542626d576f 2026-10-21T23:46:44+08:00 (list carried over from b-3f938d83f0c7: that chain change did not touch any uniform read)
# chain b-b6b52a84ad2c 2026-10-21T23:46:44+08:00 (list carried over from b-2542626d576f: that chain change did not touch any uniform read)
# 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)
000325b608b12e54 1:448 9:32 10:288
000b783c5701c854 1:112
1 # chain b-2542626d576f 2026-10-21T23:46:44+08:00 (list carried over from b-3f938d83f0c7: that chain change did not touch any uniform read) # chain b-b6b52a84ad2c 2026-10-21T23:46:44+08:00 (list carried over from b-2542626d576f: that chain change did not touch any uniform read)
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 000325b608b12e54
4 000b783c5701c854
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 01009BF0072D4000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "2542626d576f"
#define DMSH_CHAIN "b6b52a84ad2c"
+1 -1
View File
@@ -1 +1 @@
# chain b-6f84388c810c 2026-10-21T23:29:40+08:00
# chain b-8f07d70199df 2026-10-21T23:29:40+08:00 (list carried over from b-6f84388c810c: that chain change did not touch any c1 read)
1 # chain b-6f84388c810c 2026-10-21T23:29:40+08:00 # chain b-8f07d70199df 2026-10-21T23:29:40+08:00 (list carried over from b-6f84388c810c: that chain change did not touch any c1 read)
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-6f84388c810c 2026-10-21T23:29:40+08:00
# chain b-8f07d70199df 2026-10-21T23:29:40+08:00 (list carried over from b-6f84388c810c: that chain change did not touch any uniform read)
# 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)
00b66ee69b7fde95 1:16 2:80
02d134235f7f498b 0:128 1:16
1 # chain b-6f84388c810c 2026-10-21T23:29:40+08:00 # chain b-8f07d70199df 2026-10-21T23:29:40+08:00 (list carried over from b-6f84388c810c: that chain change did not touch any uniform read)
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 00b66ee69b7fde95
4 02d134235f7f498b
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 0100A00019DE0000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "6f84388c810c"
#define DMSH_CHAIN "8f07d70199df"
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-08f1551e74a6 2026-10-21T23:29:58+08:00
# chain b-ce3518fb269a 2026-10-21T23:29:58+08:00 (list carried over from b-08f1551e74a6: that chain change did not touch any c1 read)
101c4d73dbb253e2
a277d45a64c92ab6
b14f0b0add21e60a
1 # chain b-08f1551e74a6 2026-10-21T23:29:58+08:00 # chain b-ce3518fb269a 2026-10-21T23:29:58+08:00 (list carried over from b-08f1551e74a6: that chain change did not touch any c1 read)
2 101c4d73dbb253e2 101c4d73dbb253e2
3 a277d45a64c92ab6 a277d45a64c92ab6
4 b14f0b0add21e60a b14f0b0add21e60a
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-08f1551e74a6 2026-10-21T23:29:58+08:00
# chain b-ce3518fb269a 2026-10-21T23:29:58+08:00 (list carried over from b-08f1551e74a6: that chain change did not touch any uniform read)
# 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)
00336aabde0f0d1f 0:160
006b3aaf17be0cf3 0:144
1 # chain b-08f1551e74a6 2026-10-21T23:29:58+08:00 # chain b-ce3518fb269a 2026-10-21T23:29:58+08:00 (list carried over from b-08f1551e74a6: that chain change did not touch any uniform read)
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 00336aabde0f0d1f
4 006b3aaf17be0cf3
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 0100A5C00D162000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "08f1551e74a6"
#define DMSH_CHAIN "ce3518fb269a"
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-1e9d905b402b 2026-10-22T08:31:13+08:00
# chain b-4e53dc950190 2026-10-22T08:31:13+08:00 (list carried over from b-1e9d905b402b: that chain change did not touch any c1 read)
001275671d2c9985
0021d75a567e2122
00a98da1158c6f14
1 # chain b-1e9d905b402b 2026-10-22T08:31:13+08:00 # chain b-4e53dc950190 2026-10-22T08:31:13+08:00 (list carried over from b-1e9d905b402b: that chain change did not touch any c1 read)
2 001275671d2c9985
3 0021d75a567e2122
4 00a98da1158c6f14
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-1e9d905b402b 2026-10-22T08:31:16+08:00
# chain b-4e53dc950190 2026-10-22T08:31:16+08:00 (list carried over from b-1e9d905b402b: that chain change did not touch any uniform read)
# 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)
0005f969d0503763 2:160 3:16 4:96
001275671d2c9985 0:160 3:128
1 # chain b-1e9d905b402b 2026-10-22T08:31:16+08:00 # chain b-4e53dc950190 2026-10-22T08:31:16+08:00 (list carried over from b-1e9d905b402b: that chain change did not touch any uniform read)
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 0005f969d0503763
4 001275671d2c9985
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 0100A6301214E000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "1e9d905b402b"
#define DMSH_CHAIN "4e53dc950190"
+1 -1
View File
@@ -1 +1 @@
# chain b-0779e8984a06 2026-10-21T23:46:07+08:00 (list carried over from b-a2bf485acea0: that chain change did not touch any c1 read)
# chain b-8435636c1fe2 2026-10-21T23:46:07+08:00 (list carried over from b-0779e8984a06: that chain change did not touch any c1 read)
1 # chain b-0779e8984a06 2026-10-21T23:46:07+08:00 (list carried over from b-a2bf485acea0: that chain change did not touch any c1 read) # chain b-8435636c1fe2 2026-10-21T23:46:07+08:00 (list carried over from b-0779e8984a06: that chain change did not touch any c1 read)
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-0779e8984a06 2026-10-21T23:46:44+08:00 (list carried over from b-a2bf485acea0: that chain change did not touch any uniform read)
# chain b-8435636c1fe2 2026-10-21T23:46:44+08:00 (list carried over from b-0779e8984a06: that chain change did not touch any uniform read)
# 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)
004a663e75543071 0:192
014cebfd5986868a 0:64
1 # chain b-0779e8984a06 2026-10-21T23:46:44+08:00 (list carried over from b-a2bf485acea0: that chain change did not touch any uniform read) # chain b-8435636c1fe2 2026-10-21T23:46:44+08:00 (list carried over from b-0779e8984a06: that chain change did not touch any uniform read)
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 004a663e75543071
4 014cebfd5986868a
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 0100A83022A22000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "0779e8984a06"
#define DMSH_CHAIN "8435636c1fe2"
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-31f046bb1876 2026-10-21T22:57:30+08:00
# chain b-e73db6780302 2026-10-21T22:57:30+08:00 (list carried over from b-31f046bb1876: that chain change did not touch any c1 read)
00096443b71e7fef
001540067ae4cdf5
002e5538fc05267e
1 # chain b-31f046bb1876 2026-10-21T22:57:30+08:00 # chain b-e73db6780302 2026-10-21T22:57:30+08:00 (list carried over from b-31f046bb1876: that chain change did not touch any c1 read)
2 00096443b71e7fef
3 001540067ae4cdf5
4 002e5538fc05267e
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-31f046bb1876 2026-10-21T22:58:18+08:00
# chain b-e73db6780302 2026-10-21T22:58:18+08:00 (list carried over from b-31f046bb1876: that chain change did not touch any uniform read)
# 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)
000034e55812e369 0:64 1:16 2:16 3:48 4:32 s1 s2 s3 s4
00066a38d5c8ec32 0:112
Can't render this file because it is too large.
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 0100B04011742000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "31f046bb1876"
#define DMSH_CHAIN "e73db6780302"
+1 -1
View File
@@ -1 +1 @@
# chain b-4a9d20dadae5 2026-10-21T23:23:14+08:00 (list carried over from b-befa9a8c84ac: that chain change did not touch any c1 read)
# chain b-2d235ff29ffb 2026-10-21T23:23:14+08:00 (list carried over from b-4a9d20dadae5: that chain change did not touch any c1 read)
1 # chain b-4a9d20dadae5 2026-10-21T23:23:14+08:00 (list carried over from b-befa9a8c84ac: that chain change did not touch any c1 read) # chain b-2d235ff29ffb 2026-10-21T23:23:14+08:00 (list carried over from b-4a9d20dadae5: that chain change did not touch any c1 read)
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 0100D9F01D474000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "4a9d20dadae5"
#define DMSH_CHAIN "2d235ff29ffb"
+1 -1
View File
@@ -1,2 +1,2 @@
# chain b-59674bb2e85b 2026-10-22T01:20:27+08:00
# chain b-117c46a619bc 2026-10-22T01:20:27+08:00 (list carried over from b-59674bb2e85b: that chain change did not touch any c1 read)
c62d1fc4921c1377
1 # chain b-59674bb2e85b 2026-10-22T01:20:27+08:00 # chain b-117c46a619bc 2026-10-22T01:20:27+08:00 (list carried over from b-59674bb2e85b: that chain change did not touch any c1 read)
2 c62d1fc4921c1377 c62d1fc4921c1377
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-59674bb2e85b 2026-10-22T01:20:28+08:00
# chain b-117c46a619bc 2026-10-22T01:20:28+08:00 (list carried over from b-59674bb2e85b: that chain change did not touch any uniform read)
# 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)
00398cc3ae952e94 0:336
007b5324a6b09547 1:144
1 # chain b-59674bb2e85b 2026-10-22T01:20:28+08:00 # chain b-117c46a619bc 2026-10-22T01:20:28+08:00 (list carried over from b-59674bb2e85b: that chain change did not touch any uniform read)
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 00398cc3ae952e94
4 007b5324a6b09547
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 0100E44016C22000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "59674bb2e85b"
#define DMSH_CHAIN "117c46a619bc"
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-1cc65a8faa4d 2026-10-21T23:19:16+08:00
# chain b-b1873f7f20e2 2026-10-21T23:19:16+08:00 (list carried over from b-1cc65a8faa4d: that chain change did not touch any c1 read)
35cbc686dd8461b8
4872bec9a5567f8b
759c14c6d029f531
1 # chain b-1cc65a8faa4d 2026-10-21T23:19:16+08:00 # chain b-b1873f7f20e2 2026-10-21T23:19:16+08:00 (list carried over from b-1cc65a8faa4d: that chain change did not touch any c1 read)
2 35cbc686dd8461b8 35cbc686dd8461b8
3 4872bec9a5567f8b 4872bec9a5567f8b
4 759c14c6d029f531 759c14c6d029f531
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-1cc65a8faa4d 2026-10-21T23:19:34+08:00
# chain b-b1873f7f20e2 2026-10-21T23:19:34+08:00 (list carried over from b-1cc65a8faa4d: that chain change did not touch any uniform read)
# 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)
0001a896bf9bd7cc 5:496 6:2256 12:432
00027f7dc4646be2 5:496 6:2320 7:112 10:96 12:944 s4
Can't render this file because it is too large.
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 0100F2C0115B6000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "1cc65a8faa4d"
#define DMSH_CHAIN "b1873f7f20e2"
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-975400df340b 2026-10-21T23:23:14+08:00 (list carried over from b-12de0799766c: that chain change did not touch any c1 read)
# chain b-fecf6b2424cc 2026-10-21T23:23:14+08:00 (list carried over from b-975400df340b: that chain change did not touch any c1 read)
000566ec19dc74f7
000b591830b6fa73
0027e56f73d8e10e
1 # chain b-975400df340b 2026-10-21T23:23:14+08:00 (list carried over from b-12de0799766c: that chain change did not touch any c1 read) # chain b-fecf6b2424cc 2026-10-21T23:23:14+08:00 (list carried over from b-975400df340b: that chain change did not touch any c1 read)
2 000566ec19dc74f7
3 000b591830b6fa73
4 0027e56f73d8e10e
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 0100F43008C44000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "975400df340b"
#define DMSH_CHAIN "fecf6b2424cc"
+1 -1
View File
@@ -1,2 +1,2 @@
# chain b-3e79ab909aaf 2026-10-22T00:21:36+08:00 (list carried over from b-70a5a3b5dbd2: that chain change did not touch any c1 read)
# chain b-f04564f60060 2026-10-22T00:21:36+08:00 (list carried over from b-3e79ab909aaf: that chain change did not touch any c1 read)
e5ac49d4582a339c
1 # chain b-3e79ab909aaf 2026-10-22T00:21:36+08:00 (list carried over from b-70a5a3b5dbd2: that chain change did not touch any c1 read) # chain b-f04564f60060 2026-10-22T00:21:36+08:00 (list carried over from b-3e79ab909aaf: that chain change did not touch any c1 read)
2 e5ac49d4582a339c e5ac49d4582a339c
+1 -1
View File
@@ -1,4 +1,4 @@
# chain b-3e79ab909aaf 2026-10-22T00:21:36+08:00 (list carried over from b-70a5a3b5dbd2: that chain change did not touch any uniform read)
# chain b-f04564f60060 2026-10-22T00:21:36+08:00 (list carried over from b-3e79ab909aaf: that chain change did not touch any uniform read)
# 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)
008f89e94d37f17f 1:144
00a30a0f46c385b7 1:512
1 # chain b-3e79ab909aaf 2026-10-22T00:21:36+08:00 (list carried over from b-70a5a3b5dbd2: that chain change did not touch any uniform read) # chain b-f04564f60060 2026-10-22T00:21:36+08:00 (list carried over from b-3e79ab909aaf: that chain change did not touch any uniform read)
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 008f89e94d37f17f
4 00a30a0f46c385b7
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 0100F5D01DA3A000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "3e79ab909aaf"
#define DMSH_CHAIN "f04564f60060"
+1 -1
View File
@@ -1,2 +1,2 @@
// Generated file (dm/gfx/tools/dmsh_chain.py --header, game 0100FD8022DAA000): the B shader library translation chain version this source tree expects (default options); the host loads only <library dir>/b-<V>/
#define DMSH_CHAIN "a450cde97d1b"
#define DMSH_CHAIN "fe1b5345e93c"
+8 -5
View File
@@ -164,19 +164,22 @@ GPU_DRIVERS = {'NintendoSDK_OpenGL', 'NintendoSDK_Vulkan'}
# ---- Shader sources: decided from the game files at every build, not kept in the profile (scripts/build_ipa_from_nsp.py; shader_split.py reads what those steps write) ----
def shader_sources(imports, glsl_src, glslc_mods, spirv, unreal_paks, trinity_packs=0):
def shader_sources(imports, glsl_src, glslc_mods, spirv, unreal_paks, trinity_packs=0, legacy_paks=0):
"""{name: (value, reason)}: graphics (vulkan when main imports Vulkan functions and not nvnBootstrapLoader, else nvn), vk_capture (a Vulkan game without SPIR-V files generates its shaders at
run time), glslc_capture (main holds GLSL sources and calls glslcCompile in a module the game loads: it compiles some shaders at run time), ue_decode (Unreal paks keep the shaders Oodle-compressed
in their shader code archives, which only the game's own Oodle decodes), oodle_packs (Trinity packs, Oodle-compressed file by file, which the shader split decodes with the game's own decoder in
the Mac host's tool mode). Checked against every profile: Hades and Super Mario Galaxy Vulkan, Galaxy alone with no SPIR-V file, Three Houses alone compiling shaders (13 Sentinels holds 106 GLSL
sources but does not import glslcCompile; Captain Toad: Treasure Tracker imports it with agl's GLSL sources, but no module defines it), Octopath Traveler 0 alone with Unreal paks, Pokémon Legends: Z-A alone with Trinity packs"""
the Mac host's tool mode), ue_legacy (legacy Unreal paks, version 4: the shaders are inline in the cooked packages, which the shader split reads through the game's own decoder the same way).
unreal_paks counts the version 11 paks, legacy_paks the older ones. Checked against every profile: Hades and Super Mario Galaxy Vulkan, Galaxy alone with no SPIR-V file, Three Houses alone compiling
shaders (13 Sentinels holds 106 GLSL sources but does not import glslcCompile; Captain Toad: Treasure Tracker imports it with agl's GLSL sources, but no module defines it), Octopath Traveler 0
alone with version 11 Unreal paks, DRAGON QUEST XI S alone with legacy ones, Pokémon Legends: Z-A alone with Trinity packs"""
vk = sum(1 for s in imports if re.match(r'vk[A-Z]', s)); vulkan = bool(vk) and 'nvnBootstrapLoader' not in imports
return {'graphics': ('vulkan', f'main imports {vk} Vulkan functions and not nvnBootstrapLoader: the host\'s Vulkan layer (dm/gfx/vk/hle_vk.m)') if vulkan else ('nvn', 'main imports nvnBootstrapLoader'),
'vk_capture': (vulkan and not spirv, 'no SPIR-V file in the RomFS: the game generates its shaders at run time (Super Mario Galaxy), captured by booting it on the Mac'),
'glslc_capture': (not vulkan and bool(glsl_src) and 'glslcCompile' in imports and bool(glslc_mods),
f'main holds {glsl_src} GLSL sources and calls glslcCompile in {", ".join(glslc_mods) or "no module it loads"}: shaders compiled at run time (Fire Emblem: Three Houses), captured by booting it on the Mac'),
'ue_decode': (not vulkan and bool(unreal_paks), f'{unreal_paks} Unreal pak(s): the shaders are Oodle-compressed in their shader code archives (Octopath Traveler 0), decoded by booting the game on the Mac'),
'oodle_packs': (not vulkan and bool(trinity_packs), f'{trinity_packs} Trinity pack file(s) (ONEPACK, arc/data.trpfs: Pokémon Legends: Z-A), Oodle-compressed: the shader split decodes them with the game\'s own decoder in the Mac host (dm/host/hle_oodle.c DM_OODLE_SERVE)')}
'oodle_packs': (not vulkan and bool(trinity_packs), f'{trinity_packs} Trinity pack file(s) (ONEPACK, arc/data.trpfs: Pokémon Legends: Z-A), Oodle-compressed: the shader split decodes them with the game\'s own decoder in the Mac host (dm/host/hle_oodle.c DM_OODLE_SERVE)'),
'ue_legacy': (not vulkan and bool(legacy_paks), f'{legacy_paks} legacy Unreal pak(s) (version 4, UE 4.18: DRAGON QUEST XI S): the shaders are inline in the cooked packages, compressed in the pak; the shader split decodes them with the game\'s own decoder in the Mac host (dm/host/hle_oodle.c DM_OODLE_SERVE)')}
def build_sources(exefs, romfs, modules):
@@ -194,7 +197,7 @@ def build_sources(exefs, romfs, modules):
for n in names:
if n.endswith('.trpfs'):
with open(os.path.join(d, n), 'rb') as f: trinity += f.read(8) == b'ONEPACK\0'
return shader_sources(imports, glsl_src, glslc_mods, spirv, len(uepak.unreal_paks(str(romfs))), trinity)
return shader_sources(imports, glsl_src, glslc_mods, spirv, len(uepak.unreal_paks(str(romfs), legacy=False)), trinity, len(uepak.unreal_paks(str(romfs), legacy=True)))
def va_heap(sysres, imports): # virtual address memory (an NPDM system resource) and a heap: the alias / heap split is a starting point the heap check corrects (Wonder, Tears of the Kingdom; Engage's pool is its own measurement)
+12
View File
@@ -381,6 +381,13 @@ NVN(nvnTextureInitialize, NVNboolean, NVNtexture *t, const NVNtextureBuilder *b)
nvn_tex_layout(&o->d, &o->L);
o->mem = s->pool && s->pool->mem ? s->pool->mem + s->off : NULL;
o->serial = nvn_new_serial(); mtl_tex_init(o);
{ // DM_NVN_TEXINFO=WxH also logs every texture of that size as it is initialized (render targets included): which ones share storage, and how their descriptions differ
static int ti = -1; static uint32_t tw, th;
if (ti < 0) { const char *e = getenv("DM_NVN_TEXINFO"); ti = e && sscanf(e, "%ux%u", &tw, &th) == 2; }
if (ti && o->d.width == tw && o->d.height == th) { static uint64_t n; if (n++ < 200) LOG("[nvn] TEXINFO init ser=%llu mem=%p pool=%p(mem %p size %#llx flags %#x) off=%#llx target=%d format=%#x flags=%#x samples=%d %ux%ux%u levels=%u stride=%u size=%#llx",
(unsigned long long)o->serial, (void *)o->mem, (void *)o->pool, o->pool ? (void *)o->pool->mem : NULL, o->pool ? (unsigned long long)o->pool->size : 0ull, o->pool ? o->pool->flags : 0,
(unsigned long long)o->off, o->d.target, o->d.format, o->d.flags, o->d.samples, o->d.width, o->d.height, o->d.depth, o->d.levels, o->d.stride, (unsigned long long)o->L.size); }
}
// See SetPackagedTextureData. Linear textures are not re-uploaded as packaged: they are created on the pool buffer (tex_linear), whatever the pool's CPU access, and read what was copied in
if (s->pkg_data && o->mem) {
memcpy(o->mem, s->pkg_data, o->L.size);
@@ -403,6 +410,8 @@ NVN(nvnTextureGetLevels, int, const NVNtexture *t) { return (int)CTX(t)->d.level
NVN(nvnTextureGetSamples, int, const NVNtexture *t) { return CTX(t)->d.samples; }
NVN(nvnTextureGetMemoryPool, NVNmemoryPool *, const NVNtexture *t) { return (NVNmemoryPool *)CTX(t)->pool; }
NVN(nvnTextureGetMemoryOffset, ptrdiff_t, const NVNtexture *t) { return (ptrdiff_t)CTX(t)->off; }
// The GPU address of the texture's storage (DRAGON QUEST XI S, Unreal Engine 4.18, asks for it at start): pool address + offset, the same address space as nvnMemoryPoolGetBufferAddress
NVN(nvnTextureGetTextureAddress, NVNtextureAddress, const NVNtexture *t) { const tex_t *o = CTX(t); return o->pool ? (NVNtextureAddress)(o->pool->mem + o->off) : 0; }
// The size of a texture's ZCull save area (SaveZCullData / RestoreZCullData). The host does no ZCull and never touches the area, but games allocate it from their own pools, and NVN
// does not report 0. Cuphead (Unity 2017.4) allocates one per depth target (main+0x1948060) from its pool allocator, which gives a zero-size request the offset of a live block; its
// map of allocations then loses that block (the zero-size one's size overwrites it, and freeing the zero-size one deletes it), and the block's own free later pushes a garbage-sized
@@ -1973,6 +1982,9 @@ NVN_NOPROTO(nvnWindowSetNumActiveTextures, void, NVNwindow *w, int n) { (void)w;
NVN(nvnCommandBufferGetCommandMemoryUsed, size_t, const NVNcommandBuffer *c) { const cmd_t *m = (const cmd_t *)c;
return m->begin ? (size_t)m->used : 0; }
NVN(nvnCommandBufferGetControlMemoryUsed, size_t, const NVNcommandBuffer *c) { (void)c; return 0; }
// Size and free bytes of the command memory the game last added (DRAGON QUEST XI S, Unreal Engine 4.18): as in NVN, size = used + free; free is what is left in the current segment
NVN(nvnCommandBufferGetCommandMemoryFree, size_t, const NVNcommandBuffer *c) { const cmd_t *m = (const cmd_t *)c; return m->cur && m->end > m->cur ? (size_t)(m->end - m->cur) : 0; }
NVN(nvnCommandBufferGetCommandMemorySize, size_t, const NVNcommandBuffer *c) { return h_nvnCommandBufferGetCommandMemoryUsed(c) + h_nvnCommandBufferGetCommandMemoryFree(c); }
NVN_NOPROTO(nvnCommandBufferSetCommandMemoryCallbackEnabled, void, NVNcommandBuffer *c, uint32_t on) { (void)c; (void)on; }
// nvnProgramSetShadersExt (NVN 55): each stage is 0x18 bytes = NVNshaderData{data, control} + one 8-byte extension field (filled from SMBW main 0x1074ff0).
+53 -11
View File
@@ -1329,7 +1329,9 @@ static MTLTextureDescriptor *tex_desc(const tex_t *t, int packaged) {
// Cube textures are created as 6-layer 2D arrays and sampled through cube / cube array views made by tex_fit: Metal only allows cube array views of a 2D array base texture (not of a cube base).
// The Legend of Zelda: Breath of the Wild has 1512 shaders that sample the environment light probes as a cube array while a single cube texture is bound (texture ID 1064 onwards): with mismatched types the reads are undefined, and every lit surface came out black
case 8: d.textureType = MTLTextureType2DArray; d.arrayLength = 6; break;
case 9: d.textureType = MTLTextureTypeCubeArray; d.arrayLength = t->d.depth; break;
// Cube arrays likewise, as a 2D array of all their faces: NVN gives a cube array's depth in layer-faces (a multiple of 6), where a Metal cube array's arrayLength counts cubes.
// Created as a cube array with arrayLength = depth, DRAGON QUEST XI S's 510-face one (85 cubes, made when its prologue movie ends) asked for 510 cubes, past Metal's 341, and Metal aborted the process
case 9: d.textureType = MTLTextureType2DArray; d.arrayLength = t->d.depth; break;
case 10: RED_ONCE(1, "buffer textures (TARGET_BUFFER) not implemented"); return nil;
default: d.textureType = MTLTextureType2D; break;
}
@@ -1593,8 +1595,7 @@ static id<MTLTexture> pkg_upload_(const tex_t *t, uint64_t *stamp, id<MTLTexture
if (sparse) nvn_sparse_protect(t->pool, (uint64_t)(uintptr_t)t->mem, t->L.size); else nvn_dirty_protect(t->mem, t->L.size);
*stamp = nvn_dirty_now(); }
int bw, bh, bpb = nvn_fmt_bpb(t->d.format, &bw, &bh);
unsigned layers = (t->d.target == 4 || t->d.target == 9) ? t->d.depth : t->d.target == 8 ? 6 : t->d.target == 3 ? t->d.height : 1;
if (t->d.target == 9) layers *= 6;
unsigned layers = (t->d.target == 4 || t->d.target == 9) ? t->d.depth : t->d.target == 8 ? 6 : t->d.target == 3 ? t->d.height : 1; // a cube array's depth counts layer-faces (tex_desc)
size_t total = 0;
for (unsigned l = 0; l < d.mipmapLevelCount; l++) {
uint32_t w = MAX(t->d.width >> l, 1u), h = MAX(t->d.height >> l, 1u), z = t->d.target == 2 ? MAX(t->d.depth >> l, 1u) : 1;
@@ -2662,6 +2663,7 @@ static void shmap_load(void) {
}
@interface NvShader : NSObject { @public id<MTLFunction> fn, tf_fn; uint16_t tf_stride[4]; uint32_t cb_ext[16], sb_ro; nvn_refl rf; id<MTLArgumentEncoder> ae; NSUInteger arg_len; int stage, range_ro, afr, layer_out; const char *builtin; uint64_t logged; char key[24]; id<MTLBuffer> c1buf; uint64_t c1_gpu; size_t c1off;
int gs_loc, lay_loc; char gs_comp, lay_comp; // a slice-writing geometry shader (no function: the layer comes from input gs_loc.gs_comp) / a vertex shader with layer entry points writing output lay_loc.lay_comp (dmsh_build.py gs_slice, vs_layer_entry); -1 = none
// The argument buffer encoded last time
id<MTLBuffer> ab_buf; uint64_t ab_gpu; size_t ab_off; uint64_t ab_pass; uint32_t ab_binst; // ab_pass: serial of the render pass that encoded this argument block - reused only within the same pass (the arena holding a block from another pass / another submission may already have been reclaimed and rewritten)
id<MTLLibrary> bflib; int bflib_tried; NSMutableDictionary<NSString *, id<MTLFunction>> *bffn; // bflib: readable-tile version <key>_bf (merge B step 2)
@@ -2790,15 +2792,24 @@ void mtl_set_depth_zero(int on) {
int n = atomic_load(&g_vs_loaded);
if (n) MLOG("[RED] depth mode changed after %d vertex shaders were loaded: they keep the old convention", n);
}
static NvShader *shader_load(NSString *pc, int stage) {
// layer: a vertex shader's layer entry points (entry_layer / entry_layer_z, which also write the layer) instead of main0 / main0_z, for a program whose geometry shader is a slice writer (prog_fold_gs)
static NvShader *shader_load(NSString *pc, int stage, int layer) {
NSError *e = nil;
NSString *lp = [NSString stringWithFormat:@"%@/%@.metallib", g_msl_dir, pc];
id<MTLLibrary> lib = lib_from_file(lp, &e);
NSData *jd = [NSData dataWithContentsOfFile:[NSString stringWithFormat:@"%@/%@.json", g_msl_dir, pc]];
NSDictionary *j = jd ? [NSJSONSerialization JSONObjectWithData:jd options:0 error:nil] : nil;
if (j && stage == 2 && [j[@"gs_slice"] isKindOfClass:[NSDictionary class]] && [j[@"chain"] isEqual:@DMSH_CHAIN]) { // a slice-writing geometry shader has no library: a stub prog_fold_gs replaces
NvShader *o = [NvShader new]; o->stage = 2; o->lay_loc = -1; strlcpy(o->key, pc.UTF8String, sizeof o->key);
o->gs_loc = [j[@"gs_slice"][@"loc"] intValue]; o->gs_comp = [[j[@"gs_slice"][@"comp"] description] UTF8String][0];
return o;
}
if (stage == 2 && !j) return nil; // any other geometry shader has no library (no Metal counterpart): the caller's red line says so
NSString *lp = [NSString stringWithFormat:@"%@/%@.metallib", g_msl_dir, pc];
id<MTLLibrary> lib = lib_from_file(lp, &e);
if (!lib || !j) { MLOG("[RED] failed to load shader %s: %s", pc.UTF8String, e ? e.localizedDescription.UTF8String : "no reflection json"); return nil; }
if (![j[@"chain"] isEqual:@DMSH_CHAIN]) { MLOG("[RED] shader %s has translation chain version %s, but this host expects %s", pc.UTF8String, [[j[@"chain"] description] UTF8String], DMSH_CHAIN); return nil; }
NvShader *o = [NvShader new];
if (layer && !j[@"entry_layer"]) { MLOG("[RED] vertex shader %s has no layer entry points", pc.UTF8String); return nil; }
NvShader *o = [NvShader new]; o->gs_loc = o->lay_loc = -1;
if (stage == 0 && [j[@"layer_from"] isKindOfClass:[NSDictionary class]]) { o->lay_loc = [j[@"layer_from"][@"loc"] intValue]; o->lay_comp = [[j[@"layer_from"][@"comp"] description] UTF8String][0]; }
char err[160];
if (nvn_refl_from_json(j, &o->rf, err, sizeof err)) { MLOG("[RED] shader %s reflection conversion failed: %s", pc.UTF8String, err); return nil; }
if (o->rf.dyn && !DM_GAME_TEX_DYN_SLOTS) { // dynamic texture handles (TEXD / SAMPD) only where the profile says the game picks them from c2's tables, as before otherwise
@@ -2808,9 +2819,12 @@ static NvShader *shader_load(NSString *pc, int stage) {
if (stage == 0) {
atomic_fetch_add(&g_vs_loaded, 1);
if (atomic_load(&g_depth_zero)) { if (j[@"entry_z"]) entry = j[@"entry_z"]; else RED_ONCE(85, "vertex shader %s has no near=0 entry (entry_z): depth stays in the GL convention", pc.UTF8String); }
if (layer) entry = atomic_load(&g_depth_zero) ? j[@"entry_layer_z"] : j[@"entry_layer"];
}
o->fn = [lib newFunctionWithName:entry]; o->stage = stage; strlcpy(o->key, pc.UTF8String, sizeof o->key); o->ventry = entry;
if (stage == 0) o->layer_out = t_lib_layer; // writes the layer (render_target_array_index, named in the library's metadata): its passes are layered and its pipelines get an input class (pso_desc)
// writes the layer (render_target_array_index, named in the library's metadata): its passes are layered and its pipelines get an input class (pso_desc). A library with layer entry points
// names it for those alone: its main0 / main0_z write no layer
if (stage == 0) o->layer_out = layer || (t_lib_layer && !j[@"entry_layer"]);
// A function that declares function constants cannot go into a pipeline until it is specialized, even when every constant is optional. SPIRV-Cross declares one for shaders that read a
// texel buffer (spvLinearTextureAlignmentOverride, used only through is_function_constant_defined; one of Pokémon Legends: Z-A's imgui pixel shaders, 2bd5037003980716): an empty set of
// values keeps its default. Without it the first pipeline with that shader failed Metal's validation (`fragmentFunction main0 cannot be used to build a pipeline state`)
@@ -2843,7 +2857,7 @@ static inline uint64_t cb_len(NvShader *sh, int reg, uint64_t size) { uint32_t e
static int stage_of(NSString *stage) {
if ([stage isEqualToString:@"vertex"]) return 0; if ([stage isEqualToString:@"pixel"]) return 1; if ([stage isEqualToString:@"compute"]) return 5;
if ([stage isEqualToString:@"hull"]) return 3; if ([stage isEqualToString:@"domain"]) return 4; return -1;
if ([stage isEqualToString:@"hull"]) return 3; if ([stage isEqualToString:@"domain"]) return 4; if ([stage isEqualToString:@"geometry"]) return 2; return -1;
}
#define NVN_RT_RANGE_RO_IMPL 1
// Private continuation accepts only B reflection that is explicitly RO; a missing rw on sbuf does not let the default 0 count as proof; does not change the existing reflection / translation chain.
@@ -2872,7 +2886,7 @@ static NvShader *shader_get(NSString *pc, int stage) {
if (!g_shcache) g_shcache = [NSMutableDictionary dictionary];
NvShader *o = g_shcache[pc];
if (o) return o;
uint64_t bt = bp_now(); o = shader_load(pc, stage);
uint64_t bt = bp_now(); o = shader_load(pc, stage, 0);
if (!o) return nil;
g_shcache[pc] = o;
pso_record("S\t%s\t%d\n", pc.UTF8String, stage);
@@ -2880,6 +2894,31 @@ static NvShader *shader_get(NSString *pc, int stage) {
g_bp_lib_n++; g_bp_lib_ns += bp_now() - bt;
return o;
}
// The layer entry points of vertex shader vs as a shader of their own (cached beside it as <key>#l)
static NvShader *shader_get_layer(NvShader *vs) {
NSString *pc = @(vs->key), *ck = [pc stringByAppendingString:@"#l"];
NvShader *o = g_shcache[ck];
if (!o && (o = shader_load(pc, 0, 1))) g_shcache[ck] = o;
return o;
}
// A program whose geometry shader is a slice writer (a stub with no function, shader_load) draws with its vertex shader's layer entry points instead, which write the layer the geometry
// shader would have picked: the pass is layered (layer_out) and the geometry stage goes. Metal has no geometry stage. Unreal Engine binds each stage from a program of its own (DRAGON QUEST XI S:
// stage masks 1, 2, 4, 8, 16, an empty program for the stages a draw does not use), so a vertex shader is often combined with the previous draw's geometry shader for a moment before that
// stage is rebound: a stub that does not fit its vertex shader stays in the program, and only a draw with it gets a red line (draw_gs_check)
static void prog_fold_gs(NvProg *o) {
NvShader *g = o->sh[2];
if (!g || g->fn || !o->sh[0]) return;
NvShader *v = o->sh[0], *l = v->lay_loc == g->gs_loc && v->lay_comp == g->gs_comp ? shader_get_layer(v) : nil;
if (!l) return;
o->sh[0] = l; o->sh[2] = nil;
static int once; if (!once++) MLOG("geometry shader %s writes only the layer: vertex shader %s draws with its layer entry points instead (layered pass)", g->key, v->key);
}
// A draw whose geometry stage has no function (a slice writer that does not fit the vertex shader, prog_fold_gs) runs without it, reaching only the first layer, as before
static void draw_gs_check(NvProg *pr) {
NvShader *g = pr->sh[2]; static int n;
if (g && !g->fn && n++ < 8) MLOG("[RED] geometry shader %s picks the layer from input %d.%c, but vertex shader %s has no layer entry points for it (layer_from %d.%c): the draw reaches only the first layer",
g->key, g->gs_loc, g->gs_comp, pr->sh[0] ? pr->sh[0]->key : "-", pr->sh[0] ? pr->sh[0]->lay_loc : -1, pr->sh[0] && pr->sh[0]->lay_loc >= 0 ? pr->sh[0]->lay_comp : '-');
}
static id<MTLLibrary> g_builtin_lib;
static NSMutableDictionary<NSString *, NvShader *> *g_builtin;
static NvShader *builtin_get(const char *ctl) {
@@ -2928,6 +2967,7 @@ static __unsafe_unretained NvProg *prog_get(const prog_t *cp) { // Called only
NvShader *sh = st >= 0 ? shader_get([k stringByAppendingString:@"_b"], st) : nil;
if (sh) o->sh[st] = sh; else MLOG("[RED] shader %s (%s) failed to load: build the shaders with dm/gfx/tools/dmsh_build.py first", k.UTF8String, stage.UTF8String);
}
prog_fold_gs(o); // a slice-writing geometry shader: the vertex shader's layer entry points instead
if (p->serial) serial_put(p->serial, o);
return o;
}
@@ -3442,6 +3482,7 @@ static const prog_t *bind_stages(const prog_t *p, int stages) {
if (cp[i].serial) serial_del(cp[i].serial);
NvProg *o = [NvProg new];
for (int st = 0; st < 5; st++) { ck[i][st] = S.bst[st]; cs[i][st] = S.bst[st] ? S.bst[st]->serial : 0; if (S.bst[st]) { __unsafe_unretained NvProg *src = prog_get(S.bst[st]); o->sh[st] = src ? src->sh[st] : nil; } }
prog_fold_gs(o); // the geometry stage came from another program (see prog_fold_gs)
memset(&cp[i], 0, sizeof cp[i]); cp[i].magic = MAGIC('P', 'R', 'O', 'G'); cp[i].serial = nvn_new_serial(); serial_put(cp[i].serial, o);
{ static int once; if (!once++) MLOG("per-stage program binding: first combined program (vertex %s fragment %s)", o->sh[0] ? o->sh[0]->key : "-", o->sh[1] ? o->sh[1]->key : "-"); }
return &cp[i];
@@ -3943,7 +3984,7 @@ static NSString *warm_key(const char *s, size_t n) {
}
static NvShader *warm_shader(NSMutableDictionary<NSString *, NvShader *> *dict, NSString *key, int stage) {
NvShader *sh = dict[key];
if (!sh) { sh = shader_load(key, stage); if (sh) dict[key] = sh; }
if (!sh) { sh = shader_load(key, stage, 0); if (sh) dict[key] = sh; }
return sh;
}
static void warm_enqueue(WarmItem *item) {
@@ -6294,6 +6335,7 @@ static void draw(const p_draw *dw, const p_draw_idx *di) {
g_mdxc.next = NULL;
if (!S.prog) { RED_ONCE(18, "no program bound before the draw"); PD("Z %llu draw skipped: no program bound", g_stat_pass + !g_enc); return; }
__unsafe_unretained NvProg *pr = prog_get(S.prog);
draw_gs_check(pr); // a geometry stage left with no function (see prog_fold_gs)
if (S.rast_discard) { tf_draw(pr, dw, di); return; } // the vertex stage only: transform feedback capture, or nothing (tf_draw)
if (g_nubd && g_enc && ubd_conflict(pr, dw, di)) { g_ubd_conflicts++; rp_end(); } // land the deferred inline updates before this draw (see ubd_conflict)
if (zskip_can_draw(pr)) { // Zero-source skip: the output is always 0, writing 0 over 0, so this draw changes no result (see zskip_can_draw)
+1 -1
View File
@@ -51,7 +51,7 @@ static void shape(const nvn_texdesc *d, unsigned *dpl, int *layered, uint32_t *w
case 2: *dpl = 3; break; // 3D
case 4: case 6: *dpl = 2; *layered = 1; break; // 2D_ARRAY / MS_ARRAY
case 8: *dpl = 2; *z = 6; *layered = 1; break; // CUBEMAP
case 9: *dpl = 2; *z = d->depth * 6; *layered = 1; break; // CUBEMAP_ARRAY
case 9: *dpl = 2; *layered = 1; break; // CUBEMAP_ARRAY: the depth counts layer-faces, as in OpenGL (DRAGON QUEST XI S: 510 for 85 cubes)
default: *dpl = 2; *z = 1; break; // 2D / MS / RECTANGLE
}
if (*z == 0) *z = 1;
+9
View File
@@ -39,6 +39,12 @@ static NVNqueue *g_sq; static NVNcommandHandle g_sh; static NVNT_nvnQueueSubmitC
static void *susp_thr(void *a) { (void)a; pthread_mutex_lock(&g_smu); atomic_store(&g_sstarted, 1); pthread_cond_broadcast(&g_scv); pthread_mutex_unlock(&g_smu); g_sub(g_sq, 1, &g_sh); atomic_store(&g_sdone, 1); return NULL; }
static int wait_started(void) { struct timespec dl; clock_gettime(CLOCK_REALTIME, &dl); dl.tv_sec += 10; pthread_mutex_lock(&g_smu); while (!atomic_load(&g_sstarted) && pthread_cond_timedwait(&g_scv, &g_smu, &dl) == 0) {} int ok = atomic_load(&g_sstarted); pthread_mutex_unlock(&g_smu); return ok; }
static size_t tex_size3(int target, int fmt, int w, int h, int depth) { // a layered or 3D texture's storage size
NVNtextureBuilder tb;
P(nvnTextureBuilderSetDevice)(&tb, &dev); P(nvnTextureBuilderSetDefaults)(&tb); P(nvnTextureBuilderSetTarget)(&tb, target);
P(nvnTextureBuilderSetSize3D)(&tb, w, h, depth); P(nvnTextureBuilderSetFormat)(&tb, fmt);
return P(nvnTextureBuilderGetStorageSize)(&tb);
}
static size_t tex_size(int fmt, int w, int h, int flags, size_t *align) {
NVNtextureBuilder tb;
P(nvnTextureBuilderSetDevice)(&tb, &dev); P(nvnTextureBuilderSetDefaults)(&tb);
@@ -85,6 +91,9 @@ int main(void) {
// LINEAR 100×100 RGBA8: row pitch 400 aligned to 32 = 416; 416×100 = 41600, aligned to 32
EXPECT("linear 100x100 RGBA8 size", 41600, tex_size(NVN_FORMAT_RGBA8, 100, 100, NVN_TEXTURE_FLAGS_LINEAR, &al));
EXPECT("linear alignment", 32, al);
// A cube array's depth counts layer-faces, as in OpenGL: 12 = two cubes, the size of a 12-layer 2D array (it used to be read as 12 cubes, six times the storage)
EXPECT("64x64 RGBA8 cube array of depth 12 = 2D array of 12 layers", tex_size3(NVN_TEXTURE_TARGET_2D_ARRAY, NVN_FORMAT_RGBA8, 64, 64, 12),
tex_size3(NVN_TEXTURE_TARGET_CUBEMAP_ARRAY, NVN_FORMAT_RGBA8, 64, 64, 12));
// A channel change on the same storage must survive the game's descriptor
// deduplication. Exercise the public builder/init/compare API, including
+71 -1
View File
@@ -543,6 +543,62 @@ def vs_layer(src):
src = src[:i] + re.sub(r'\n float gl_PointSize \[\[point_size\]\];', '', src[i:j], count=1) + src[j:]
return re.sub(r'\n out\.gl_PointSize = [^;\n]*;', '', src)
def gs_slice(spv):
"""A geometry shader that only picks the layer: triangles in, at most 3 vertices out, each vertex copying its input position and the input components it passes on, and gl_Layer taking the
bits of one input component (Unreal Engine 4.18's FWriteToSliceGS, which writes one slice of a volume texture per instance: DRAGON QUEST XI S 0e7f58cd1f602f6d builds the 32x32x32 colour
grading LUT the tone mapper reads, and without it the screen came out a flat blue). Metal has no geometry stage, so the host drops such a shader and draws with the vertex shader's
layer entry points instead (vs_layer_entry), which write that component as the layer. Read from the SPIR-V as GLSL (SPIRV-Cross writes no MSL for a geometry stage).
→ {'loc': input location, 'comp': component} or None (any other geometry shader: one-pass cube shadows emit six transformed triangles)"""
r = subprocess.run(['spirv-cross', spv, '--version', '450'], capture_output=True, text=True)
g = r.stdout
if r.returncode or 'layout(triangles) in;' not in g or 'layout(max_vertices = 3, triangle_strip) out;' not in g or g.count('EmitVertex();') != 3 or 'EndPrimitive' in g: return None
ins = {m.group(2): int(m.group(1)) for m in re.finditer(r'layout\(location = (\d+)\) in vec4 (\w+)\[3\];', g)}
outs = {m.group(2): int(m.group(1)) for m in re.finditer(r'layout\(location = (\d+)\) out vec4 (\w+);', g)}
body = g[g.find('void main()'):]
var, layer = {}, set()
for st in (s.strip() for s in re.split(r'[;{}]', body[body.find('{') + 1:])):
if not st or st == 'if (false)' or re.fullmatch(r'int \w+ = -?\d+|\w+ = int\(0x80000000\)|int \w+ = \w+|EmitVertex\(\)', st): continue
m = re.fullmatch(r'float (\w+) = (\w+)\[\w+\]\.([xyzw])', st)
if m and m.group(2) in ins: var[m.group(1)] = (ins[m.group(2)], m.group(3)); continue
m = re.fullmatch(r'float (\w+) = gl_in\[\w+\]\.gl_Position\.([xyzw])', st)
if m: var[m.group(1)] = ('pos', m.group(2)); continue
m = re.fullmatch(r'gl_Position\.([xyzw]) = (\w+)', st)
if m and var.get(m.group(2)) == ('pos', m.group(1)): continue
m = re.fullmatch(r'(\w+)\.([xyzw]) = (\w+)', st)
if m and m.group(1) in outs and var.get(m.group(3)) == (outs[m.group(1)], m.group(2)): continue
m = re.fullmatch(r'gl_Layer = floatBitsToInt\((\w+)\)', st)
if m and m.group(1) in var and var[m.group(1)][0] != 'pos': layer.add(var[m.group(1)]); continue
return None # anything else: not a plain slice writer
if len(layer) != 1 or g.count('gl_Layer =') != 3: return None
(loc, comp), = layer
return {'loc': loc, 'comp': comp}
def vs_layer_entry(full):
"""Layer entry points for a vertex shader that writes integer bits taken from the instance index into an output component (Unreal Engine 4.18's WriteToSliceMainVS: the slice = instance −
base instance + its first slice, DRAGON QUEST XI S 7ceb7898171a9ae9). main0_l and main0_lz are main0 and main0_z writing that component as the layer (render_target_array_index), without
the point size (see vs_layer); the host draws with them when the program's geometry shader is a slice writer reading that component (gs_slice). → (source with the two entry points added,
{'loc', 'comp'}), or (source, None) when no output qualifies"""
i = full.find('struct main0_out\n{\n'); j = full.find('\n};', i)
if i < 0 or j < 0 or 'gl_InstanceIndex' not in full or '[[render_target_array_index]]' in full[i:j]: return full, None
locs = {m.group(1): int(m.group(2)) for m in re.finditer(r'\n float4 (\w+) \[\[user\(locn(\d+)\)\]\];', full[i:j])}
m = next((m for m in re.finditer(r'\n out\.(\w+)\.([xyzw]) = as_type<float>\(', full) if m.group(1) in locs), None)
if not m: return full, None
name, comp = m.group(1), m.group(2)
st = re.sub(r'\n float gl_PointSize \[\[point_size\]\];', '', full[i:j + 3]).replace('struct main0_out', 'struct main0l_out', 1)
st = st[:-3] + '\n uint gl_Layer [[render_target_array_index]];\n};'
add = '\n' + st + '\n'
for ep, new in (('main0', 'main0_l'), ('main0_z', 'main0_lz')):
a = re.search(rf'\bvertex\s+main0_out\s+{ep}\s*\(', full)
if not a: return full, None
b, depth = full.find('{', a.end()), 0
for e in range(b, len(full)):
depth += full[e] == '{'; depth -= full[e] == '}'
if not depth: break
fn = full[a.start():e + 1].replace('main0_out', 'main0l_out').replace(f' {ep}(', f' {new}(', 1)
fn = re.sub(r'\n out\.gl_PointSize = [^;\n]*;', '', fn)
add += '\n' + fn.replace('return out;', f'out.gl_Layer = as_type<uint>(out.{name}.{comp});\n return out;') + '\n'
return full + add, {'loc': locs[name], 'comp': comp}
def vs_depth(src):
"""Two entry points for the depth convention: the translator's vertex shader leaves z unchanged (NVN's near=0 convention); the main function is copied here - main0 gets the GL convention's (near=-w) z' = z/2 + w/2 before every return out
(bit-identical to the formula Ryujinx used to emit in the translator), main0_z stays as is. The convention a game sets is runtime state (nvnDeviceSetDepthMode); the host picks the entry point by it, with nothing to configure per game.
@@ -607,6 +663,13 @@ def _ep_drop(x, ids): # OpEntryPoint without the given interface IDs: model, f
def finish(key, w, j, c1):
name, k = loc[key]
msl = os.path.join(w, key + '.metal'); lib = os.path.join(out, key + '_b.metallib')
if j['stage'] == 'gs': # a geometry shader that only picks the layer is folded into the vertex shader by the host (see gs_slice); any other has no Metal counterpart and fails below
s = gs_slice(os.path.join(w, key + '.spv'))
if s:
refl = {'key': key + '_b', 'route': 'B', 'chain': CHAIN, 'dm_ctl': key, 'source': f'{name}#{k}', 'pc_paired': pc_of[key], 'stage': 'gs', 'gs_slice': s,
'notes': [], 'drv_reads': '', 'drv_unfillable': [], 'bindings': []} # no library: the host draws with the vertex shader's layer entry points
write_refl(key, refl)
return 'ok', '', refl
if j['stage'] == 'vs': spv_single_lane(os.path.join(w, key + '.spv')) # one-lane subgroups (see spv_single_lane)
r = subprocess.run(['spirv-cross', *XC, os.path.join(w, key + '.spv'), '--output', msl], capture_output=True, text=True)
if r.returncode: return 'MSL failed', r.stderr.strip()[:160], None
@@ -627,14 +690,20 @@ def finish(key, w, j, c1):
if j['stage'] == 'vs' and PAIRS.get(key): # outputs the pixel shader reads but the vertex shader does not write get (0,0,0,1) (see vs_pad)
src, pad = vs_pad(key, src)
if pad: open(msl, 'w').write(src)
lay = None
if j['stage'] == 'vs': # the two depth-convention entry points (see vs_depth); the analysis below uses the source with only main0
src, full = vs_depth(src)
if j.get('tf'): full += vs_tf(src, j['tf']) # transform feedback capture (see vs_tf)
plain = full
full, lay = vs_layer_entry(full) # layer entry points for a slice-writing geometry shader (see vs_layer_entry)
open(msl, 'w').write(full)
res = json.load(open(os.path.join(w, key + '.res.json')))
try: MTL.compile('mac', msl, lib) # MSL identical to one compiled before takes that library (dmsh_mtlcache.py)
except subprocess.CalledProcessError as e:
return 'metal failed', str(e)[:160], None
if not lay: return 'metal failed', str(e)[:160], None
open(msl, 'w').write(plain); lay = None # the layer entry points are an extra: a library that does not build with them is built without
try: MTL.compile('mac', msl, lib)
except subprocess.CalledProcessError as e: return 'metal failed', str(e)[:160], None
clone(msl, os.path.join(out, key + '_b.metal'))
if IOSOUT: # iOS: the same MSL goes through iOS's single compile convention (dm/ios/tools/ios_metal.py) to produce an iphoneos metallib
try: MTL.compile('ios', msl, os.path.join(IOSOUT, key + '_b.metallib'))
@@ -644,6 +713,7 @@ def finish(key, w, j, c1):
refl = {'key': key + '_b', 'route': 'B', 'chain': CHAIN, 'dm_ctl': key, 'source': f'{name}#{k}', 'pc_paired': pc_of[key], 'stage': j['stage'],
'entry': entry.group(2) if entry else 'main0', 'msl_entry_kind': entry.group(1) if entry else None, **({'entry_z': 'main0_z'} if j['stage'] == 'vs' else {}),
**({'entry_tf': 'main0_tf', 'tf_strides': [e[2] if e else 0 for e in j['tf']]} if j.get('tf') else {}),
**({'entry_layer': 'main0_l', 'entry_layer_z': 'main0_lz', 'layer_from': lay} if lay else {}),
'threadgroup_size': j.get('local'), 'c1': c1.hex(), 'notes': res['notes'] + (['not in the answer table, queries use defaults'] if j.get('no_facts') else []), 'drv_reads': dr, 'drv_unfillable': unfillable(dr),
'bindings': reflect(key, j['stage'], src, res),
'vertex_inputs': [{'location': int(m.group(2)), 'msl_type': m.group(1)} for m in re.finditer(r'(\w+)\s+\w+\s*\[\[attribute\((\d+)\)\]\]', src)],
+67 -7
View File
@@ -486,13 +486,14 @@ def trpfs_jobs(files, parts=64):
_OODLE = None
def oodle(comp, n):
"""The game's own OodleLZ_Decompress (Trinity's packs are Oodle Mermaid, and Oodle is linked into main): one Mac host per worker process in its tool mode (dm/host/hle_oodle.c, DM_OODLE_SERVE),
fed over a pipe. DM_OODLE_HOST = the host, DM_OODLE_MODULES = the rewritten modules it loads (the NSP script sets both for a game with Trinity packs, profile_guess.py shader_sources)"""
"""The game's own decoder: OodleLZ_Decompress (Trinity's packs are Oodle Mermaid, and Oodle is linked into main), or LZ5_decompress_safe (DRAGON QUEST XI S's legacy Unreal paks):
one Mac host per worker process in its tool mode (dm/host/hle_oodle.c, DM_OODLE_SERVE), fed over a pipe. DM_OODLE_HOST = the host, DM_OODLE_MODULES = the rewritten modules it loads
(the NSP script sets both for a game with Trinity packs or legacy Unreal paks, profile_guess.py shader_sources)"""
global _OODLE
if _OODLE is None:
import tempfile
host, mods = os.environ.get('DM_OODLE_HOST'), os.environ.get('DM_OODLE_MODULES')
if not host or not mods: red('Oodle-compressed packs: set DM_OODLE_HOST (the Mac host, make -C dm/host GAME=<id> dmhost) and DM_OODLE_MODULES (the rewritten modules) to decode them with the game\'s own decoder')
if not host or not mods: red('Oodle- or LZ5-compressed game files: set DM_OODLE_HOST (the Mac host, make -C dm/host GAME=<id> dmhost) and DM_OODLE_MODULES (the rewritten modules) to decode them with the game\'s own decoder')
err = tempfile.TemporaryFile()
_OODLE = (subprocess.Popen([host, mods], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=err, env=dict(os.environ, DM_OODLE_SERVE='1')), err)
p, err = _OODLE
@@ -501,7 +502,7 @@ def oodle(comp, n):
got = struct.unpack('<Q', p.stdout.read(8))[0]
except (BrokenPipeError, struct.error): got = None
if got != n:
err.seek(0); red(f'the Oodle decoder in the Mac host failed on a {len(comp)} → {n} byte stream; ---- host output ----\n' + err.read().decode(errors='replace')[-3000:])
err.seek(0); red(f'the game\'s decoder in the Mac host failed on a {len(comp)} → {n} byte stream; ---- host output ----\n' + err.read().decode(errors='replace')[-3000:])
return p.stdout.read(n)
def trinity_shaders(path, rel, packs, out):
@@ -519,6 +520,61 @@ def trinity_shaders(path, rel, packs, out):
else: red(f'/{rel}#{name}: file {k} has compression {comp} ({sz} → {n} bytes), which is not known')
scan_blob(data, f'/{rel}#{name}#{struct.unpack_from("<Q", d, hs[0] + 8 * k)[0]:016x}', out)
UE_LEGACY = 'UE legacy pak'
def ue_legacy_jobs(files, parts=64):
"""Legacy Unreal paks (version 4, DRAGON QUEST XI S: pakchunk0-Switch.pak and pakchunk1-Switch.pak, 13.8 GB, and the update's *_0_P.pak): the engine mounts them all as one view, and a patch pak
(name ending in _P.pak) wins over the others for the same path. Only the newest copy of each path is scanned. → ([(pak path, rel, (UE_LEGACY, names)), …] in about equal raw sizes, the pak paths taken)"""
def legacy(p):
try:
with open(p, 'rb') as f: return uepak.pak_version(f) in uepak.LEGACY
except (OSError, ValueError, struct.error): return False
paks = [(p, r) for p, r in files if p.endswith('.pak') and legacy(p)]
if not paks: return [], set()
view = {}
for p, r in sorted(paks, key=lambda x: (x[1].endswith('_P.pak'), x[1])): # patch paks last, so they replace; one view per pak folder (one per version given)
pk = uepak.Pak(p)
for name, e in pk.files.items(): view[(os.path.dirname(p), os.path.normpath(pk.mount + name))] = (p, r, name, e['raw'])
jobs, cur, size, step = [], collections.defaultdict(list), 0, sum(v[3] for v in view.values()) / parts
for p, r, name, raw in sorted(view.values(), key=lambda x: (x[1], x[2])):
cur[(p, r)].append(name); size += raw
if size >= step: jobs += [(p_, r_, (UE_LEGACY, tuple(ns))) for (p_, r_), ns in cur.items()]; cur, size = collections.defaultdict(list), 0
jobs += [(p_, r_, (UE_LEGACY, tuple(ns))) for (p_, r_), ns in cur.items()]
return jobs, {p for p, _ in paks}
_PAKS = {}
def ue_legacy_shaders(path, rel, names, out):
"""Files of a legacy Unreal pak (ue_legacy_jobs), each decoded block by block with the game's own decoder (DRAGON QUEST XI S: LZ5) and scanned for the shader records the engine keeps inline"""
pk = _PAKS.get(path) or _PAKS.setdefault(path, uepak.Pak(path))
for name in names:
d = pk.read(name, decode=oodle)
ue_inline_shaders(d, f'/{rel}#{re.sub(r"^(\.\./)+", "", os.path.normpath(pk.mount + name))}', out)
ZLIB_HEAD = re.compile(b'\x78[\x01\x5e\x9c\xda]')
def ue_inline_shaders(d, label, out):
"""Shaders kept inline in cooked Unreal packages (UE 4.18, DRAGON QUEST XI S: each material's shader map in its .uexp, 38 to 87 shaders in the ones looked at, and the engine's global shaders in
Engine/GlobalShaderCache-GLSL_SWITCH.bin, 945): each shader's code is a byte array (i32 length, then the bytes) holding one zlib stream, which inflates to an LSLG record as in the shader code
archives (uepak.lslg). Found by the zlib header and the length in front of it: a stream that inflates whole from exactly that many bytes and starts with LSLG (every such stream in the files
looked at). A record stored without compression (LSLG right after its length) is taken too. The engine combines vertex and pixel shaders per draw (separable_pairs)"""
import zlib
def take(s, at):
rec = uepak.lslg(s)
if not rec: red(f'{label}: the shader record at {at:#x} is not an LSLG record of the known layout')
tag, ctl, code = rec; st = UE_TAG.get(tag)
if not st: red(f'{label}: the shader record at {at:#x} has stage tag {tag}')
why = check(label, st, ctl, code)
if why: red(f'{label}: shader at {at:#x} ({st}): {why}')
out.append((f'{label}#UE{at:x}', st, ctl, code))
for m in ZLIB_HEAD.finditer(d):
i = m.start(); n = int.from_bytes(d[i - 4:i], 'little') if i >= 4 else 0
if not 2 <= n <= len(d) - i: continue
z = zlib.decompressobj()
try: s = z.decompress(d[i:i + n])
except zlib.error: continue
if z.eof and not z.unused_data and s[:4] == b'LSLG': take(s, i)
for m in re.finditer(b'LSLG', d):
i = m.start(); n = int.from_bytes(d[i - 4:i], 'little') if i >= 4 else 0
if 2 <= n <= len(d) - i and uepak.lslg(d[i:i + n]): take(d[i:i + n], i)
def yaz0(s):
"""Yaz0 decompression (Super Mario 3D World + Bowser's Fury: almost all of RomFS is .szs = SARC wrapped in Yaz0). Pure Python runs at about 20–45 MB/s per core, under a minute for the whole 4.7 GB RomFS in parallel, and needs no extra dependency.
Group header bits: 1 = one literal byte; 0 = back-reference, 2 bytes {length high nibble, 12-bit distance} (distance + 1); length 0 reads one more byte + 0x12, else + 2. Overlapping copies are expanded by their period"""
@@ -714,6 +770,9 @@ def scan_file(args):
if len(args) > 2 and head[:8] == ONEPACK: # a range of Trinity packs (trpfs_jobs)
trinity_shaders(path, rel, args[2], found)
return rel, uniq_keys(found), var_pairs(found), trees
if len(args) > 2 and args[2][0] == UE_LEGACY: # files of a legacy Unreal pak (ue_legacy_jobs)
ue_legacy_shaders(path, rel, args[2][1], found)
return rel, uniq_keys(found), var_pairs(found), trees
if len(args) > 2 and head[:4] == KPKA: # entries of an RE Engine PAK (kpka_jobs)
kpka_shaders(path, rel, args[2], found)
return rel, uniq_keys(found), var_pairs(found), trees
@@ -789,7 +848,7 @@ def main():
# Unreal Engine shader code archives decoded on the Mac (uepak.py): <version dir>/ue_shaders, or DM_UE_SHADERS; needed when the RomFS has Unreal paks
ued = os.environ.get('DM_UE_SHADERS') or os.path.join(os.path.dirname(os.path.abspath(romfs)), 'ue_shaders')
names = sorted(f for f in os.listdir(ued) if f.endswith('.ushaderbytecode')) if os.path.isdir(ued) else []
if not names and uepak.unreal_paks(romfs): red(f'the RomFS has Unreal paks, but there are no decoded shader code archives in {ued} (the NSP script writes them)')
if not names and uepak.unreal_paks(romfs, legacy=False): red(f'the RomFS has Unreal paks, but there are no decoded shader code archives in {ued} (the NSP script writes them)')
for nm in names: found += ue_shaders(open(os.path.join(ued, nm), 'rb').read(), f'/ue_shaders/{nm}')
results.append(('main', {skey(c, k): (w, s, c, k) for w, s, c, k in found}, var_pairs(found), {}))
zdicts = {k: v for r in romfs_v for k, v in zs_dicts(r).items()} # Tears of the Kingdom's zstd dictionaries (zs_dicts)
@@ -799,8 +858,9 @@ def main():
big = [j for p, r in files for j in (linkdata_jobs(p, r) or [])] # Three Houses' 6.9 GB DATA1.bin: one job per range of entries, not one read of the whole file
pak_jobs, paks = kpka_jobs(files) # Monster Hunter Rise's PAKs: the newest copy of each path, in ranges of entries
tr_jobs, trp = trpfs_jobs(files) # Pokémon Legends: Z-A's Trinity packs (data.trpfs and its index data.trpfd), decoded by the game's own Oodle in the Mac host
big += pak_jobs + tr_jobs
files = [(p, r) for p, r in files if not linkdata_jobs(p, r) and p not in paks and p not in trp]
ue_jobs, uep = ue_legacy_jobs(files) # DRAGON QUEST XI S's legacy Unreal paks, decoded by the game's own LZ5 in the Mac host
big += pak_jobs + tr_jobs + ue_jobs
files = [(p, r) for p, r in files if not linkdata_jobs(p, r) and p not in paks and p not in trp and p not in uep]
with cf.ProcessPoolExecutor(J, initializer=_pool_init, initargs=(common, {}, zdicts)) as ex:
results += list(ex.map(scan_file, big)) + list(ex.map(scan_file, files, chunksize=16))
trees, defer = {}, []
+58 -20
View File
@@ -13,6 +13,12 @@
# Encoded entry: u32 bits (0-5 compression block size >> 11, 0x3f = a u32 follows; 6-21 block count; 22 encrypted; 23-28 compression method, 1-based; 29 stored size fits 32 bits; 30 raw size fits
# 32 bits; 31 offset fits 32 bits), then offset, raw size, stored size (compressed entries only) and, for several blocks or an encrypted one, each block's u32 stored size. The data starts after
# the FPakEntry the pak repeats in front of it (53 bytes, + 4 + 16 per block when compressed); blocks follow each other, 16-aligned when encrypted.
# Older paks (version 4, UE 4.18: DRAGON QUEST XI S, pakchunk0-Switch.pak 6.6 GB / 264,844 files and pakchunk1-Switch.pak 7.2 GB / 147,824 files, the update's *_0_P.pak beside them) keep the
# legacy layout. Footer (the last 0x2d bytes): encrypted index flag (u8), magic, version, index offset, index size, SHA-1. Index: mount point, entry count, then per file its name (relative to the
# mount point) and its FPakEntry: offset, stored size, raw size (i64 each), compression flags (i32), SHA-1 (20), for a compressed file the block count (i32) and each block's start and end
# (i64, from the file start before version 5, from the entry after), the encrypted flag (u8) and the compression block size (u32). The FPakEntry is repeated in front of the data.
# The compression flags are no names: 1 zlib, 2 gzip, 4 custom; DRAGON QUEST XI S's engine uses 3, which is LZ5 2.0 (main exports LZ5_*; every block starts with its level, 0x2d = 45), and only the
# game's own decoder reads it (hle_oodle.c, through a decode function given to Pak.read).
# Shader code archive (version 2): shader map hashes (20 bytes each), shader hashes (20 each), shader map entries {u32 first index, u32 shader count, u32 first preload, u32 preload count}, shader
# entries {u64 offset, u32 size, u32 raw size, u8 frequency} (17 bytes, packed), preload entries (16 each), shader indices (u32): each array is a u32 count and its elements; the code follows,
# offsets relative to its start. A shader whose size differs from its raw size is compressed.
@@ -58,19 +64,32 @@ class R:
if n < 0: return self.raw(-2 * n).decode('utf-16-le')[:-1]
return self.raw(n)[:-1].decode('utf-8', 'replace') if n else ''
LEGACY = (4,) # legacy pak versions read (the per-file index); 11 has the encoded one
LEGACY_METHODS = {1: 'Zlib', 2: 'Gzip', 3: 'LZ5', 4: 'Custom'} # compression flags of a legacy pak (3: DRAGON QUEST XI S's engine, see the header)
def pak_version(f):
"""The version of a pak: a legacy footer is the last 0x2d bytes (magic 0x2c from the end); version 11's is 0xdd bytes, the magic 0xc8 from the end"""
for back in (0x2c, 0xc8):
f.seek(-back, 2); m, ver = struct.unpack('<II', f.read(8))
if m == PAK_MAGIC: return ver
raise ValueError('no pak footer')
def footer(f):
f.seek(-0xdd, 2); ft = f.read(0xdd)
k = ft.find(struct.pack('<I', PAK_MAGIC))
if k < 1: raise ValueError('no pak footer')
ver, ioff, isz = struct.unpack_from('<IQQ', ft, k + 4)
if ver != 11: raise ValueError(f'pak version {ver}, only 11 is read')
"""(encrypted index, index offset, index size, compression method names by number from 1, version)"""
ver = pak_version(f)
if ver in LEGACY:
f.seek(-0x2d, 2); ft = f.read(0x2d); ioff, isz = struct.unpack_from('<QQ', ft, 9)
return ft[0], ioff, isz, [LEGACY_METHODS.get(i, f'flags {i}') for i in range(1, 8)], ver
if ver != 11: raise ValueError(f'pak version {ver}, only {", ".join(map(str, LEGACY))} and 11 are read')
f.seek(-0xdd, 2); ft = f.read(0xdd); k = 0xdd - 0xc8
ioff, isz = struct.unpack_from('<QQ', ft, k + 8)
methods = [ft[k + 44 + 32 * i:k + 76 + 32 * i].rstrip(b'\0').decode('latin1') for i in range(5)]
return ft[k - 1], ioff, isz, methods
return ft[k - 1], ioff, isz, methods, ver
def find_key(img, lo, hi, pak):
"""The AES key among main's 32-byte windows (read-only and data segments, 4-aligned, at least 20 distinct bytes): the one that decrypts the index start to a short mount point"""
with open(pak, 'rb') as f:
_, ioff, _, _ = footer(f); f.seek(ioff); blk = f.read(16)
_, ioff, _, _, _ = footer(f); f.seek(ioff); blk = f.read(16)
for a in range(lo, hi - 32, 4):
w = img[a:a + 32]
if len(set(w)) < 20: continue
@@ -81,10 +100,21 @@ def find_key(img, lo, hi, pak):
class Pak:
def __init__(self, path, key=None):
self.path, self.key = path, key; self.f = open(path, 'rb')
enc, ioff, isz, self.methods = footer(self.f)
enc, ioff, isz, self.methods, self.version = footer(self.f)
if enc and not key: raise ValueError(f'{path}: the index is encrypted and no key was given')
r = R(self.read_at(ioff, isz, enc))
self.mount = r.fstr(); self.count = r.take('<i'); r.take('<Q')
self.mount = r.fstr(); self.count = r.take('<i')
if self.version in LEGACY: # one FPakEntry per file, kept as parsed
self.files = {}
for _ in range(self.count):
name = r.fstr(); off, size, raw, meth = r.take('<qqqi'); r.raw(20)
blocks = [r.take('<qq') for _ in range(r.take('<i'))] if meth else []
e, bsz = r.take('<BI'); head = 53 + (4 + 16 * len(blocks) if meth else 0)
if self.version < 5: blocks = [(s - off, t - s) for s, t in blocks]
else: blocks = [(s, t - s) for s, t in blocks]
self.files[name] = dict(off=off, raw=raw, size=size, method=self.methods[meth - 1] if meth else '', encrypted=e, block_size=bsz, blocks=blocks or [(head, size)])
return
r.take('<Q')
if r.take('<i'): r.take('<QQ'); r.raw(20) # path hash index (not needed with the full directory index)
if not r.take('<i'): raise ValueError(f'{path}: no full directory index')
doff, dsz = r.take('<QQ'); r.raw(20)
@@ -98,6 +128,7 @@ class Pak:
self.f.seek(off); b = self.f.read((n + 15) & ~15 if enc else n)
return aes_decrypt(self.key, b)[:n] if enc else b
def entry(self, path):
if self.version in LEGACY: return self.files[path]
r = R(self.enc, self.files[path]); v = r.take('<I')
bsz = (v & 0x3f) << 11
if v & 0x3f == 0x3f: bsz = r.take('<I')
@@ -109,11 +140,16 @@ class Pak:
else:
for _ in range(nblk): s = r.take('<I'); blocks.append((cur, s)); cur += (s + 15) & ~15 if enc else s
return dict(off=off, raw=raw, size=size, method=self.methods[meth - 1] if meth else '', encrypted=enc, block_size=bsz, blocks=blocks)
def read(self, path):
"""A stored file's bytes (compressed files need the Oodle job)"""
def read(self, path, decode=None):
"""A file's bytes. A compressed one needs decode(block bytes, raw length) → bytes, block by block (each block holds block_size raw bytes, the last one the rest):
the game's own decoder in the Mac host (shader_split.py oodle); Octopath Traveler 0's archives go through the Oodle job instead"""
e = self.entry(path)
if e['method']: raise ValueError(f'{path} is {e["method"]}-compressed in the pak')
return self.read_at(e['off'] + 53, e['size'], e['encrypted'])
if not e['method']: return self.read_at(e['off'] + 53, e['size'], e['encrypted'])
if not decode: raise ValueError(f'{path} is {e["method"]}-compressed in the pak')
out, left = [], e['raw']
for s, n in e['blocks']:
k = min(left, e['block_size']); out.append(decode(self.read_at(e['off'] + s, n, e['encrypted']), k)); left -= k
return b''.join(out)
def archive(b):
"""A shader code archive's tables: {'maps': [(first index, count)], 'shaders': [(offset, size, raw size, frequency)], 'indices': [...], 'code': start of the code}"""
@@ -142,21 +178,22 @@ def paks(romfs):
"""Every pak under the RomFS"""
return sorted(os.path.join(dp, f) for dp, _, fs in os.walk(romfs) for f in fs if f.endswith('.pak'))
def unreal_paks(romfs):
"""The paks under the RomFS that Unreal wrote (its magic in the footer, any version): a game whose shaders the NSP script decodes (decode_ue_shaders) and shader_split.py reads"""
def unreal_paks(romfs, legacy=None):
"""The paks under the RomFS that Unreal wrote (its magic in the footer; legacy None = any version, True = the legacy versions, False = the others): with version 11 the NSP script decodes
the shader code archives (decode_ue_shaders); legacy paks keep the shaders inline in the cooked packages, which shader_split.py reads through the game's decoder (ue_legacy_jobs)"""
out = []
for p in paks(romfs):
try:
with open(p, 'rb') as f: f.seek(-0xdd, 2); ft = f.read(0xdd)
except OSError: continue # shorter than a footer
if ft.find(struct.pack('<I', PAK_MAGIC)) >= 1: out.append(p)
with open(p, 'rb') as f: ver = pak_version(f)
except (OSError, ValueError, struct.error): continue # shorter than a footer, or no Unreal pak
if legacy is None or (ver in LEGACY) == legacy: out.append(p)
return out
def job(romfs, main_nso, work):
"""The shader code archives of every pak under the RomFS into <work>/archives, and one Oodle job (<work>/oodle.job) of every compressed shader in them; <work>/job.json lists them"""
os.makedirs(os.path.join(work, 'archives'), exist_ok=True)
img, lo, hi = nso_image(main_nso); key = None; listing, recs = [], []
for pak in paks(romfs):
for pak in unreal_paks(romfs, legacy=False):
with open(pak, 'rb') as f: enc = footer(f)[0]
if enc and key is None:
key = find_key(img, lo, hi, pak)
@@ -214,7 +251,8 @@ if __name__ == '__main__':
f.write('path\traw\tstored\tmethod\tencrypted\tblocks\n')
for path in sorted(p.files):
e = p.entry(path); f.write(f"{path}\t{e['raw']}\t{e['size']}\t{e['method'] or 'none'}\t{e['encrypted']}\t{len(e['blocks'])}\n")
print(f'pak version 11, mount {p.mount}, {len(p.files)} files, compression {[m for m in p.methods if m]}')
used = sorted({p.entry(x)['method'] for x in p.files} - {''})
print(f'pak version {p.version}, mount {p.mount}, {len(p.files)} files, compression {used}')
elif cmd == 'job':
listing, n = job(*sys.argv[2:5])
for x in listing: print(f"{x['name']}: {x['shaders']} shaders, {x['compressed']} compressed")
Loaded 100 of 116 files, more files were not shown because too many files have changed in this diff. Show more