ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

一次读懂UEViewer(UModel)架构:一份虚幻引擎资源包从字节到模型的完整旅程

2026/8/18 1:23:40 拓冰建站 浏览量
一次读懂UEViewer(UModel)架构:一份虚幻引擎资源包从字节到模型的完整旅程 一次读懂UEViewerUModel架构一份虚幻引擎资源包从字节到模型的完整旅程【免费下载链接】UEViewerViewer and exporter for Unreal Engine 1-4 assets (UE Viewer).项目地址: https://gitcode.com/gh_mirrors/ue/UEViewer拿到一个虚幻引擎游戏的.pak或.upk包体想提取其中的角色模型、贴图和骨骼动画时你面对的不是文件而是一团没有目录、没有文件名的原始字节。UEViewer社区俗称 UModel官方名称为 UE Viewer正是解决这个痛点的开源工具它支持从虚幻引擎 1 到 4 全系列游戏资源包中解析并导出 3D 模型、纹理、动画和材质。本文将跟随一份资源数据的一生从它被读取到被写出的完整旅程中解剖这个工具内部最关键的几个设计。这份数据要走过的四道关卡如果把一次导出任务想象成一份资源数据从磁盘到硬盘的旅行它要依次通过四道关卡每一道都只对上、下一层负责边界清晰得像流水线上的工位关卡对应模块负责的事通俗比喻第一关文件系统层Unreal/FileSystem/扫描游戏目录、解开.pak压缩/加密包、构建虚拟文件系统图书馆管理员告诉你书在哪个书架哪怕在仓库里压着第二关包解析层Unreal/UnrealPackage/读取包文件头、名称表、导入/导出表按引擎年代分流解析图书编目员把书的目录页翻译成人能懂的索引第三关对象模型层Unreal/下的UnObject.cpp、UnCore.h把导出表记录反序列化成内存中的UObject对象出版社编辑把目录还原成完整书稿第四关导出层Exporters/按对象类型分发给对应格式写出器产出 GLTF/PSK/TGA/PNG印刷厂按客户要求装订成不同版本这个分层最大的收益写在注释和代码里引擎版本升级时改动被牢牢限制在特定关卡内。UE1 到 UE2 的差异只影响包解析层UE3 新增的材质表达式网络只扰动对象模型层而导出层永远面对还原后的统一对象几乎感知不到版本号。关卡详解一FArchive——一根带版本记忆的读卡器所有 UE 资源本质上都是按固定顺序写入的字段序列所以整个项目的地基是一个名为FArchive的抽象基类定义在Unreal/UnCore.h中。它把读包和写导出文件统一成同一个接口class FArchive { public: int ArVer; // 引擎版本号 int ArLicenseeVer; // 厂商私有版本号 bool IsLoading; // 当前是读还是写 bool ReverseBytes; // 是否需要字节序反转 int Game; // 识别出的具体游戏EGame 枚举 int Platform; // 运行平台 virtual void Seek(int Pos) 0; virtual void Serialize(void *data, int size) 0; void DetectGame(); // 根据版本号包内容推断是哪一代引擎、哪个游戏 int Engine() const { return (Game GAME_ENGINE); } };人话解释这就像银行柜台前的读卡器——同一个插卡动作但读出来的账户信息会因为卡片类型银联/外币卡而走不同的校验流程。ArVer和Game两个字段让同一个序列化函数可以针对不同版本做出不同行为这是整个兼容性体系的支点。这个设计有多较真看包文件里的压缩块结构FCompressedChunkUnreal/UnrealPackage/UnPackage.h就知道它的operator里塞满了游戏特例#if MKVSDC || ROCKET_LEAGUE if ((Ar.Game GAME_MK Ar.ArVer 677) || (Ar.Game GAME_RocketLeague Ar.ArLicenseeVer 22)) { // MK X 和火箭联盟用的是 64 位文件偏移 int64 UncompressedOffset64, CompressedOffset64; Ar UncompressedOffset64 C.UncompressedSize CompressedOffset64 C.CompressedSize; return Ar; } #endif #if BULLETSTORM if (Ar.Game GAME_Bulletstorm Ar.ArLicenseeVer 21) { int32 unk10; // 子弹风暴多了一个含义不明的字段 Ar unk10; } #endif人话解释同样是一个压缩块MK X 和火箭联盟的偏移量是 64 位整数子弹风暴额外塞了一个未知字段——如果不按游戏区分读出来的字节序列就会整体错位后面的所有数据全部崩溃。正是这种逐个游戏打补丁的执着让 UModel 能覆盖十几年前的老游戏。而FArchive::DetectGame()Unreal/GameDatabase.cpp负责在打开包文件后第一时间猜出这是谁它根据版本号与包内容推断引擎代际甚至能识别《堡垒之夜》《火箭联盟》等具体作品——游戏库Unreal/GameDefines.h与GameDatabase.cpp集中维护了这些特征参数是定位格式特例的第一站。关卡详解二UnPackage——版本分流的四车道包解析的核心入口是UnPackageUnreal/UnrealPackage/UnPackage.h首要任务是读取FPackageFileSummary包文件标签PACKAGE_FILE_TAG0x9E2A83C1、版本号、名称表与导出表的数量与偏移、FGenerationInfo代数信息等。#define PACKAGE_V2 100 // UE2 起的分界版本 #define PACKAGE_V3 180 // UE3 起的分界版本 #define PACKAGE_FILE_TAG 0x9E2A83C1人话解释DetectGame()相当于先安检再分流版本号一旦确定解析逻辑就被分流到UnPackageReader.cpp、UnPackage2.cpp、UnPackage3.cpp、UnPackage4.cpp这四个文件——每个文件对应一代引擎的格式差异互不干扰。这就是前面说的改动被限制在特定关卡内的最直观体现想加 UE5 支持大概率只需要新增一个UnPackage5.cpp而不是重写整个工具。UE4 还引入了两个麻烦事无版本号包unversioned package和自定义版本容器FCustomVersionContainer。没有版本号怎么读UModel 在版本缺失时会通过回调向用户询问或者用自定义版本的 GUID 表反查——这正是它兼容 UE4 各版本的关键所在。关卡详解三Exporters——照着菜单点单后厨按菜名出菜对象还原完成后数据终于要以文件形式落地了。Exporters/Exporters.h采用了一个非常优雅的注册表设计每种对象类型对应一个导出函数启动时注册进一张静态表导出时按对象类名查找分发// 注册把类名 → 导出函数登记到全局表 templateclass T FORCEINLINE void RegisterExporter(void (*Func)(const T*)) { RegisterExporter(T::StaticGetTypeinfo()-Name 1, (ExporterFunc_t)Func); } // 使用各自的实现文件在初始化时调用 // ExportPsk.cpp: RegisterExporter(ExportPsk); // ExportTexture.cpp: RegisterExporter(ExportTexture); // ExportGLTF.cpp: RegisterExporter(ExportSkeletalMeshGLTF);人话解释这就是照着菜单点单后厨按菜名出菜。RegisterExporter的实现Exporters/Exporters.cpp维护一个容量为MAX_EXPORTERS的静态数组新增一种导出格式只需要在Exporters/下新建文件、实现函数、追加一行注册代码——全程不需要碰分发逻辑二次开发的成本被刻意压得很浅。更巧妙的是导出去重机制。ExportContext用基于包指针 导出索引的哈希表记录哪些对象已导出过ItemExists/AddItem并在ExportObject入口处先行拦截if (IsObjectExported(Obj)) return true; // 同一对象只导出一次人话解释在 UE3/UE4 里一张纹理可能被上千个材质引用导出一次就够如果每个材质都把纹理重新写一遍磁盘和时间的浪费是灾难性的。更狠的是OnObjectLoad回调——纹理一旦导出后续加载时直接用空对象替换注释里写着一个真实案例580 张纹理被加载了 18000 次。这个机制把批量导出的耗时从小时级压到分钟级。顺带一提怎么把它编译起来UModel 没有用 CMake 这类常见构建工具而是维护了一套基于 Perl 的自定义构建系统Tools/genmake把人类友好的项目描述文件根目录的common.project、UmodelTool/umodel.project转换成平台对应的 Makefile再由build.sh统一驱动支持--6464 位编译、--debug、--profile等选项。跨平台方面Windows 专用逻辑集中在Core/CoreWin32.cppSDL2 仅通过libs/SDL2/SDL2Loader.cpp动态加载避免强依赖Linux 构建直接复用系统的 zlib、libpng。想试水的话clone 仓库后直接执行./build.sh即可。它的价值、边界与你会踩的坑典型使用场景游戏资产考古从 UE1–UE3 老游戏的包体中批量导出模型与贴图用于 MOD 制作或美术参考命令行模式下可一次遍历整个包目录配合去重机制高效产出。引擎格式研究把UnPackage2.cpp与UnPackage4.cpp的差异当作活教材直接看到十几年间字段的增删历史。自动化流水线集成通过Exporters.h暴露的ExportObject、GetExportPath接口把导出能力嵌入自己的批处理工具。客观局限只覆盖视觉资源着色器还原、蓝图逻辑等非可视化数据不在范围内。UE4 加密与新版兼容存在滞后AES 加密包体需要用户提供密钥见UmodelTool/UE4AesKeyDialog.h且引擎不断迭代对新版本的支持往往要靠社区跟进。解析依赖逆向推断部分厂商私有格式是逆向出来的个别字段含义不明时就跳过见UnCore.h中的USE_COMPACT_PACKAGE_STRUCTS宏极端情况下模型会出现少量瑕疵。给想深入的人一条路径先读Unreal/UnCore.h的FArchive定义建立版本感知序列化的心智模型再通读Unreal/UnrealPackage/UnPackage.h的FPackageFileSummary与FGenerationInfo理解包结构最后对照Exporters/Exporters.cpp的注册与去重机制亲手写一个最简导出器。你在逆向解析游戏资源时最常被哪个环节卡住——是版本兼容、纹理压缩格式还是骨骼动画绑定欢迎在评论区分享你的经历也期待看到你基于这套架构扩展出的新玩法。【免费下载链接】UEViewerViewer and exporter for Unreal Engine 1-4 assets (UE Viewer).项目地址: https://gitcode.com/gh_mirrors/ue/UEViewer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考