RULES.TEMPLATE 的 CHUNK TYPE 解析

结论

[CHUNK_RANDOM]TYPE 不是磁盘目录名,也不存在前缀匹配或去 _LANDMARK 的特殊规则。它精确引用同一份模板里的 [CHUNKTYPE].NAME

原版的完整链路是:

text
CHUNK_RANDOM.TYPE
        │ 精确字符串比较

CHUNKTYPE.NAME  (CChunkType + 0x44)

        ├─ FOLDER 缺省 NAME
        │  最终目录 = 模板自身目录 + FOLDER + "/"

        ├─ 有 INCLUSIVE_FILES
        │      只加载 FILE + ".LAYOUT"

        └─ 无 INCLUSIVE_FILES
               枚举最终目录,排除 /MERGE/,排序


                    CChunk 列表

这关闭了 HANDOFF_RE_BACKLOG.md 的 C-1。原检查器使用“目录精确 → 去 _LANDMARKTYPE_ 前缀”的启发式,所以把 170 个引用笼统报成“TYPE 解析不出”;那不是引擎语法。

静态证据

目标为 Torchlight2.exe v1.25.9.5,IDA imagebase 0x00400000,加载函数为 sub_604D20

地址行为
0x606DD5..0x606E8C收集全部 CHUNKTYPE,读取 NAME
0x606FEA..0x6070C2getString(FOLDER, NAME);模板目录与结果拼接,保证尾部斜杠
0x6073D9..0x6074D2INCLUSIVE_FILES 时,逐项读取 FILE、补 .LAYOUT、存在才加入
0x60776B..0x6078B6无清单时枚举目录;0x607819 排除 /MERGE/;随后排序
0x6079F1..0x607A7B创建 CChunk,保存所属类型和 .LAYOUT 路径
0x60836C..0x6083E3读取 CHUNK_RANDOM.TYPE,从类型向量头线性精确比较 CChunkType+0x44

类型比较核心字节(0x6083B5,48 字节):

text
8B 46 30 3B 6E 38 73 03 8D 04 A8 8B 00 8D 8C 24
60 01 00 00 83 C0 44 51 50 FF 15 68 68 11 02 83 C4
08 84 C0 75 08 45 3B 6E 34 72 D4 EB 02 8B DD

83 C0 44 把当前 CChunkType* 调整到 NAME 字段,随后调用宽字符串 operator==;失败就递增索引并继续线性扫描。因此,模板出现同名声明时,首个声明 胜出。

字段与对象布局

本轮确认的必要字段如下:

对象偏移语义
CChunkType+0x18拼好的最终文件夹路径
CChunkType+0x34INCLUSIVE_FILES 成功加载的布局指针向量
CChunkType+0x44NAME 宽字符串
CChunk+0x08所属类型/类型索引
CChunk+0x14.LAYOUT 路径宽字符串

FOLDER 不存在时,CDataGroup::getString 的默认参数正是刚读出的 NAME。这个 细节解释了绝大多数普通模板为什么看起来像“TYPE 就是目录”:它们只是通过默认值 恰好同名;一旦模板显式写 FOLDER,目录启发式便会错。

全库重跑结果

命令:

powershell
python -B tools/m4_seam_check.py --json build/m4/seam_check.json

固定 seed 1234 的结果:

指标数值
模板186
有预设、实际进入检查的模板119
预设286
CHUNK_RANDOM 引用2,479
精确匹配 CHUNKTYPE.NAME2,477(99.9%)
通过目录枚举得到可加载变体2,009
通过 INCLUSIVE_FILES 得到可加载变体316
成功摆下2,325
匹配到类型但无可加载布局152
模板未声明该类型2
实际覆盖的不同瓦片610
接触相邻对3,015
真接缝洞0

旧 170 个启发式 miss 中,16 个由正确的 FOLDER/INCLUSIVE_FILES 关系救回。 剩余问题也被重新定性:它们不是“TYPE 语法未知”,而是发行语料或模板可达性债。

剩余 154 个为什么不能擅自“修复”

其中 152 个引用能找到 CHUNKTYPE.NAME,但按原版规则没有任何可加载布局:

模板引用数
ACT1/SNOW/RULES.TEMPLATE144
TEST/RULES.TEMPLATE4
ACT2_CAVES/MANAVENT_RULES.TEMPLATE1
DRAGON/CASTLERUINS_RULES.TEMPLATE1
MAINMENUS/MAINMENU_SPLASHRULES.TEMPLATE1
TEST/TESTROOM_RULES.TEMPLATE1

另外两项连类型声明都没有:

  • ACT3_Z2/NG_DWARFARMORY_RULES.TEMPLATE1X1SINGLE_ROOM_BOSS
  • TESTROOM/TESTROOMRULES.TEMPLATE1X1SINGLE_ROOM_BOSS

最显眼的是 ACT1/SNOW/RULES.TEMPLATE:它的 25 个类型按原版规则都指向 ACT1/SNOW/<NAME>/,但发行目录里只有模板及其 BINDAT;相似瓦片实际位于上一级 ACT1/<NAME>/。静态代码没有“退回父目录”的分支,因此检查器也不能为了把 skipped 降到零而添加这种猜测。需要另行证明这些旧模板是否可达,或确认发行资源是否缺失。

对复刻实现的要求

实现时应先解析整份模板的 CHUNKTYPE 表,再解析预设。对每个 CHUNK_RANDOM.TYPE 做大小写语义与原版宽字符串一致的精确查找;候选布局必须来自该 类型自己的 FOLDER/INCLUSIVE_FILES。不要扫描兄弟目录,不要做前缀匹配,也不要把 _LANDMARK 当语法后缀剥掉。

可执行实现与真值登记位于:

  • tools/m4_seam_check.py
  • 原版游戏分析/reversed_cpp/REGISTRY/chunk_type_resolution.json
  • tests/truth/test_m4_seam_check.py
  • build/m4/seam_check.json