news 2026/10/16 11:19:11

用ReSkate解析Frostbite数据格式:EBX、TOC与CAS的Mod合并原理全揭秘

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用ReSkate解析Frostbite数据格式:EBX、TOC与CAS的Mod合并原理全揭秘

【免费下载链接】ReSkate

项目地址:https://gitcode.com/gh_mirrors/re/ReSkate
点击查看免费下载

ReSkate 是一款面向 skate.(Frostbite 引擎)的开源增强工具,它能离线运行游戏、自建服务器,并把你放进Mods/文件夹里的 Mod 在启动时自动合并进游戏。本文将带你彻底搞懂 ReSkate 是如何解析 Frostbite 的三大核心数据格式——EBX、TOC 与 CAS,再把多个 Mod 的修改"缝"成一个干净补丁的完整原理。

ReSkate 是什么,为什么值得研究

ReSkate 是玩家自制项目(非官方),包含三大部分:

组件作用
ReSkateLauncher.exe启动器:校验游戏版本、安装游戏文件、管理 Mod
ReSkate.dll运行时:注入游戏,提供菜单、控制台、Mod 合并等
ReSkateServer.exe专用服务器:无需游戏与 Steam 的无头大厅

其中最有技术含量的部分,就是Mod 合并系统:你放进Mods/的每个 Mod 本质上都是"对游戏原始数据层的增量修改",而游戏引擎一次只认一层补丁。ReSkate 的工作,就是把这些增量翻译成引擎能读懂的 Frostbite 数据格式,再合成为唯一的一层补丁。

要理解这个过程,先要认识 Frostbite 的三个数据格式。

一、Frostbite 的三层数据格式

Frostbite 引擎组织游戏资源的方式可以概括为:EBX 是内容,TOC 是目录,CAS 是仓库。

1. EBX:游戏资产的"对象文档"

EBX 是 Frostbite 的序列化对象格式。游戏里的滑板、角色、物品、关卡配置,本质上都是 EBX 资产里的"实例"(instance)。

ReSkate 在 Engine/Resource/ebx_document.h 中完整实现了 EBX 的读取,核心结构Document包含:

  • 反射数据(reflection):TypeDescriptor与FieldDescriptor描述每个类的字段名、字段类型(整型、字符串、GUID、指针、数组等,见FieldType枚举)——这是 EBX 能"自描述"的关键;
  • 实例(InstanceRecord):真正的对象数据,每个实例有自己的 GUID,可被导出、被其它文档引用;
  • 导入表(ImportReference):跨文档引用靠fileGuid + classGuid定位,这是 Mod 之间"互相指认"资产的基础;
  • 数组区(ArrayRecord):根对象上的列表(比如"本关卡包含哪些对象")就存在这里,Mod 添加物品往往就是往这些数组追加条目。

也就是说,改一个 EBX,要么是改某个值(一个数字、一个字符串),要么是加一个实例(一个新物品),要么是往根数组追加引用(把新物品挂进游戏的主列表)。这三种变化,正是后面合并算法要处理的全部情形。

2. TOC:Superbundle 的目录清单

单个 EBX 文件很小,游戏内容被组织成bundle(捆绑包)再打包进superbundle(超级捆绑包)。而 TOC(Table of Contents)就是超级捆绑包的"总目录",定义见 Engine/Resource/toc.h:

  • TocBundle:目录中每个 bundle 的条目;
  • TocChunk:每个 chunk 的 GUID、在 CAS 中的位置(CasIdentifier)、字节偏移与大小;
  • bundleHashMap/chunkHashMap:两张哈希索引表,引擎靠它快速定位资源。

toc_hash使用的 FNV 种子0x811C9DC5与引擎完全一致——合并后重建的哈希表必须让引擎原样解析,这正是 write_patch_toc 能写出"引擎可解析的补丁 TOC"的原因。

3. CAS:压缩存储的"集装箱"

