原版 BINLAYOUT 执行装载控制

本批结果

新增 core_sim/NativeLayoutLoader.cs,将前批完整二进制文档推进到统一执行装载控制:

  • Group 预选上下文与同步 RNG 保存/恢复。
  • 描述符解析、创建前筛选、工厂创建、属性应用。
  • 创建后 Group 接受检查与拒绝销毁。
  • 接受列表、首条优先的 SourceId 映射、扩展程序队列。
  • 扩展处理、统一初始化、描述符后处理和场景收尾。

5,120 组参考直接执行原版正常二进制路径的 50BA10、递归 50B680、4D8710 和 RNG 保存/恢复;移植结果的对象列表、字段、映射、随机状态和调用顺序全部一致。

累计 1279 条核心断言、167 项相关 Python 检查通过;Godot C# 构建零警告、零错误,coresimlint 通过。

这是执行装载控制器,不是所有宿主回调的完整实现。 Group 选择算法、各类实际对象/属性/extra 解释器、视觉后端和世界注册仍由明确宿主接口承担,尚未全部接入正式地图。全武器目标未完成,构造覆盖不因本批测试改算。

与完整数据入口连接

F2NativeLayouts 的文档、记录和属性实现核心只读接口,无需再把数据重组为仅包含 Room Piece 的列表。

新增保留:

  • SourceBytes:完整原始文件输入缓冲。
  • ExtraOffset:原 extra 在文件中的精确起点。

因此扩展解释器既能获得当前 payload,也能在有证据支持时使用完整源文件上下文;没有强行将原版接收的完整 reader 缩成一个无法跨引用的短 buffer。

已重新运行全量独立 C# 核验:8,987 文件回写/属性指纹继续一致,44,696 条实际 Group 对象块引用匹配,2,528 条合成根,无未解析引用。

50B680 的对象执行顺序

  1. 空记录直接返回。
  2. 解析 descriptor;确实不存在时跳过整棵子树。
  3. 当前 descriptor 为 Group,且实际 Group pool 非空时,先询问 pool 是否允许该 SourceId。
  4. 有继承上下文且其 byte+98 开启限制时,只允许 Layout Link 或 Layout Link Controller;否则跳过整棵子树。
  5. 调用 NativeSceneFactory.Create,不传模板、不传立即父对象、不立即初始化。
  6. 符合条件时先应用继承的 feature 上下文。
  7. 经对象虚入口设置父 RuntimeId。
  8. 直接写原 SourceId。
  9. 执行属性处理。
  10. 对 pooled Group 执行 61F660 接受检查;拒绝则销毁对象并跳过其子树。
  11. 接受的 pooled Group 先清 byte+607,再读取 pool 标志,写 byte+602。
  12. 将对象加入接受列表。
  13. 建立 SourceId → RuntimeId 映射。
  14. 非空 extra 入队,暂不执行。
  15. 为 pooled Group 查找新的子上下文;查不到则继承原上下文。
  16. 递归读取子记录,每次使用父对象当前的 RuntimeId。

接受列表的顺序是实际成功执行并接受的预序,不是全部源记录,也不能通过事后剔除对象来假装等价。被创建后拒绝的对象已经产生过构造/属性副作用,必须由实际销毁后端处理。

普通 Group 在没有 pool 时不走 pooled Group 的接受/标记分支。

两个上下文字段不能误认

  • 50A3E0 只做两个 descriptor 身份比较:Layout Link 与 Layout Link Controller,不是宽泛的“所有 feature 对象”分类。
  • 继承上下文的 byte+98 控制是否限制为这两类;它不是尾部序列化的 NO TAG FOUND byte+25。
  • 上下文 +92 为本次解析后的 tag。
  • loader+112 允许、类型匹配,而且“byte+98 开启或 tag!=-1”时,才在属性之前调用 50ABE0。

核心接口按这些行为命名,不将 byte+98 和原始尾部布尔字段合并。

SourceId 映射:4D8710

  • SourceId=-1 或 RuntimeId=-1 时不建映射。
  • 键首次出现:保存当前 RuntimeId。
  • 键重复:记录问题,但保留首条值。
  • 重复 SourceId 的对象本身仍可各自存在于接受列表。

因此不能用字典覆盖来选择“最后一个对象”,也不能因键重复直接去掉后续场景对象。核心映射保留完整 signed Int64,另有边界测试覆盖最小/最大值。

原版实际映射函数参与本批机器码参考,不只由测试桩重复实现预期结果。

顶层装载阶段

NativeLayoutLoader.Load 对 v11 正常二进制输入执行:

  1. 确认 Group、Layout Link、Layout Link Controller 三个必需描述符;确实缺少时返回明确 MissingDescriptors 状态。
  2. 将文件第二字节 flags 传给场景布局版本相关状态。
  3. 若文件有 Group 尾部,读取 pool。
  4. 在 pool 读取之后保存完整同步 RNG 状态。
  5. 执行 pool 预选。
  6. 恢复 RNG,再进行预选后的处理。
  7. 读取根对象。
  8. 按 extra 队列执行扩展程序。
  9. 按接受列表调用对象当前 descriptor 的初始化入口。
  10. 场景 +312 接收源映射。
  11. 描述符后处理 4D88C0。
  12. 查询场景 +52,再将结果交给 +48。
  13. 4D6630(force=true) 刷新场景 Group。
  14. 场景 +316 收尾。

