diff --git a/AGENTS.md b/AGENTS.md index d23097b..6276887 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # Notes for agents -- This repository runs Nintendo Switch games natively on Apple platforms: the game's own code runs, rewritten only where Apple's kernel requires it. Super Mario Bros. Wonder is supported (base game v0 and update 1.2.1, profile `010015100B514000`); Fire Emblem Engage (update 2.0.0, profile `0100A6301214E000`) is in bring-up and already packs into an IPA. Metroid Dread (update 2.1.0, profile `010093801237C000`) is in bring-up: on the Mac a new game reaches gameplay. Start with [README.md](README.md) for status and build steps. +- This repository runs Nintendo Switch games natively on Apple platforms: the game's own code runs, rewritten only where Apple's kernel requires it. Super Mario Bros. Wonder is supported (base game v0 and update 1.2.1, profile `010015100B514000`); Fire Emblem Engage (update 2.0.0, profile `0100A6301214E000`) is in bring-up and already packs into an IPA. Metroid Dread (update 2.1.0, profile `010093801237C000`) is in bring-up: on the Mac a new game reaches gameplay. 13 Sentinels: Aegis Rim (update 1.10, profile `01003FC01670C000`) is in bring-up: on the Mac a new game reaches the first battle. Start with [README.md](README.md) for status and build steps. - Each game's constants and shader tables form a game profile in `dm/games//`, where `` is the base game's title ID: `game.tsv` plus the tables and `dmsh_chain.h`. Mario's profile is `010015100B514000`. Tools pick the profile with `DM_GAME`; `make -C dm/host` and `build_ipa.sh` pick it with `GAME`. There is no default game: set it every time (the tools stop and list the profiles otherwise). Put per-game values in the profile, not in code ([dm/games/README.md](dm/games/README.md)). - Per-build knowledge is keyed by the NSO build ID: rewriter cuts in `dm/tools/patch_nso.py`, the profile's `build` rows (the builds the NSP script accepts, and their display versions), and host behaviour that uses fixed module addresses checks `module_t.build_id` (from `fixups.tsv`; `dm_main_build()`, `DM_D2R_BUILD` for code inherited from D2R). When a new game build appears, carry addresses over with `dm/tools/nso_fnmap.py` and check them by disassembly; never let fixed addresses run on a build they were not found in. - To analyse or port **another** Switch title, first run `scripts/build_ipa_from_nsp.py` on its NSP folder. With no matching profile, it writes a survey to `/_survey/survey.md`. Then follow [docs/agents](docs/agents/README.md) (extraction, binary survey, host bring-up failure catalogue, graphics specifics, debugging recipes) and add a profile. diff --git a/README.md b/README.md index 7033b56..209d262 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ This project is built on other people's work: | Super Mario Bros. Wonder | `010015100B514000` | the base game (1.0.0) and update 1.2.1 | **Playable on the iPhone with sound.** An 11-minute session through several levels ran at 60 fps with no crash. The IPA packs 1.2.1 (`-p v0` packs 1.0.0) and is about 3.9 GB | | Fire Emblem Engage | `0100A6301214E000` | the base game and update 2.0.0; DLC NSPs (Expansion Pass) are packed too when present | **In bring-up.** On the Mac it plays the prologue, battles included. The first iPhone run (2026-10-15) went through a new game's opening movie and name entry at 60 fps with no crash. After a trip to the background, one of the game's two audio outputs stopped being fed; this is being investigated. It runs at 60 fps at the right speed: the Switch version runs at 30, and one patch to its code switches it to the 60 fps timing the game already has, so nothing runs too fast. The IPA is about 15 GB | | Metroid Dread | `010093801237C000` | the base game and update 2.1.0 | **In bring-up, Mac only so far.** A new game runs on the Mac through the title, the story recap and the opening cutscene into the first area of ZDR, at 60 fps with movies and music. No IPA has been tested yet | +| 13 Sentinels: Aegis Rim | `01003FC01670C000` | the base game and update 1.10 | **In bring-up, Mac only so far.** A new game runs on the Mac through the logos, the title, the prologue and its cutscenes into the first battle, at 60 fps with movies and sound. No IPA has been tested yet | For any other game, the script writes a survey instead of an IPA (see [Port another game](#port-another-game)). diff --git a/dm/games/01003FC01670C000/bf_want.tsv b/dm/games/01003FC01670C000/bf_want.tsv new file mode 100644 index 0000000..54caeb8 --- /dev/null +++ b/dm/games/01003FC01670C000/bf_want.tsv @@ -0,0 +1 @@ +# generated diff --git a/dm/games/01003FC01670C000/c1_full.tsv b/dm/games/01003FC01670C000/c1_full.tsv new file mode 100644 index 0000000..7f92a7a --- /dev/null +++ b/dm/games/01003FC01670C000/c1_full.tsv @@ -0,0 +1 @@ +# chain b-671e2ee0a6e9 2026-10-15T21:55:01+08:00 diff --git a/dm/games/01003FC01670C000/dmsh_chain.h b/dm/games/01003FC01670C000/dmsh_chain.h new file mode 100644 index 0000000..300bc5a --- /dev/null +++ b/dm/games/01003FC01670C000/dmsh_chain.h @@ -0,0 +1,2 @@ +// 生成文件(dm/gfx/tools/dmsh_chain.py --header,游戏 01003FC01670C000):本源码树期望的 B 着色器库翻译链版本(缺省选项);宿主只加载 <库目录>/b-<V>/ +#define DMSH_CHAIN "671e2ee0a6e9" diff --git a/dm/games/01003FC01670C000/game.tsv b/dm/games/01003FC01670C000/game.tsv new file mode 100644 index 0000000..8935450 --- /dev/null +++ b/dm/games/01003FC01670C000/game.tsv @@ -0,0 +1,57 @@ +# 十三机兵防卫圈(Switch,基础版 v0 + 更新 1.10 = v65536)的游戏档案。移植中:Mac 上新游戏能从序章进到第一场战斗 +# Game profile of 13 Sentinels: Aegis Rim (Switch, base game v0 + update 1.10 = v65536). In bring-up: on the Mac a new game gets through the prologue into the first battle +# 格式见 dm/games/README.md | Format: see dm/games/README.md +# Vanillaware 自己的引擎,SDK 11.4.3(sdk 与 subsdk0 与银河战士 生存恐惧的逐字节相同),NVN;数据全在 6 个 CRI CPK 里(ROBO_*.CPK),音频 CRI ADX2(acb / awb),影片 CRI USM。 +# 着色器不在 RomFS:838 个 GLSLC 编译输出(魔数 0x19866891)编进了 main 的只读段,由 .data 里的表引用(shader_split.py 读,见 shader_main_glslc) +# Vanillaware's own engine, SDK 11.4.3 (sdk and subsdk0 are byte-identical to Metroid Dread's), NVN; all data is in 6 CRI CPK archives (ROBO_*.CPK), audio is CRI ADX2 (acb / awb), movies are CRI USM. +# The shaders are not in RomFS: 838 GLSLC compiler outputs (magic 0x19866891) are compiled into main's read-only segment and referenced from a table in .data (read by shader_split.py, see shader_main_glslc) + +# ---- 身份 | Identity ---- +title_id 01003FC01670C000 +program_id 01003FC01670C000 + +# ---- App ---- +# 游戏名:只给档案列表与 Mac 窗口标题当标签;iOS App 名取基础版 NSP 里的游戏名,包名按基础版标题号(build_ipa.sh / NSP 脚本) +# Game name: only a label for profile listings and the Mac window title; the iOS app name is the game name in the base NSP, and the bundle ID comes from the base game title ID (build_ipa.sh / NSP script) +name 13 Sentinels: Aegis Rim +ipa 13Sentinels.ipa +app_category public.app-category.role-playing-games + +# ---- 版本 | Versions ---- +# 只解了合并 1.10 的那份;基础版单独的构建号用到时再补 | only the merged 1.10 copy is extracted; the base game's own build IDs are added when needed +build v65536 1.10 main=4b632573ca5c160eabb70519fb622718 subsdk0=507f19d1240d09d00992248e8a0fd77e sdk=2d369d9999e3e10e665106f840ffff9b +package v65536 +shader_versions v65536 + +# ---- 文件 | Files ---- +# 装载顺序同 Horizon:main、subsdk*、sdk(subsdk0 = NintendoSDK_movie 11.4.3) | load order as on Horizon: main, subsdk*, sdk (subsdk0 = NintendoSDK_movie 11.4.3) +modules main subsdk0 sdk +romfs_marker ROBO_US.CPK +zip_store .CPK:.metallib + +# ---- 内存 | Memory ---- +# NPDM 不声明系统资源(META +0x14 = 0):走 SetHeapSize。main 的 bss 只有约 21 MB。实测(DM_SVC=1):堆按「池 − 已用」要 0xc9400000(3.14 GB);别名区只在 SDK 初始化时映射一页; +# 线程栈经 MapMemory 放进栈区。块 = 前置 256 MB + 别名区 256 MB + 栈区 256 MB + 堆区 3.25 GB = 4 GB。Mac 上进第一场战斗时常驻约 2.2 GB +# The NPDM declares no system resource (META +0x14 = 0): the game uses SetHeapSize. main's bss is only about 21 MB. Measured (DM_SVC=1): the heap request is pool − used = 0xc9400000 (3.14 GB); the alias region gets one page during SDK initialization; +# thread stacks go into the stack region through MapMemory. Block = 256 MB in front + 256 MB alias region + 256 MB stack region + 3.25 GB heap region = 4 GB. On the Mac about 2.2 GB is resident in the first battle +mem_reserve 0x100000000 +mem_alias 0x10000000 +mem_stack 0x10000000 +app_pool 0xCD500000 + +# ---- NVN 驱动常量段(dmsh_build.py 出包、nvn_mtl.m 运行时现填) | NVN driver constant segments (dmsh_build.py at packaging, nvn_mtl.m filled at run time) ---- +# 沿用马力欧的。全部 751 个着色器按这组基址翻译,绑定都解出了槽号(dmsh_build 报「绑定没解出槽号的着色器 0」);贴图走无绑定句柄 | Mario's values. All 751 shaders translate with these bases and every binding resolves to a slot (dmsh_build: 0 shaders with unresolved bindings); textures go through bindless handles +nvn_tex_base 0x20 +nvn_tex_slots 32 +nvn_samp_base 0x568 +nvn_img_base 0x120 +nvn_img_slots 8 +nvn_ssbo_bases vs=0x110 ps=0x510 cs=0x310 + +# ---- 着色器拆分(shader_split.py;main 里的 GLSLC 输出) | Shader split (shader_split.py; GLSLC outputs in main) ---- +shader_main_glslc on +# 每份 GLSLC 输出各建一个单阶段程序(初始化时 838 次 ProgramSetShaders),绘制时 BindProgram 按阶段位拼顶点、像素:配对表按覆盖写(shader_split.py separable_pairs) +# Each GLSLC output becomes a single-stage program (838 ProgramSetShaders at startup), and BindProgram's stage bits combine vertex and pixel per draw: the pair table is written as a cover (shader_split.py separable_pairs) +shader_separable on +shader_groups ROBO=cpk +shader_note 十三机兵防卫圈着色器 diff --git a/dm/games/01003FC01670C000/pso_manifest.tsv b/dm/games/01003FC01670C000/pso_manifest.tsv new file mode 100644 index 0000000..7151d63 --- /dev/null +++ b/dm/games/01003FC01670C000/pso_manifest.tsv @@ -0,0 +1,4 @@ +# 预热种子清单(随包,与 shader_map.tsv 同目录)。行:S<TAB>着色器键<TAB>阶段 / R<TAB>渲染管线键(pso_skey 十六进制)/ C<TAB>计算着色器键。 +# 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. +# 本作尚未录制(DM_PSO_RECORD):空表 = 不预热。行序 = 首次使用顺序,不要排序。 +# Not recorded yet for this game (DM_PSO_RECORD): an empty list = no prewarm. Row order = first-use order; do not sort. diff --git a/dm/games/01003FC01670C000/shader_facts.tsv b/dm/games/01003FC01670C000/shader_facts.tsv new file mode 100644 index 0000000..1baaf9e --- /dev/null +++ b/dm/games/01003FC01670C000/shader_facts.tsv @@ -0,0 +1 @@ +# 翻译查询的作答表(dm/gfx/tools/shader_split.py 生成,勿手改):键 <TAB> 事实 JSON。本作没有 PC 版可配对:只有计算着色器的线程组 / 共享内存(取自控制段),其余查询按翻译器缺省 diff --git a/dm/games/01003FC01670C000/shader_map.tsv b/dm/games/01003FC01670C000/shader_map.tsv new file mode 100644 index 0000000..c996fa8 --- /dev/null +++ b/dm/games/01003FC01670C000/shader_map.tsv @@ -0,0 +1,754 @@ +# kind: evidence 十三机兵防卫圈着色器(RomFS + main 里的 GLSLC)→ 控制段 + 代码区键(dm/gfx/tools/shader_split.py) +# 第 5 列记每个键首次出现的位置;本作没有 PC 版配对 +package index stage dm_ctl_sha16 source +ps_main.bin 0 像素 e7f38f5ff5f39bf7 main#GLSLC@0x58dac0 +vs_main.bin 0 顶点 034fd5775426fb0a main#GLSLC@0x592e50 +ps_main.bin 1 像素 67fa3164f2b2b54f main#GLSLC@0x594e38 +ps_main.bin 2 像素 d50b237a8cd4b490 main#GLSLC@0x597338 +ps_main.bin 3 像素 0c5fb21e0d4e31b0 main#GLSLC@0x5991a0 +vs_main.bin 1 顶点 8f9a4446b3356f03 main#GLSLC@0x59a448 +vs_main.bin 2 顶点 0f3f9e510179be23 main#GLSLC@0x59b6a8 +ps_main.bin 4 像素 ee7ae7ef4ae4c028 main#GLSLC@0x59cd18 +ps_main.bin 5 像素 f09ee4f9c031897c main#GLSLC@0x59e3b8 +ps_main.bin 6 像素 391de7f9e70f441c main#GLSLC@0x59fd80 +ps_main.bin 7 像素 1037a2384ec6970c main#GLSLC@0x5a1498 +ps_main.bin 8 像素 88185a3d20c0b7b3 main#GLSLC@0x5a2fc8 +ps_main.bin 9 像素 378fc41a6fb740f9 main#GLSLC@0x5a4b20 +vs_main.bin 3 顶点 f2983b4ec002fac4 main#GLSLC@0x5a6818 +ps_main.bin 10 像素 0ca99e0593045cac main#GLSLC@0x5a7f38 +ps_main.bin 11 像素 c7d5d9ba6c4c556d main#GLSLC@0x5a9970 +ps_main.bin 12 像素 66eee3e69bcf3f27 main#GLSLC@0x5b0970 +ps_main.bin 13 像素 99ac30542b10a587 main#GLSLC@0x5b1998 +vs_main.bin 4 顶点 6119e2affa309a00 main#GLSLC@0x5b5328 +vs_main.bin 5 顶点 a9bf7470e2fad37c main#GLSLC@0x5b8d40 +ps_main.bin 14 像素 37c8bcb74d398646 main#GLSLC@0x5ba8a0 +ps_main.bin 15 像素 14effc2dede3fa57 main#GLSLC@0x5bc748 +ps_main.bin 16 像素 804bc47a5a52c682 main#GLSLC@0x5bd798 +ps_main.bin 17 像素 377106c4d4c5ba1d main#GLSLC@0x5befc0 +ps_main.bin 18 像素 3769521d23444ce4 main#GLSLC@0x5c0840 +ps_main.bin 19 像素 72249a39fa56c6ae main#GLSLC@0x5c22e0 +ps_main.bin 20 像素 b6f9a30612be37f9 main#GLSLC@0x5c3a80 +ps_main.bin 21 像素 d1275371e06e539d main#GLSLC@0x5c5400 +ps_main.bin 22 像素 41ad677d6e9b5780 main#GLSLC@0x5c7390 +ps_main.bin 23 像素 05363af3087acda3 main#GLSLC@0x5c89f0 +ps_main.bin 24 像素 f1f638d1d859cc61 main#GLSLC@0x5ca238 +ps_main.bin 25 像素 059b25011e019a1d main#GLSLC@0x5cbab8 +ps_main.bin 26 像素 97a9a455aef6ec80 main#GLSLC@0x5cd660 +ps_main.bin 27 像素 793af3323431348c main#GLSLC@0x5cee10 +vs_main.bin 6 顶点 414a68b26cddfbab main#GLSLC@0x5d0798 +vs_main.bin 7 顶点 ce4adf04f3fce75d main#GLSLC@0x5d1998 +ps_main.bin 28 像素 eefd20dad5a7b08a main#GLSLC@0x5d3130 +ps_main.bin 29 像素 dd63bff6133d182e main#GLSLC@0x5d4da8 +vs_main.bin 8 顶点 dbde49ce80239494 main#GLSLC@0x5d6c10 +vs_main.bin 9 顶点 a61d46af96db2e03 main#GLSLC@0x5d9578 +vs_main.bin 10 顶点 6a593450bb293e26 main#GLSLC@0x5dc118 +vs_main.bin 11 顶点 d5baa610ddf1d8dc main#GLSLC@0x5decd8 +vs_main.bin 12 顶点 a4ccb368846ce4c6 main#GLSLC@0x5e1b38 +vs_main.bin 13 顶点 28fd3e0769029b79 main#GLSLC@0x5e4808 +vs_main.bin 14 顶点 d0fba5c88a6ad021 main#GLSLC@0x5e7708 +vs_main.bin 15 顶点 94a6dcc6311af787 main#GLSLC@0x5ea630 +vs_main.bin 16 顶点 139b480e75a6c1ec main#GLSLC@0x5ed7f0 +vs_main.bin 17 顶点 8b9ceaa3fd78f94d main#GLSLC@0x5f0578 +vs_main.bin 18 顶点 4ef43b090d4aa792 main#GLSLC@0x5f3428 +vs_main.bin 19 顶点 7f2a6b38040a9d23 main#GLSLC@0x5f6310 +vs_main.bin 20 顶点 d99c2b313152b167 main#GLSLC@0x5f9498 +vs_main.bin 21 顶点 90b21c4032406dcd main#GLSLC@0x5fc540 +vs_main.bin 22 顶点 80e0798b12b23163 main#GLSLC@0x5ff700 +vs_main.bin 23 顶点 656da77697f3452e main#GLSLC@0x602900 +ps_main.bin 30 像素 8dcef7171d920ac0 main#GLSLC@0x605d90 +ps_main.bin 31 像素 43cda48bc6f87b8a main#GLSLC@0x606d48 +ps_main.bin 32 像素 5f77b3888494b325 main#GLSLC@0x607fa8 +ps_main.bin 33 像素 2aca0f51a69203ff main#GLSLC@0x60a208 +ps_main.bin 34 像素 82fb1f67219b9d18 main#GLSLC@0x60b4a0 +ps_main.bin 35 像素 d047388eb7a0d6c0 main#GLSLC@0x60c8c8 +ps_main.bin 36 像素 0ac7bcd086662236 main#GLSLC@0x60ece8 +ps_main.bin 37 像素 4d80a86e517151f5 main#GLSLC@0x610e50 +ps_main.bin 38 像素 68c8cd907318f7d0 main#GLSLC@0x613118 +ps_main.bin 39 像素 06e1ea4094189dba main#GLSLC@0x615518 +ps_main.bin 40 像素 2cfe6de787c2a6bd main#GLSLC@0x617858 +ps_main.bin 41 像素 1e491ec218d183dc main#GLSLC@0x619ce0 +ps_main.bin 42 像素 be370f06c37f487a main#GLSLC@0x61c2a0 +ps_main.bin 43 像素 1835add527114581 main#GLSLC@0x61e520 +ps_main.bin 44 像素 50d5a361e2cbcf68 main#GLSLC@0x6208f8 +ps_main.bin 45 像素 8c7ecf1867ee7745 main#GLSLC@0x622f18 +ps_main.bin 46 像素 16973133690146e6 main#GLSLC@0x625370 +ps_main.bin 47 像素 70d126e8a37083f7 main#GLSLC@0x627a10 +ps_main.bin 48 像素 9fa89f6cffd53cf1 main#GLSLC@0x62a1f0 +ps_main.bin 49 像素 f0f16afbe0848533 main#GLSLC@0x62b848 +ps_main.bin 50 像素 19a5120409cace01 main#GLSLC@0x62cf50 +ps_main.bin 51 像素 6d6cd4fd6f7e4467 main#GLSLC@0x62f650 +ps_main.bin 52 像素 61a76342dd42bacf main#GLSLC@0x630e78 +ps_main.bin 53 像素 de02569524ff804c main#GLSLC@0x632758 +ps_main.bin 54 像素 a7d3eb68c93e77c7 main#GLSLC@0x635030 +ps_main.bin 55 像素 7fe488f38f8c555e main#GLSLC@0x637680 +ps_main.bin 56 像素 8b917f45ef866149 main#GLSLC@0x639d80 +ps_main.bin 57 像素 3b73581d86c205fa main#GLSLC@0x63c5a0 +ps_main.bin 58 像素 be9878db26b9f59f main#GLSLC@0x63edc8 +ps_main.bin 59 像素 c3bcdbfaff1f821e main#GLSLC@0x6416a0 +ps_main.bin 60 像素 3e9592ffd8ab389f main#GLSLC@0x6440c0 +ps_main.bin 61 像素 0ddfcfcdcc3ddf9e main#GLSLC@0x6468b8 +ps_main.bin 62 像素 542af76c0f7f6331 main#GLSLC@0x649230 +ps_main.bin 63 像素 1549752359be9243 main#GLSLC@0x64bce8 +ps_main.bin 64 像素 fc4fb54243dc47f2 main#GLSLC@0x64e6b0 +ps_main.bin 65 像素 ac3ad93eccfd6081 main#GLSLC@0x651200 +vs_main.bin 24 顶点 f2e3868276e465a7 main#GLSLC@0x653ec8 +vs_main.bin 25 顶点 00b73c6360ebc8a3 main#GLSLC@0x655660 +ps_main.bin 66 像素 a720c8bf0b1eb77d main#GLSLC@0x657030 +vs_main.bin 26 顶点 60d4c4ad76e48d5a main#GLSLC@0x658628 +ps_main.bin 67 像素 b4a331141ff0f388 main#GLSLC@0x659b88 +ps_main.bin 68 像素 53b13bbb24694389 main#GLSLC@0x65b280 +ps_main.bin 69 像素 3d38b9965c284bdc main#GLSLC@0x660d10 +ps_main.bin 70 像素 eff059cf1c0ae444 main#GLSLC@0x668898 +ps_main.bin 71 像素 f15698017484e856 main#GLSLC@0x66e8f0 +ps_main.bin 72 像素 e1cc9c2215442694 main#GLSLC@0x670e40 +ps_main.bin 73 像素 e8d32c7319ab0540 main#GLSLC@0x678ef0 +ps_main.bin 74 像素 ffca0d0601277255 main#GLSLC@0x67f3b0 +ps_main.bin 75 像素 781fe6d04a9719bd main#GLSLC@0x685498 +ps_main.bin 76 像素 2668ed65546dee7e main#GLSLC@0x690ec0 +ps_main.bin 77 像素 9215f34ca2a2848d main#GLSLC@0x69b900 +ps_main.bin 78 像素 5cb92d4243b69b7a main#GLSLC@0x69cb40 +ps_main.bin 79 像素 f768872a27de3aaf main#GLSLC@0x69dec8 +ps_main.bin 80 像素 e1ce09cf71536ec0 main#GLSLC@0x6a01c8 +ps_main.bin 81 像素 01f059409b9f67a6 main#GLSLC@0x6a3638 +ps_main.bin 82 像素 5a7902803b87d628 main#GLSLC@0x6a4ff8 +ps_main.bin 83 像素 a3ebc4e92e1c24fd main#GLSLC@0x6a6960 +ps_main.bin 84 像素 fd43d4110e9a4b93 main#GLSLC@0x6a9e00 +ps_main.bin 85 像素 db30991486672cbe main#GLSLC@0x6ab7e8 +ps_main.bin 86 像素 1016abf3a39a6777 main#GLSLC@0x6ae1d8 +vs_main.bin 27 顶点 618e53bc4f5c3ba8 main#GLSLC@0x6af7c8 +ps_main.bin 87 像素 8218d54372531eab main#GLSLC@0x6b07f0 +ps_main.bin 88 像素 6441a8afa1af6d3b main#GLSLC@0x6b1b50 +ps_main.bin 89 像素 6c2460e94a3346f3 main#GLSLC@0x6b2fc8 +ps_main.bin 90 像素 1aad34fa737726f7 main#GLSLC@0x6b42b0 +vs_main.bin 28 顶点 e3558a505e21b602 main#GLSLC@0x6b5558 +vs_main.bin 29 顶点 3146131971489b3b main#GLSLC@0x6b67b0 +ps_main.bin 91 像素 9a7fe795da700655 main#GLSLC@0x6b78b0 +ps_main.bin 92 像素 160678fe1c06cefe main#GLSLC@0x6ba1b8 +vs_main.bin 30 顶点 d724f07bcc0b3ea2 main#GLSLC@0x6bc490 +vs_main.bin 31 顶点 a122a0d6250751d5 main#GLSLC@0x6bead0 +vs_main.bin 32 顶点 7cde12631edfadde main#GLSLC@0x6c1778 +vs_main.bin 33 顶点 d2933506ff29ef9c main#GLSLC@0x6c4788 +vs_main.bin 34 顶点 3caec795d3d3bff1 main#GLSLC@0x6c78d0 +vs_main.bin 35 顶点 3d9cd4be9f3b5a2e main#GLSLC@0x6ca020 +vs_main.bin 36 顶点 553b071e8012918d main#GLSLC@0x6cca90 +vs_main.bin 37 顶点 cac8c8837d11dc19 main#GLSLC@0x6cfd88 +vs_main.bin 38 顶点 ed092f9a172af2b6 main#GLSLC@0x6d32e8 +vs_main.bin 39 顶点 717ddb7e3e3ca319 main#GLSLC@0x6d6a88 +vs_main.bin 40 顶点 c8ed61edcc059ae4 main#GLSLC@0x6d9278 +vs_main.bin 41 顶点 005dc0a0507ca0c0 main#GLSLC@0x6dc0c8 +vs_main.bin 42 顶点 213868c7c4d50ca9 main#GLSLC@0x6df180 +vs_main.bin 43 顶点 2b4eee86e82db581 main#GLSLC@0x6e2470 +vs_main.bin 44 顶点 27857033dddfde91 main#GLSLC@0x6e58e8 +vs_main.bin 45 顶点 c9491774a1fa7c0e main#GLSLC@0x6e9190 +vs_main.bin 46 顶点 955fecb9caa5c59a main#GLSLC@0x6ecb68 +vs_main.bin 47 顶点 0732be065f1ccb64 main#GLSLC@0x6f1048 +vs_main.bin 48 顶点 43dce950e3ce9e00 main#GLSLC@0x6f4c70 +vs_main.bin 49 顶点 7f16b8534971d61e main#GLSLC@0x6f8ba8 +vs_main.bin 50 顶点 cb81f6d218f3babc main#GLSLC@0x6fcc20 +vs_main.bin 51 顶点 4b33aac14cc96596 main#GLSLC@0x701890 +vs_main.bin 52 顶点 6b07b70e665775fd main#GLSLC@0x705298 +vs_main.bin 53 顶点 b9a682c336cef810 main#GLSLC@0x708fb8 +vs_main.bin 54 顶点 83badef66a026c4e main#GLSLC@0x70ce18 +vs_main.bin 55 顶点 4f5acb13923bf8d0 main#GLSLC@0x711870 +vs_main.bin 56 顶点 b46878c348125e2c main#GLSLC@0x713ee8 +vs_main.bin 57 顶点 669e8a6b312d7da5 main#GLSLC@0x716bb0 +vs_main.bin 58 顶点 b052e30fd03229c8 main#GLSLC@0x719bf0 +vs_main.bin 59 顶点 a8e9b32f805036e5 main#GLSLC@0x71cd68 +vs_main.bin 60 顶点 403a5758e49fbf9e main#GLSLC@0x71f4d8 +vs_main.bin 61 顶点 a0c08c3dcd1610e9 main#GLSLC@0x721f78 +vs_main.bin 62 顶点 87a0b64f386e2f48 main#GLSLC@0x725298 +vs_main.bin 63 顶点 3f19991694c1b137 main#GLSLC@0x728830 +vs_main.bin 64 顶点 4bf525a99134ebcf main#GLSLC@0x72c000 +vs_main.bin 65 顶点 27c1e9abb64a354d main#GLSLC@0x72e820 +vs_main.bin 66 顶点 34471f37fab5afec main#GLSLC@0x731698 +vs_main.bin 67 顶点 05521a638f5a68e0 main#GLSLC@0x734788 +vs_main.bin 68 顶点 3490021606823f09 main#GLSLC@0x737aa8 +vs_main.bin 69 顶点 89891eabe54feb48 main#GLSLC@0x73af40 +vs_main.bin 70 顶点 82b68976b8f3d729 main#GLSLC@0x73e820 +vs_main.bin 71 顶点 5482dc93a27cf699 main#GLSLC@0x742230 +vs_main.bin 72 顶点 b10316f74f5492e2 main#GLSLC@0x746840 +vs_main.bin 73 顶点 b56e967305958a3c main#GLSLC@0x74a488 +vs_main.bin 74 顶点 5a40c4889fe10994 main#GLSLC@0x74e3f8 +vs_main.bin 75 顶点 75f1ce743e36beb0 main#GLSLC@0x7525a8 +vs_main.bin 76 顶点 7e051b2bd08543ee main#GLSLC@0x757250 +vs_main.bin 77 顶点 e9d8fba810148eaa main#GLSLC@0x75ac80 +vs_main.bin 78 顶点 0a5a4fb79479e894 main#GLSLC@0x75e9d0 +vs_main.bin 79 顶点 fad8e21db86e0da6 main#GLSLC@0x762960 +vs_main.bin 80 顶点 c10272c5fe59f478 main#GLSLC@0x7673e8 +vs_main.bin 81 顶点 a0b16dc3fd19ec28 main#GLSLC@0x76a6d8 +vs_main.bin 82 顶点 06514574c231f3dc main#GLSLC@0x76dcf8 +vs_main.bin 83 顶点 20a2ce4b867bf227 main#GLSLC@0x771540 +vs_main.bin 84 顶点 6c5a06abbf8385ac main#GLSLC@0x7753f0 +vs_main.bin 85 顶点 6fed094f83ec834c main#GLSLC@0x779508 +vs_main.bin 86 顶点 e4ec47e63db966e8 main#GLSLC@0x77d850 +vs_main.bin 87 顶点 beaafe2c6e95c93c main#GLSLC@0x7815f0 +vs_main.bin 88 顶点 825ad973231b45a7 main#GLSLC@0x7859f8 +vs_main.bin 89 顶点 b336d3c4c49dcf3f main#GLSLC@0x78a168 +vs_main.bin 90 顶点 4a1794705ca53384 main#GLSLC@0x78ea08 +vs_main.bin 91 顶点 daa09ca1aefc87da main#GLSLC@0x791db0 +vs_main.bin 92 顶点 0d4d61c0a868f9a7 main#GLSLC@0x795580 +vs_main.bin 93 顶点 9b588c01a4a0552c main#GLSLC@0x798e70 +vs_main.bin 94 顶点 954f082c4e31acb4 main#GLSLC@0x79cec8 +vs_main.bin 95 顶点 46780f757806d67d main#GLSLC@0x7a1188 +vs_main.bin 96 顶点 952ecd6fcb3885d8 main#GLSLC@0x7a5578 +vs_main.bin 97 顶点 1787a4f06b8a0102 main#GLSLC@0x7a91a8 +vs_main.bin 98 顶点 53bac653c73c418a main#GLSLC@0x7ad438 +vs_main.bin 99 顶点 b906d3a4a34ee750 main#GLSLC@0x7b1938 +vs_main.bin 100 顶点 c25d49635e691ab8 main#GLSLC@0x7b6068 +vs_main.bin 101 顶点 051881873373d1da main#GLSLC@0x7ba330 +vs_main.bin 102 顶点 4b148d6596f3d23a main#GLSLC@0x7bec58 +vs_main.bin 103 顶点 a05e50da03818a3e main#GLSLC@0x7c37e0 +vs_main.bin 104 顶点 de82d5f529d3850f main#GLSLC@0x7c85a0 +vs_main.bin 105 顶点 4eac8f4c27d2c3e8 main#GLSLC@0x7cc650 +vs_main.bin 106 顶点 456f25b32b3251a5 main#GLSLC@0x7d0d60 +vs_main.bin 107 顶点 d66b2bf0ca4d8d99 main#GLSLC@0x7d56d0 +vs_main.bin 108 顶点 e4682699786919f3 main#GLSLC@0x7da278 +vs_main.bin 109 顶点 3d9d2c26e744da49 main#GLSLC@0x7dd588 +vs_main.bin 110 顶点 5d83bf515bb746c2 main#GLSLC@0x7e0bd8 +vs_main.bin 111 顶点 8682158094645e50 main#GLSLC@0x7e4450 +vs_main.bin 112 顶点 0195aaa2774f7f94 main#GLSLC@0x7e8320 +vs_main.bin 113 顶点 e2dbc95f27854c48 main#GLSLC@0x7ec468 +vs_main.bin 114 顶点 f406c638e6ce73aa main#GLSLC@0x7f07e0 +vs_main.bin 115 顶点 7c073b91c37e838d main#GLSLC@0x7f45b8 +vs_main.bin 116 顶点 c8ef856680691deb main#GLSLC@0x7f89e0 +vs_main.bin 117 顶点 a865b4f477c1d6b8 main#GLSLC@0x7fd188 +vs_main.bin 118 顶点 02ca876f24e6ee00 main#GLSLC@0x801a60 +vs_main.bin 119 顶点 afb7a89e47c0c6af main#GLSLC@0x804e28 +vs_main.bin 120 顶点 b97c703a0c8f34b7 main#GLSLC@0x808628 +vs_main.bin 121 顶点 6c477ce4fb255429 main#GLSLC@0x80bf48 +vs_main.bin 122 顶点 0ffefa790db21654 main#GLSLC@0x80ffc0 +vs_main.bin 123 顶点 5020390f0855186a main#GLSLC@0x8142b0 +vs_main.bin 124 顶点 51ac57eb5f67fedc main#GLSLC@0x8187d8 +vs_main.bin 125 顶点 c57463065d7fc12a main#GLSLC@0x81c438 +vs_main.bin 126 顶点 f1fa64e81d7f9379 main#GLSLC@0x8206f0 +vs_main.bin 127 顶点 c9ef0fcdc619f8a8 main#GLSLC@0x824c20 +vs_main.bin 128 顶点 cee7c1f5d45decbe main#GLSLC@0x829380 +vs_main.bin 129 顶点 8b27c197c8a99d04 main#GLSLC@0x82d680 +vs_main.bin 130 顶点 fa918d47492767ec main#GLSLC@0x831fd0 +vs_main.bin 131 顶点 de9fdd1491f8f1af main#GLSLC@0x836b90 +vs_main.bin 132 顶点 713048c1a4d9c9b6 main#GLSLC@0x83b988 +vs_main.bin 133 顶点 e16db18046c16690 main#GLSLC@0x83fa68 +vs_main.bin 134 顶点 d555fca572db63c0 main#GLSLC@0x844198 +vs_main.bin 135 顶点 9d4cb74596316043 main#GLSLC@0x848c38 +ps_main.bin 93 像素 fecb3b4884cb4923 main#GLSLC@0x84e9c0 +ps_main.bin 94 像素 9e641b1a149809b0 main#GLSLC@0x84fd50 +ps_main.bin 95 像素 b94977a9fd877777 main#GLSLC@0x852480 +ps_main.bin 96 像素 c78266d47ce4a6e6 main#GLSLC@0x8569a8 +ps_main.bin 97 像素 5c7688eb040aa639 main#GLSLC@0x858790 +ps_main.bin 98 像素 648171746e615511 main#GLSLC@0x85b918 +ps_main.bin 99 像素 b28657fe34217b46 main#GLSLC@0x85ffe0 +ps_main.bin 100 像素 83ee75623aa81e9a main#GLSLC@0x861530 +ps_main.bin 101 像素 30de397838b07ff6 main#GLSLC@0x863e28 +ps_main.bin 102 像素 8ec24135955a6184 main#GLSLC@0x8686d8 +ps_main.bin 103 像素 03fb9c7ed541610a main#GLSLC@0x86a680 +ps_main.bin 104 像素 9d06a897896c4406 main#GLSLC@0x86d9d0 +ps_main.bin 105 像素 96c1cc6962e78cec main#GLSLC@0x870ed0 +ps_main.bin 106 像素 f5650be1db2a97f9 main#GLSLC@0x873410 +ps_main.bin 107 像素 a161aa477f9b5ff4 main#GLSLC@0x875b08 +ps_main.bin 108 像素 2a7f04b0d3d60ce2 main#GLSLC@0x8785e8 +ps_main.bin 109 像素 3fc5f98e9bb2d280 main#GLSLC@0x87b378 +ps_main.bin 110 像素 d3988c83c2ed6522 main#GLSLC@0x87e328 +ps_main.bin 111 像素 777f203ce7bb39c2 main#GLSLC@0x881580 +ps_main.bin 112 像素 a2bff07f519914f0 main#GLSLC@0x884d68 +ps_main.bin 113 像素 1ed6df817c71028b main#GLSLC@0x887468 +ps_main.bin 114 像素 ca0ff630837275f6 main#GLSLC@0x889e20 +ps_main.bin 115 像素 98bebb02dc6760d4 main#GLSLC@0x88cac8 +ps_main.bin 116 像素 7590b8fad1f79f5e main#GLSLC@0x88fa18 +ps_main.bin 117 像素 c1da6fb76bbb945a main#GLSLC@0x892b88 +ps_main.bin 118 像素 65a051853eeba404 main#GLSLC@0x895fa0 +ps_main.bin 119 像素 b986067db0478ca1 main#GLSLC@0x8be268 +ps_main.bin 120 像素 609279b3b1e48f3e main#GLSLC@0x8c2480 +ps_main.bin 121 像素 3195bdb152b7bb07 main#GLSLC@0x8cbf30 +ps_main.bin 122 像素 d81999203ca8c2ba main#GLSLC@0x8ce6c8 +ps_main.bin 123 像素 0289d1c53871cfd8 main#GLSLC@0x8d0af8 +ps_main.bin 124 像素 23356e4eb828fb83 main#GLSLC@0x8d30c8 +ps_main.bin 125 像素 7e97419919fa57f4 main#GLSLC@0x8d5aa0 +ps_main.bin 126 像素 011944e3919d9086 main#GLSLC@0x8d8928 +ps_main.bin 127 像素 51c19c739e594d19 main#GLSLC@0x8dbd40 +ps_main.bin 128 像素 3c57cac8dab4f30d main#GLSLC@0x8df308 +ps_main.bin 129 像素 a80161416ae4089d main#GLSLC@0x8e1900 +ps_main.bin 130 像素 212650750751cbad main#GLSLC@0x8e4090 +ps_main.bin 131 像素 ebbea3767437f264 main#GLSLC@0x8e6c28 +ps_main.bin 132 像素 e8142918c4ee0963 main#GLSLC@0x8e9c78 +ps_main.bin 133 像素 2a2ab3f137f2f816 main#GLSLC@0x8ed258 +ps_main.bin 134 像素 96c36820dd97d0dd main#GLSLC@0x8f09e0 +ps_main.bin 135 像素 e94a8606fdcc3cb8 main#GLSLC@0x8f3140 +ps_main.bin 136 像素 7eefb9d8881d27a4 main#GLSLC@0x8f5a58 +ps_main.bin 137 像素 a0a7b4504253c250 main#GLSLC@0x8f8758 +ps_main.bin 138 像素 adc7a33f678ae433 main#GLSLC@0x8fb608 +ps_main.bin 139 像素 d4fb7b2f1bfaafeb main#GLSLC@0x8fe7d8 +ps_main.bin 140 像素 59439742561f60a1 main#GLSLC@0x901c50 +ps_main.bin 141 像素 182310bcbaf83a29 main#GLSLC@0x905658 +ps_main.bin 142 像素 db30bc8dc757fc03 main#GLSLC@0x907f78 +ps_main.bin 143 像素 94493455bc6c52af main#GLSLC@0x90aa50 +ps_main.bin 144 像素 d623167f7aaff236 main#GLSLC@0x90d920 +ps_main.bin 145 像素 339f73ba6e5b41f0 main#GLSLC@0x910990 +ps_main.bin 146 像素 b6af8657f766f967 main#GLSLC@0x913d20 +ps_main.bin 147 像素 6c3b4678d1b4fbf0 main#GLSLC@0x917358 +ps_main.bin 148 像素 2923044e32509c5d main#GLSLC@0x94bed0 +ps_main.bin 149 像素 79da88ce1d76a5d2 main#GLSLC@0x950660 +ps_main.bin 150 像素 5fb685c49afc65d1 main#GLSLC@0x95f398 +ps_main.bin 151 像素 b5f13902c9ece469 main#GLSLC@0x9643e8 +vs_main.bin 136 顶点 146a3716c4f9826b main#GLSLC@0x966e28 +vs_main.bin 137 顶点 c1b170b990c90ae3 main#GLSLC@0x968f10 +vs_main.bin 138 顶点 2c5f640d1306cd67 main#GLSLC@0x96b328 +vs_main.bin 139 顶点 e1871737507bc29c main#GLSLC@0x96dfc8 +vs_main.bin 140 顶点 5e8f30c04b6d74d8 main#GLSLC@0x970270 +vs_main.bin 141 顶点 e1cb7c2d0d958dd2 main#GLSLC@0x972838 +vs_main.bin 142 顶点 dfb5058a40908e39 main#GLSLC@0x976978 +vs_main.bin 143 顶点 2240a91ab3ac898b main#GLSLC@0x97ad20 +vs_main.bin 144 顶点 890671ec1c58d860 main#GLSLC@0x97f4f8 +vs_main.bin 145 顶点 285bd23319484bb0 main#GLSLC@0x983f28 +vs_main.bin 146 顶点 ca41a02a5ff34d57 main#GLSLC@0x9884e8 +vs_main.bin 147 顶点 cb474a72243bae59 main#GLSLC@0x98cd08 +vs_main.bin 148 顶点 01f5cb5257d9f97b main#GLSLC@0x98ee18 +vs_main.bin 149 顶点 e22932bbf66f6aeb main#GLSLC@0x991260 +vs_main.bin 150 顶点 59672edd5f258046 main#GLSLC@0x993f30 +vs_main.bin 151 顶点 c1c5ec50ec09752f main#GLSLC@0x9961f8 +vs_main.bin 152 顶点 aea4a1ec55b1f487 main#GLSLC@0x9987f0 +vs_main.bin 153 顶点 9e9a4cbbce6b5989 main#GLSLC@0x99c950 +vs_main.bin 154 顶点 9fa9e192bf0d940c main#GLSLC@0x9a0d30 +vs_main.bin 155 顶点 2484e5994cef257c main#GLSLC@0x9a5530 +vs_main.bin 156 顶点 be7228b37b341ece main#GLSLC@0x9a9f98 +vs_main.bin 157 顶点 a62f428b10a4dd40 main#GLSLC@0x9ae578 +vs_main.bin 158 顶点 fa4c270c5660e4d8 main#GLSLC@0x9b2dc8 +vs_main.bin 159 顶点 6a502c6cd5bfabf4 main#GLSLC@0x9b6620 +vs_main.bin 160 顶点 28a54478ae1ca813 main#GLSLC@0x9ba1a0 +vs_main.bin 161 顶点 881f2b21dade164b main#GLSLC@0x9bd870 +vs_main.bin 162 顶点 d28272643d6906ed main#GLSLC@0x9c1278 +vs_main.bin 163 顶点 ac35a63615eb5fe6 main#GLSLC@0x9c4ff8 +vs_main.bin 164 顶点 4a7231ce50ac4458 main#GLSLC@0x9c9090 +vs_main.bin 165 顶点 95405b8f9257e987 main#GLSLC@0x9ccbf0 +vs_main.bin 166 顶点 5c44c76019a7d1ec main#GLSLC@0x9d0a70 +vs_main.bin 167 顶点 ebec8e0cf92da97c main#GLSLC@0x9d42e8 +vs_main.bin 168 顶点 6eef6583df6b6c50 main#GLSLC@0x9d7f98 +vs_main.bin 169 顶点 42c908bcacacab78 main#GLSLC@0x9db688 +vs_main.bin 170 顶点 fc18ecbf4168f637 main#GLSLC@0x9df0c0 +vs_main.bin 171 顶点 c7d10687f1fec5e6 main#GLSLC@0x9e2e60 +vs_main.bin 172 顶点 95fc0eecb5d5694c main#GLSLC@0x9e6f28 +vs_main.bin 173 顶点 3a19ad025b16f11d main#GLSLC@0x9eaab0 +ps_main.bin 152 像素 ddb159a9644f6486 main#GLSLC@0x9ee960 +ps_main.bin 153 像素 77e107589a6aec66 main#GLSLC@0x9f1ea0 +ps_main.bin 154 像素 615cff6a5adcc5eb main#GLSLC@0xa04df0 +ps_main.bin 155 像素 dc1913b7423b2269 main#GLSLC@0xa07168 +ps_main.bin 156 像素 4687812fcc097f4a main#GLSLC@0xa09ce0 +ps_main.bin 157 像素 abd2312f21636325 main#GLSLC@0xa0cd10 +ps_main.bin 158 像素 ae1aeb9889c29fa4 main#GLSLC@0xa0fa48 +ps_main.bin 159 像素 3760c0afee65d856 main#GLSLC@0xa12c38 +ps_main.bin 160 像素 56e5dbc3e071f016 main#GLSLC@0xa16398 +vs_main.bin 174 顶点 10839897b2ae647d main#GLSLC@0xa19d50 +vs_main.bin 175 顶点 613211a136a1cf52 main#GLSLC@0xa1b6a0 +vs_main.bin 176 顶点 1a28da408ff83ff4 main#GLSLC@0xa1d248 +vs_main.bin 177 顶点 499bee5c432ebce8 main#GLSLC@0xa1f110 +vs_main.bin 178 顶点 ec503adf8210c554 main#GLSLC@0xa22460 +vs_main.bin 179 顶点 58258b4cece0dd8a main#GLSLC@0xa259e0 +vs_main.bin 180 顶点 366a9a9aab88c22e main#GLSLC@0xa29280 +vs_main.bin 181 顶点 555d1d604a71abce main#GLSLC@0xa2b4a0 +vs_main.bin 182 顶点 4dd9c2e6a40c0394 main#GLSLC@0xa2d8e0 +vs_main.bin 183 顶点 00f37717bf4bac4f main#GLSLC@0xa30048 +vs_main.bin 184 顶点 9a909735d9ba1aea main#GLSLC@0xa33398 +vs_main.bin 185 顶点 a890c3a180606eb8 main#GLSLC@0xa36910 +vs_main.bin 186 顶点 72aed5a33cd82126 main#GLSLC@0xa3a2b0 +vs_main.bin 187 顶点 ba335dc18c2f3e30 main#GLSLC@0xa3c360 +vs_main.bin 188 顶点 9451af39e4a54358 main#GLSLC@0xa3e668 +vs_main.bin 189 顶点 876c17d32e40fcc0 main#GLSLC@0xa40d98 +vs_main.bin 190 顶点 5d71ed0f2686224f main#GLSLC@0xa44260 +vs_main.bin 191 顶点 a296958ec33de2c6 main#GLSLC@0xa47948 +vs_main.bin 192 顶点 0803c807f16ba3d1 main#GLSLC@0xa4b358 +vs_main.bin 193 顶点 99cb04ae8d0485c3 main#GLSLC@0xa4d618 +vs_main.bin 194 顶点 e5c827ed6dbac282 main#GLSLC@0xa4faf8 +vs_main.bin 195 顶点 910e1e3beb86a3e2 main#GLSLC@0xa522f8 +vs_main.bin 196 顶点 b983170ff0fbf0aa main#GLSLC@0xa557d8 +vs_main.bin 197 顶点 78878b5627402d1b main#GLSLC@0xa58ed8 +vs_main.bin 198 顶点 3f689fa3167ede95 main#GLSLC@0xa5c908 +vs_main.bin 199 顶点 dfc13dee1219be5d main#GLSLC@0xa5ea08 +vs_main.bin 200 顶点 6d7cf4c78dbabd40 main#GLSLC@0xa60d60 +vs_main.bin 201 顶点 a2d43f1cbc503e1f main#GLSLC@0xa634d8 +vs_main.bin 202 顶点 3e8c65894c62edcf main#GLSLC@0xa669e8 +vs_main.bin 203 顶点 749e645455b4d6f9 main#GLSLC@0xa6a128 +vs_main.bin 204 顶点 862f4e924ef2931d main#GLSLC@0xa6db80 +vs_main.bin 205 顶点 88d767d663df8369 main#GLSLC@0xa6fe88 +vs_main.bin 206 顶点 968c43b11286bb9f main#GLSLC@0xa723b0 +vs_main.bin 207 顶点 a6cbedc8dae81d3b main#GLSLC@0xa74bf0 +vs_main.bin 208 顶点 dbe59bbc08be74b0 main#GLSLC@0xa78120 +vs_main.bin 209 顶点 6098ad0051905c99 main#GLSLC@0xa7b860 +vs_main.bin 210 顶点 9e8d0f978c50f5ec main#GLSLC@0xa7f2d0 +vs_main.bin 211 顶点 330a677ae5094609 main#GLSLC@0xa81d20 +vs_main.bin 212 顶点 9a1a3516b416471a main#GLSLC@0xa849c8 +vs_main.bin 213 顶点 80b8c29864a6abee main#GLSLC@0xa87a98 +vs_main.bin 214 顶点 f1d399cb6802b1a4 main#GLSLC@0xa8b270 +vs_main.bin 215 顶点 201b4d77416f979a main#GLSLC@0xa8ed60 +vs_main.bin 216 顶点 98d5b4f928be9df8 main#GLSLC@0xa922a8 +vs_main.bin 217 顶点 8393fa9fc9b1a637 main#GLSLC@0xa95b10 +vs_main.bin 218 顶点 6b400d24e283fb89 main#GLSLC@0xa992e0 +vs_main.bin 219 顶点 683bb6316a7ae2aa main#GLSLC@0xa9cdd0 +vs_main.bin 220 顶点 c6e74d5b96b2a9b9 main#GLSLC@0xaa02d8 +vs_main.bin 221 顶点 5b83ea06a6982c94 main#GLSLC@0xaa3b08 +vs_main.bin 222 顶点 420ed53c9f81b96b main#GLSLC@0xaa7340 +vs_main.bin 223 顶点 6251be1afb429c0e main#GLSLC@0xaaaea0 +vs_main.bin 224 顶点 f8cf171ceb721fe2 main#GLSLC@0xaae580 +vs_main.bin 225 顶点 4b7f451b92e9d9fa main#GLSLC@0xab1f88 +vs_main.bin 226 顶点 28f1d8f6fd56635e main#GLSLC@0xab57e0 +vs_main.bin 227 顶点 e1e9a5ddf3a54bfa main#GLSLC@0xab9460 +vs_main.bin 228 顶点 01b67911b0eba290 main#GLSLC@0xabc9a8 +vs_main.bin 229 顶点 a621f8c9d891e2c7 main#GLSLC@0xac0220 +vs_main.bin 230 顶点 d0747b5db7010662 main#GLSLC@0xac3aa0 +vs_main.bin 231 顶点 5b6a1b16861461f7 main#GLSLC@0xac7640 +vs_main.bin 232 顶点 6a24f57943c4ec67 main#GLSLC@0xacac58 +vs_main.bin 233 顶点 9664bd8e4a677425 main#GLSLC@0xace6a0 +vs_main.bin 234 顶点 7a537520dd53b0e4 main#GLSLC@0xad1f28 +ps_main.bin 161 像素 b9357b88726f2f11 main#GLSLC@0xad6c98 +ps_main.bin 162 像素 688d2affa94b39e2 main#GLSLC@0xad8660 +ps_main.bin 163 像素 0a74604275389143 main#GLSLC@0xada180 +ps_main.bin 164 像素 e92f9e4f8e1e881d main#GLSLC@0xadb3a8 +ps_main.bin 165 像素 706b36197ad381dd main#GLSLC@0xadc738 +ps_main.bin 166 像素 ebcd512464db79f5 main#GLSLC@0xade2a8 +ps_main.bin 167 像素 4612a2438a1ae4d2 main#GLSLC@0xae25e8 +ps_main.bin 168 像素 34d961561ff1327f main#GLSLC@0xae43a8 +ps_main.bin 169 像素 c5c5150ac8e5ddc2 main#GLSLC@0xae5778 +ps_main.bin 170 像素 568ff969a753e58c main#GLSLC@0xae6cb0 +ps_main.bin 171 像素 ed9d0a999217d361 main#GLSLC@0xae8ac8 +ps_main.bin 172 像素 87812d8e3fea6240 main#GLSLC@0xaeaa48 +ps_main.bin 173 像素 ffa69cca312c08b8 main#GLSLC@0xaecc48 +ps_main.bin 174 像素 0ef727c7d38740c5 main#GLSLC@0xaef108 +ps_main.bin 175 像素 6745353ff6a61b06 main#GLSLC@0xaf2008 +ps_main.bin 176 像素 8d7b8aac7c3e458c main#GLSLC@0xaf4518 +ps_main.bin 177 像素 69e6dcdb6d8d00e5 main#GLSLC@0xaf6b88 +ps_main.bin 178 像素 ea69c6fba6becbe8 main#GLSLC@0xaf9ad8 +ps_main.bin 179 像素 ad6eb2b7ea4f69b5 main#GLSLC@0xafcb90 +ps_main.bin 180 像素 c748e00e3f098ec3 main#GLSLC@0xaff048 +ps_main.bin 181 像素 4064c152d3890cac main#GLSLC@0xb016c0 +ps_main.bin 182 像素 1420b58f7143823a main#GLSLC@0xb04500 +ps_main.bin 183 像素 5c3c40c319b617fe main#GLSLC@0xb075b0 +ps_main.bin 184 像素 633e60fdef1e18ef main#GLSLC@0xb09dd0 +ps_main.bin 185 像素 fd2bd113912d8011 main#GLSLC@0xb0cec8 +ps_main.bin 186 像素 f60f343439ab79b1 main#GLSLC@0xb10128 +ps_main.bin 187 像素 0bf3b0c8200753fc main#GLSLC@0xb12298 +ps_main.bin 188 像素 e3a5a16c121eafd4 main#GLSLC@0xb146c8 +ps_main.bin 189 像素 54b5c405d9816d26 main#GLSLC@0xb17428 +ps_main.bin 190 像素 946a7c2c6d2bc4f0 main#GLSLC@0xb198a0 +ps_main.bin 191 像素 8038764dd5c8c75b main#GLSLC@0xb1be70 +ps_main.bin 192 像素 ef300dd784340c34 main#GLSLC@0xb1ec30 +ps_main.bin 193 像素 3949ab9849265857 main#GLSLC@0xb21c48 +ps_main.bin 194 像素 15f653b15298ad2d main#GLSLC@0xb24060 +ps_main.bin 195 像素 a6c70a8b3bec32a8 main#GLSLC@0xb26638 +ps_main.bin 196 像素 4278d7860b7f1472 main#GLSLC@0xb293f0 +ps_main.bin 197 像素 800dd083bb92a94a main#GLSLC@0xb2c400 +ps_main.bin 198 像素 0093b425acd3f401 main#GLSLC@0xb2eb78 +ps_main.bin 199 像素 9644447135c794af main#GLSLC@0xb31bd0 +ps_main.bin 200 像素 8698df5dfeb8d544 main#GLSLC@0xb34d90 +ps_main.bin 201 像素 318caf466b19bd3e main#GLSLC@0xb371b8 +ps_main.bin 202 像素 d7e06ed10f6aeffa main#GLSLC@0xb39890 +ps_main.bin 203 像素 573f306c6d82a2a7 main#GLSLC@0xb3c8a8 +ps_main.bin 204 像素 5630fc0cbd15d4ed main#GLSLC@0xb3efd0 +ps_main.bin 205 像素 17eca5cde94f7aa0 main#GLSLC@0xb41858 +ps_main.bin 206 像素 21448c3f9d89f216 main#GLSLC@0xb448c0 +ps_main.bin 207 像素 1f4a63b490f62a34 main#GLSLC@0xb47b90 +ps_main.bin 208 像素 7b39574d6126ebab main#GLSLC@0xb4a260 +ps_main.bin 209 像素 c5a1db84cc484193 main#GLSLC@0xb4caf0 +ps_main.bin 210 像素 e45943977c868cbf main#GLSLC@0xb4fb48 +ps_main.bin 211 像素 f86142aa00318401 main#GLSLC@0xb52e08 +ps_main.bin 212 像素 b1fc882e248dd361 main#GLSLC@0xb55838 +ps_main.bin 213 像素 596d41d48b634aff main#GLSLC@0xb58b48 +ps_main.bin 214 像素 dacb1a0303c7400b main#GLSLC@0xb5bfc0 +ps_main.bin 215 像素 be66c83c7a8b1953 main#GLSLC@0xb5ebb0 +ps_main.bin 216 像素 6add555f75b69aaf main#GLSLC@0xb61ac0 +ps_main.bin 217 像素 80a7cc64abb3d449 main#GLSLC@0xb64b38 +ps_main.bin 218 像素 0c07df41c7981b43 main#GLSLC@0xb678c0 +ps_main.bin 219 像素 71fd0ce3ce52829d main#GLSLC@0xb6a7a0 +ps_main.bin 220 像素 f5036d895bf95221 main#GLSLC@0xb6d860 +ps_main.bin 221 像素 eb6166b61a28b189 main#GLSLC@0xb70a88 +ps_main.bin 222 像素 72093b96f7b0c189 main#GLSLC@0xb73800 +ps_main.bin 223 像素 7204b5c9cdf6a2d9 main#GLSLC@0xb766e0 +ps_main.bin 224 像素 8378cc242b355150 main#GLSLC@0xb79790 +ps_main.bin 225 像素 697e3ab9dcf2464f main#GLSLC@0xb7c9a8 +ps_main.bin 226 像素 8c3cdf4d7980c929 main#GLSLC@0xb7f8c0 +ps_main.bin 227 像素 b9aef45ceed3a383 main#GLSLC@0xb82a40 +ps_main.bin 228 像素 d1bc0adc690b333a main#GLSLC@0xb85cb0 +ps_main.bin 229 像素 a5659569335af3b7 main#GLSLC@0xb88b38 +ps_main.bin 230 像素 5664a33faa83fd33 main#GLSLC@0xb8bd00 +ps_main.bin 231 像素 a68ef2ae82efe989 main#GLSLC@0xb8f030 +ps_main.bin 232 像素 3fd93343962695cd main#GLSLC@0xb92060 +ps_main.bin 233 像素 40abd0c3805fded3 main#GLSLC@0xb951f0 +ps_main.bin 234 像素 9fbdb671c065b029 main#GLSLC@0xb98568 +ps_main.bin 235 像素 f85664219babf3c7 main#GLSLC@0xb9ba48 +ps_main.bin 236 像素 40fc399fa393d3f2 main#GLSLC@0xb9ea78 +ps_main.bin 237 像素 3100f6738db9f068 main#GLSLC@0xba1c10 +ps_main.bin 238 像素 feea3ea6b40dc9d9 main#GLSLC@0xba4f78 +ps_main.bin 239 像素 2432602e6a95c9b9 main#GLSLC@0xba8448 +ps_main.bin 240 像素 25824022a2c6b470 main#GLSLC@0xbab618 +ps_main.bin 241 像素 aaddc37431adf19f main#GLSLC@0xbaea50 +ps_main.bin 242 像素 0d6f1997de8db140 main#GLSLC@0xbb1f78 +ps_main.bin 243 像素 4abc7b9d65a714ad main#GLSLC@0xbb4d70 +ps_main.bin 244 像素 235b67cad9698821 main#GLSLC@0xbb7da0 +ps_main.bin 245 像素 291d0488d9d84314 main#GLSLC@0xbbb038 +ps_main.bin 246 像素 fcaf6e39b4f97c02 main#GLSLC@0xbbdfd0 +ps_main.bin 247 像素 e9a7e3179a9cd1fc main#GLSLC@0xbc10d0 +ps_main.bin 248 像素 8a4d27d1abc25239 main#GLSLC@0xbc43b8 +ps_main.bin 249 像素 7d95d243aea2b2cc main#GLSLC@0xbc7808 +ps_main.bin 250 像素 8e21cfdcc960fbcc main#GLSLC@0xbca7a0 +ps_main.bin 251 像素 0a2f043d53ce738a main#GLSLC@0xbcd8a0 +ps_main.bin 252 像素 4f6927becce1f051 main#GLSLC@0xbd0b78 +ps_main.bin 253 像素 7de9d89c30323513 main#GLSLC@0xbd3fc8 +ps_main.bin 254 像素 73952b7e6005b452 main#GLSLC@0xbd7108 +ps_main.bin 255 像素 c82c38faa5439f85 main#GLSLC@0xbda4b0 +ps_main.bin 256 像素 7adec5f9978a5c86 main#GLSLC@0xbdd940 +ps_main.bin 257 像素 a10f747bf02dd679 main#GLSLC@0xbe09f0 +ps_main.bin 258 像素 140accf5ff3960ce main#GLSLC@0xbe3cd8 +ps_main.bin 259 像素 84ec21ece3b5f93f main#GLSLC@0xbe7228 +ps_main.bin 260 像素 cff7df07b8b7a949 main#GLSLC@0xbea478 +ps_main.bin 261 像素 cce69e11034510bb main#GLSLC@0xbed830 +ps_main.bin 262 像素 d49dbc75eb874280 main#GLSLC@0xbf0dd0 +ps_main.bin 263 像素 a91aa1213a5aedba main#GLSLC@0xbf44d8 +ps_main.bin 264 像素 d485c105f171e9a9 main#GLSLC@0xbf7728 +ps_main.bin 265 像素 d9bd1d6903931958 main#GLSLC@0xbfaae0 +ps_main.bin 266 像素 1459f681755b8ed1 main#GLSLC@0xbfe070 +ps_main.bin 267 像素 868b63cdf2e00113 main#GLSLC@0xc01768 +ps_main.bin 268 像素 cacc367ed9b9b59f main#GLSLC@0xc04b60 +ps_main.bin 269 像素 97d0858d2861a5a3 main#GLSLC@0xc080c0 +vs_main.bin 235 顶点 1d936fdedaeaec26 main#GLSLC@0xc0b808 +vs_main.bin 236 顶点 f1528b8c282af317 main#GLSLC@0xc0edb0 +vs_main.bin 237 顶点 6be5bba922b9b7dc main#GLSLC@0xc120d8 +vs_main.bin 238 顶点 dc7e23921ede07a6 main#GLSLC@0xc15680 +vs_main.bin 239 顶点 53d9cd70a28ee776 main#GLSLC@0xc18938 +vs_main.bin 240 顶点 57d4fd6e75bddc11 main#GLSLC@0xc1bf58 +vs_main.bin 241 顶点 de674aadc76f9a4c main#GLSLC@0xc1f420 +vs_main.bin 242 顶点 382ed7f5e7018312 main#GLSLC@0xc22a58 +vs_main.bin 243 顶点 09792f3958306ae6 main#GLSLC@0xc25d60 +vs_main.bin 244 顶点 892ef45238e6139b main#GLSLC@0xc293c8 +vs_main.bin 245 顶点 5aaf2555837387a2 main#GLSLC@0xc2c7d8 +ps_main.bin 270 像素 ac6dea3227be07a9 main#GLSLC@0xc30d58 +ps_main.bin 271 像素 9c41dee31f9dd507 main#GLSLC@0xc328c8 +ps_main.bin 272 像素 c0a082621ee59964 main#GLSLC@0xc35568 +ps_main.bin 273 像素 ded22c2a18beaec3 main#GLSLC@0xc37c20 +ps_main.bin 274 像素 b6407d8089c65b4a main#GLSLC@0xc3a818 +ps_main.bin 275 像素 16a74e9f2eb1e75e main#GLSLC@0xc3ce30 +ps_main.bin 276 像素 64f095d6ad432cb6 main#GLSLC@0xc3fce8 +ps_main.bin 277 像素 d38e714c1b376779 main#GLSLC@0xc425b8 +ps_main.bin 278 像素 26fa49ee946269b3 main#GLSLC@0xc452f0 +ps_main.bin 279 像素 383f8aabb836eb9d main#GLSLC@0xc486c8 +ps_main.bin 280 像素 2849e228f3a1df29 main#GLSLC@0xc4b6a8 +ps_main.bin 281 像素 b4836acd9a223438 main#GLSLC@0xc4ed38 +ps_main.bin 282 像素 acda869417b7a59c main#GLSLC@0xc51c98 +ps_main.bin 283 像素 4c960fa2c25b109d main#GLSLC@0xc55290 +ps_main.bin 284 像素 d062d8978d839eca main#GLSLC@0xc58490 +vs_main.bin 246 顶点 fb9093bc49a5a108 main#GLSLC@0xc5bd98 +ps_main.bin 285 像素 732a18f5dee4bafb main#GLSLC@0xc60e28 +vs_main.bin 247 顶点 0972cb63d2599938 main#GLSLC@0xc63e48 +vs_main.bin 248 顶点 06481a7157e38253 main#GLSLC@0xc681f0 +ps_main.bin 286 像素 cd9a9ed6684c86db main#GLSLC@0xc6c8b8 +ps_main.bin 287 像素 5cfff484d4ca3dc8 main#GLSLC@0xc6eca0 +ps_main.bin 288 像素 18890ab424a6d8b7 main#GLSLC@0xc716a8 +ps_main.bin 289 像素 7297cf79a8131e27 main#GLSLC@0xc746b0 +vs_main.bin 249 顶点 35911bd28ea1d381 main#GLSLC@0xc77ca0 +ps_main.bin 290 像素 d7c3cbd147bedbe5 main#GLSLC@0xc7a278 +ps_main.bin 291 像素 193839e199081fef main#GLSLC@0xc7c190 +vs_main.bin 250 顶点 900ad4c80b0eefed main#GLSLC@0xc86678 +vs_main.bin 251 顶点 b6969c79fe2b954a main#GLSLC@0xc89288 +ps_main.bin 292 像素 a39d1509aeb14f2f main#GLSLC@0xc8d548 +ps_main.bin 293 像素 d6b8129763eaf8c5 main#GLSLC@0xc90a90 +vs_main.bin 252 顶点 d8672870ca5ec2d0 main#GLSLC@0xc92448 +vs_main.bin 253 顶点 2d717589fd0904d6 main#GLSLC@0xc97278 +vs_main.bin 254 顶点 e34865342668e8c5 main#GLSLC@0xc9d3b8 +vs_main.bin 255 顶点 965477e64296aab4 main#GLSLC@0xca3ea0 +vs_main.bin 256 顶点 05a143a899574963 main#GLSLC@0xcad930 +vs_main.bin 257 顶点 c6f43c0eacaaab60 main#GLSLC@0xcb8590 +ps_main.bin 294 像素 1166cc27e685f802 main#GLSLC@0xcc3dd0 +ps_main.bin 295 像素 6b3cdff59a6d3b3f main#GLSLC@0xcc8978 +vs_main.bin 258 顶点 ebecb12e1a9163e0 main#GLSLC@0xcca118 +vs_main.bin 259 顶点 2de21173609569dd main#GLSLC@0xcce408 +vs_main.bin 260 顶点 08c29a91d2dec428 main#GLSLC@0xcd35b0 +ps_main.bin 296 像素 c3cefa0ef6765996 main#GLSLC@0xcd9b18 +ps_main.bin 297 像素 bde9d97585eeecac main#GLSLC@0xcde000 +ps_main.bin 298 像素 d3af478e244d42d0 main#GLSLC@0xce2ba8 +ps_main.bin 299 像素 63fb8375bf0a966e main#GLSLC@0xce7338 +vs_main.bin 261 顶点 2f763a93aae9ce99 main#GLSLC@0xcebf70 +ps_main.bin 300 像素 dcda5d53b4ce3749 main#GLSLC@0xcf0238 +ps_main.bin 301 像素 79931b3b0a01b599 main#GLSLC@0xcf2b00 +vs_main.bin 262 顶点 4f914211462adb63 main#GLSLC@0xcf5058 +vs_main.bin 263 顶点 31d8cd9e425c28e7 main#GLSLC@0xcfadd0 +vs_main.bin 264 顶点 0e685e6294a6cbb9 main#GLSLC@0xd01308 +vs_main.bin 265 顶点 088b2410056cd167 main#GLSLC@0xd08758 +vs_main.bin 266 顶点 ea7514a9dd3f5f62 main#GLSLC@0xd13098 +vs_main.bin 267 顶点 960299b97cb48b5e main#GLSLC@0xd1e080 +ps_main.bin 302 像素 40a0c914fd7f6675 main#GLSLC@0xd29fc8 +ps_main.bin 303 像素 22980b1ae5d2019e main#GLSLC@0xd2db28 +ps_main.bin 304 像素 68cfd018dfcee1bc main#GLSLC@0xd32638 +ps_main.bin 305 像素 964edbea4a852e35 main#GLSLC@0xd36568 +vs_main.bin 268 顶点 bce015ea372217c5 main#GLSLC@0xd5fdd8 +vs_main.bin 269 顶点 932b9fef87b605fb main#GLSLC@0xd616e8 +vs_main.bin 270 顶点 9040dcd5171e3bea main#GLSLC@0xd63240 +vs_main.bin 271 顶点 14fa46d06cb79527 main#GLSLC@0xd64c68 +ps_main.bin 306 像素 f475dfcf73b751ec main#GLSLC@0xd67b80 +ps_main.bin 307 像素 88dfd71bb5dbe427 main#GLSLC@0xd6a0a0 +ps_main.bin 308 像素 5b5254fa605c9448 main#GLSLC@0xd6b408 +ps_main.bin 309 像素 f62dfeb6dcdde0c2 main#GLSLC@0xd6cb38 +ps_main.bin 310 像素 2b0757eb8ca43d70 main#GLSLC@0xd6e290 +ps_main.bin 311 像素 b69e1d39e335537b main#GLSLC@0xd6fb28 +vs_main.bin 272 顶点 eeb8c0221b011e3b main#GLSLC@0xd71418 +vs_main.bin 273 顶点 29b497b6b2edd540 main#GLSLC@0xd72440 +ps_main.bin 312 像素 ba65191be2f53792 main#GLSLC@0xd73338 +ps_main.bin 313 像素 371ab8cfaa29e496 main#GLSLC@0xd74a38 +ps_main.bin 314 像素 7881cf9d310ff202 main#GLSLC@0xd762b0 +vs_main.bin 274 顶点 0e74ffd870215814 main#GLSLC@0xd7a688 +vs_main.bin 275 顶点 766b37a4dc8c15ec main#GLSLC@0xd7c5c0 +vs_main.bin 276 顶点 7115d177d5bead87 main#GLSLC@0xd7e518 +vs_main.bin 277 顶点 4a75def0fc2737bc main#GLSLC@0xd80758 +vs_main.bin 278 顶点 9e52ae3fd8ce5931 main#GLSLC@0xd829c8 +vs_main.bin 279 顶点 53f25eb27c4fe13c main#GLSLC@0xd84e48 +vs_main.bin 280 顶点 b650d7381139e885 main#GLSLC@0xd872f0 +vs_main.bin 281 顶点 ce279d77518e9929 main#GLSLC@0xd8a270 +vs_main.bin 282 顶点 c0faef7c67715d4b main#GLSLC@0xd8d210 +vs_main.bin 283 顶点 40d75abc4deab1e5 main#GLSLC@0xd8f1d8 +vs_main.bin 284 顶点 bcbd9e943b56e0df main#GLSLC@0xd911c0 +vs_main.bin 285 顶点 e3a8610ec4ba9fc6 main#GLSLC@0xd93578 +vs_main.bin 286 顶点 2bdf4c3fb6666f51 main#GLSLC@0xd95958 +vs_main.bin 287 顶点 26f78cd3aa4b8926 main#GLSLC@0xd97e40 +vs_main.bin 288 顶点 630ce4863fa3d979 main#GLSLC@0xd9a350 +vs_main.bin 289 顶点 4af25802e991c32f main#GLSLC@0xd9d348 +vs_main.bin 290 顶点 d030f91466a97a2b main#GLSLC@0xda0368 +vs_main.bin 291 顶点 6d1106cc9e332d9a main#GLSLC@0xda2330 +vs_main.bin 292 顶点 703cb25e0ea3ca8f main#GLSLC@0xda4320 +vs_main.bin 293 顶点 3ff941d5eece2395 main#GLSLC@0xda67d8 +vs_main.bin 294 顶点 0040f948baa144f4 main#GLSLC@0xda8cc0 +vs_main.bin 295 顶点 440d4449ff9ba011 main#GLSLC@0xdab4c8 +vs_main.bin 296 顶点 31c046a24904b56e main#GLSLC@0xdadcf0 +vs_main.bin 297 顶点 1a44d7fac8dec25a main#GLSLC@0xdb1220 +vs_main.bin 298 顶点 255e762cdb6f75ea main#GLSLC@0xdb4770 +vs_main.bin 299 顶点 250628de525d2512 main#GLSLC@0xdb8008 +vs_main.bin 300 顶点 d5dd59882bd4e47d main#GLSLC@0xdbb8c8 +vs_main.bin 301 顶点 ef6a423bd63b942d main#GLSLC@0xdbf488 +vs_main.bin 302 顶点 0216f89788d5754b main#GLSLC@0xdc3078 +vs_main.bin 303 顶点 3771b9464681d485 main#GLSLC@0xdc51d0 +vs_main.bin 304 顶点 800a2ae29d353701 main#GLSLC@0xdc7348 +vs_main.bin 305 顶点 4194dcf325d08bbf main#GLSLC@0xdc9880 +vs_main.bin 306 顶点 cce56c92e2006039 main#GLSLC@0xdcbdd8 +vs_main.bin 307 顶点 6cf96e7eb288d0e5 main#GLSLC@0xdce640 +vs_main.bin 308 顶点 391760ea30da7aa0 main#GLSLC@0xdd0ed8 +vs_main.bin 309 顶点 7f18c58f68165922 main#GLSLC@0xdd4578 +vs_main.bin 310 顶点 d472c77ef3cdf85e main#GLSLC@0xdd7c40 +vs_main.bin 311 顶点 46170991de5fc38b main#GLSLC@0xddb548 +vs_main.bin 312 顶点 18079c9be4553cfd main#GLSLC@0xddee78 +vs_main.bin 313 顶点 22597996ce6b1886 main#GLSLC@0xde2ba8 +vs_main.bin 314 顶点 3dd8c00d3acc5ff6 main#GLSLC@0xde6900 +vs_main.bin 315 顶点 b669d711c7213dfe main#GLSLC@0xde8a70 +vs_main.bin 316 顶点 0a6c03b820351de6 main#GLSLC@0xdead00 +vs_main.bin 317 顶点 d547a708156058fd main#GLSLC@0xded278 +vs_main.bin 318 顶点 ffe1d3e915a20e65 main#GLSLC@0xdef820 +vs_main.bin 319 顶点 3d4ac4ca90260022 main#GLSLC@0xdf1ec0 +vs_main.bin 320 顶点 65d39061656b3ac6 main#GLSLC@0xdf4590 +vs_main.bin 321 顶点 cd59bb7e15fe3be6 main#GLSLC@0xdf73a0 +vs_main.bin 322 顶点 5bfd4ece6f0a0196 main#GLSLC@0xdfa1d0 +vs_main.bin 323 顶点 96d29cfddd6730a5 main#GLSLC@0xdfd248 +vs_main.bin 324 顶点 7018eeb2ade4406c main#GLSLC@0xe002e8 +vs_main.bin 325 顶点 b18e57feacfd9f8d main#GLSLC@0xe03590 +vs_main.bin 326 顶点 e163e64ffce467ca main#GLSLC@0xe06860 +vs_main.bin 327 顶点 11e0b14a9b1ac080 main#GLSLC@0xe08b60 +vs_main.bin 328 顶点 09d7ba33f95453a6 main#GLSLC@0xe0ae78 +vs_main.bin 329 顶点 00870f86a49cf3b9 main#GLSLC@0xe0d468 +vs_main.bin 330 顶点 67644fc5976390b9 main#GLSLC@0xe0fa80 +vs_main.bin 331 顶点 58915ad3486f4c58 main#GLSLC@0xe12288 +vs_main.bin 332 顶点 8229d319cca04b38 main#GLSLC@0xe14ac8 +vs_main.bin 333 顶点 77f37072c47af3bb main#GLSLC@0xe17948 +vs_main.bin 334 顶点 be811c75cd4f797f main#GLSLC@0xe1a7f0 +vs_main.bin 335 顶点 90db03a33353717d main#GLSLC@0xe1d8e0 +vs_main.bin 336 顶点 b148a2e31b0f9c7c main#GLSLC@0xe209f8 +vs_main.bin 337 顶点 2d2533c0d817a29b main#GLSLC@0xe23d10 +ps_main.bin 315 像素 396ff9cf7f2a3801 main#GLSLC@0xe281f8 +ps_main.bin 316 像素 60d928216c2a1605 main#GLSLC@0xe29c80 +ps_main.bin 317 像素 21a82d67b5798281 main#GLSLC@0xe2b8c8 +ps_main.bin 318 像素 6f867edbd352fa3f main#GLSLC@0xe2cc50 +ps_main.bin 319 像素 91dfd159ac1467cb main#GLSLC@0xe2e8a8 +ps_main.bin 320 像素 ad8ae8a8373b7117 main#GLSLC@0xe306b8 +ps_main.bin 321 像素 01db2042ca1d750f main#GLSLC@0xe31a38 +ps_main.bin 322 像素 b348f28e7c5d1db4 main#GLSLC@0xe336a8 +ps_main.bin 323 像素 993d0b03e9ec2e66 main#GLSLC@0xe35480 +ps_main.bin 324 像素 ea8f896bd86a997d main#GLSLC@0xe369c8 +ps_main.bin 325 像素 6f551ccdb24ec54a main#GLSLC@0xe38810 +ps_main.bin 326 像素 d3f7c67176eaca56 main#GLSLC@0xe3a7b8 +ps_main.bin 327 像素 53b264ca9f0d505a main#GLSLC@0xe3b970 +ps_main.bin 328 像素 c6981da4a8a4231d main#GLSLC@0xe3cca8 +ps_main.bin 329 像素 bbb3342e0b1b5359 main#GLSLC@0xe3e8f0 +ps_main.bin 330 像素 bbcb132e1ed1d47e main#GLSLC@0xe40690 +ps_main.bin 331 像素 97686736eb71320f main#GLSLC@0xe41a20 +ps_main.bin 332 像素 55ea27cee5554264 main#GLSLC@0xe42f18 +ps_main.bin 333 像素 c684f5e2831c26f8 main#GLSLC@0xe44d30 +ps_main.bin 334 像素 98d74ade393817c3 main#GLSLC@0xe46ca8 +ps_main.bin 335 像素 0adf12429c4cc25a main#GLSLC@0xe48030 +ps_main.bin 336 像素 6a08c33a0239e639 main#GLSLC@0xe49510 +ps_main.bin 337 像素 a6bafda23cdd2c01 main#GLSLC@0xe4b2f0 +ps_main.bin 338 像素 ef84d7516f9134ef main#GLSLC@0xe4d328 +ps_main.bin 339 像素 4ca2b9b88a46a3ea main#GLSLC@0xe4e880 +ps_main.bin 340 像素 ef323a6338db6c71 main#GLSLC@0xe4ff38 +ps_main.bin 341 像素 d773afc0b06d3334 main#GLSLC@0xe51ee8 +vs_main.bin 338 顶点 3867bcd17d582723 main#GLSLC@0xe540f8 +vs_main.bin 339 顶点 b1deba985a353b7d main#GLSLC@0xe56be0 +vs_main.bin 340 顶点 6bce4645d7604b5c main#GLSLC@0xe596e8 +vs_main.bin 341 顶点 9efedd8fde0757eb main#GLSLC@0xe5c430 +vs_main.bin 342 顶点 752cc0e608f4f799 main#GLSLC@0xe5f1a0 +vs_main.bin 343 顶点 69eb51bc0826e12f main#GLSLC@0xe61cf8 +vs_main.bin 344 顶点 e78bce134e6dfa09 main#GLSLC@0xe64970 +vs_main.bin 345 顶点 fbd73b0ac3c81978 main#GLSLC@0xe67838 +ps_main.bin 342 像素 488d4158abab0ea1 main#GLSLC@0xe6b620 +ps_main.bin 343 像素 c7acb52cdad3fe6d main#GLSLC@0xe6c6f0 +ps_main.bin 344 像素 f2b9942d4e434dec main#GLSLC@0xe6d908 +vs_main.bin 346 顶点 e45c4956a58b41c3 main#GLSLC@0xe6ed90 +vs_main.bin 347 顶点 dc1706074ee94ba0 main#GLSLC@0xe70808 +vs_main.bin 348 顶点 55afd0aca24c0419 main#GLSLC@0xe725b0 +vs_main.bin 349 顶点 6f120567ffd086a8 main#GLSLC@0xe74140 +vs_main.bin 350 顶点 4cfa0ce6a7fe1079 main#GLSLC@0xe75f28 +vs_main.bin 351 顶点 04b77059f98a3b78 main#GLSLC@0xe78138 +vs_main.bin 352 顶点 b138aa31ff10b5f6 main#GLSLC@0xe79d18 +vs_main.bin 353 顶点 dcaca96d07536509 main#GLSLC@0xe7bb20 +ps_main.bin 345 像素 1163a9f7c3d20076 main#GLSLC@0xe812e0 +ps_main.bin 346 像素 29f2198cc62a4589 main#GLSLC@0xe824b0 +ps_main.bin 347 像素 b5b3a75d5922471e main#GLSLC@0xe83848 +ps_main.bin 348 像素 9a97bc2bc8da87b3 main#GLSLC@0xe84be0 +vs_main.bin 354 顶点 ca1e8e4dc8ee46e8 main#GLSLC@0xe860e0 +vs_main.bin 355 顶点 9d071ac8c1ad7fff main#GLSLC@0xe88838 +vs_main.bin 356 顶点 9137311b2d7c9ad7 main#GLSLC@0xe8a030 +vs_main.bin 357 顶点 dd285934e902ecdd main#GLSLC@0xe8ba68 +vs_main.bin 358 顶点 fc1a4540d58457a7 main#GLSLC@0xe8d608 +vs_main.bin 359 顶点 1c10ccd1d18c52ef main#GLSLC@0xe8f160 +vs_main.bin 360 顶点 ac87699e1f3ab8cf main#GLSLC@0xe90e18 +vs_main.bin 361 顶点 372bf5948213053f main#GLSLC@0xe92720 +vs_main.bin 362 顶点 d5273f87dc2447cb main#GLSLC@0xe94368 +vs_main.bin 363 顶点 67225779b9b79641 main#GLSLC@0xe96110 +vs_main.bin 364 顶点 2f64ecfbeb5ae4c2 main#GLSLC@0xe97b40 +vs_main.bin 365 顶点 d4369eedb6dbc67e main#GLSLC@0xe998a8 +vs_main.bin 366 顶点 7c2a95f22a5e64c2 main#GLSLC@0xe9b770 +vs_main.bin 367 顶点 02c1823e16d48275 main#GLSLC@0xe9d058 +vs_main.bin 368 顶点 3a40f12f7ee5639d main#GLSLC@0xe9ec88 +vs_main.bin 369 顶点 9eec816e90054180 main#GLSLC@0xea0a18 +vs_main.bin 370 顶点 47629ffac9999ad4 main#GLSLC@0xea2428 +vs_main.bin 371 顶点 fe33a79e4e5cd5f9 main#GLSLC@0xea4180 +ps_main.bin 349 像素 0335ab33d0ca2cc2 main#GLSLC@0xea6040 +ps_main.bin 350 像素 0b95121f1a3b7fbf main#GLSLC@0xea71f0 +ps_main.bin 351 像素 3c08bb61a7e87844 main#GLSLC@0xea83c0 +ps_main.bin 352 像素 8aca98a23ccf28e5 main#GLSLC@0xea9898 +ps_main.bin 353 像素 a86b2448ba78675b main#GLSLC@0xeaaef8 +ps_main.bin 354 像素 7cd7b99f6c2f759b main#GLSLC@0xeac0b0 +ps_main.bin 355 像素 5e394f669dc7492c main#GLSLC@0xead3e8 +ps_main.bin 356 像素 e8206f973b44c823 main#GLSLC@0xeae740 +ps_main.bin 357 像素 d147a874d995d079 main#GLSLC@0xeafd90 +ps_main.bin 358 像素 9204295a3ffb0607 main#GLSLC@0xeb12f8 +ps_main.bin 359 像素 30b45b0045b5b7a2 main#GLSLC@0xeb2a28 +ps_main.bin 360 像素 654c7d071ded3f97 main#GLSLC@0xeb4170 +ps_main.bin 361 像素 4f14b5d6cb6e8516 main#GLSLC@0xeb5bb0 +ps_main.bin 362 像素 1bf6bd9c542b5cd9 main#GLSLC@0xeb7788 +ps_main.bin 363 像素 4a671745ba6531d9 main#GLSLC@0xeb8ec0 +ps_main.bin 364 像素 1da14272a93cd491 main#GLSLC@0xebab70 +vs_main.bin 372 顶点 e80556c390d8a563 main#GLSLC@0xebc9b8 +ps_main.bin 365 像素 2af393b271843524 main#GLSLC@0xebe2d0 +ps_main.bin 366 像素 503163a4dc0eb9db main#GLSLC@0xebf1c8 +ps_main.bin 367 像素 67a69e3417fb3354 main#GLSLC@0xec09b0 +ps_main.bin 368 像素 9d10049ad94a723c main#GLSLC@0xec2250 +vs_main.bin 373 顶点 62b7c53bf1277850 main#GLSLC@0xec3b50 +ps_main.bin 369 像素 55926e406b5fcf8b main#GLSLC@0xec4e78 +ps_main.bin 370 像素 6bd75749fba374a8 main#GLSLC@0xec6298 +ps_main.bin 371 像素 d1b13f9b7463f2d9 main#GLSLC@0xec7700 +ps_main.bin 372 像素 757cfe3c9c0fb1d3 main#GLSLC@0xec97b8 +vs_main.bin 374 顶点 bc2a16fd4a149dc8 main#GLSLC@0xeca5a0 +ps_main.bin 373 像素 f2390a27574885bb main#GLSLC@0xecb498 +vs_main.bin 375 顶点 ee98caab18503b7a main#GLSLC@0xecc440 +ps_main.bin 374 像素 12f1b945891c5ccd main#GLSLC@0xece758 diff --git a/dm/games/01003FC01670C000/shader_map.tsv.conflicts.tsv b/dm/games/01003FC01670C000/shader_map.tsv.conflicts.tsv new file mode 100644 index 0000000..5be7a60 --- /dev/null +++ b/dm/games/01003FC01670C000/shader_map.tsv.conflicts.tsv @@ -0,0 +1,2 @@ +# kind: evidence 控制段键冲突裁定:表空 | control-section key conflict rulings: empty +dm_ctl_sha16 chosen_pc class candidates records diff --git a/dm/games/01003FC01670C000/shader_pairs.tsv b/dm/games/01003FC01670C000/shader_pairs.tsv new file mode 100644 index 0000000..90d6689 --- /dev/null +++ b/dm/games/01003FC01670C000/shader_pairs.tsv @@ -0,0 +1,377 @@ +# kind: evidence BNSH 变体里的顶点 / 像素着色器配对(dm/gfx/tools/shader_split.py 生成,勿手改):顶点键 <TAB> 像素键 | vertex / pixel shader pairs within BNSH variations (generated, do not edit): vertex key <TAB> pixel key +0040f948baa144f4 0ddfcfcdcc3ddf9e +005dc0a0507ca0c0 0ddfcfcdcc3ddf9e +00870f86a49cf3b9 0ddfcfcdcc3ddf9e +00b73c6360ebc8a3 0ddfcfcdcc3ddf9e +00f37717bf4bac4f 0ddfcfcdcc3ddf9e +0195aaa2774f7f94 0ddfcfcdcc3ddf9e +01b67911b0eba290 0ddfcfcdcc3ddf9e +01f5cb5257d9f97b 0ddfcfcdcc3ddf9e +0216f89788d5754b 0ddfcfcdcc3ddf9e +02c1823e16d48275 0ddfcfcdcc3ddf9e +02ca876f24e6ee00 0ddfcfcdcc3ddf9e +034fd5775426fb0a 0ddfcfcdcc3ddf9e +04b77059f98a3b78 0ddfcfcdcc3ddf9e +051881873373d1da 0ddfcfcdcc3ddf9e +05521a638f5a68e0 0ddfcfcdcc3ddf9e +05a143a899574963 0ddfcfcdcc3ddf9e +06481a7157e38253 0ddfcfcdcc3ddf9e +06514574c231f3dc 0ddfcfcdcc3ddf9e +0732be065f1ccb64 0ddfcfcdcc3ddf9e +0803c807f16ba3d1 0ddfcfcdcc3ddf9e +088b2410056cd167 0ddfcfcdcc3ddf9e +08c29a91d2dec428 0ddfcfcdcc3ddf9e +0972cb63d2599938 0ddfcfcdcc3ddf9e +09792f3958306ae6 0ddfcfcdcc3ddf9e +09d7ba33f95453a6 0ddfcfcdcc3ddf9e +0a5a4fb79479e894 0ddfcfcdcc3ddf9e +0a6c03b820351de6 0ddfcfcdcc3ddf9e +0d4d61c0a868f9a7 0ddfcfcdcc3ddf9e +0e685e6294a6cbb9 0ddfcfcdcc3ddf9e +0e74ffd870215814 0ddfcfcdcc3ddf9e +0f3f9e510179be23 0ddfcfcdcc3ddf9e +0ffefa790db21654 0ddfcfcdcc3ddf9e +10839897b2ae647d 0ddfcfcdcc3ddf9e +11e0b14a9b1ac080 0ddfcfcdcc3ddf9e +139b480e75a6c1ec 0ddfcfcdcc3ddf9e +146a3716c4f9826b 0ddfcfcdcc3ddf9e +14fa46d06cb79527 0ddfcfcdcc3ddf9e +1787a4f06b8a0102 0ddfcfcdcc3ddf9e +18079c9be4553cfd 0ddfcfcdcc3ddf9e +1a28da408ff83ff4 0ddfcfcdcc3ddf9e +1a44d7fac8dec25a 0ddfcfcdcc3ddf9e +1c10ccd1d18c52ef 0ddfcfcdcc3ddf9e +1d936fdedaeaec26 0ddfcfcdcc3ddf9e +201b4d77416f979a 0ddfcfcdcc3ddf9e +20a2ce4b867bf227 0ddfcfcdcc3ddf9e +213868c7c4d50ca9 0ddfcfcdcc3ddf9e +2240a91ab3ac898b 0ddfcfcdcc3ddf9e +22597996ce6b1886 0ddfcfcdcc3ddf9e +2484e5994cef257c 0ddfcfcdcc3ddf9e +250628de525d2512 0ddfcfcdcc3ddf9e +255e762cdb6f75ea 0ddfcfcdcc3ddf9e +26f78cd3aa4b8926 0ddfcfcdcc3ddf9e +27857033dddfde91 0ddfcfcdcc3ddf9e +27c1e9abb64a354d 0ddfcfcdcc3ddf9e +285bd23319484bb0 0ddfcfcdcc3ddf9e +28a54478ae1ca813 0ddfcfcdcc3ddf9e +28f1d8f6fd56635e 0ddfcfcdcc3ddf9e +28fd3e0769029b79 0ddfcfcdcc3ddf9e +29b497b6b2edd540 0ddfcfcdcc3ddf9e +2b4eee86e82db581 0ddfcfcdcc3ddf9e +2bdf4c3fb6666f51 0ddfcfcdcc3ddf9e +2c5f640d1306cd67 0ddfcfcdcc3ddf9e +2d2533c0d817a29b 0ddfcfcdcc3ddf9e +2d717589fd0904d6 0ddfcfcdcc3ddf9e +2de21173609569dd 0ddfcfcdcc3ddf9e +2f64ecfbeb5ae4c2 0ddfcfcdcc3ddf9e +2f763a93aae9ce99 0ddfcfcdcc3ddf9e +3146131971489b3b 0ddfcfcdcc3ddf9e +31c046a24904b56e 0ddfcfcdcc3ddf9e +31d8cd9e425c28e7 0ddfcfcdcc3ddf9e +330a677ae5094609 0ddfcfcdcc3ddf9e +34471f37fab5afec 0ddfcfcdcc3ddf9e +3490021606823f09 0ddfcfcdcc3ddf9e +35911bd28ea1d381 0ddfcfcdcc3ddf9e +366a9a9aab88c22e 0ddfcfcdcc3ddf9e +372bf5948213053f 0ddfcfcdcc3ddf9e +3771b9464681d485 0ddfcfcdcc3ddf9e +382ed7f5e7018312 0ddfcfcdcc3ddf9e +3867bcd17d582723 0ddfcfcdcc3ddf9e +391760ea30da7aa0 0ddfcfcdcc3ddf9e +3a19ad025b16f11d 0ddfcfcdcc3ddf9e +3a40f12f7ee5639d 0ddfcfcdcc3ddf9e +3caec795d3d3bff1 0ddfcfcdcc3ddf9e +3d4ac4ca90260022 0ddfcfcdcc3ddf9e +3d9cd4be9f3b5a2e 0ddfcfcdcc3ddf9e +3d9d2c26e744da49 0ddfcfcdcc3ddf9e +3dd8c00d3acc5ff6 0ddfcfcdcc3ddf9e +3e8c65894c62edcf 0ddfcfcdcc3ddf9e +3f19991694c1b137 0ddfcfcdcc3ddf9e +3f689fa3167ede95 0ddfcfcdcc3ddf9e +3ff941d5eece2395 0ddfcfcdcc3ddf9e +403a5758e49fbf9e 0ddfcfcdcc3ddf9e +40d75abc4deab1e5 0ddfcfcdcc3ddf9e +414a68b26cddfbab 0ddfcfcdcc3ddf9e +4194dcf325d08bbf 0ddfcfcdcc3ddf9e +420ed53c9f81b96b 0ddfcfcdcc3ddf9e +42c908bcacacab78 0ddfcfcdcc3ddf9e +43dce950e3ce9e00 0ddfcfcdcc3ddf9e +440d4449ff9ba011 0ddfcfcdcc3ddf9e +456f25b32b3251a5 0ddfcfcdcc3ddf9e +46170991de5fc38b 0ddfcfcdcc3ddf9e +46780f757806d67d 0ddfcfcdcc3ddf9e +47629ffac9999ad4 0ddfcfcdcc3ddf9e +499bee5c432ebce8 0ddfcfcdcc3ddf9e +4a1794705ca53384 0ddfcfcdcc3ddf9e +4a7231ce50ac4458 0ddfcfcdcc3ddf9e +4a75def0fc2737bc 0ddfcfcdcc3ddf9e +4af25802e991c32f 0ddfcfcdcc3ddf9e +4b148d6596f3d23a 0ddfcfcdcc3ddf9e +4b33aac14cc96596 0ddfcfcdcc3ddf9e +4b7f451b92e9d9fa 0ddfcfcdcc3ddf9e +4bf525a99134ebcf 0ddfcfcdcc3ddf9e +4cfa0ce6a7fe1079 0ddfcfcdcc3ddf9e +4dd9c2e6a40c0394 0ddfcfcdcc3ddf9e +4eac8f4c27d2c3e8 0ddfcfcdcc3ddf9e +4ef43b090d4aa792 0ddfcfcdcc3ddf9e +4f5acb13923bf8d0 0ddfcfcdcc3ddf9e +4f914211462adb63 0ddfcfcdcc3ddf9e +5020390f0855186a 0ddfcfcdcc3ddf9e +51ac57eb5f67fedc 0ddfcfcdcc3ddf9e +53bac653c73c418a 0ddfcfcdcc3ddf9e +53d9cd70a28ee776 0ddfcfcdcc3ddf9e +53f25eb27c4fe13c 0ddfcfcdcc3ddf9e +5482dc93a27cf699 0ddfcfcdcc3ddf9e +553b071e8012918d 0ddfcfcdcc3ddf9e +555d1d604a71abce 0ddfcfcdcc3ddf9e +55afd0aca24c0419 0ddfcfcdcc3ddf9e +57d4fd6e75bddc11 0ddfcfcdcc3ddf9e +58258b4cece0dd8a 0ddfcfcdcc3ddf9e +58915ad3486f4c58 0ddfcfcdcc3ddf9e +59672edd5f258046 0ddfcfcdcc3ddf9e +5a40c4889fe10994 0ddfcfcdcc3ddf9e +5aaf2555837387a2 0ddfcfcdcc3ddf9e +5b6a1b16861461f7 0ddfcfcdcc3ddf9e +5b83ea06a6982c94 0ddfcfcdcc3ddf9e +5bfd4ece6f0a0196 0ddfcfcdcc3ddf9e +5c44c76019a7d1ec 0ddfcfcdcc3ddf9e +5d71ed0f2686224f 0ddfcfcdcc3ddf9e +5d83bf515bb746c2 0ddfcfcdcc3ddf9e +5e8f30c04b6d74d8 0ddfcfcdcc3ddf9e +6098ad0051905c99 0ddfcfcdcc3ddf9e +60d4c4ad76e48d5a 0ddfcfcdcc3ddf9e +6119e2affa309a00 0ddfcfcdcc3ddf9e +613211a136a1cf52 0ddfcfcdcc3ddf9e +618e53bc4f5c3ba8 0ddfcfcdcc3ddf9e +6251be1afb429c0e 0ddfcfcdcc3ddf9e +62b7c53bf1277850 0ddfcfcdcc3ddf9e +630ce4863fa3d979 0ddfcfcdcc3ddf9e +656da77697f3452e 0ddfcfcdcc3ddf9e +65d39061656b3ac6 0ddfcfcdcc3ddf9e +669e8a6b312d7da5 0ddfcfcdcc3ddf9e +67225779b9b79641 0ddfcfcdcc3ddf9e +67644fc5976390b9 0ddfcfcdcc3ddf9e +683bb6316a7ae2aa 0ddfcfcdcc3ddf9e +69eb51bc0826e12f 0ddfcfcdcc3ddf9e +6a24f57943c4ec67 0ddfcfcdcc3ddf9e +6a502c6cd5bfabf4 0ddfcfcdcc3ddf9e +6a593450bb293e26 0ddfcfcdcc3ddf9e +6b07b70e665775fd 0ddfcfcdcc3ddf9e +6b400d24e283fb89 0ddfcfcdcc3ddf9e +6bce4645d7604b5c 0ddfcfcdcc3ddf9e +6be5bba922b9b7dc 0ddfcfcdcc3ddf9e +6c477ce4fb255429 0ddfcfcdcc3ddf9e +6c5a06abbf8385ac 0ddfcfcdcc3ddf9e +6cf96e7eb288d0e5 0ddfcfcdcc3ddf9e +6d1106cc9e332d9a 0ddfcfcdcc3ddf9e +6d7cf4c78dbabd40 0ddfcfcdcc3ddf9e +6eef6583df6b6c50 0ddfcfcdcc3ddf9e +6f120567ffd086a8 0ddfcfcdcc3ddf9e +6fed094f83ec834c 0ddfcfcdcc3ddf9e +7018eeb2ade4406c 0ddfcfcdcc3ddf9e +703cb25e0ea3ca8f 0ddfcfcdcc3ddf9e +7115d177d5bead87 0ddfcfcdcc3ddf9e +713048c1a4d9c9b6 0ddfcfcdcc3ddf9e +717ddb7e3e3ca319 0ddfcfcdcc3ddf9e +72aed5a33cd82126 0ddfcfcdcc3ddf9e +749e645455b4d6f9 0ddfcfcdcc3ddf9e +752cc0e608f4f799 0ddfcfcdcc3ddf9e +75f1ce743e36beb0 0ddfcfcdcc3ddf9e +766b37a4dc8c15ec 0ddfcfcdcc3ddf9e +77f37072c47af3bb 0ddfcfcdcc3ddf9e +78878b5627402d1b 0ddfcfcdcc3ddf9e +7a537520dd53b0e4 0ddfcfcdcc3ddf9e +7c073b91c37e838d 0ddfcfcdcc3ddf9e +7c2a95f22a5e64c2 0ddfcfcdcc3ddf9e +7cde12631edfadde 0ddfcfcdcc3ddf9e +7e051b2bd08543ee 0ddfcfcdcc3ddf9e +7f16b8534971d61e 0ddfcfcdcc3ddf9e +7f18c58f68165922 0ddfcfcdcc3ddf9e +7f2a6b38040a9d23 0ddfcfcdcc3ddf9e +800a2ae29d353701 0ddfcfcdcc3ddf9e +80b8c29864a6abee 0ddfcfcdcc3ddf9e +80e0798b12b23163 0ddfcfcdcc3ddf9e +8229d319cca04b38 0ddfcfcdcc3ddf9e +825ad973231b45a7 0ddfcfcdcc3ddf9e +82b68976b8f3d729 0ddfcfcdcc3ddf9e +8393fa9fc9b1a637 0ddfcfcdcc3ddf9e +83badef66a026c4e 0ddfcfcdcc3ddf9e +862f4e924ef2931d 0ddfcfcdcc3ddf9e +8682158094645e50 0ddfcfcdcc3ddf9e +876c17d32e40fcc0 0ddfcfcdcc3ddf9e +87a0b64f386e2f48 0ddfcfcdcc3ddf9e +881f2b21dade164b 0ddfcfcdcc3ddf9e +88d767d663df8369 0ddfcfcdcc3ddf9e +890671ec1c58d860 0ddfcfcdcc3ddf9e +892ef45238e6139b 0ddfcfcdcc3ddf9e +89891eabe54feb48 0ddfcfcdcc3ddf9e +8b27c197c8a99d04 0ddfcfcdcc3ddf9e +8b9ceaa3fd78f94d 0ddfcfcdcc3ddf9e +8f9a4446b3356f03 0ddfcfcdcc3ddf9e +900ad4c80b0eefed 0ddfcfcdcc3ddf9e +9040dcd5171e3bea 0ddfcfcdcc3ddf9e +90b21c4032406dcd 0ddfcfcdcc3ddf9e +90db03a33353717d 0ddfcfcdcc3ddf9e +910e1e3beb86a3e2 0ddfcfcdcc3ddf9e +9137311b2d7c9ad7 0ddfcfcdcc3ddf9e +932b9fef87b605fb 0ddfcfcdcc3ddf9e +9451af39e4a54358 0ddfcfcdcc3ddf9e +94a6dcc6311af787 0ddfcfcdcc3ddf9e +952ecd6fcb3885d8 0ddfcfcdcc3ddf9e +95405b8f9257e987 0ddfcfcdcc3ddf9e +954f082c4e31acb4 0ddfcfcdcc3ddf9e +955fecb9caa5c59a 0ddfcfcdcc3ddf9e +95fc0eecb5d5694c 0ddfcfcdcc3ddf9e +960299b97cb48b5e 0ddfcfcdcc3ddf9e +965477e64296aab4 0ddfcfcdcc3ddf9e +9664bd8e4a677425 0ddfcfcdcc3ddf9e +968c43b11286bb9f 0ddfcfcdcc3ddf9e +96d29cfddd6730a5 0ddfcfcdcc3ddf9e +98d5b4f928be9df8 0ddfcfcdcc3ddf9e +99cb04ae8d0485c3 0ddfcfcdcc3ddf9e +9a1a3516b416471a 0ddfcfcdcc3ddf9e +9a909735d9ba1aea 0ddfcfcdcc3ddf9e +9b588c01a4a0552c 0ddfcfcdcc3ddf9e +9d071ac8c1ad7fff 0ddfcfcdcc3ddf9e +9d4cb74596316043 0ddfcfcdcc3ddf9e +9e52ae3fd8ce5931 0ddfcfcdcc3ddf9e +9e8d0f978c50f5ec 0ddfcfcdcc3ddf9e +9e9a4cbbce6b5989 0ddfcfcdcc3ddf9e +9eec816e90054180 0ddfcfcdcc3ddf9e +9efedd8fde0757eb 0ddfcfcdcc3ddf9e +9fa9e192bf0d940c 0ddfcfcdcc3ddf9e +a05e50da03818a3e 0ddfcfcdcc3ddf9e +a0b16dc3fd19ec28 0ddfcfcdcc3ddf9e +a0c08c3dcd1610e9 0ddfcfcdcc3ddf9e +a122a0d6250751d5 0ddfcfcdcc3ddf9e +a296958ec33de2c6 0ddfcfcdcc3ddf9e +a2d43f1cbc503e1f 0ddfcfcdcc3ddf9e +a4ccb368846ce4c6 0ddfcfcdcc3ddf9e +a61d46af96db2e03 0ddfcfcdcc3ddf9e +a621f8c9d891e2c7 0ddfcfcdcc3ddf9e +a62f428b10a4dd40 0ddfcfcdcc3ddf9e +a6cbedc8dae81d3b 0ddfcfcdcc3ddf9e +a865b4f477c1d6b8 0ddfcfcdcc3ddf9e +a890c3a180606eb8 0ddfcfcdcc3ddf9e +a8e9b32f805036e5 0ddfcfcdcc3ddf9e +a9bf7470e2fad37c 0ddfcfcdcc3ddf9e +ac35a63615eb5fe6 0ddfcfcdcc3ddf9e +ac87699e1f3ab8cf 0ddfcfcdcc3ddf9e +aea4a1ec55b1f487 0ddfcfcdcc3ddf9e +afb7a89e47c0c6af 0ddfcfcdcc3ddf9e +b052e30fd03229c8 0ddfcfcdcc3ddf9e +b10316f74f5492e2 0ddfcfcdcc3ddf9e +b138aa31ff10b5f6 0ddfcfcdcc3ddf9e +b148a2e31b0f9c7c 0ddfcfcdcc3ddf9e +b18e57feacfd9f8d 0ddfcfcdcc3ddf9e +b1deba985a353b7d 0ddfcfcdcc3ddf9e +b336d3c4c49dcf3f 0ddfcfcdcc3ddf9e +b46878c348125e2c 0ddfcfcdcc3ddf9e +b56e967305958a3c 0ddfcfcdcc3ddf9e +b650d7381139e885 0ddfcfcdcc3ddf9e +b669d711c7213dfe 0ddfcfcdcc3ddf9e +b6969c79fe2b954a 0ddfcfcdcc3ddf9e +b906d3a4a34ee750 0ddfcfcdcc3ddf9e +b97c703a0c8f34b7 0ddfcfcdcc3ddf9e +b983170ff0fbf0aa 0ddfcfcdcc3ddf9e +b9a682c336cef810 0ddfcfcdcc3ddf9e +ba335dc18c2f3e30 0ddfcfcdcc3ddf9e +bc2a16fd4a149dc8 0ddfcfcdcc3ddf9e +bcbd9e943b56e0df 0ddfcfcdcc3ddf9e +bce015ea372217c5 0ddfcfcdcc3ddf9e +be7228b37b341ece 0ddfcfcdcc3ddf9e +be811c75cd4f797f 0ddfcfcdcc3ddf9e +beaafe2c6e95c93c 0ddfcfcdcc3ddf9e +c0faef7c67715d4b 0ddfcfcdcc3ddf9e +c10272c5fe59f478 0ddfcfcdcc3ddf9e +c1b170b990c90ae3 0ddfcfcdcc3ddf9e +c1c5ec50ec09752f 0ddfcfcdcc3ddf9e +c25d49635e691ab8 0ddfcfcdcc3ddf9e +c57463065d7fc12a 0ddfcfcdcc3ddf9e +c6e74d5b96b2a9b9 0ddfcfcdcc3ddf9e +c6f43c0eacaaab60 0ddfcfcdcc3ddf9e +c7d10687f1fec5e6 0ddfcfcdcc3ddf9e +c8ed61edcc059ae4 0ddfcfcdcc3ddf9e +c8ef856680691deb 0ddfcfcdcc3ddf9e +c9491774a1fa7c0e 0ddfcfcdcc3ddf9e +c9ef0fcdc619f8a8 0ddfcfcdcc3ddf9e +ca1e8e4dc8ee46e8 0ddfcfcdcc3ddf9e +ca41a02a5ff34d57 0ddfcfcdcc3ddf9e +cac8c8837d11dc19 0ddfcfcdcc3ddf9e +cb474a72243bae59 0ddfcfcdcc3ddf9e +cb81f6d218f3babc 0ddfcfcdcc3ddf9e +cce56c92e2006039 0ddfcfcdcc3ddf9e +cd59bb7e15fe3be6 0ddfcfcdcc3ddf9e +ce279d77518e9929 0ddfcfcdcc3ddf9e +ce4adf04f3fce75d 0ddfcfcdcc3ddf9e +cee7c1f5d45decbe 0ddfcfcdcc3ddf9e +d030f91466a97a2b 0ddfcfcdcc3ddf9e +d0747b5db7010662 0ddfcfcdcc3ddf9e +d0fba5c88a6ad021 0ddfcfcdcc3ddf9e +d28272643d6906ed 0ddfcfcdcc3ddf9e +d2933506ff29ef9c 0ddfcfcdcc3ddf9e +d4369eedb6dbc67e 0ddfcfcdcc3ddf9e +d472c77ef3cdf85e 0ddfcfcdcc3ddf9e +d5273f87dc2447cb 0ddfcfcdcc3ddf9e +d547a708156058fd 0ddfcfcdcc3ddf9e +d555fca572db63c0 0ddfcfcdcc3ddf9e +d5baa610ddf1d8dc 0ddfcfcdcc3ddf9e +d5dd59882bd4e47d 0ddfcfcdcc3ddf9e +d66b2bf0ca4d8d99 0ddfcfcdcc3ddf9e +d724f07bcc0b3ea2 0ddfcfcdcc3ddf9e +d8672870ca5ec2d0 0ddfcfcdcc3ddf9e +d99c2b313152b167 0ddfcfcdcc3ddf9e +daa09ca1aefc87da 0ddfcfcdcc3ddf9e +dbde49ce80239494 0ddfcfcdcc3ddf9e +dbe59bbc08be74b0 0ddfcfcdcc3ddf9e +dc1706074ee94ba0 0ddfcfcdcc3ddf9e +dc7e23921ede07a6 0ddfcfcdcc3ddf9e +dcaca96d07536509 0ddfcfcdcc3ddf9e +dd285934e902ecdd 0ddfcfcdcc3ddf9e +de674aadc76f9a4c 0ddfcfcdcc3ddf9e +de82d5f529d3850f 0ddfcfcdcc3ddf9e +de9fdd1491f8f1af 0ddfcfcdcc3ddf9e +dfb5058a40908e39 0ddfcfcdcc3ddf9e +dfc13dee1219be5d 0ddfcfcdcc3ddf9e +e163e64ffce467ca 0ddfcfcdcc3ddf9e +e16db18046c16690 0ddfcfcdcc3ddf9e +e1871737507bc29c 0ddfcfcdcc3ddf9e +e1cb7c2d0d958dd2 0ddfcfcdcc3ddf9e +e1e9a5ddf3a54bfa 0ddfcfcdcc3ddf9e +e22932bbf66f6aeb 0ddfcfcdcc3ddf9e +e2dbc95f27854c48 0ddfcfcdcc3ddf9e +e34865342668e8c5 0ddfcfcdcc3ddf9e +e3558a505e21b602 0ddfcfcdcc3ddf9e +e3a8610ec4ba9fc6 0ddfcfcdcc3ddf9e +e45c4956a58b41c3 0ddfcfcdcc3ddf9e +e4682699786919f3 0ddfcfcdcc3ddf9e +e4ec47e63db966e8 0ddfcfcdcc3ddf9e +e5c827ed6dbac282 0ddfcfcdcc3ddf9e +e78bce134e6dfa09 0ddfcfcdcc3ddf9e +e80556c390d8a563 0ddfcfcdcc3ddf9e +e9d8fba810148eaa 0ddfcfcdcc3ddf9e +ea7514a9dd3f5f62 0ddfcfcdcc3ddf9e +ebec8e0cf92da97c 0ddfcfcdcc3ddf9e +ebecb12e1a9163e0 0ddfcfcdcc3ddf9e +ec503adf8210c554 0ddfcfcdcc3ddf9e +ed092f9a172af2b6 0ddfcfcdcc3ddf9e +ee98caab18503b7a 0ddfcfcdcc3ddf9e +eeb8c0221b011e3b 0ddfcfcdcc3ddf9e +ef6a423bd63b942d 0ddfcfcdcc3ddf9e +f1528b8c282af317 0ddfcfcdcc3ddf9e +f1d399cb6802b1a4 0ddfcfcdcc3ddf9e +f1fa64e81d7f9379 0ddfcfcdcc3ddf9e +f2983b4ec002fac4 0ddfcfcdcc3ddf9e +f2e3868276e465a7 0ddfcfcdcc3ddf9e +f406c638e6ce73aa 0ddfcfcdcc3ddf9e +f8cf171ceb721fe2 0ddfcfcdcc3ddf9e +fa4c270c5660e4d8 0ddfcfcdcc3ddf9e +fa918d47492767ec 0ddfcfcdcc3ddf9e +fad8e21db86e0da6 0ddfcfcdcc3ddf9e +fb9093bc49a5a108 0ddfcfcdcc3ddf9e +fbd73b0ac3c81978 0ddfcfcdcc3ddf9e +fc18ecbf4168f637 0ddfcfcdcc3ddf9e +fc1a4540d58457a7 0ddfcfcdcc3ddf9e +fe33a79e4e5cd5f9 0ddfcfcdcc3ddf9e +ffe1d3e915a20e65 0ddfcfcdcc3ddf9e diff --git a/dm/games/README.md b/dm/games/README.md index 020b0d6..de2d1c3 100644 --- a/dm/games/README.md +++ b/dm/games/README.md @@ -36,10 +36,12 @@ None of them has a default game: without one they stop and list the profiles. To | `romfs_marker` | A path that must exist in the RomFS | | `zip_store` | Extensions the IPA stores without compressing | | `mem_reserve`, `mem_alias`, `mem_stack`, `app_pool` | Guest address-space partition and application pool (`hle_svc.c`) | -| `present_interval` | `game` (the default): frames are paced by the present interval the game sets, as on the Switch (a game asking for 2 gets 30 fps, and on iOS the display link then runs at 30 Hz; Engage asks for 2 on the Switch, and a rewriter cut on its 2.0.0 `main` makes it ask for 1, see `0100A6301214E000/game.tsv`). `display`: one frame per display tick whatever the game asks, which is only right for games whose logic does not count frames (D2R's runs on its own 25 Hz clock). `DM_PRESENT_INTERVAL=game` / `display` overrides it, also from `Documents/env.txt` | +| `present_interval` | `game` (the default): frames are paced by the present interval the game sets, as on the Switch (a frame presented with interval 2 stays on screen for two display ticks; a game that keeps asking for 2 for a second gets the 30 fps target, and on iOS the display link then runs at 30 Hz; Mario 1.2.1 switches to 2 for single frames after a slow one; Engage asks for 2 on the Switch, and a rewriter cut on its 2.0.0 `main` makes it ask for 1, see `0100A6301214E000/game.tsv`). `display`: one frame per display tick whatever the game asks, which is only right for games whose logic does not count frames (D2R's runs on its own 25 Hz clock). `DM_PRESENT_INTERVAL=game` / `display` overrides it, also from `Documents/env.txt` | | `core_preempt` | `on` / `off` (the default): approximate Horizon's per-core preemption. When an SVC wakes a higher-priority thread pinned to the caller's core, the caller waits (at most 5 ms) until such threads block again (`hle_thread.c`). Metroid Dread's movie player relies on it; Mario's loading gets slower with it (first frame 0.6 → 2.9 s), so it is off unless a game needs it. `DM_PREEMPT=0` / `1` overrides | | `nvn_tex_base`, `nvn_tex_slots`, `nvn_samp_base`, `nvn_img_base`, `nvn_img_slots`, `nvn_ssbo_bases` | NVN driver constant segments, measured over all shaders | | `shader_groups`, `shader_note` | `shader_split.py` source groups and the map header note (Engage: `engine` for `data.unity3d` and `Resources/`, `assets` for the Addressables bundles) | +| `shader_main_glslc` | `on` / `off` (the default): `shader_split.py` also reads GLSLC compiler outputs (magic `0x19866891`) in `main`. 13 Sentinels keeps all its shaders there; Engage's `main` has 11 that it does not use, so the key stays off for other games | +| `shader_separable` | `on` / `off` (the default): the game builds single-stage programs and combines them per draw, so the real vertex / pixel pairs exist only at run time. `shader_split.py` then pairs every vertex shader with a cover of pixel shaders that together read every input any pixel shader reads, and `dmsh_build.py`'s `vs_pad` makes every vertex shader link with any pixel shader (13 Sentinels) | The translator (`dmsh_xlate/Program.cs`) still has Mario's texture / sampler bases and the depth convention built in. It uses the bases only to look up per-slot texture / image types in `shader_facts.tsv`; `dmsh_build.py` decodes the bindings from the profile. So a profile with other bases (Engage: `0x168` / `0x568`) builds as long as its facts table has no per-slot texture / image entries; otherwise `dmsh_build.py` stops, and the translator must take the bases from the profile first. diff --git a/dm/games/README.zh-CN.md b/dm/games/README.zh-CN.md index a53dd13..37b984a 100644 --- a/dm/games/README.zh-CN.md +++ b/dm/games/README.zh-CN.md @@ -36,10 +36,12 @@ | `romfs_marker` | RomFS 里必须有的路径 | | `zip_store` | IPA 里不再压缩的扩展名 | | `mem_reserve`、`mem_alias`、`mem_stack`、`app_pool` | 客体地址空间分区与应用内存池(`hle_svc.c`) | -| `present_interval` | `game`(缺省):按游戏设的出图间隔出帧,同 Switch(游戏设 2 就是 30 帧,iOS 上显示节拍随之 30 Hz;Engage 在 Switch 上设 2,2.0.0 `main` 的改写剪口让它设 1,见 `0100A6301214E000/game.tsv`)。`display`:不管游戏设多少,每个显示节拍一帧——只适合逻辑不按帧计数的游戏(D2R 的逻辑按自己的 25 Hz 走)。`DM_PRESENT_INTERVAL=game` / `display` 可覆盖,也可写在 `Documents/env.txt` | +| `present_interval` | `game`(缺省):按游戏设的出图间隔出帧,同 Switch(按间隔 2 出的一帧在屏上留两个显示节拍;游戏连续一秒都要 2 才换 30 帧目标,iOS 上显示节拍随之 30 Hz;马力欧 1.2.1 在慢一帧之后只给下一帧设 2;Engage 在 Switch 上设 2,2.0.0 `main` 的改写剪口让它设 1,见 `0100A6301214E000/game.tsv`)。`display`:不管游戏设多少,每个显示节拍一帧——只适合逻辑不按帧计数的游戏(D2R 的逻辑按自己的 25 Hz 走)。`DM_PRESENT_INTERVAL=game` / `display` 可覆盖,也可写在 `Documents/env.txt` | | `core_preempt` | `on` / `off`(缺省):近似 Horizon 的按核抢占。一次 SVC 叫醒了钉在调用线程本核、优先级更高的线程,调用线程就等这些线程再次阻塞(最多 5 ms,`hle_thread.c`)。银河战士 生存恐惧的影片播放器靠它;马力欧开了读盘变慢(首帧 0.6 → 2.9 s),所以只有需要的游戏才开。`DM_PREEMPT=0` / `1` 可覆盖 | | `nvn_tex_base`、`nvn_tex_slots`、`nvn_samp_base`、`nvn_img_base`、`nvn_img_slots`、`nvn_ssbo_bases` | NVN 驱动常量段,按全库着色器实测 | | `shader_groups`、`shader_note` | `shader_split.py` 的来源组与映射表表头说明 | +| `shader_main_glslc` | `on` / `off`(缺省):`shader_split.py` 另读 `main` 里的 GLSLC 编译输出(魔数 `0x19866891`)。十三机兵防卫圈的着色器全在那里;Engage 的 `main` 里有 11 份用不到的,所以别的游戏不开 | +| `shader_separable` | `on` / `off`(缺省):游戏建单阶段程序、绘制时按阶段拼,真正的顶点 / 像素配对只在运行时才有。`shader_split.py` 于是给每个顶点着色器配一组「覆盖」——读到的输入合起来等于全部像素着色器读的输入的像素着色器,`dmsh_build.py` 的 `vs_pad` 让每个顶点着色器能接任何像素着色器(十三机兵防卫圈) | 翻译器(`dmsh_xlate/Program.cs`)里仍内置贴图 / 采样器句柄表基址与深度口径。档案的 `nvn_tex_base` / `nvn_samp_base` 与之不同时 `dmsh_build.py` 会停下,所以需要别的值的游戏要先让翻译器可配。 diff --git a/dm/gfx/nvn/hle_nvn.c b/dm/gfx/nvn/hle_nvn.c index 917016d..25426a6 100644 --- a/dm/gfx/nvn/hle_nvn.c +++ b/dm/gfx/nvn/hle_nvn.c @@ -259,6 +259,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(); + // 见 SetPackagedTextureData。线性贴图不按预排布重传:CPU 可见池里的直接建在池缓冲上(tex_linear),读的就是拷进去的内容 + // See SetPackagedTextureData. Linear textures are not re-uploaded as packaged: in a CPU-visible pool they are created on the pool buffer (tex_linear) and read what was copied in + if (s->pkg_data && o->mem) { + memcpy(o->mem, s->pkg_data, o->L.size); + if (!(o->d.flags & (NVN_TEXTURE_FLAGS_LINEAR | NVN_TEXTURE_FLAGS_LINEAR_RENDER_TARGET))) mtl_tex_cpu_written(o); + else if (o->pool->flags & NVN_MEMORY_POOL_FLAGS_CPU_NO_ACCESS) { static int once_; if (!once_++) LOG("[nvn] 【红】线性贴图带预排布数据、池 CPU 不可见:宿主建私有贴图,数据不上传(%ux%u 格式 %#x)", o->d.width, o->d.height, o->d.format); } + } OLOG("Texture %p %ux%ux%u lv%u fmt=%#x tgt=%d flags=%#x size=%#llx", (void *)t, o->d.width, o->d.height, o->d.depth, o->d.levels, o->d.format, o->d.target, o->d.flags, o->L.size); return 1; @@ -921,6 +928,7 @@ static const char *const g_nops[] = { "nvnCommandBufferPushDebugGroupDynamic", "nvnCommandBufferPopDebugGroup", "nvnCommandBufferInsertDebugMarkerDynamic", + "nvnCommandBufferInsertDebugMarker", // 十三机兵防卫圈 | 13 Sentinels "nvnCommandBufferWaitEvent", "nvnWindowFinalize", }; @@ -1199,9 +1207,14 @@ static void sleep_until_ns(uint64_t dl) { // 单调时钟上的绝对时刻 | static int g_hz = -1, g_display_mode; static _Atomic int g_game_itv = 1; // 按游戏间隔出帧时,游戏此刻设的出图间隔(game 模式下 2 = 30 帧,nvn_game_fps 报给图形层定显示节拍) | when pacing by the game's interval, the interval the game currently sets (in game mode 2 = 30 fps; nvn_game_fps reports it to the graphics layer, which sets the display link from it) void nvn_present_config(int hz, int display_mode) { g_hz = hz; g_display_mode = display_mode; } -// 游戏自己要的帧数:game 模式且出图间隔 ≥ 2 时 30(显示节拍随之 30 Hz,mtl_fps_target),否则 60。间隔 3 以上(20 帧以下)没有对应的节拍档,按 30 走 -// The frame rate the game itself asks for: 30 in game mode with a present interval >= 2 (the display link follows at 30 Hz, mtl_fps_target), otherwise 60. Intervals of 3 or more (20 fps or less) have no matching display-link mode and run at 30 -int nvn_game_fps(void) { return g_hz > 0 && !g_display_mode && atomic_load(&g_game_itv) >= 2 ? 30 : 60; } +static _Atomic uint64_t g_itv2_since; // 游戏这一段连续设间隔 ≥ 2 的起点(now_ns;0 = 此刻是 1) | start of the game's current run of intervals >= 2 (now_ns; 0 = it is 1 now) +// 游戏自己要的帧数:game 模式且出图间隔 ≥ 2 持续满 1 秒时 30(显示节拍随之 30 Hz,mtl_fps_target),否则 60;间隔回到 1 立即 60。间隔 3 以上(20 帧以下)没有对应的节拍档,按 30 走。 +// 要持续 1 秒:马力欧 1.2.1 按上一帧的耗时动态选间隔(main+0xcfd24),一帧慢了下一帧设 2、再设回 1(iPhone 两分钟 123 次)。逐帧跟着切显示节拍档会来回抖; +// 单帧的间隔 2 由显示节拍那边按这一帧的周期多留一拍(nvn_mtl.m 节拍回调),不靠切档 +// The frame rate the game itself asks for: 30 in game mode when the present interval has been >= 2 for a full second (the display link follows at 30 Hz, mtl_fps_target), otherwise 60; back to 60 at once when the interval returns to 1. Intervals of 3 or more (20 fps or less) have no matching display-link mode and run at 30. +// The one-second hold: Mario 1.2.1 picks the interval from the previous frame's time (main+0xcfd24), setting 2 for the frame after a slow one and then 1 again (123 times in two minutes on an iPhone). Following that frame by frame would flap the display-link mode; +// a single interval-2 frame is instead kept on screen for its own period by the display-link callback (nvn_mtl.m), without changing the mode +int nvn_game_fps(void) { uint64_t s = atomic_load(&g_itv2_since); return g_hz > 0 && !g_display_mode && s && now_ns() - s >= 1000000000ull ? 30 : 60; } static void present_cfg(const win_t *o) { if (g_hz >= 0) return; // 间隔模式按游戏档案(present_interval,缺省 game):是否按游戏设的出图间隔出帧,取决于游戏逻辑是否按帧计数,各游戏不同。环境变量可覆盖,也可覆盖固定 60 Hz。 @@ -1223,7 +1236,12 @@ static void present_cfg(const win_t *o) { } static uint64_t present_period(const win_t *o) { present_cfg(o); - if (o->interval != atomic_load(&g_game_itv)) atomic_store(&g_game_itv, o->interval > 0 ? o->interval : 1); + int was = atomic_load(&g_game_itv), want = o->interval > 0 ? o->interval : 1; + if (want != was) { + atomic_store(&g_game_itv, want); + if (want < 2) atomic_store(&g_itv2_since, 0); + else if (was < 2) atomic_store(&g_itv2_since, now_ns() | 1); // | 1:起点不会是 0 | | 1: the start is never 0 + } int itv = g_display_mode ? 1 : o->interval > 0 ? o->interval : 1, hz = g_hz; return hz > 0 ? (uint64_t)itv * 1000000000ull / (uint64_t)hz : 0; } @@ -1236,6 +1254,11 @@ NVN(nvnQueuePresentTexture, void, NVNqueue *q, NVNwindow *w, int idx) { { static int gate; if (!gate && g_nvn_loadscreen_up && g_nvn_loadscreen_up()) { gate = 1; int ms = mtl_warm_wait(15000); if (ms) LOG("[nvn] 进局读取画面等管线预热 %d ms", ms); } } present_cfg(o); + { // 测试开关 DM_TEST_PRESENT_LAG=每 N 次出图,毫秒:在第 N、2N…次出图前多睡这么久,模仿真机偶尔慢一帧(马力欧 1.2.1 随后设出图间隔 2,验显示节拍按间隔留拍)。产品不设 + // Test switch DM_TEST_PRESENT_LAG=every N presents,milliseconds: sleep that long before the Nth, 2Nth… present, imitating an occasional slow frame on a phone (Mario 1.2.1 then sets present interval 2; checks that the display link holds frames by interval). Not set in the product + static int lag_n = -1, lag_ms; if (lag_n < 0) { const char *e = getenv("DM_TEST_PRESENT_LAG"); lag_n = 0; if (e && sscanf(e, "%d,%d", &lag_n, &lag_ms) == 2 && lag_n > 0) LOG("DM_TEST_PRESENT_LAG:每 %d 次出图多睡 %d ms", lag_n, lag_ms); else lag_n = 0; } + if (lag_n && o->presents % lag_n == 0) usleep((useconds_t)lag_ms * 1000); + } gfx_enter(); uint64_t t0 = now_ns(); if (idx >= 0 && idx < o->ntex && idx < 4) o->ticket[idx] = mtl_present(CTX(o->tex[idx]), present_period(o) / 1e9, o->crop); // 只入队,立即返回 | enqueue only, return immediately @@ -1441,7 +1464,9 @@ NVN(nvnTextureBuilderSetSwizzle, void, NVNtextureBuilder *b, NVNtextureSwizzle r NVN(nvnTextureBuilderSetDepthStencilMode, void, NVNtextureBuilder *b, NVNtextureDepthStencilMode m) { (void)b; if (m) ONCE_LOG("TextureBuilderSetDepthStencilMode %d:宿主按深度读", (int)m); } NVN(nvnTextureBuilderGetStorageClass, NVNstorageClass, const NVNtextureBuilder *b) { (void)b; return 0; } NVN(nvnTextureBuilderGetZCullStorageSize, size_t, const NVNtextureBuilder *b) { (void)b; return 0; } -NVN(nvnTextureBuilderSetPackagedTextureData, void, NVNtextureBuilder *b, const void *data) { (void)b; (void)data; ONCE_LOG("【红】TextureBuilderSetPackagedTextureData:宿主未实现(数据指针忽略)"); } +// 预排布贴图数据在 CPU 内存里(不由游戏写进池):NVN 在 TextureInitialize 时把它拷进贴图存储(可压缩池 CPU 写不进,要驱动代拷)。十三机兵防卫圈的贴图都这样建 +// Packaged texture data in CPU memory (not written into the pool by the game): NVN copies it into the texture storage at TextureInitialize (the CPU cannot write a compressible pool; the driver copies). 13 Sentinels creates all its textures this way +NVN(nvnTextureBuilderSetPackagedTextureData, void, NVNtextureBuilder *b, const void *data) { TB(b)->pkg_data = data; } NVN(nvnTextureGetFormat, NVNformat, const NVNtexture *t) { return (NVNformat)CTX(t)->d.format; } NVN(nvnTextureGetTarget, NVNtextureTarget, const NVNtexture *t) { return (NVNtextureTarget)CTX(t)->d.target; } NVN(nvnTextureSetDebugLabel, void, NVNtexture *t, const char *s) { (void)t; (void)s; } diff --git a/dm/gfx/nvn/nvn_mtl.m b/dm/gfx/nvn/nvn_mtl.m index 538d4e9..366daa0 100644 --- a/dm/gfx/nvn/nvn_mtl.m +++ b/dm/gfx/nvn/nvn_mtl.m @@ -84,6 +84,7 @@ static pthread_cond_t g_link_cv = PTHREAD_COND_INITIALIZER; static pthread_mutex_t g_link_mu = PTHREAD_MUTEX_INITIALIZER; static uint64_t g_link_tick_ns, g_link_tick_n; static uint64_t g_link_dropped; +static double g_link_hold_until; static uint64_t g_link_held; // 只在显示节拍回调线程里改:上一张按出图间隔要显示到的时刻(0 = 不留),因此没取新帧的节拍数 | Changed only on the display-link callback thread: the time the previous frame must stay on screen until by its present interval (0 = no hold), and the ticks that took no new frame because of it static int g_pq_inflight; // 持 g_pq_mu:已提交给 GPU、完成回调还没来的上屏数 | Under g_pq_mu: number of presents committed to the GPU whose completion callback has not arrived yet int g_dm_present_link; // 出图节拍模式(DM_PRESENT_LINK 的运行时值,同进程交替测量时直接改它):0 = 自己取 drawable;1 = 显示节拍 60 Hz;2 = 120 Hz 节拍、游戏隔拍出一帧 | Present pacing mode (runtime value of DM_PRESENT_LINK, modified directly during same-process alternating measurement): 0 = acquire the drawable itself; 1 = display link at 60 Hz; 2 = display link at 120 Hz, the game presents a frame every other display-link tick static int g_present_latency = 1; @@ -853,6 +854,11 @@ void mtl_drain(void) { present_drop_for_suspend(); mtl_present_flush(); mtl_init #include "../../host/front.h" static int g_red_once[80]; #define RED_ONCE(k, ...) do { if (!g_red_once[k]++) MLOG("【红】" __VA_ARGS__); } while (0) +// NVN 顶点流 0–15 → Metal 顶点缓冲槽:0–13 放 16–29(30 是常量回退缓冲),14、15 放 14、15。Metal 只有 0–30 共 31 个槽;翻译出的着色器只用 buffer(0)(参数缓冲), +// 宿主自己的顶点函数不读顶点缓冲。十三机兵防卫圈用到流 15,原先的 16 + 流号越界(Metal 断言 buffer index (31) must be < 31) +// NVN vertex streams 0–15 → Metal vertex buffer slots: 0–13 go to 16–29 (30 is the constant fallback buffer), 14 and 15 to 14 and 15. Metal has only the 31 slots 0–30; translated shaders use only buffer(0) (the argument buffer), +// and the host's own vertex functions read no vertex buffers. 13 Sentinels uses stream 15, and the old 16 + stream went out of range (Metal assertion: buffer index (31) must be < 31) +#define VB_SLOT(st) ((st) < 14 ? 16 + (st) : (st)) // ---------- 贴图 → MTLTexture ---------- // ---------- Texture → MTLTexture ---------- @@ -2797,10 +2803,10 @@ static MTLRenderPipelineDescriptor *pso_desc(const pso_key *k, NvShader *vs, NvS } int st = k->attr[a].stream; vf = vfmt_for_ct(vf, rf->v[v].ct, k->attr[a].fmt); - vd.attributes[loc].format = vf; vd.attributes[loc].offset = k->attr[a].offset; vd.attributes[loc].bufferIndex = 16 + st; - vd.layouts[16 + st].stride = k->stream[st].stride ? k->stream[st].stride : 4; - vd.layouts[16 + st].stepFunction = k->stream[st].stride == 0 ? MTLVertexStepFunctionConstant : k->stream[st].divisor ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex; - vd.layouts[16 + st].stepRate = k->stream[st].stride == 0 ? 0 : k->stream[st].divisor ? k->stream[st].divisor : 1; + vd.attributes[loc].format = vf; vd.attributes[loc].offset = k->attr[a].offset; vd.attributes[loc].bufferIndex = VB_SLOT(st); + vd.layouts[VB_SLOT(st)].stride = k->stream[st].stride ? k->stream[st].stride : 4; + vd.layouts[VB_SLOT(st)].stepFunction = k->stream[st].stride == 0 ? MTLVertexStepFunctionConstant : k->stream[st].divisor ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex; + vd.layouts[VB_SLOT(st)].stepRate = k->stream[st].stride == 0 ? 0 : k->stream[st].divisor ? k->stream[st].divisor : 1; } d.vertexDescriptor = vd; return d; @@ -3241,6 +3247,7 @@ static int encode_args_(id<MTLCommandEncoder> en, NvShader *sh, int nvn_stage, i return 1; } static id<MTLBuffer> g_quad_ib; static uint64_t g_quad_gpu; +static id<MTLBuffer> g_fan_ib; static uint64_t g_fan_gpu; // 无索引三角扇:共享索引 (0, i, i+1),基顶点 = first | non-indexed triangle fans: shared indices (0, i, i+1), base vertex = first static id<MTL4RenderCommandEncoder> rp_begin(void); // ---------- 通道前瞻:本通道是否用到深度 / 模板 ---------- // 开通道时从当前绘制的下一条起往后扫,模拟深度模板状态的变化,直到本通道必然结束处(重设目标、拷贝、计算派发—— @@ -4834,7 +4841,7 @@ frz_plain:; if (!S.vbuf[s_].address) continue; void *mb; uint64_t mo; uint64_t gpu; - if (mtl_gpu_addr(S.vbuf[s_].address, 1, &mb, &mo, &gpu)) { enc_r_vbuf(en, 16 + s_, gpu); use_rec("buf", S.vbuf[s_].address, S.vbuf[s_].size, 0, NULL); } + if (mtl_gpu_addr(S.vbuf[s_].address, 1, &mb, &mo, &gpu)) { enc_r_vbuf(en, VB_SLOT(s_), gpu); use_rec("buf", S.vbuf[s_].address, S.vbuf[s_].size, 0, NULL); } } static id<MTLBuffer> zero; if (!zero) { zero = [g_dev newBufferWithLength:64 options:MTLResourceStorageModeShared]; res_add(zero); } static uint64_t zero_gpu; if (!zero_gpu) zero_gpu = zero.gpuAddress; @@ -4848,7 +4855,7 @@ frz_plain:; MTLPrimitiveType mp; switch (prim) { case 0: mp = MTLPrimitiveTypePoint; break; case 1: mp = MTLPrimitiveTypeLine; break; case 3: mp = MTLPrimitiveTypeLineStrip; break; - case 4: case 7: mp = MTLPrimitiveTypeTriangle; break; case 5: mp = MTLPrimitiveTypeTriangleStrip; break; + case 4: case 6: case 7: mp = MTLPrimitiveTypeTriangle; break; case 5: mp = MTLPrimitiveTypeTriangleStrip; break; // 6 三角扇、7 四边形:Metal 没有,展开成三角形列表 | 6 triangle fan, 7 quads: Metal has neither, expanded into triangle lists default: RED_ONCE(19, "图元类型 %d 未实现", prim); return; } if (g_vis_counting) { // 遮挡查询计数:本通道新槽(报告 / 清零后也换新槽) | Occlusion query counting: a new slot for this render pass (also switch to a new slot after a report / reset) @@ -4926,11 +4933,39 @@ frz_plain:; g_quad_ib = [g_dev newBufferWithBytes:ix length:n * 24 options:MTLResourceStorageModeShared]; g_quad_gpu = g_quad_ib.gpuAddress; res_add(g_quad_ib); free(ix); } if (dw->count >= 4) enc_r_draw_indexed(en, mp, (dw->count / 4) * 6, MTLIndexTypeUInt32, g_quad_gpu, g_quad_ib.length, dw->instances > 0 ? dw->instances : 1, dw->first, dw->base_instance); + } else if (dw && prim == 6) { // 三角扇(十三机兵防卫圈):(0, i, i+1) 共享索引,first 作基顶点 | triangle fans (13 Sentinels): shared indices (0, i, i+1), first as the base vertex + if (!g_fan_ib) { + uint32_t n = 65536; uint32_t *ix = malloc(n * 12); + for (uint32_t i = 0; i < n; i++) { uint32_t v[3] = {0, i + 1, i + 2}; memcpy(ix + i * 3, v, 12); } + g_fan_ib = [g_dev newBufferWithBytes:ix length:n * 12 options:MTLResourceStorageModeShared]; g_fan_gpu = g_fan_ib.gpuAddress; res_add(g_fan_ib); free(ix); + } + if (dw->count >= 3) enc_r_draw_indexed(en, mp, (dw->count - 2) * 3, MTLIndexTypeUInt32, g_fan_gpu, g_fan_ib.length, dw->instances > 0 ? dw->instances : 1, dw->first, dw->base_instance); } else if (dw) { if (dw->count && (dw->instances > 1 || dw->base_instance)) enc_r_draw_instanced(en, mp, dw->first, dw->count, dw->instances > 0 ? dw->instances : 1, dw->base_instance); else if (dw->count) enc_r_draw(en, mp, dw->first, dw->count); } else { - if (prim == 7) { RED_ONCE(20, "带索引的四边形未实现"); return; } + if (prim == 6 || prim == 7) { // 带索引的三角扇 / 四边形(十三机兵防卫圈):回放时从客体内存读索引,展开成 32 位三角形列表放进本帧竞技场(同 8 位索引) + // indexed triangle fans / quads (13 Sentinels): at replay the indices are read from guest memory and expanded into a 32-bit triangle list in this frame's arena (as for 8-bit indices) + int isz = di->itype == 0 ? 1 : di->itype == 1 ? 2 : 4; + uint32_t nt = prim == 7 ? (uint32_t)(di->count / 4) * 2 : di->count >= 3 ? (uint32_t)di->count - 2 : 0; + if (!nt) return; + void *mb; uint64_t mo; const uint8_t *src; + if (!mtl_addr(di->ibuf, (uint64_t)di->count * isz, &mb, &mo) || !(src = [(__bridge id<MTLBuffer>)mb contents])) { RED_ONCE(22, "索引缓冲不在内存池里"); return; } + if (pend_hits(di->ibuf, (uint64_t)di->count * isz)) RED_ONCE(73, "三角扇 / 四边形索引缓冲本提交里有 GPU 侧写入:按当前内容展开"); + src += mo; + #define IX(i) (isz == 1 ? (uint32_t)src[i] : isz == 2 ? (uint32_t)((const uint16_t *)src)[i] : ((const uint32_t *)src)[i]) + id<MTLBuffer> ab; size_t off = arena_alloc(((size_t)nt * 12 + 15) & ~(size_t)15, &ab); + uint32_t *d = (uint32_t *)((uint8_t *)ab.contents + off); + for (uint32_t t = 0; t < nt; t++) { + if (prim == 6) { d[3 * t] = IX(0); d[3 * t + 1] = IX(t + 1); d[3 * t + 2] = IX(t + 2); } + else { uint32_t q = (t >> 1) * 4, h = t & 1; d[3 * t] = IX(q); d[3 * t + 1] = IX(q + 1 + h); d[3 * t + 2] = IX(q + 2 + h); } + } + #undef IX + use_rec("buf", di->ibuf, (uint64_t)di->count * isz, 0, NULL); + enc_r_draw_indexed(en, mp, nt * 3, MTLIndexTypeUInt32, g_cur_arena->gpu + off, (NSUInteger)nt * 12, di->instances ? di->instances : 1, di->base_vertex, di->base_instance); + if (g_rt_range_pending) { PD("j rt_level_range pass=%llu vc=%u", g_stat_pass, (unsigned)di->count); g_rt_range_pending = 0; } + return; + } if (di->itype == 0) { // 8 位索引:Metal 只有 16 / 32 位。回放时从客体内存读出、展开成 16 位放进本帧竞技场(SMBW 的全屏通道与最后拷进窗口那一下都用 8 位索引,不展开就整屏黑) // 8-bit indices: Metal only has 16 / 32-bit. At replay they are read from guest memory and widened to 16-bit in this frame's arena (SMBW's full-screen passes and the final copy into the window all use 8-bit indices; without this the whole screen is black) if (!di->count) return; @@ -6133,8 +6168,8 @@ static int present_now(const tex_t *t, double period, uint64_t frame, uint64_t t for (int i = 1; i < 300; i++) { double v = a[i]; int j = i - 1; while (j >= 0 && a[j] > v) { a[j + 1] = a[j]; j--; } a[j + 1] = v; } double b[300]; memcpy(b, e2e, sizeof b); for (int i = 1; i < 300; i++) { double v = b[i]; int j = i - 1; while (j >= 0 && b[j] > v) { b[j + 1] = b[j]; j--; } b[j + 1] = v; } - MLOG("显示节拍 至 %llu:节拍 300、出图 %llu、空节拍 %llu、合并丢弃旧帧 %llu;提交领先目标上屏 p50 %.3f ms / 最小 %.3f ms;游戏入队到目标上屏 p50 %.3f ms / 最小 %.3f / 最大 %.3f ms", - (unsigned long long)g_link_tick_n, (unsigned long long)g_present_n, (unsigned long long)(g_link_tick_n - g_present_n), + MLOG("显示节拍 至 %llu:节拍 300、出图 %llu、空节拍 %llu(其中按出图间隔留拍 %llu)、合并丢弃旧帧 %llu;提交领先目标上屏 p50 %.3f ms / 最小 %.3f ms;游戏入队到目标上屏 p50 %.3f ms / 最小 %.3f / 最大 %.3f ms", + (unsigned long long)g_link_tick_n, (unsigned long long)g_present_n, (unsigned long long)(g_link_tick_n - g_present_n), (unsigned long long)g_link_held, (unsigned long long)g_link_dropped, a[149], a[0], b[149], b[0], b[299]); } } @@ -6206,6 +6241,11 @@ __attribute__((weak)) uint64_t dm_focus_in_since(void) { return 0; } pthread_mutex_unlock(&g_link_mu); pthread_mutex_lock(&g_pq_mu); if (!g_dm_present_link || g_pq_read == g_pq_in) { pthread_mutex_unlock(&g_pq_mu); return; } + // 上一张的出图间隔 ≥ 2(周期 > 25 ms):显示满它的周期再取下一张,同 Switch 的「隔 N 个垂直同步换帧」。节拍档仍是 60 Hz 时也对(马力欧 1.2.1 单帧设 2,见 hle_nvn.c nvn_game_fps); + // 原先每拍都取最新一张,这一帧连同游戏为它多走的一步时间只显示 16.7 ms,看起来一下快进 + // The previous frame's present interval was >= 2 (period > 25 ms): keep it on screen for its whole period before taking the next one, as the Switch flips only every N vertical syncs. This also holds while the link runs at 60 Hz (Mario 1.2.1 sets 2 for single frames, see nvn_game_fps in hle_nvn.c); + // before, every tick took the newest frame, so that frame, carrying the extra time step the game advanced for it, was shown for only 16.7 ms and the game looked like it jumped ahead + if (g_link_hold_until > 0 && update.targetPresentationTimestamp < g_link_hold_until - 0.002) { g_link_held++; pthread_mutex_unlock(&g_pq_mu); return; } uint64_t first = g_pq_read + 1, ticket = g_pq_in; pq_item it = g_pq[(g_pq_in - 1) % PQ]; g_link_it_tin = it.t_in; g_pq_read = g_pq_in; @@ -6215,6 +6255,7 @@ __attribute__((weak)) uint64_t dm_focus_in_since(void) { return 0; } pthread_mutex_unlock(&g_pq_mu); int submitted; @autoreleasepool { submitted = present_now(&it.t, it.period, it.frame, ticket, it.crop, update.drawable, update.targetPresentationTimestamp); } + g_link_hold_until = submitted && it.period > 0.025 ? update.targetPresentationTimestamp + it.period : 0; if (!submitted) { pthread_mutex_lock(&g_pq_mu); if (ticket > g_pq_done) g_pq_done = ticket; diff --git a/dm/gfx/nvn/nvn_obj.h b/dm/gfx/nvn/nvn_obj.h index 58008d1..da1dd4a 100644 --- a/dm/gfx/nvn/nvn_obj.h +++ b/dm/gfx/nvn/nvn_obj.h @@ -29,6 +29,7 @@ FITS(dpool_t, NVNtexturePool); FITS(dpool_t, NVNsamplerPool); typedef struct { NVNdevice *dev; nvn_texdesc d; pool_t *pool; int64_t off; uint8_t has_layout, layout[8]; + const void *pkg_data; // TextureBuilderSetPackagedTextureData:TextureInitialize 时拷进存储(十三机兵防卫圈) | TextureBuilderSetPackagedTextureData: copied into the storage at TextureInitialize (13 Sentinels) uint8_t set_swz; uint16_t swz; // TextureBuilderSetSwizzle:r | g << 3 | b << 6 | a << 9(NVN 与 Metal 的通道枚举同值:0 零 1 一 2 R 3 G 4 B 5 A) | TextureBuilderSetSwizzle: r | g << 3 | b << 6 | a << 9 (NVN and Metal channel enums have the same values: 0 zero 1 one 2 R 3 G 4 B 5 A) } tex_builder_t; FITS(tex_builder_t, NVNtextureBuilder); // 字段顺序压到 0x80:较新 SDK(SMBW,NVN 55)按 0x80 字节分配 NVNtexture(main 0x978c3c 起分配、0x978cd4 初始化), diff --git a/dm/gfx/tests/t_mip_extent.m b/dm/gfx/tests/t_mip_extent.m index a0039c3..5f5b817 100644 --- a/dm/gfx/tests/t_mip_extent.m +++ b/dm/gfx/tests/t_mip_extent.m @@ -1,6 +1,10 @@ // 真实NVN录制入口→生产Metal回放;8x6各mip独立手算,未设置viewport不借显式viewport规避。 // Real NVN recording entry → production Metal replay; each mip of 8x6 is hand-calculated independently; with no viewport set, an explicit viewport is not used to dodge it. +// nvn_mtl.m 的 nvn_game_fps 是弱定义缺省值,hle_nvn.c 给强定义;同一编译单元里把弱的那份改名(同 t_present_crop.m) +// nvn_mtl.m's nvn_game_fps is a weak default and hle_nvn.c gives the strong definition; in one translation unit the weak one is renamed (as in t_present_crop.m) +#define nvn_game_fps nvn_game_fps_mtl_default #include "../nvn/nvn_mtl.m" +#undef nvn_game_fps #include "../nvn/hle_nvn.c" uint64_t g_t0_ns; // 宿主 main.c 的进程起点(hle_nvn 逐帧 @t_s 用),单测替身 | process start time from host main.c (used by hle_nvn's per-frame @t_s); unit test stub void *front_metal_layer(void) { return NULL; } diff --git a/dm/gfx/tests/t_present_crop.m b/dm/gfx/tests/t_present_crop.m index b1f06ac..10809ca 100644 --- a/dm/gfx/tests/t_present_crop.m +++ b/dm/gfx/tests/t_present_crop.m @@ -1,6 +1,10 @@ // 出图裁切回归:彩色8×8纹理,闭式RGBA期望;真实Metal渲染,不依赖游戏或跨局确定性。 // Present crop regression: colored 8×8 texture, closed-form RGBA expectations; real Metal rendering, no dependence on the game or on cross-run determinism. +// nvn_mtl.m 的 nvn_game_fps 是弱定义缺省值,hle_nvn.c 给强定义;两者进同一个编译单元就重定义,把弱的那份改名(本测试不看帧数档) +// nvn_mtl.m's nvn_game_fps is a weak default and hle_nvn.c gives the strong definition; in one translation unit that is a redefinition, so the weak one is renamed (this test does not look at the frame-rate target) +#define nvn_game_fps nvn_game_fps_mtl_default #include "../nvn/nvn_mtl.m" +#undef nvn_game_fps #include "../nvn/hle_nvn.c" uint64_t g_t0_ns; // 宿主 main.c 的进程起点(hle_nvn 逐帧 @t_s 用),单测替身 | process start time from host main.c (used by hle_nvn's per-frame @t_s); unit test stub void *front_metal_layer(void) { return NULL; } diff --git a/dm/gfx/tools/shader_split.py b/dm/gfx/tools/shader_split.py index 3a27d58..d054680 100644 --- a/dm/gfx/tools/shader_split.py +++ b/dm/gfx/tools/shader_split.py @@ -51,6 +51,8 @@ import collections, concurrent.futures as cf, hashlib, json, os, re, struct, sub sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../games')); import game as _game GAME = _game.load() # 当前游戏档案(DM_GAME,必给):来源组规则与表头说明 | the current game profile (DM_GAME, required): source group rules and the table header note GROUPS, NOTE = GAME.pairs('shader_groups'), GAME.get('shader_note') +GLSLC = GAME.get('shader_main_glslc', 'off') == 'on' # main 里的 GLSLC 编译输出(glslc_shaders) | GLSLC compiler outputs in main (glslc_shaders) +SEPARABLE = GAME.get('shader_separable', 'off') == 'on' # 单阶段程序、绘制时按阶段拼(separable_pairs) | single-stage programs combined per stage at draw time (separable_pairs) CTL_MAGIC, GFX_MAGIC, CS_MAGIC = bytes.fromhex('34127698'), bytes.fromhex('78563412'), bytes.fromhex('79563412') STAGES = ['vs', 'hs', 'ds', 'gs', 'ps', 'cs'] @@ -117,6 +119,25 @@ def mshd_shaders(d, label): if len(out) % 2: red(f'{label} MSHD:{len(out)} 个阶段,不成对') return out +def glslc_shaders(d, label): + """GLSLC 编译输出(十三机兵防卫圈 main 只读段里 838 个,各一个阶段):魔数 0x19866891 起,+0x44 = 整份长;份内恰有一个控制段,代码区紧跟其后(控制段 +0x20 = 控制段长)。 + 魔数是 4 字节,别的游戏的 main 里也有(火焰纹章 Engage 有 11 份),所以只在档案 shader_main_glslc 开时读,且只收三重核对都过的 + GLSLC compiler outputs (838 in 13 Sentinels' main read-only segment, one stage each): from the magic 0x19866891, +0x44 = the whole output's length; each output holds exactly one control section with the code area right after it (control +0x20 = control length). + The magic is only 4 bytes and other games' mains have it too (Fire Emblem Engage has 11), so this runs only when the profile's shader_main_glslc is on, and keeps only outputs that pass the triple check""" + out = [] + for m in re.finditer(b'\x91\x68\x86\x19', d): + h = m.start(); size = struct.unpack_from('<I', d, h + 0x44)[0] + c = d.find(CTL_MAGIC, h, h + size) if 0x880 < size <= len(d) - h else -1 + if c < 0 or d.find(CTL_MAGIC, c + 4, h + size) >= 0: continue + nctl, ncode = struct.unpack_from('<I', d, c + 0x20)[0], struct.unpack_from('<I', d, c + 0x704)[0] + if nctl < 0x880 or c + nctl + ncode > h + size: continue + ctl, code = d[c:c + nctl], d[c + nctl:c + nctl + ncode] + st = 'cs' if code[:4] == CS_MAGIC else SPH_STAGE.get((struct.unpack_from('<I', code, 0x30)[0] >> 10) & 0xf) if code[:4] == GFX_MAGIC else None + if not st or check(label, st, ctl, code): continue + out.append((f'{label}#GLSLC@{h:#x}', st, ctl, code)) + if not out: red(f'{label}:档案开了 shader_main_glslc,却没有一份 GLSLC 输出') + return out + def sarc_members(d): hl, bom, size, doff = struct.unpack_from('<HHII', d, 4) n = struct.unpack_from('<H', d, hl + 6)[0] @@ -177,6 +198,24 @@ def group(label): if k in label: return g return 'other' +def ps_inputs(code): + """像素着色器读的通用属性:SPH 第 6–13 字是 32 个属性各 8 位的输入图(Ryujinx ShaderHeader.ImapTypes),非 0 = 读;翻译出的 MSL 输入就是 user(locn<属性号>) + Generic attributes a pixel shader reads: SPH words 6–13 are the input map, 8 bits for each of the 32 attributes (Ryujinx ShaderHeader.ImapTypes), non-zero = read; the translated MSL inputs are exactly user(locn<attribute>)""" + return {i for i in range(32) if struct.unpack_from('<I', code, 0x30 + 4 * (6 + (i >> 2)))[0] >> ((i & 3) * 8) & 0xff} + +def separable_pairs(keys): + """单阶段程序(十三机兵防卫圈:每份 GLSLC 输出建一个只有一个阶段的程序,绘制时 BindProgram 按阶段位把顶点、像素程序拼起来),真配对只在绘制时才有。 + vs_pad 只用配对像素着色器读的位置的并集:所以每个顶点着色器配上一组「覆盖」——读到的位置合起来等于全部像素着色器读的位置的最少几个像素着色器(贪心,同样多时取键小的), + 补完的顶点着色器能接任何一个像素着色器。全配对要 376 × 375 行,覆盖只要几行 ×376 + Single-stage programs (13 Sentinels: each GLSLC output becomes a program with one stage, and BindProgram's stage bits combine vertex and pixel programs per draw) have their real pairs only at draw time. + vs_pad uses only the union of the locations the paired pixel shaders read, so every vertex shader is paired with a "cover": the fewest pixel shaders whose read locations together equal those of all pixel shaders (greedy, ties go to the smaller key); + padded that way, a vertex shader links with any pixel shader. All pairs would be 376 × 375 rows; the cover needs a few rows × 376""" + ins = {k: ps_inputs(v[3]) for k, v in keys.items() if v[1] == 'ps'} + left, cover = set().union(*ins.values()) if ins else set(), [] + while left: + k = min(ins, key=lambda k: (-len(ins[k] & left), k)); cover.append(k); left -= ins[k] + return {(v, p) for v in keys if keys[v][1] == 'vs' for p in cover}, cover + def var_pairs(found): """BNSH 同一变体里的(顶点键, 像素键) | (vertex key, pixel key) within the same BNSH variation""" var = collections.defaultdict(dict) @@ -231,6 +270,7 @@ def main(): try: common = unityfs.common_strings(img) except ValueError: common = None # 不是 Unity 游戏 | not a Unity game found = []; scan_blob(img, 'main' + ('@' + tag if tag else ''), found) + if GLSLC: found += glslc_shaders(img, 'main' + ('@' + tag if tag else '')) results.append(('main', {hashlib.sha256(c[:0x880] + k).hexdigest()[:16]: (w, s, c, k) for w, s, c, k in found}, var_pairs(found), {})) keys = {} # 第一遍:全部文件;Unity 资源包带类型树,顺手收下 Shader 的树。第二遍:没带类型树的 Unity 序列化文件借这些树再扫 @@ -255,6 +295,9 @@ def main(): if keys[key][2] != v[2] or keys[key][3] != v[3]: red(f'键 {key} 相同而控制段或代码区不同({v[0]} / {keys[key][0]})') continue keys[key] = v; per_src[group(v[0])] += 1 + if SEPARABLE: + sp, cover = separable_pairs(keys); pairs |= sp + print(f'单阶段程序:每个顶点着色器配 {len(cover)} 个像素着色器作覆盖({" ".join(cover)}) | single-stage programs: every vertex shader is paired with a cover of {len(cover)} pixel shader(s)') os.makedirs(pkg, exist_ok=True) for f in os.listdir(pkg): if f.endswith('.bin'): os.unlink(os.path.join(pkg, f)) @@ -274,7 +317,7 @@ def main(): with open(os.path.join(pkg, name), 'wb') as f: f.write(b''.join(rr)) with open(mapout, 'w') as f: kinds = ' / '.join(k for k, hit in (('BNSH / BAHS', any('#BNSH@' in r[4] or '#BAHS@' in r[4] for r in rows)), ('Unity Shader', any('#var' in r[4] and '#BNSH@' not in r[4] and '#MSHD#' not in r[4] for r in rows)), - ('MSHD', any('#MSHD#' in r[4] for r in rows))) if hit) + ('MSHD', any('#MSHD#' in r[4] for r in rows)), ('GLSLC', any('#GLSLC@' in r[4] for r in rows))) if hit) f.write(f'# kind: evidence {NOTE}(RomFS + main 里的 {kinds})→ 控制段 + 代码区键(dm/gfx/tools/shader_split.py)\n') f.write('# 第 5 列记每个键首次出现的位置;本作没有 PC 版配对\n') f.write('package\tindex\tstage\tdm_ctl_sha16\tsource\n') diff --git a/dm/host/hle_audio.c b/dm/host/hle_audio.c index f2adb8c..08e198c 100644 --- a/dm/host/hle_audio.c +++ b/dm/host/hle_audio.c @@ -243,6 +243,7 @@ HLE_FORCE("_ZN2nn5audio13CloseAudioOutEPNS0_8AudioOutE", "关闭音频输出", v HLE_FORCE("_ZN2nn5audio16GetAudioOutStateEPKNS0_8AudioOutE", "播放状态", int, h_au_State, const void *g) { return atomic_load_explicit(&ao(g)->state, memory_order_acquire); } HLE_FORCE("_ZN2nn5audio21GetAudioOutSampleRateEPKNS0_8AudioOutE", "采样率", int, h_au_Rate, const void *g) { (void)ao(g); return RATE; } HLE_FORCE("_ZN2nn5audio23GetAudioOutChannelCountEPKNS0_8AudioOutE", "声道数", int, h_au_Ch, const void *g) { return ao(g)->ch; } +HLE_FORCE("_ZN2nn5audio15GetAudioOutNameEPKNS0_8AudioOutE", "设备名;宿主只有缺省设备", const char *, h_au_Name, const void *g) { (void)g; return "DeviceOut"; } HLE_FORCE("_ZN2nn5audio23GetAudioOutSampleFormatEPKNS0_8AudioOutE", "采样格式;只做 PCM16", int, h_au_Fmt, const void *g) { (void)ao(g); return FMT_PCM16; } HLE_FORCE("_ZN2nn5audio21SetAudioOutBufferInfoEPNS0_14AudioOutBufferEPvmm", "缓冲描述;写进客体结构体前 24 字节", void, h_au_SetBuf, gbuf *b, void *p, uint64_t cap, uint64_t size) { diff --git a/dm/host/hle_fs.c b/dm/host/hle_fs.c index 4edb767..64f1fc3 100644 --- a/dm/host/hle_fs.c +++ b/dm/host/hle_fs.c @@ -773,6 +773,21 @@ static int rm_tree(const char *p) { HLE_FORCE("_ZN2nn2fs26DeleteDirectoryRecursivelyEPKc", "fsp-srv → 递归删除(只在可写挂载内)", Result, h_fs_DeleteDirRec, const char *path) { HP(path); RW(); return rm_tree(hp) ? err_res("rmtree", hp) : RES_OK; } +// 清空目录、留下目录本身(十三机兵防卫圈) | empty a directory and keep the directory itself (13 Sentinels) +HLE_FORCE("_ZN2nn2fs25CleanDirectoryRecursivelyEPKc", "fsp-srv → 递归删除目录内容、保留目录(只在可写挂载内)", Result, h_fs_CleanDirRec, const char *path) { + HP(path); RW(); + DIR *d = opendir(hp); if (!d) return err_res("cleandir", hp); + struct dirent *e; char c[1024]; int rc = 0; + while ((e = readdir(d))) { + if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, "..")) continue; + snprintf(c, sizeof c, "%s/%s", hp, e->d_name); + rc |= e->d_type == DT_DIR ? rm_tree(c) : unlink(c); + } + closedir(d); + return rc ? err_res("cleandir", hp) : RES_OK; +} +// 文件数据缓存(fs 进程里的全局读缓存):宿主直读页缓存,接下缓冲区不用 | file data cache (a global read cache in the fs process): the host reads through the page cache and leaves the buffer unused +HLE_FORCE("_ZN2nn2fs25EnableGlobalFileDataCacheEPvm", "fsp-srv 全局读缓存;宿主不用", void, h_fs_EnableGlobalCache, void *buf, size_t sz) { (void)buf; (void)sz; } // SDK 11(银河战士 生存恐惧):删空目录 | SDK 11 (Metroid Dread): delete an empty directory HLE_FORCE("_ZN2nn2fs15DeleteDirectoryEPKc", "fsp-srv → rmdir(2)(目录须空)", Result, h_fs_DeleteDir, const char *path) { HP(path); RW(); return rmdir(hp) ? err_res("rmdir", hp) : RES_OK; diff --git a/dm/host/hle_hid.c b/dm/host/hle_hid.c index 05d81ec..41a502e 100644 --- a/dm/host/hle_hid.c +++ b/dm/host/hle_hid.c @@ -147,6 +147,11 @@ HLE_FORCE("_ZN2nn3hid6detail19SendVibrationValuesEPKNS0_21VibrationDeviceHandleE // Mouse / keyboard: not connected HLE_FORCE("_ZN2nn3hid15InitializeMouseEv", "鼠标;宿主不提供", void, h_hid_InitMouse, void) {} HLE_FORCE("_ZN2nn3hid14GetMouseStatesEPNS0_10MouseStateEi", "鼠标状态:0 条", int, h_hid_MouseStates, void *out, int n) { (void)out; (void)n; return 0; } +// 单条(十三机兵防卫圈每帧调;没接管时 sdk 里没初始化的鼠标资源中止):MouseState 0x28 字节 {采样号 i64, x, y, dx, dy, 滚轮 dx, dy, 按键, 属性},属性 0 = 未连接 +// Single state (13 Sentinels calls it every frame; without the hook the sdk aborts on its uninitialized mouse resource): MouseState is 0x28 bytes {sampling number i64, x, y, dx, dy, wheel dx, dy, buttons, attributes}; attributes 0 = not connected +HLE_FORCE("_ZN2nn3hid13GetMouseStateEPNS0_10MouseStateE", "鼠标状态:未连接(MouseState 0x28 字节清零)", void, h_hid_MouseState, void *s) { + memset(s, 0, 0x28); ((int64_t *)s)[0] = atomic_fetch_add(&g_sampling, 1) + 1; +} HLE_FORCE("_ZN2nn3hid18InitializeKeyboardEv", "键盘;宿主不提供", void, h_hid_InitKbd, void) {} HLE_FORCE("_ZN2nn3hid16GetKeyboardStateEPNS0_13KeyboardStateE", "键盘状态:未连接(KeyboardState 0x30 字节清零)", void, h_hid_KbdState, void *s) { memset(s, 0, 0x30); ((int64_t *)s)[0] = atomic_fetch_add(&g_sampling, 1) + 1; diff --git a/dm/host/hle_oe.c b/dm/host/hle_oe.c index c8c19cb..a6e92e7 100644 --- a/dm/host/hle_oe.c +++ b/dm/host/hle_oe.c @@ -78,6 +78,7 @@ static int opmode(void) { return dm_opmode(); } // 截图 / 录像开关:宿主无系统截图服务,空操作 // Screenshot / video recording switches: the host has no system screenshot service, no-op HLE_FORCE("_ZN2nn2oe17FinishStartupLogoEv", "收起系统启动画面(经 am 应用代理);宿主没有启动画面", void, h_oe_FinishLogo, void) {} // 银河战士 生存恐惧在 main+0xadfed0 调 | Metroid Dread calls it at main+0xadfed0 +HLE_FORCE("_ZN2nn2oe44SetUserInactivityDetectionTimeExtendedUnsafeEb", "无操作自动熄屏时长(经 am);宿主不熄屏", void, h_oe_InactivityExt, int on) { (void)on; } HLE_FORCE("_ZN2nn2oe15EnableRecordingEv", "录像开关;宿主无系统录像", void, h_oe_EnableRec, void) {} HLE_FORCE("_ZN2nn2oe16DisableRecordingEv", "录像开关;宿主无系统录像", void, h_oe_DisableRec, void) {} HLE_FORCE("_ZN2nn2oe16EnableScreenShotEv", "截图开关;宿主无系统截图", void, h_oe_EnableShot, void) {} diff --git a/dm/host/hle_os.c b/dm/host/hle_os.c index f4022f0..908bcb5 100644 --- a/dm/host/hle_os.c +++ b/dm/host/hle_os.c @@ -123,3 +123,31 @@ HLE("getentropy", int, g_getentropy, void *p, size_t n) { for (size_t i = 0; i < n; i += 8) { uint64_t v = dm_entropy64(); memcpy((uint8_t *)p + i, &v, n - i < 8 ? n - i : 8); } return 0; } + +// ---------- nn::mem::StandardAllocator::Allocate(size):十三机兵防卫圈的大块按 64 KB 对齐 ---------- +// 十三机兵防卫圈 1.10(main 4b632573…)自带的 malloc(main+0x310550)就是 StandardAllocator::Allocate(size)。它的显存池存储用 malloc 拿、只按 4 KB 取整(main+0x3073b8), +// 池内再按 64 KB 对齐(相对显存地址,main+0x30c118)切出三个环形堆,池大小正好是三者之和(main+0x307adc)。Switch 上池的显存地址按大页对齐;宿主的显存地址就是 CPU 地址,只有 4 KB 对齐, +// 开头丢掉最多 60 KB,第三个环形堆(16 MB,命令缓冲记录期间主线程的分配用它)切不出来、基址留 0,第一次记录时分配就写空指针(main+0x2fa9a0,序章进第一场战斗时)。 +// 所以本构建 ≥ 64 KB 的分配改按 64 KB 对齐(Allocate(size, 0x10000)),池存储起点即 64 KB 对齐。别的构建原样转调 +// ---------- nn::mem::StandardAllocator::Allocate(size): 13 Sentinels' large blocks aligned to 64 KB ---------- +// 13 Sentinels 1.10's own malloc (main 4b632573…, main+0x310550) is StandardAllocator::Allocate(size). It gets the storage of a GPU memory pool from malloc rounded only to 4 KB (main+0x3073b8), +// then cuts three ring heaps out of the pool at 64 KB alignment relative to the GPU address (main+0x30c118), and the pool size is exactly their sum (main+0x307adc). On the Switch a pool's GPU address is aligned to a big page; +// the host's GPU address is the CPU address, which is only 4 KB aligned, so up to 60 KB is lost at the start, the third ring heap (16 MB, used by the main thread's allocations while a command buffer is recorded) does not fit and keeps base 0, +// and the first allocation while recording writes through a null pointer (main+0x2fa9a0, when the prologue enters its first battle). +// So on this build allocations of 64 KB or more are aligned to 64 KB (Allocate(size, 0x10000)), which puts the pool storage on a 64 KB boundary. Other builds forward unchanged +#define DM_SENTINELS_110_BUILD "4b632573ca5c160eabb70519fb622718" +static void *(*g_sa_alloc)(void *, size_t), *(*g_sa_alloc_al)(void *, size_t, size_t); +static int g_sa_big_align; +static pthread_once_t g_sa_once = PTHREAD_ONCE_INIT; +static void sa_init(void) { + module_t *ms = NULL; for (int i = 0; i < g_nmod; i++) if (!strcmp(g_mod[i].name, "sdk")) ms = &g_mod[i]; + g_sa_alloc = ms ? module_sym(ms, "_ZN2nn3mem17StandardAllocator8AllocateEm") : NULL; + g_sa_alloc_al = ms ? module_sym(ms, "_ZN2nn3mem17StandardAllocator8AllocateEmm") : NULL; + if (!g_sa_alloc || !g_sa_alloc_al) die("SDK 里找不到 StandardAllocator::Allocate"); + g_sa_big_align = !strcmp(dm_main_build(), DM_SENTINELS_110_BUILD); + if (g_sa_big_align) LOG("StandardAllocator::Allocate:本构建 ≥ 64 KB 的分配按 64 KB 对齐(显存池存储,见 hle_os.c)"); +} +HLE_FORCE("_ZN2nn3mem17StandardAllocator8AllocateEm", "原样转调 SDK;十三机兵防卫圈 1.10 的 ≥ 64 KB 分配按 64 KB 对齐(显存池起点)", void *, h_mem_SAAllocate, void *self, size_t size) { + pthread_once(&g_sa_once, sa_init); + return g_sa_big_align && size >= 0x10000 ? g_sa_alloc_al(self, size, 0x10000) : g_sa_alloc(self, size); +} diff --git a/dm/host/hle_time.c b/dm/host/hle_time.c index 60160cc..1c2d953 100644 --- a/dm/host/hle_time.c +++ b/dm/host/hle_time.c @@ -84,6 +84,16 @@ HLE_FORCE("_ZN2nn4time14ToCalendarTimeEPNS0_12CalendarTimeEPNS0_22CalendarAdditi ai->dst = tm.tm_isdst > 0; ai->offset = (int32_t)tm.tm_gmtoff; } return RES_OK; } +// 反向:本地日历 → 绝对秒(十三机兵防卫圈)。夏令时切换时一个本地时刻可对应 0 或 2 个绝对秒,宿主 mktime 只给一个 +// The reverse: local calendar → absolute seconds (13 Sentinels). Around a daylight-saving switch one local time maps to 0 or 2 absolute seconds; the host's mktime gives one +HLE_FORCE("_ZN2nn4time11ToPosixTimeEPiPNS0_9PosixTimeEiRKNS0_12CalendarTimeE", + "连 time 服务的时区规则;改用宿主 mktime", Result, h_time_ToPosixTime, int *count, int64_t *out, int n, const CalendarTime *ct) { + struct tm tm = {.tm_year = ct->year - 1900, .tm_mon = ct->month - 1, .tm_mday = ct->day, .tm_hour = ct->hour, .tm_min = ct->minute, .tm_sec = ct->second, .tm_isdst = -1}; + time_t t = mktime(&tm); + if (count) *count = n > 0; + if (n > 0 && out) out[0] = (int64_t)t; + return RES_OK; +} // 本地时刻换算强制接宿主 libc:SDK 自带的 localtime / localtime_r / mktime 没有时区数据、恒按 UTC(宿主实测:编年史「首次发现」由 // main+0xb04d34 调 localtime 再 strftime 出,差 8 小时)。真机 Switch 的 SDK 按系统时区换算,这里跟宿主系统时区走才一致; // 宿主 TZ 平时 = 系统时区,只在 DM_FIXED_TIME 确定性对拍时钉 UTC(main.c)。struct tm 布局两边相同(9 个 int、long 偏移、时区名指针)。 diff --git a/docs/agents/02-survey.md b/docs/agents/02-survey.md index 34b8986..dea0ad8 100644 --- a/docs/agents/02-survey.md +++ b/docs/agents/02-survey.md @@ -60,6 +60,8 @@ The tables below are what to check by hand afterwards. | Fonts | Font archives and use of the system shared font | `Font/*.bfarc`; the shared font goes through `nn::fontll::ScalableFontEngineHelper::Decode` (BFTTF) | | Languages | Per-language archives | `Mals/<Region><lang>.sarc`, `Voice/Resource/<Region><lang>` | +Archives the game reads itself need nothing from the host but plain file access. 13 Sentinels keeps everything in six CRI CPK files (`ROBO_*.CPK`, 4.3 GB) that CRI File System opens through `nn::fs`. To look inside one: the header and TOC are `@UTF` tables (big-endian columns), masked with CRI's byte stream (`m = 0x655f`, then `m *= 0x4115` per byte); file offsets in the TOC are relative to the smaller of `TocOffset` and `ContentOffset`, and files whose `ExtractSize` differs from `FileSize` are CRILAYLA-compressed. Its listing showed no shader container, which is how the shaders were found in `main` instead ([04](04-graphics.md)). + ## Ghidra and REA - Convert with `nso2elf.py`, create one persistent headless project per module, and analyse once (Mario `main`, 28 MB: ~35 min, 138k functions). Ghidra's image base for these ELFs is 0x100000, so `FUN_xxxx` = module offset + 0x100000. diff --git a/docs/agents/03-bringup.md b/docs/agents/03-bringup.md index 57456ce..042ffe8 100644 --- a/docs/agents/03-bringup.md +++ b/docs/agents/03-bringup.md @@ -47,6 +47,7 @@ Each entry: symptom → root cause → fix (where the fix lives). Check these fi - **`MapPhysicalMemory` faults on part of a page** → round to 16 KB host pages. - **`SetHeapSize` asks for almost the whole application pool and fails at boot** (Metroid Dread: 0xc9400000, larger than the heap region) → the SDK sizes the heap as total − used memory, and the host reported "used" as heap + 64 MB. That 64 MB covers ordinary modules (Mario's two are about 74 MB, and its heap request matches hardware), but Dread's `main` carries a 1.8 GB bss that the Switch counts as used. Used memory now adds the bss of modules in the front-patch layout (`big_bss_bytes` in `hle_svc.c`); Dread then asks for 1.38 GB. - **Some textures are re-uploaded every frame (several MB per frame)** → the dirty-page tracker registers each memory pool rounded to 16 KB host pages, but NVN pools are only 4 KB aligned, so neighbouring pools share their first / last host page and one address can lie in several registered ranges. Taking the first range that contains an address judged textures in the second pool untracked (always dirty), released only one range on a write fault, and could unregister the neighbour when a pool was destroyed. `nvn_dirty.c` now uses the range that holds the whole span, releases every range covering a faulting page, and removes a range by the pool's exact start (`t_dirty_slots` covers it). +- **A null write as soon as the main thread starts recording a command buffer** (13 Sentinels: a constructor at `main+0x2fa9a0` runs on `this = 0` when the prologue enters its first battle) → the object came from one of the engine's ring heaps (`main+0x2d83ec`), and that heap had base 0. At startup the engine takes one GPU memory pool, sized as exactly the sum of three ring heaps, and cuts the heaps out at 64 KB alignment *relative to the pool's GPU address* (`main+0x30c118`). On the Switch a pool's GPU address is big-page aligned; the host's GPU address is the CPU address, and the storage came from the game's `malloc` rounded only to 4 KB. Up to 60 KB was lost at the front, the third heap (16 MB) did not fit, and the sub-allocator returned 0. The game's `malloc` is `nn::mem::StandardAllocator::Allocate(size)`; on this build the host routes allocations of 64 KB or more to `Allocate(size, 0x10000)` (`hle_os.c`). Probing the heap object at the allocation (`DM_PROBE=main:2bec60@x0+8`) showed the zero base directly. - **Write through a null pointer at +0x1c0 during startup** → `GetInfo(UserExceptionContextAddress)` must return a real buffer. - **Heap allocations fail late in a busy scene** → the reported application pool (3285 MB, `APP_POOL`) and used memory decide the game's heap size; report Switch-accurate numbers. @@ -69,6 +70,7 @@ Each entry: symptom → root cause → fix (where the fix lives). Check these fi - **Crash in `nn::ro::InitializeWithPortName`** → `nn::ro` connects to `ldr:ro`; take over `Initialize` and answer `LookupSymbol` from the loaded modules (`hle_ro.c`). - **SDK 12 names** → older SDKs export other names for the same calls (`ListAudioDeviceName` / `GetActiveAudioDeviceName`, singular `hid::GetNpadState`, `OpenDefaultAudioOut`); hook both generations. - **SDK 11 names** (Metroid Dread) → `fs::CommitSaveData(mount)` is the older name of `fs::Commit`; `fs::DeleteDirectory` (an empty directory) was missing too. +- **More SDK 11 calls that reach a service** (13 Sentinels) → `hid::GetMouseState` (the first boot aborted in it: the SDK's mouse resource was never initialized), `fs::CleanDirectoryRecursively`, `fs::EnableGlobalFileDataCache`, `oe::SetUserInactivityDetectionTimeExtendedUnsafe`, `time::ToPosixTime` (host `mktime`) and `audio::GetAudioOutName`. Finding them before the first boot: list `main`'s `nn::` imports that no host hook names (the survey's import table, filtered against `HLE_FORCE` names); most `nn::os` / `nn::mem` imports run natively in `sdk` and need nothing. - **Abort in `nn::sm::Initialize` under `nn::oe::FinishStartupLogo`** (Metroid Dread, right after the first frames) → it reaches the applet proxy through `am::GetApplicationFunctions`. The host has no startup logo: a no-op hook (`hle_oe.c`). - **Abort in `nn::sm::Initialize` under `nn::hid::VibrationNode::Update`** (Metroid Dread) → the SDK's vibration graph flushes queued values with `hid::detail::SendVibrationValues`, which opens the hid service. The game sets handles on its `VibrationTarget`s even when the host reports no vibration device. The flush calls it through the PLT, so an `HLE_FORCE` on that symbol catches it and returns success (`hle_hid.c`). - **An Atk task aborts in a `switch` default (`UnexpectedDefaultImpl`, then `AbortImpl`) while a stream sound loads** (Metroid Dread, `main+0x1225a8c`) → the stream path was `rom:/system/snd/../../sounds/streams/…`, and the host answered "not found" for any `..` component. Horizon's path normalizer resolves `.` and `..` lexically and fails only above the mount root; `host_path_ex` now does the same (`norm_rest` in `hle_fs.c`). diff --git a/docs/agents/04-graphics.md b/docs/agents/04-graphics.md index 25dd226..b29eb05 100644 --- a/docs/agents/04-graphics.md +++ b/docs/agents/04-graphics.md @@ -11,6 +11,7 @@ Do not stop at the obvious archive. In Mario the main `bfsha` held 4,888 of the - **BNSH** (`BNSH\0\0\0\0` + `grsc` block at +0x60): inside `bfsha`, particle effects (`esetb`, VFXB), UI layouts (`blarc`), and `main` itself. Variation table at +0x80 (count at +0x7c); binary program at variation +0x10; six stage pointers from program +0x08 (vs, hs, ds, gs, ps, cs); stage code descriptor: control ptr +0x08, code ptr +0x10, code size +0x18, control size +0x1c. All pointers relative to the BNSH start. - **Unity Shader objects** (Engage): Unity 2020.3 keeps each shader's compiled programs in its `Shader` object (class 48), in UnityFS bundles (`StreamingAssets/aa`) and in the player's own serialized files (`data.unity3d`, `Resources/unity default resources`). `dm/gfx/tools/unityfs.py` reads them: UnityFS blocks (LZ4 / LZMA), the serialized file, the object through its type tree, then `compressedBlob` per platform (LZ4 segments; each segment starts with a u32 count and 12-byte `{offset, length, segment}` entries). The player's files have no type trees; they borrow the bundles' `Shader` tree with the same type hash (same Unity build, same layout). Type-tree strings with the high bit set index Unity's common string table, which `unityfs.common_strings` reads from the game's own `main` (`AABB\0AnimationClip\0…`). On Switch every subprogram is one whole NVN program: its code is a 0x60-byte header of four `i32 ×6` arrays indexed by NVN stage (0 vertex, 1 fragment, 2 geometry, 3 tess control, 4 tess evaluation, 5 compute): an unused / scratch size, the block offset from +0x60 (−1 = no stage), the control size and the code size. Each block is the control section followed by the code area. The vertex and fragment stages of one program form a pair. Engage: 209 `Shader` objects in 188 bundles plus the engine files, 9,269 stage shaders (4,634 vertex / pixel pairs and one geometry shader), no compute shaders. - **MSHD** (Metroid Dread, MercurySteam's engine: `system/shd/*.bshdat`, the only place its shaders live; nothing in `main` or the `.pkg` packs): each stage is a control section, whose length is at control +0x20, sometimes followed by a small compiler-option table (`fastmath true`), then the code area at the next code header. Every file holds vertex / pixel stages in alternating order (1, 2 or 4 programs) and is paired two by two. The control section carries NV assembly text after its first 0x880 bytes, and the SASS disassembly follows each code area, so the binding layout can be read straight from the files (`TEX` / `TEXS` handle immediates). 478 files, 1,342 stages, 1,010 unique keys (359 vertex, 651 pixel), 659 pairs. +- **GLSLC outputs in `main`** (13 Sentinels, Vanillaware's engine: no shader file in RomFS, whose six CRI CPK archives hold textures, scripts, CRI audio and USM movies): 838 GLSLC compiler outputs sit in `main`'s read-only segment, each referenced from a `{pointer, size, 0}` table in `.data`. An output starts with the magic `0x19866891`, its total size is at +0x44, and it holds exactly one control section with the code area right after it (control +0x20 = control length); a debug section with the NV assembly and the GLSL source follows. Engage's `main` also has 11 of them, so `shader_split.py` reads them only when the profile sets `shader_main_glslc on` (and accepts only outputs that pass the control / code / length check). The stage comes from SPH word 0, as for BAHS. - **BAHS** (agl `sharcb`, engine utility shaders): code region at header +0x1c (size +0x18). Binary entries in the table area are 16 bytes `{code offset, 0, control size (0x880/0x88c), signed offset from this entry to the control section}`. Control sections are shared between programs, so pair by entry, never by searching for code near a control. - **Code area**: graphics = 0x30-byte header (magic `0x12345678`) + 0x50 SPH + instructions (at +0x80); compute = 0x30-byte header (magic `0x12345679`) + **zero padding, instructions at +0x100** (all 71 of Mario's). **Control section**: magic `0x98761234`; +0x700 = c1 offset in the code area, +0x704 = code size, +0x748 = compute thread-group size (u32 ×3), +0x754 = compute shared memory bytes. - **The program start must be exact.** Ryujinx's decoder treats every 32-byte boundary *relative to the start* as a scheduling word. Mario's compute code was first fed from +0x30: the groups were misaligned by 0x10, instructions were dropped or misread, and 12 compute shaders (particles, post effects) translated into an endless loop that hung the GPU at the first gameplay scene. Check a new title: the first non-zero word after the header must be a control word, and the trailing `EXIT` (`e30000000007000f`) / `BRA`-to-self (`e2400fffff87000f`) must sit in instruction slots of 32-byte groups counted from your chosen start. `dmsh_build.py` now refuses compute code whose padding is not zero. @@ -68,6 +69,8 @@ Consequence: any optimization that infers "which textures a draw reads" from bou - **Every pixel shader input must be a vertex shader output.** Maxwell attributes are untyped slots: a pixel shader that reads a slot the vertex shader never wrote gets a default. Metal refuses to build the pipeline (`Fragment input(s) user(locnN) mismatching vertex shader output type(s) or not written by vertex shader`), and the draw is skipped. Translating one shader at a time cannot know what the partner reads, so `shader_split.py` writes the pairs of each BNSH variation (`shader_pairs.tsv`) and `dmsh_build.py` gives each vertex shader the outputs its partners read but it doesn't write, as `float4` (0, 0, 0, 1) (`vs_pad`; vertex shaders with partners are finished after the pixel shaders, and the partner list is part of the shader's cache rule). In Mario 331 of 9,043 pairs failed; the opening course used none of them, later levels did (about 11,500 failed builds in one 11-minute session). Check all pairs offline after a build (recipe in [05](05-debugging.md)). The host caches failed pipelines and reports each once with its vertex / fragment keys (`【红】建渲染管线失败(顶点 … 片元 …)`); before that, every draw retried the failing compile. - **iOS math mode**: the iOS libraries are compiled with `-fmetal-math-mode=relaxed` (`dm/ios/tools/ios_metal.py`). The translated shaders reproduce Maxwell's NaN behaviour with `isnan` selects (8,337 of 12,809 use them), and the default fast mode allows the GPU compiler to assume there is no NaN. Relaxed keeps fast math's reassociation but not that assumption. +- **Dynamic present interval (Mario 1.2.1 on the iPhone)**: Mario times every frame (`main+0xcfd24`); after a frame that ran past one vertical sync it sets present interval 2 for the next frame *and* sets its own time step to the interval (a float at `main+0x36ceff0`), so that frame catches up 2/60 s. A user's 2-minute iPhone session switched 123 times, mostly for one frame. In `display` mode those frames were shown for 16.7 ms and the game visibly jumped ahead ("sometimes the speed increases when jumping"). In `game` mode (the default since 2026-10-15) the display-link callback keeps a frame whose period is over 25 ms on screen for its whole period before taking the next one (`g_link_hold_until` in `nvn_mtl.m`; the `显示节拍 至` log line counts these ticks as `按出图间隔留拍`), and the 30 fps target that drops the display link to 30 Hz (`nvn_game_fps` in `hle_nvn.c`) waits until interval 2 has held for a full second, so one-frame switches no longer flip the display-link mode. `DM_TEST_PRESENT_LAG=90,30` (sleep 30 ms before every 90th present) makes Mario do this on the Mac; run it with `DM_WINDOW=1` to get the display link. + ### Seen in Engage (Unity) - **Program control sections are freed after `nvnProgramSetShaders`**: shader keys are computed inside that call ([03](03-bringup.md), graphics entries). @@ -87,6 +90,14 @@ Consequence: any optimization that infers "which textures a draw reads" from bou - **Cutscenes at 30 fps, gameplay at 60, as on the Switch**: the game keeps present interval 1 throughout. In a cutscene its main loop spins in 1 ms steps until the cutscene's audio clock advances, then renders a frame; that clock moves with a 30 Hz sound thread ([03](03-bringup.md), timer slack entry). - **One draw per render pass**: the cutscene frames have about 180 passes with one draw each. The game updates uniforms inline (`CommandBufferUpdateUniformBuffer`) between draws, and every copy or update ends the host's render pass. This is correct but costs submit time (about 5 ms per frame on the Mac). +### Seen in 13 Sentinels + +- **Single-stage programs**: the game builds one NVN program per GLSLC output (838 `ProgramSetShaders` at startup) and combines vertex and pixel programs per draw with `BindProgram` stage bits, so the composite-program path from Metroid Dread does all the pairing. The real pairs exist only at draw time. Every pixel shader input is a plain `float4` at locations 1–9 (the SPH input map, words 6–13, matches the translated MSL inputs for all 375 pixel shaders), so with `shader_separable on` `shader_split.py` pairs every vertex shader with a *cover*: the fewest pixel shaders whose inputs together are every input any pixel shader reads (here one shader). `vs_pad` then gives each vertex shader all of them, and any vertex / pixel combination links. +- **`dmsh_build.py`'s shader cache is keyed without the pairs**: a vertex shader whose pairs changed is restored unpadded from `cas/`. After changing a profile's pairs, delete that profile's cache (`build/cache/metal_shader_cache/cas` and `b-*`) and rebuild. The NSP script always starts from its own cache, which has the same problem only when a profile's pairs change between two of its runs. +- **Triangle fans and indexed quads**: Metal has neither. Non-indexed fans draw through a shared `(0, i, i+1)` index buffer with `first` as the base vertex; indexed fans and quads are expanded on the CPU into a 32-bit triangle list in the frame arena at replay (`nvn_mtl.m`, as for 8-bit indices). +- **Vertex streams 14 and 15**: the host bound NVN stream *n* to Metal vertex buffer 16 + *n*, but Metal has slots 0–30 only (30 is the host's constant fallback), and stream 15 hit the assertion `buffer index (31) must be < 31` a minute and a half into the prologue. Translated shaders use only `buffer(0)`, so streams 14 and 15 now go to slots 14 and 15 (`VB_SLOT`). +- **Packaged texture data from CPU memory**: every texture is created with `TextureBuilderSetPackagedTextureData`; NVN copies that data into the texture storage at `TextureInitialize` (a compressible pool cannot be written by the CPU). The host copies it and marks the texture CPU-written, so it is de-laid-out and uploaded like a packaged texture. Linear textures (the save thumbnails) are excluded: in a CPU-visible pool they are read straight from the pool buffer. + ## 7. Diagnostic switches (host) | Switch | Output | @@ -102,6 +113,7 @@ Consequence: any optimization that infers "which textures a draw reads" from bou | `DM_NVN_TEXINFO=WxH` | Storage and description (pool, offset, flags, packaged or not) of every texture of that size on first use | | `DM_NVN_TEXRAW=info` (with `TEXDUMP`) | Raw bytes of the non-render-target textures `TEXINFO` picked out, at the `TEXDUMP` frame | | `DM_NVN_T2T_TRACE=WxH:file` (+ `DM_NVN_T2T_FROM=f`) | After each texture copy into an RGBA16F texture of that size, a GPU-timeline copy of the result into a trace buffer; 240 entries written to `file` (header line, 240 frame numbers, then the texels) | +| `DM_TEST_PRESENT_LAG=n,ms` | Sleep `ms` before every `n`th present: a slow frame on demand (Mario then switches to present interval 2) | | `DM_NVN_BLIT_ORDER=0` | No barriers between dependent operations in the copy encoder (reproduces the fountain bug) | | `MTL_DEBUG_LAYER=1 MTL_DEBUG_LAYER_ERROR_MODE=nslog` | Metal validation without aborting | diff --git a/docs/agents/README.md b/docs/agents/README.md index b8875ab..6140e3c 100644 --- a/docs/agents/README.md +++ b/docs/agents/README.md @@ -2,7 +2,7 @@ These pages are for an agent asked to analyse a Switch NSP and port it with the native-execution approach of this repository and [d2r-mobile](https://github.com/jhaizhou-ops/d2r-mobile): the game's own aarch64 code runs natively on Apple hardware, a rewriter patches only the instructions Apple's kernel cannot run, the host replaces Horizon system services, and NVN is reimplemented on Metal. -Everything here was learned on Diablo II: Resurrected (SDK ~12, NVN 53, the original target of d2r-mobile) and Super Mario Bros. Wonder (SDK 16.2.0, NVN 55, this repository; its update 1.2.1 uses SDK 16.2.12), then extended by Fire Emblem Engage (Unity, SDK 12.3.7) and Metroid Dread (MercurySteam's engine, SDK 11.4.3). Where titles differed, each is given; the next title will differ again, so treat every number below as "measure it, don't assume it". +Everything here was learned on Diablo II: Resurrected (SDK ~12, NVN 53, the original target of d2r-mobile) and Super Mario Bros. Wonder (SDK 16.2.0, NVN 55, this repository; its update 1.2.1 uses SDK 16.2.12), then extended by Fire Emblem Engage (Unity, SDK 12.3.7) Metroid Dread (MercurySteam's engine, SDK 11.4.3) and 13 Sentinels: Aegis Rim (Vanillaware's engine, SDK 11.4.3, the same `sdk` and movie module as Dread). Where titles differed, each is given; the next title will differ again, so treat every number below as "measure it, don't assume it". | Page | Covers | |---|---| diff --git a/scripts/build_ipa_from_nsp.py b/scripts/build_ipa_from_nsp.py index a438cd0..67d8524 100755 --- a/scripts/build_ipa_from_nsp.py +++ b/scripts/build_ipa_from_nsp.py @@ -206,7 +206,7 @@ def survey(nsp_dir, app, fam, keys, repo, py, env): # 代码模块 | Code modules R += ['', '## 代码模块 | Code modules', '', '| 模块 module | 构建号 build ID | text / ro / data (MB) |', '|---|---|---|'] - found, sdkver, comps, unity, mods = {}, set(), set(), set(), [] + found, sdkver, comps, unity, mods, glslc = {}, set(), set(), set(), [], 0 for p in sorted((ex / 'ExeFS').iterdir()): if p.read_bytes()[:4] != b'NSO0': continue img, bid, szs = nso_image(p); mods.append(p) @@ -214,6 +214,7 @@ def survey(nsp_dir, app, fam, keys, repo, py, env): sdkver |= {m.decode() for m in re.findall(rb'(?i)NintendoSDK_nnSdk-([0-9_]+)', img)} # SDK 12 写作 NintendoSdk_,16 写作 NintendoSDK_ | SDK 12 spells it NintendoSdk_, SDK 16 NintendoSDK_ comps |= {m.decode() for m in re.findall(rb'SDK MW\+Nintendo\+([\w]+-[0-9_]+)', img)} unity |= {m.decode() for m in re.findall(rb'20[12][0-9]\.[0-9]+\.[0-9]+f[0-9]', img)} if p.name == 'main' else set() + if p.name == 'main': glslc = img.count(b'\x91\x68\x86\x19') # GLSLC 编译输出的魔数(shader_split.py glslc_shaders) | magic of GLSLC compiler outputs (shader_split.py glslc_shaders) for pat, name in MARKERS: if pat in img: found.setdefault(name, []).append(p.name) npdm = ex / 'ExeFS/main.npdm' @@ -258,9 +259,10 @@ def survey(nsp_dir, app, fam, keys, repo, py, env): # 对移植意味着什么 | What it means for a port notes = [] if 'NVN' not in found: notes.append('没找到 NVN:宿主只实现了 NVN | no NVN found: the host implements only NVN') - if extra: notes.append(f'除 main / sdk 外还有 {", ".join(extra)}:装载器能装最多 8 个模块,但打包与改写目前只处理 main + sdk | modules besides main / sdk: {", ".join(extra)}; the loader takes up to 8 modules, but packaging and rewriting handle only main + sdk today') + if extra: notes.append(f'除 main / sdk 外还有 {", ".join(extra)}:写进档案的 modules(装载顺序 main、subsdk*、sdk),改写、打包与装载都按它走(银河战士 生存恐惧、十三机兵防卫圈的 subsdk0 是 NintendoSDK_movie) | modules besides main / sdk: {", ".join(extra)}; list them in the profile\'s modules (load order main, subsdk*, sdk), which rewriting, packaging and loading all follow (subsdk0 of Metroid Dread and 13 Sentinels is NintendoSDK_movie)') if mags['UnityFS'] or 'Unity IL2CPP' in found: notes.append('Unity:着色器是 UnityFS 包与 data.unity3d 里的 Shader 对象,shader_split.py 经 unityfs.py 读出(按 Unity 2020.3 的 Switch 格式写成,别的 Unity 版本先核对) | Unity: the shaders are Shader objects in UnityFS bundles and data.unity3d, which shader_split.py reads through unityfs.py (written for Unity 2020.3\'s Switch format; check other Unity versions first)') - if not (mags['BNSH'] or mags['BAHS']) and not mags['UnityFS']: notes.append('顶层没有 BNSH / BAHS:着色器在压缩包或别的容器里,要先找到 | no BNSH / BAHS at the top level: the shaders are inside compressed or other containers and must be located first') + if glslc: notes.append(f'main 里有 {glslc} 处 GLSLC 编译输出的魔数(0x19866891):着色器可能编进了 main(十三机兵防卫圈),档案开 shader_main_glslc 由 shader_split.py 读出 | main has {glslc} GLSLC compiler output magics (0x19866891): the shaders may be compiled into main (13 Sentinels); the profile key shader_main_glslc makes shader_split.py read them') + if not (mags['BNSH'] or mags['BAHS']) and not mags['UnityFS']: notes.append('顶层没有 BNSH / BAHS:着色器在压缩包、别的容器或 main 里,要先找到 | no BNSH / BAHS at the top level: the shaders are inside compressed or other containers or in main, and must be located first') major = {v.split('_')[0] for v in sdkver} if not major: notes.append('没找到 SDK 版本串 | no SDK version string found') elif major != {'16'}: notes.append(f'SDK {"/".join(sorted(major))},不是马力欧的 16:NVN 对象尺寸、API 版本与服务布局按 SDK 不同(宿主已从 D2R 的 SDK 12 改成 16 的尺寸),按 docs/agents/03 逐项核对 | SDK {"/".join(sorted(major))}, not Mario\'s 16: NVN object sizes, API version and service layouts differ by SDK (the host was converted from D2R\'s SDK 12 to SDK 16 sizes); check them one by one per docs/agents/03')