TOC 里的位置最终指向磁盘上的cas_NN.cas归档文件。CAS 是 Frostbite 的分块压缩容器,ReSkate 的编解码器见 Engine/Resource/cas_codec.h:

  • 支持 Oodle 的kraken、selkie、leviathan三种压缩模式;
  • 运行时直接链接 External/ 下的 lz4、zstd、miniz,Oodle 则复用游戏自带的oo2core模块。

bundle 的资产清单(manifest)也存放在 CAS 里,结构见 Engine/Resource/binary_bundle.h:一份 manifest 列出三类资产——ebx、resources(如音频、Lua 脚本)、chunks(流式纹理块),每个资产带 SHA1、GUID 与所在 CAS 文件的偏移。

📦 一句话总结:TOC 回答"资源在哪",CAS 回答"字节在哪、怎么解压",EBX 回答"资源长什么样"。

二、Mod 合并原理:把 N 个 Mod 缝成 1 层补丁

引擎对每个 superbundle 只合成一个补丁层,所以"两个 Mod 都改了同一个 bundle"时,谁都不能直接覆盖谁。ReSkate 的合并入口是 merge_mods,它把所有启用的 Mod 合成为Mods/.reskate/下的统一补丁。整个流程分四步:

第 1 步:重新编号 CAS 归档。每个 Mod 出厂时都假设自己的cas_00.cas是"1 号归档",多个 Mod 放一起必然撞号。合并器给每个 Mod 的归档分一个专属编号,并重写 Mod 补丁内的所有引用(mod_merge_internal.h 中的ArchivePlacement)。由于位置用 32 位偏移寻址,单个归档不能超过 4 GB,"一个 Mod 一号段"恰好让合并后的补丁没有自己的大小上限。

第 2 步:合并物理材质网格。这是最精巧的一步。地图 Mod 可以定义自己的碰撞表面,每份地图都从游戏网格的第 116 号槽位开始追加自己的行——两个 Mod 都"声称"第 116 号是它的。material_grid.h 的Combiner把每个 Mod 的新增行依次拼接到一张网格的尾部,并返回槽位重映射表,把每个 Mod 的碰撞数据改写为"新槽位号"。这样引擎读到网格里的每个槽位都合法——因为引擎对槽位表不做越界检查,读错一格就是碰撞瞬间崩溃。

第 3 步:逐个 superbundle 合并 bundle。这是主体工作,由 mod_merge_combine.cpp 完成,核心是逐资产比较:

资产情形合并策略
游戏有、Mod 没动保留原版
多个 Mod 都改了同一个 EBX调用 EBX 三向合并(见下)
Mod 新增的资产追加进 manifest,连同它引用的新文档一起搬走
无任何 manifest 可读的 bundle整体透传,优先级最高的 Mod 胜出

纹理chunk还有一层细节:chunk 元数据列表必须按 chunk GUID 排序、且替换的 chunk 要继承原版记录的首层 mip,否则流式加载会出错(merge_chunk_metadata)。

第 4 步:写出补丁与指纹。合并结果带一份指纹(SDK 版本 + 各 Mod 文件 + 被合并的原始文件)。下次启动若指纹没变,直接复用上次的Mods/.reskate/,秒进游戏(mod_merge_stamp.cpp)。报告结构MergeReport(mod_merge.h)还会记录每个 Mod 的失败原因,日志里能点名"是哪个 Mod 拖累了哪个关卡"。

核心算法:EBX 三向合并

两个 Mod 都基于同一份原版 EBX 各自修改后,如何合成?merge_documents 以原版为 base做三向合并,规则非常克制:

  1. 新增实例:各 Mod 添加的实例全部保留;
  2. 根数组追加:两边都追加的同一个引用只记一次;
  3. 原地改值:同一字段被多个 Mod 改成不同值时,按优先级顺序应用,最后一个赢;
  4. 其它形状的变化(指针改指别处、列表中间插值):无法安全合并,保留原版,并在MergeSummary里记录是哪份编辑没被采纳。

MergeSummary::exact()会告诉你这次合并是否"每个编辑的每处改动都完整落地",为日志与排查提供依据。

三、进阶:游戏运行中也能热合并