+52/+48 保留为实际场景回调边界,没有凭槽号将其误写成通用 visible 设置。

根记录数量的原检查是 signed short;子记录数量是 unsigned short。核心保留该差异,另测根数量高位设置的路径。当前原版文档仍由严格读取器先验证边界。

原私有 context 的内存分配/释放、进度 UI、文本回退路径和错误 reader 的容错字节计数不属于本核心正常路径的完整等价性声明。

RNG 边界

原顺序为:

text
读取 Group pool
保存 g_seed_synced 的 lo/carry
预选 Group
恢复完整状态
预选后处理
创建实际对象

676970 恢复全零的 64 位状态时,会改用 GetTickCount,carry 为 0。非零状态不触发时钟读取。核心通过宿主明确提供该外部输入,不在纯核心中调用系统时钟。

恢复只还原随机状态;Mwc.TotalAdvances 是移植端的诊断计数,不因状态恢复而倒退。

真实宿主仍须让后续对象构造/ID 生成、视觉选择及其他消费者使用正确的同一同步流。此处的保存恢复不意味着整张地图随机调用序列已经全部补齐。

队列是动态的

50BA10 在循环中重新读取当前队列长度:

  • extra 处理期间新增的 extra,仍会在本轮处理。
  • extra 期间加入的接受对象,进入后续初始化。
  • 初始化期间加入的接受对象,也在本轮初始化。
  • 若调用方要求输出对象列表,先把当前对象加入输出,再执行其初始化。

核心没有在进入阶段前 ToArray 成固定快照,也没有把初始化新增对象统一推迟到下一帧。

测试夹具明确追加了 runtime 2000/source 999 的 extra 对象和 runtime 3000/source 998 的初始化对象;原版与移植遍历顺序一致。它们是控制测试输入,不是原版武器/地图对象定义。

文件头名称不执行 setter

50B680 在 v11 读取对象名称到临时 wstring,随后销毁该临时字符串;这条路径没有把它送入名称 setter。

文档仍完整保留 wire name,但执行器不因读取名称而主动改对象运行时名称,也不将其当脚本或指令执行。

真实 Room 叶节点组合验证

从原版 PASS_JD_A 选择一个无子记录、无 extra、全部属性均已有实现的真实 Room 叶节点:

  • SourceId:4658743717804970463。
  • 通过统一 Load 控制器,而不是直接跳到资源初始化。
  • 工厂创建对象,按原字段应用位置/方向/缩放、GUID 和 VISUAL。
  • 属性阶段不提前请求视觉模型。
  • 在统一初始化阶段进入 NativeRoomResourceLifecycle 和真实 F2NativeRoomResourceHost。

视觉模型后端仍是明示夹具。此测试不外推为全部 Room 属性、所有地图对象或正式场景已完成;不支持的字段没有通过丢弃来让样例通过。

参考工具与边界

原 EXE SHA256: 186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e

  • tools/build_native_layout_loader_oracle.py/tools/native_layout_loader_oracle.c。
  • 原样执行正常二进制装载函数、递归记录函数、源 ID 映射、同步 RNG 保存/恢复。
  • 文件读取、分配器、字符串、descriptor、工厂、属性、Group 规则、extra 和实际场景操作使用明确 spy。
  • 保留完整调用状态;参考工具额外检查确实覆盖创建、拒绝和映射,不能只靠大量空返回冒充覆盖。
  • 描述符 wire code 的实际参数是 byte,原调用的 DWORD 高位可有无关栈内容;参考桩按真实宽度读取。
  • 原装载函数清理后,不能再读取其局部栈向量;工具在场景收尾时复制取样结果。
  • 所有探针在隔离 32 位进程运行,没有 attach 原游戏。

产物:

  • build/native_layout_loader_oracle/results.json:5120 案例及完整状态/调用顺序。
  • build/native_layout_loader_core.log:1279 断言。
  • build/native_layout_loader_python.log:167 检查。
  • build/native_layout_loader_godot.log:零警告、零错误。
  • build/native_layout_csharp_audit.json:更新后的全量数据入口核验。

尚未完成的宿主工作

INativeLayoutLoadHost 没有默认“成功空操作”实现。实际宿主必须落实:

  • 真正的 Group pool 解析、选择、模式/主题/唯一性上下文。
  • 每种 descriptor 的构造和销毁。
  • 全属性默认值/setter 绑定。
  • 逻辑/时间线等 extra 的真实解释和引用修复。
  • 实际节点/模型/碰撞注册和世界分区。

Finished 仅表示这套装载控制与提供的宿主回调正常走完,不是完整世界查询资格或全武器完成标记。正式接入前应使用可丢弃的分阶段场景宿主;失败不会在本核心里假造成功、自动改存档或隐式回滚未知外部副作用。

NativeLayoutLoadState 当前是单次装载临时状态;尚未纳入持久世界和快照,v29 未改。按 godot-master 的分层方式,本批没有替换正式地图画面。

main 原地工作、未提交;默认存档 SHA256 保持: 9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f

下一批继续 Group 选择和具体场景宿主,将这条执行路径接到真实世界注册,再补完整攻击型触发技能与全武器验收。