完整 BINLAYOUT 数据入口与独立 C# 核验
本批结果
此前 F2BinaryRoomInputs 只提供有序 Room Piece 属性投影;它不足以为统一装载器提供 Group、控制器、扩展数据和完整父子记录。
本批新增完整的冻结数据入口:
- tools/build_native_layout_programs.py:冻结原始 BINLAYOUT 文件及类型解码指纹。
- core_sim.io/F2NativeLayouts.cs:独立 C# 文档读取、回写、Group 尾部读取、属性解码和目录加载。
- core_sim.cli/NativeLayoutAudit.cs:全量独立核验命令。
- 原有 Room 投影接口和解析报告保留,不覆盖原版文件或重新生成正式地图。
全量结果:
| 内容 | 数量 |
|---|---|
| 原版 BINLAYOUT 文件 | 8,987 |
| 对象记录 | 1,492,024 |
| 属性记录 | 7,401,033 |
| Group 尾部记录 | 47,224 |
| 对象扩展数据 | 1,673,518 字节 |
| Group 尾部数据 | 1,668,136 字节 |
| 冻结文件总大小 | 210,661,477 字节 |
全部文件由 C# 独立读取并重新编码,与源字节一致。全部属性的 C# 类型解码指纹与 Python 一致。源文件集合及 SHA256 同时与前批已完成文本/二进制对照的 8,987 文件清单一致。
读取完整不等于执行完整。 冻结目录和核验报告均明确 native_initialization_complete=false;未因此宣称所有对象已实例化、选择已完成或所有武器可战斗。
完整文档模型
NativeLayoutDocument 保留:
- 文件版本 byte、第二个 flags byte、Group 尾部偏移。
- 所有根槽位,包含空记录。
- 所有 descriptor,不再只导出 Room Piece。
- 每个对象的 SourceId、原始名称、起止字节偏移和 ParentOffset。
- 属性原顺序、重复属性及每个原始 payload。
- 每个对象的 extra 数据。
- 子槽位顺序及空子记录。
- 完整的 Group 尾部树。
同一个 SourceId 可以出现在多个不同记录中,不能据此合并对象。文档中的 Offset/ParentOffset 描述当前源文件内的记录位置,不是运行时对象 ID。
名称保留 UTF-16 内容、空格、嵌入 NUL 和代理对;解析阶段不会把名称当成执行指令,也不会自动调用任何名称 setter。
未知 descriptor 或未知属性的原始数据可在文档层保留和回写。类型解码返回未知而不猜值;冻结的原版全量集合没有未知属性。后续执行器必须区分“原版允许跳过的缺 descriptor/属性”与“移植适配器尚未实现”,不能默默把缺实现当原版空处理。
Group 尾部字段含义核对
依据现有 Rust 编译/反编译实现,并结合原版 50AD70 的读取位置,使用以下字段:
| 尾部顺序 | 本批字段 | 原数据 |
|---|---|---|
| int64 | SourceId | 对应 Group 源 ID,合成根为 -1 |
| byte | Choice | CHOICE 编码 |
| uint32 | Randomization | RANDOMIZATION 整数字段,不按 float 解释 |
| byte | Number | NUMBER |
| uint32 | ObjectBlockOffset | Group 对象块的文件偏移,不是标志位 |
| int32 | FeatureTag | TAG ID |
| byte | NoTagRaw | NO TAG FOUND 的原始字节 |
| byte | LevelUniqueRaw | LEVEL UNIQUE 原始字节,不是普通“已保存组”标记 |
| byte | GameModeRaw | GAME MODE 编码 |
| uint32 count + int64[] | ActiveThemes | ACTIVE THEMES |
| uint32 count + int64[] | DeactiveThemes | DEACTIVE THEMES |
| uint16 count + children | Children | Group 子树 |
布尔类字段保留原始字节,不在冻结和回写时将所有非零值改为 1。此前独立 Rust 文本回编发现的非规范 NO TAG FOUND 字节仍能原样保留。
这里保留的是原数据,不执行 TAG、LEVEL UNIQUE、游戏模式或主题筛选。50AD70 的运行时派生标记与筛选动作属于后续装载上下文。
对象块引用的全量交叉核验
额外核对 Group 尾部的 ObjectBlockOffset:
- 44,696 条实际 Group 记录:偏移均对应一个真实 Group 对象块,且对象 SourceId 与尾部 SourceId 相同。
- 2,528 条合成根:仅将“尾部树根且 SourceId=-1”的记录识别为合成根,不对任意 -1 记录一概豁免。
- 未解析/悬空引用:0。
这为后续装载器提供精确的记录对应关系,不必假设 SourceId 在整个文件中唯一。
证据位置:
- 原版 50AD70:SourceId 写 +8,Choice 写 +16,Randomization 写 +20,Number 写 +24,对象块偏移写 +28,TAG 写 +92,三个字节写 +25/+26/+27。
- D:/WizardProject/TL2/tl2-mikuro-mod-packer-rs/src/binlayout.rs 的 emit_dg_node。
- 同工程 binlayout_decompile.rs 的 read_dg_node 与 ACTIVE THEMES/DEACTIVE THEMES 输出。
- build/native_layout_csharp_audit.json 的 group_links。
属性解码与指纹
支持当前冻结 schema 的 9 种编码:
- string:长度前缀 UTF-16,保留内容。
- u32bool:保留原始 DWORD。
- u32int、int64:保留有符号整数值。
- float、g2、g3:保留原始 IEEE 32 位字。
- curve、u32list:保留前缀数量及每个原始 DWORD。
浮点 NaN/无穷的位模式在数据层不被改写;具体数学消费者继续验证自己的有限值、可逆性等适用条件。不能在读取阶段擅自钳位或补默认数值。
Python 与 C# 使用相同的明确定义指纹格式,但各自独立读取/解码:
- 按对象预序写 descriptor、SourceId、原始名称、属性数。
- 按属性原顺序写 index、字段键、编码名称。
- 字符串写精确 UTF-16 内容;整数写有符号值;布尔同时写原 DWORD 和逻辑真假;浮点/列表写数量与原始字。
- 对该内容取 SHA256。
它不是仅比较总数或源文件 hash;重复属性顺序、字符串空格、整数符号和 float bits 的解码差异都会影响指纹。
完整对象 extra 和 Group 树由独立结构回写的逐字节比较覆盖。extra 仍是原始 payload,没有因此被宣布为已编译、已绑定或已执行的逻辑程序。
严格读取和缓存边界
- 仅支持本次已验证的 v11 framing,不猜旧版本。
- 验证对象边界、属性长度、额外数据长度、子对象数、Group 尾部边界和主题列表数量。
- 保留原版空记录槽位。
- 对异常深度/数量、越界、截断和未消费字节报错。
- 长度为 0 的特殊属性仍要求原版类型相关解释;当前原版全量集合没有此类条目,不假装支持未来所有 mod 编码。
- 读取时复制输入缓冲区,不因调用方随后修改原 byte[] 而改变已解析内容。
- 文件入口校验冻结 schema hash、源文件 hash、记录/属性/Group/extra 统计。
- 默认还校验独立回写和类型指纹。
- 目录路径必须解析在当前冻结目录内,不接受目录穿越。
缓存位于 build/f2/native_layout_programs/files,wire_schema.json 和 catalog.json 与之配套。读取正式缓存不需要回到原游戏目录临时查文件。
文档是冻结资源数据,不是场景会话状态;不得把它的数组/记录原地改成某个实例的运行时属性。实例仍应由工厂和状态对象承载。
实际数据到属性派发的组合验证
测试读取 PASS_JD_A 的完整原始文档:
- 该源文件有 2,158 个对象、68 条 Group 尾部记录。
- 文档同时保留 Room Piece、Group、控制器/触发器类型和非空 extra。
- 从 Room Piece 的原属性顺序解码 IEEE 字,直接送入 NativePropertyDispatch/NativeSpatialProperties。
- 位置结果与原始属性位值对应。
这里的 2,158 个对象是一个原始文件,不是递归展开 Layout Link 之后的整张 Echo Pass 世界。测试没有完成整图实例化或实际节点/视觉验收。
命令与验证产物
python -m tools.build_native_layout_programs --all
dotnet run --project core_sim.cli -- native-layout-audit- build/native_layout_freeze.log:Python 冻结与解码。
- build/native_layout_csharp_audit.log:全量 C# 核验及 Group 引用结果。
- build/native_layout_csharp_audit.json:源 hash、工具 hash、指纹、类型数量与引用统计。
- build/native_layout_program_core.log:1269 条核心断言。
- build/native_layout_program_python.log:163 项相关 Python 回归。
- build/native_layout_program_godot.log:Godot C# 构建零警告、零错误。
- coresimlint 通过。
本批不需要 attach 原游戏,也未写入原版 MEDIA/PAKS。按 godot-master 的数据/表现分层,正式地图和画面未替换;main 原地工作、未提交、快照 v29 未变。默认存档 SHA256 保持: 9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f。
下一步把完整数据入口接入执行装载器:原 Group 预选/上下文 → 工厂创建 → 属性派发 → 扩展数据处理 → 统一初始化 → Group 后处理 → 实际节点/碰撞注册。世界分区、完整攻击型 proc 和全武器验收仍属于未完成工作;构造覆盖没有因读取完成而从 1053/1419 改算。