更妙的是,ReSkate 支持活合并(live merge):在游戏不重启的情况下,于两次关卡加载之间重新合并。难点在于引擎不会释放已挂载的归档,所以活合并不替换任何已打开的文件——新清单追加到 1 号归档末尾,superbundle TOC 每次挂载都会重读、因此可以放心重写(mod_merge.h)。这就是为什么"大多数 Mod 变更无需重启游戏即可生效"。

四、想深入源码?从这里开始

想看的模块路径
EBX 解析与三向合并Engine/Resource/ 下的ebx_document.*、ebx_merge.*
TOC 读写与哈希Engine/Resource/toc.cpp
CAS 压缩编解码Engine/Resource/cas_codec.cpp
Mod 合并主流程Engine/Vfs/mod_merge.cpp、mod_merge_combine.cpp
归档重编号Engine/Vfs/mod_merge_cas_store.cpp
材质网格合并Engine/Resource/material_grid.cpp

🧩总结:ReSkate 的 Mod 合并本质上是一套"格式级"的三向合并系统——用 EBX 反射数据理解内容,用 TOC 哈希表重建目录,用 CAS 归档重组存储,最后把 N 个 Mod 的差异收敛成引擎唯一接受的一层补丁。理解这三套格式后,你不仅能看懂 ReSkate 的日志,也具备了给 skate. 开发或调试 Mod 的底层知识。

【免费下载链接】ReSkate

项目地址:https://gitcode.com/gh_mirrors/re/ReSkate
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 11:17:43

PyTorch深度学习实战指南:从环境搭建到模型部署全解析

在深度学习圈子里混了这么多年,PyTorch 几乎成了我每天都要打交道的工具。不管是快速搭个原型验证想法,还是做完整的模型训练与部署,它都是我最先会想起的那个框架。如果你刚接触深度学习,或者从别的框架转过来,这篇内…

作者头像 李华
网站建设 2026/10/16 11:17:42

Agent冒烟测试实战:让智能体在工具调用中不失控

冒烟测试这事儿,在传统软件工程里不算新鲜:硬件工程师拿到一块新板子,第一件事就是上电,如果哪个元件冒烟了,说明这板子根本不能要,后面不用再测了。软件团队也把这个词继承下来,每次构建完跑一…

作者头像 李华
网站建设 2026/10/16 11:17:09

PyTorch SGD实操指南:学习率、动量与权重衰减调优

1. 这不是“又一篇SGD教程”,而是你真正能看懂、能调对、能debug的实操指南“Pytorch入门之一文看懂SGD”——这个标题背后藏着太多新手的真实困境:学了公式却不会写代码,调了学习率模型却不收敛,loss曲线像心电图一样乱跳&#x…

作者头像 李华
网站建设 2026/10/16 11:15:31

free-claude-code:开源终端AI编程助手的免费方案与部署实践

最近我在折腾终端里的 AI 编程工具时,发现一个叫free-claude-code的开源项目,它把那些要订阅费、绑定特定账号的命令行编程助手,整个重做成了一套可以自己掌控的免费方案。这篇文章就来聊聊这个项目能做什么、怎么部署、以及我在真实任务里踩…

作者头像 李华
网站建设 2026/10/16 11:13:46

软件测试实战指南:从测试用例设计到自动化测试落地

1. 软件测试到底测什么:从一段崩掉的代码说起我入行做软件开发的第三个月,接手了一个看起来特别简单的登录模块。功能描述只有一句话:用户输入账号密码之后,跳转到主页。当时觉得这有什么好测的,能跑就行。结果上线当天…

作者头像 李华
网站建设 2026/10/16 11:12:05

Go标准库strings与bytes用法详解:性能差异、底层原理与实战优化

最近在做一次线上日志解析的优化,抓到一个很有意思的现象:同样的逻辑,用strings.Split还是bytes.Split,性能差了近三倍;可看代码时,两套API几乎长得一模一样。于是我把Go标准库里的bytes和strings从头到尾翻…

作者头像 李华