尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

AssetStudio 如何把二进制还原成可导出的 Unity 资源?——加载链路里的 4 个关键机制

AssetStudio 如何把二进制还原成可导出的 Unity 资源?——加载链路里的 4 个关键机制 AssetStudio 如何把二进制还原成可导出的 Unity 资源——加载链路里的 4 个关键机制【免费下载链接】AssetStudioAssetStudio is a tool for exploring, extracting and exporting assets and assetbundles.项目地址: https://gitcode.com/gh_mirrors/as/AssetStudioAssetStudio 是一款开源的 Unity 资源提取工具把 AssetBundle 或 .assets 文件丢给它能输出纹理、模型、音频、GameObject 层级等可直接导出的资源。它的核心链路是文件进内存 → 判定身份 → 解包解压 → 重建对象。下面顺着一个文件被读取后的完整旅程拆开其中的魔数检测、AssetBundle 分块解析、PPtr 依赖解析与内存池这几段机制。第一关用前 40 个字节判断手里拿的是什么魔数检测就像海关验箱子先看箱体贴的标签标签能直接判明货型就走快速通道没贴标签的才开箱验货。FileReader拿到文件流后的第一件事是把开头 20 字节当字符串读出来当签名。命中UnityWeb/UnityRaw/UnityArchive/UnityFS就判定为 AssetBundleUnityWebData1.0判定为 WebFile。没命中的文件走开箱验货流程先读前 2 字节对 GZip 魔数0x1f8b再跳到偏移 0x20 处找 6 字节的brotli串然后尝试把头部当序列化文件头做自洽校验都不行再看 ZIP 魔数最后兜底为原始资源文件。var signature this.ReadStringToNull(20); // 读前 20 字节当签名 Position 0; switch (signature) { case UnityWeb: case UnityRaw: case UnityArchive: case UnityFS: return FileType.BundleFile; ... // 不匹配时依次查 GZip / Brotli 魔数、序列化头自洽性、ZIP 魔数 }这段判断里最容易踩的坑是序列化文件头自洽校验它不只是看有没有头而是要求头里声明的文件总大小与实际文件大小完全相等、数据偏移不超过文件长度且版本 22 以上的文件头改用 64 位字段——头部结构变了读错位置就会把合法文件误判成不是资源。注意 F 这个回边压缩壳解压出来的内容会重新走一遍完整判定所以bundle 套 gzip 套 assets这种多层包装能自然剥开。打开集装箱AssetBundle 是怎么按块拆开的AssetBundle 可以理解成一个集装箱加一张货单文件头列出每个数据块的压缩/解压尺寸目录表则把文件名映射到数据池中的偏移 大小。工具要做的就是把整箱货解压出来再按货单切件。两种常见封装的拆法不同UnityWeb / UnityRaw先把整块数据一次性LZMA解完再从中读出目录表UnityFS块信息与目录交错存放逐块按标志位选算法解压、写入一块连续数据池最后按目录表切出各个文件。块级压缩标志与实际处理方式的对应关系块标志算法处理方式None无压缩原样写入数据池LzmaLZMA走内置 7z 解压流Lz4 / Lz4HCLZ4 系走 K4os LZ4 编解码器LzhamLZHAM实际项目中极少见未实现数据池有个关键的边界处理所有块解压后总尺寸一旦达到 2GB 以上就不放在MemoryStream里而是落到一个DeleteOnClose的临时文件上——程序退出或读取结束系统自动清理既绕开了 32 位长度上限也省得自己写清理逻辑。切出来的每个文件并不会直接解析而是重新包一层FileReader再验一次货是 assets 就当场读入是 bundle 或 web 文件就再次递归拆开。也就是说 bundle 里套 bundle、bundle 里套 zip靠的都是这条递归分发路径没有专门的嵌套分支。WebFile 则是简化形态头表只有偏移、长度、路径三元组无压缩直接按偏移取数。依赖藏在哪PPtr 指针、外部文件列表与索引缓存Unity 的对象很少独自存在——材质指纹理、GameObject 指组件引用一律通过 PPtr 表达一个m_FileID加一个m_PathID。m_FileID为 0 表示对象就在本文件内大于 0 则表示本文件m_Externals外部文件列表里的第 N 项。难点不在解析指针本身而在于外部文件可能压根不在手边。加载每个 assets 文件时工具会顺带扫描它的m_Externals按同级目录 → 整棵子树递归搜索 → 记入不存在名单的顺序处理引用目标找到的立即塞进加载队列——注意外层循环必须用索引 for 而不是 foreach因为队列在加载过程中还在变长。foreach (var sharedFile in assetsFile.m_Externals) { var sharedFilePath Path.Combine(Path.GetDirectoryName(reader.FullPath), sharedFile.fileName); if (!File.Exists(sharedFilePath)) { var findFiles Directory.GetFiles(Path.GetDirectoryName(reader.FullPath), sharedFile.fileName, SearchOption.AllDirectories); // 命中则加入加载队列找不到则记入 noexistFiles避免反复搜索 } }所有文件加载完毕后PPtr.TryGet的工作就退化成两次查表先用一个不区分大小写的Dictionarystring, int缓存把外部文件名定位到已加载文件列表的下标每个文件名只搜一次再在目标文件的对象字典里按 PathID 取对象。这就是整个资源依赖图构建的全部——没有显式的图结构靠先齐货、后对单的顺序保证引用可解。别让内存爆掉池化、流与递归的边界处理 GB 级 bundle 时内存管理有三个实际动作大块缓冲池化解压缩用的大数组来自一个共享ArrayPool上限 64MB用完归还避免反复分配大对象触发 GC。private static readonly ArrayPoolT s_shared ArrayPoolT.Create(64 * 1024 * 1024, 3); public static ArrayPoolT Shared s_shared;全程留内存、不落盘bundle 内容默认只在内存流里流转唯一例外是上文提到的 2GB 临时文件分支。统一的释放出口每读一个文件都在finally里Disposereader整体加载结束调用Clear()时集中清空对象列表、关闭所有资源文件 reader 并重置索引缓存。对象级解析还有一层错误隔离单个对象反序列化失败只记录来自哪个 assets、哪个 PathID、什么类型的日志不会中断整批文件的处理——这是批量导入时部分成功能成立的原因。解不开的时候先查什么文件被识别成 ResourceFile说明没通过任何签名/魔数/结构校验先确认它是不是标准 Unity 产物版本过新超出 3.4–2022.1 支持范围也会落到这里。报version stripped被裁掉版本号的 bundle 需要在选项里手动指定 Unity 版本否则无法解析。大 bundle 内存吃紧官方 README 也提示了——用Extract folder先把 bundle 解到磁盘再读解压后的产物比直接整包加载温和得多。嵌套 bundle 报错错误日志会带上来自哪个外层文件的路径顺着它回查外层容器比单独看内层文件更快定位。核心加载模块 与 对象模型定义 是这条链路的主体代码想深挖某个机制时从这里入手最省时间。一句话概括AssetStudio 把判定—解压—建索引—重建对象做成了同一条递归流水线文件只要能通过第一关的身份判定后面的环节就会自动接力。下次遇到解不开的 bundle先看它开头的签名再决定怎么剥壳——是 UnityFS 本体还是外面套了一层压缩决定了你该走哪条路。【免费下载链接】AssetStudioAssetStudio is a tool for exploring, extracting and exporting assets and assetbundles.项目地址: https://gitcode.com/gh_mirrors/as/AssetStudio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表