Dumper-7 代码生成管线
本文是 Dumper-7 解读系列的第四篇,承接:
- Dumper-7 偏移推断算法:偏移推断
- Dumper-7 FName 名称系统:FName 名称解析
- Dumper-7 数据收集与管理层:数据收集与管理层
本文讲解最后一个阶段:把 Manager 层整理好的结构化数据,输出成可编译的 C++ SDK,以及三种辅助格式(USMAP / IDA 映射 / Dumpspace JSON)。
目录
- 概述:Generate 阶段与生成器协议
- CppGenerator 总流程与文件结构
- GenerateStruct:单个结构体/类的生成
- GenerateMembers:成员与 padding 生成
- GenerateEnum:枚举生成
- 函数生成与 ProcessEvent 调用桩
- 类型字符串映射
- Basic 基础设施与运行时定位
- 预定义成员与函数注入
- 文件头与依赖 include
- SDK 编译自测
- 三种辅助输出格式
- 关键函数索引
1. 概述:Generate 阶段与生成器协议
main() 在 InitEngineCore() 与 InitInternal() 之后,依次驱动四个生成器:
Generator::Generate<CppGenerator>(); // C++ SDK(主产物)
Generator::Generate<MappingGenerator>(); // .usmap 二进制映射
Generator::Generate<IDAMappingGenerator>();// .idmap IDA 符号
Generator::Generate<DumpspaceGenerator>();// Dumpspace JSON
四个生成器都满足同一个 C++20 concept GeneratorImplementation(见 Generator.h),即必须提供:
template<typename GeneratorType>
concept GeneratorImplementation = requires(GeneratorType t)
{
GeneratorType::PredefinedMembers; // PredefinedMemberLookupMapType
GeneratorType::MainFolderName; // 主目录名
GeneratorType::SubfolderName; // 子目录名
GeneratorType::MainFolder; GeneratorType::Subfolder; // fs::path
GeneratorType::Generate();
GeneratorType::InitPredefinedMembers();
GeneratorType::InitPredefinedFunctions();
};
Generator::Generate<T>() 是统一模板驱动:首次调用时建立输出目录、转储 GObjects 与 EditorOnly 元数据,然后挂接该生成器的预定义成员表,最后调用其 Generate():
template<GeneratorImplementation GeneratorType>
static void Generate()
{
if (DumperFolder.empty()) { SetupDumperFolder(); ObjectArray::DumpObjects(...); ... }
SetupFolders(GeneratorType::MainFolderName, GeneratorType::MainFolder, ...);
GeneratorType::InitPredefinedMembers();
GeneratorType::InitPredefinedFunctions();
MemberManager::SetPredefinedMemberLookupPtr(&GeneratorType::PredefinedMembers);
GeneratorType::Generate();
}
这套协议让"再加一种输出格式"变得容易:实现一个满足 concept 的类即可,无需改动收集层。
2. CppGenerator 总流程与文件结构
CppGenerator 是项目的核心,约 9000 行。Generate() 分三个阶段。
2.1 三阶段流程
void CppGenerator::Generate()
{
// ── 阶段一:全局基础文件 ──
GenerateSDKHeader(SDK.hpp); // 总 include 枢纽
GeneratePropertyFixupFile(PropertyFixup.hpp); // 未知属性占位
GenerateNameCollisionsInl(NameCollisions.inl); // 跨包重名的前向声明
GenerateUnrealContainers(UnrealContainers.hpp); // TArray/TMap/TSet/FString
GenerateUnicodeLib(UtfN.hpp); // Unicode 编解码
// ── 阶段二:SDK 运行时基础设施 ──
GenerateBasicFiles(Basic.hpp, Basic.cpp, Assertions.inl);
// ── 阶段三:逐包生成 ──
for (PackageInfoHandle Package : PackageManager::IterateOverPackageInfos())
{
// 按需创建 {Package}_classes.hpp / _structs.hpp / _parameters.hpp / _functions.cpp
// 生成枚举 → structs 文件
// 按拓扑序生成结构体、类(DependencyManager 回调)
}
if constexpr (Settings::Debug::bShouldGenerateSDKCompilationTestScript)
GenerateSDKTestScript(SDKTest.py);
}
2.2 产物目录结构
{GameVersion}-{GameName}/ (DumperFolder)
└── CppSDK/ (MainFolder)
├── SDK.hpp 总入口:#include 所有包文件
├── PropertyFixup.hpp 未识别属性的占位定义
├── NameCollisions.inl 跨包同名类型的前向声明
├── UnrealContainers.hpp TArray / TMap / TSet / TWeakObjectPtr / FString...
├── UtfN.hpp UTF 编解码库
├── Assertions.inl (可选) static_assert 宏集中文件
├── SDKTest.py (可选) 编译自测脚本
└── SDK/ (Subfolder)
├── Basic.hpp UObject/UClass/FName/ProcessEvent/偏移常量...
├── Basic.cpp GetImageBase / FindClass 等实现
├── {Package}_classes.hpp
├── {Package}_structs.hpp
├── {Package}_parameters.hpp
└── {Package}_functions.cpp
把 struct / class / parameters / functions 拆成四类文件,是为了能用前向声明 + 分文件包含来打破循环依赖(见数据收集文档第 4 节)。
3. GenerateStruct:单个结构体/类的生成
GenerateStruct() 负责输出一个 struct/class/union 的完整定义。流程:
- 头部注释 + 声明行:包含全名、相对大小(本类新增字节)、总大小、父类大小,以及
class/struct/union、alignas、final、: public Super等修饰:
StructFile << std::format(R"(
// {FullName}
// 0x{SizeWithoutSuper:04X} (0x{TotalSize:04X} - 0x{SuperSize:04X})
{pragmaPack}{templateText}{class|struct|union}{alignas} {UniqueName}{final}{: public Super}
{{
)", ...);
- 成员:调用
GenerateMembers()(见第 4 节)。 - 函数:调用
GenerateFunctions(),包括自动注入的StaticClass()、GetDefaultObj()等。 - 闭合
};。 - 静态断言(可选):若开启
bGenerateAssertionFile,为该类生成校验内存布局的宏:
static_assert(alignof(UniqueName) == 0x...);
static_assert(sizeof(UniqueName) == 0x...);
static_assert(offsetof(UniqueName, Member) == 0x...);
静态断言是 SDK 正确性的最后一道防线:一旦生成的布局与游戏实际内存不符,编译期就会报错,而不是运行时崩溃。它们可内联在结构体后,也可集中到
Assertions.inl(由bGenerateInlineAssertionsForStructSize/Members控制)。
4. GenerateMembers:成员与 padding 生成
GenerateMembers() 是保证"生成的结构体内存布局与游戏完全一致"的核心。它在成员之间精确插入字节填充与位域填充。
4.1 状态跟踪
遍历成员时维护若干游标:
int32 PrevPropertyEnd = SuperSize; // 上一个成员的结束偏移
int32 PrevBitPropertyEnd = 0;
int32 PrevBitPropertyEndBit = 1; // 上一个位域在其字节内的结束位
uint8 PrevBitPropertySize = 0x1;
uint64 PrevNumBitsInUnderlayingType = 0x8;
bool bLastPropertyWasBitField = false;
4.2 三类填充
对每个成员,按需生成三种填充:
① 成员间字节填充——当前成员偏移大于上一个成员结束偏移时:
if (MemberOffset > PrevPropertyEnd && !bIsUnion)
OutMembers += GenerateBytePadding(PrevPropertyEnd, MemberOffset - PrevPropertyEnd, "Fixing Size After Last Property");
② 位域内比特填充——同一字节内两个位域之间有空隙时:
if (PrevBitPropertyEndBit < BitFieldIndex && !bIsUnion)
OutMembers += GenerateBitPadding(MemberSize, PrevBitPropertyEndBit, MemberOffset,
BitFieldIndex - PrevBitPropertyEndBit, "Fixing Bit-Field Size Between Bits");
③ 结构体尾部填充——遍历结束后,若总大小大于最后成员结束:
const int32 MissingByteCount = Struct.GetUnalignedSize() - PrevPropertyEnd;
if (MissingByteCount > 0x0)
OutMembers += GenerateBytePadding(PrevPropertyEnd, MissingByteCount, "Fixing Struct Size After Last Property");
两个填充生成器分别产出字节数组与匿名位域:
// uint8 Pad_0x394[0x4]; // 0x0394(0x0004)(原因)
GenerateBytePadding(Offset, PadSize, Reason);
// uint32 BitPad_0x398_1 : 7; // 0x0398(0x0004)(原因)
GenerateBitPadding(UnderlayingSizeBytes, PrevBitEndBit, Offset, PadSize, Reason);
4.3 成员声明
非填充成员根据类型生成声明,并处理数组维度、位域宽度、默认值:
if (Member.GetArrayDim() > 1) MemberName += std::format("[0x{:X}]", Member.GetArrayDim());
else if (bIsBitField) MemberName += " : " + std::to_string(Member.GetBitCount());
if (Member.HasDefaultValue()) MemberName += " = " + Member.GetDefaultValue();
4.4 生成示例(示意)
// Engine.Character
// 0x0020 (0x03A0 - 0x0380)
class ACharacter final : public APawn
{
public:
class USkeletalMeshComponent* Mesh; // 0x0380(0x0008)
float Health; // 0x0390(0x0004)
uint8 Pad_0x394[0x4]; // 0x0394(0x0004)(Fixing Size After Last Property)
bool bCanMove : 1; // 0x0398(0x0001)
uint8 BitPad_0x398_1 : 7; // 0x0398(0x0001)(Fixing Bit-Field Size Between Bits)
uint32 MovementMode; // 0x039C(0x0004)
};
static_assert(offsetof(ACharacter, Health) == 0x0390);
static_assert(sizeof(ACharacter) == 0x03A0);
5. GenerateEnum:枚举生成
GenerateEnum() 输出 enum class,底层类型由 EnumManager 推断(见数据收集文档第 6 节),值名由冲突信息决定后缀:
// Engine.EWeaponType
// NumValues: 0x0003
enum class EWeaponType : uint8
{
EWT_Rifle = 0,
EWT_Pistol = 1,
EWT_Sniper = 2,
};
底层类型字符串由大小与符号性组合得到(int8/uint8/int16/uint16/.../uint64)。
6. 函数生成与 ProcessEvent 调用桩
UE 的蓝图可调用函数(UFunction)在 SDK 中被包装成普通 C++ 方法,内部通过 UObject::ProcessEvent 间接调用引擎。GenerateSingleFunction() 同时产出"头文件声明"与"cpp 实现"。
6.1 GenerateFunctionInfo:解析签名
GenerateFunctionInfo() 遍历函数的参数属性(带 Parm 标志),按 UE 的参数标志决定 C++ 形参写法:
| UE 属性标志 | C++ 处理 |
|---|---|
ReturnParm |
作为返回类型 |
OutParm 且非引用 |
指针 T* |
ReferenceParm |
引用 T& |
ConstParm |
加 const |
| 结构体/数组/字符串/Map/Set 等大对象(非 out) | 按 const T& 传,并标记 bIsMoveParam |
struct ParamInfo
{
bool bIsOutPtr, bIsOutRef, bIsMoveParam, bIsRetParam, bIsConst;
EPropertyFlags PropFlags;
std::string Type, Name;
};
6.2 实现体:ProcessEvent 桩
生成的实现把实参填入参数结构体,调用 ProcessEvent,再把 out 参数回写:
bool ACharacter::SetMovementSpeed(float NewSpeed)
{
static class UFunction* Func = nullptr;
if (Func == nullptr)
Func = Class->GetFunction("Character", "SetMovementSpeed");
Params::ACharacter_SetMovementSpeed_Params Parms{};
Parms.NewSpeed = NewSpeed;
UObject::ProcessEvent(Func, &Parms);
return Parms.ReturnValue;
}
要点:
Func用static缓存,只查一次;- Native 函数调用前后会临时清/恢复
FunctionFlags中的Native位(否则引擎会走原生执行路径而非 ProcessEvent); - out 指针参数:
if (p != nullptr) *p = Parms.p;;out 引用参数:p = Parms.p;;可移动类型用std::move; - 静态函数通过
GetDefaultObj()->调用,接口函数通过AsUObject()->调用。
6.3 参数结构体
每个有参数的函数生成一个 _params 结构体(放在 _parameters.hpp 的 Params 命名空间),其布局同样经 GenerateStruct 精确填充:
namespace Params
{
struct ACharacter_SetMovementSpeed_Params
{
float NewSpeed; // 0x0000(0x0004)
bool ReturnValue; // 0x0004(0x0001)
};
}
参数结构体本质上就是把
FunctionWrapper::AsStruct()当成普通结构体生成——这正是 Wrapper 抽象层(数据收集文档第 10 节)带来的复用。
7. 类型字符串映射
GetMemberTypeStringWithoutConst() 把 UE 反射属性映射成 C++ 类型字符串,是连接"反射类型系统"与"C++ 类型"的桥梁。它按 EClassCastFlags 逐类判定:
| UE 属性 | C++ 类型 |
|---|---|
ByteProperty(带枚举) |
枚举名;否则 uint8 |
Int/Int64/UInt32/... |
int32/int64/uint32/... |
Float/Double |
float/double |
BoolProperty |
原生 bool,或按大小取整型(位域) |
NameProperty/StrProperty/TextProperty |
class FName/FString/FText |
ObjectProperty |
class X* |
ClassProperty |
class UClass* 或 TSubclassOf<X> |
StructProperty |
struct X(循环时走修复类型) |
ArrayProperty |
TArray<内部类型>(递归) |
MapProperty |
TMap<K, V>(递归) |
SetProperty |
TSet<元素>(递归) |
WeakObjectProperty |
TWeakObjectPtr<X> |
Lazy/Soft Object/Class |
TLazyObjectPtr/TSoftObjectPtr/TSoftClassPtr<X> |
DelegateProperty |
TDelegate<签名> |
MulticastInlineDelegateProperty |
TMulticastInlineDelegate<签名> |
InterfaceProperty |
TScriptInterface<I> |
EnumProperty |
枚举名;否则底层属性类型 |
| 无法识别 | 类名_(带下划线后缀,并在 PropertyFixup.hpp 占位) |
容器类型通过递归调用自身得到内部类型字符串,因此 TArray<TMap<FName, TArray<int32>>> 这样的深层嵌套也能正确展开。
循环依赖修复:当
StructProperty的目标结构体与当前包构成循环(IsCyclicWithPackage),不能直接#include,于是生成一个等大的占位类型(GetCycleFixupType),保证sizeof正确,留待使用者强转。
8. Basic 基础设施与运行时定位
GenerateBasicFiles() 生成 SDK 运行所必需的"地基"。
8.1 Basic.hpp 主要内容
- 命名空间与对齐宏:
SDK_NAMESPACE_START/END、SDK_ALIGN等; - 偏移常量:把 Dumper 找到的关键偏移写死进 SDK,供运行时直接访问:
namespace Offsets
{
constexpr int32 GObjects = 0x...; // 全局对象数组
constexpr int32 AppendString = 0x...; // FName::AppendString
constexpr int32 GNames = 0x...; // 名称表
constexpr int32 GWorld = 0x...; // 当前 UWorld
constexpr int32 ProcessEvent = 0x...; // ProcessEvent 函数偏移
constexpr int32 ProcessEventIdx = 0x...; // ProcessEvent 在虚表中的索引
}
InSDKUtils:GetImageBase()(可由Settings::CppGenerator::GetImageBaseFuncBody自定义)、GetVirtualFunction()、CallGameFunction();- 对象查找辅助:
FindClassByName/FindClassByFullName/GetObjectByIndex等; StaticClass模板:为每个类提供按名缓存的StaticClass()实现,蓝图生成类则用"对象索引 + FName 校验"做缓存失效检测。
8.2 Basic.cpp
实现平台相关的 GetImageBase()(默认 GetModuleHandle(0))、FindClass*、StringToName 等,并 #include CoreUObject 与 Engine 的类文件。
bAddManualOverrideOptions开启时,SDK 还会暴露手动设置GObjects/GNames/AppendString地址的接口,应对 Dumper 自动定位失败的加密游戏。
9. 预定义成员与函数注入
InitPredefinedMembers() / InitPredefinedFunctions() 把无法从反射获取的成员/方法注入核心类(机制见数据收集文档第 11 节)。
9.1 注入的成员
为 UObject/UField/UStruct/UEnum/UFunction/UClass 等手工补齐反射里没有的字段,例如 UObject:
PredefinedMember { .Type = "inline class TUObjectArrayWrapper", .Name = "GObjects", .bIsStatic = true };
PredefinedMember { .Type = "void*", .Name = "VTable", .Offset = Off::UObject::Vft };
PredefinedMember { .Type = "EObjectFlags", .Name = "Flags", .Offset = Off::UObject::Flags };
PredefinedMember { .Type = "int32", .Name = "Index", .Offset = Off::UObject::Index };
PredefinedMember { .Type = "class UClass*", .Name = "Class", .Offset = Off::UObject::Class };
PredefinedMember { .Type = "class FName", .Name = "Name", .Offset = Off::UObject::Name };
PredefinedMember { .Type = "class UObject*",.Name = "Outer", .Offset = Off::UObject::Outer };
还包括两个 SDK 用户最常用的"惊喜"成员,直接用 Dumper 找到的偏移注入:
ULevel::Actors(TArray<AActor*>);UDataTable::RowMap(TMap<FName, uint8*>)。
9.2 注入的函数
为 UObject 注入一整套使用 SDK 必备的方法:
| 方法 | 作用 |
|---|---|
FindObject<T> / FindObjectFast<T> |
遍历 GObjects 按名查找对象 |
FindClass / FindClassFast |
查找 UClass |
GetName() / GetFullName() |
取对象名 / 完整名 |
IsA(UClass*) / IsA(FName) / IsA(EClassCastFlags) |
三种类型检查重载 |
ProcessEvent(UFunction*, void*) |
通过虚表索引调用引擎 ProcessEvent(内联) |
UStruct 则注入 IsChildOf、GetProperties、FindMember 等。这些函数体以原始字符串字面量直接写在 PredefinedFunction::Body 中,bIsBodyInline 决定它写进头文件还是 cpp。
10. 文件头与依赖 include
WriteFileHead() 为每个文件写头部,最关键的是根据 PackageManager 的依赖信息生成 #include 列表:
const DependencyListType& List =
(Type == EFileType::Structs) ? Dep.StructsDependencies :
(Type == EFileType::Classes) ? Dep.ClassesDependencies :
Dep.ParametersDependencies;
for (const auto& [PackageIndex, Requirements] : List)
{
if (Requirements.bShouldIncludeStructs) File << "#include \"" + Name + "_structs.hpp\"\n";
if (Requirements.bShouldIncludeClasses) File << "#include \"" + Name + "_classes.hpp\"\n";
}
由于循环依赖已在 PackageManager::PostInit() 阶段打破(删边 + 前向声明),这里写出的依赖图保证无环,#include 顺序安全。文件头还负责:UTF-8 BOM、#pragma once、Basic.hpp 包含、32 位平台的 #pragma pack(push, 0x4)、SDK 命名空间开启等。WriteFileEnd() 则负责闭合命名空间与 #pragma pack。
11. SDK 编译自测
为了验证生成的 SDK 真的能编译,CppGenerator 可生成并执行一个 Python 脚本:
GenerateSDKTestScript():写出SDKTest.py,内含一段示例TestMain.cpp(演示获取 UEngine/UWorld、遍历 GObjects、访问 Level->Actors 等)与一份CMakeLists.txt,用 MSVC 和 Clang 两种编译器各编译一次。ExecuteSDKCompilationTestScript():用CreateProcessW调起python SDKTest.py,必要时临时生成脚本并在跑完后自删。
由 Settings::Debug::bShouldGenerateSDKCompilationTestScript 与 bExecuteSDKTestScript 两个开关控制。
12. 三种辅助输出格式
除 C++ SDK 外,Dumper-7 还生成三种面向不同工具链的格式。
| 生成器 | 产物 | 格式 | 消费方 |
|---|---|---|---|
MappingGenerator |
.usmap |
二进制(压缩) | UE 资源反序列化工具(Unversioned Properties) |
IDAMappingGenerator |
.idmap + ReadMe.txt |
二进制(无压缩) | IDA 插件(自动命名虚表与 Exec 函数) |
DumpspaceGenerator |
JSON | 文本 | Dumpspace 在线查看器 |
12.1 MappingGenerator(USMAP)
用途:UE4.12+ 的"Unversioned Properties"在序列化时不再写入属性名,外部工具需要一张 .usmap 映射表才能解析 pak/uasset。
文件结构:
[文件头]
uint16 Magic = 0x30C4
uint8 Version (EUsmapVersion,当前 ExplicitEnumValues = 4)
[可选包版本信息]
uint8 CompressionMethod (None/Oodle/Brotli/ZStandard,默认 ZStandard)
uint32 CompressedSize
uint32 DecompressedSize
[压缩载荷]
[名称表] [枚举表] [结构表]
- 名称表:
uint32 数量+ 每项uint16 长度 + 字符串。可选去重(GNameMap哈希表)以减小体积。 - 枚举表:每项含名称索引、成员数(v3 起为
uint16,支持大枚举)、每个成员的(显式值 int64, 名称索引)(v4 起含显式值)。 - 结构表:每项含名称索引、父类索引、属性数、可序列化属性数,以及每个属性的编码。
- 属性编码:
属性索引 + 数组维度 + 名称索引 + 类型枚举(EMappingsTypeFlags),容器类型(Array/Set/Map/Optional)递归编码内部类型,枚举/结构追加目标名称索引。ByteProperty若带枚举则被编码为EnumProperty。
压缩使用 ZSTD_compress(最大压缩等级)。
12.2 IDAMappingGenerator(.idmap)
用途:把虚表与原生(Exec)函数的"偏移 → 名称"导入 IDA,配合插件 IDAExecFunctionsImporter 自动重命名。
文件结构:纯粹是一个 Identifier 数组:
struct Identifier
{
uint32 Offset; // 相对镜像基址
uint16 NameLength;
char Name[NameLength]; // 非 null 结尾
};
- 虚表:遍历 CDO,名称为
{ClassName}_VFT,偏移取DefaultObject.GetVft();与父类虚表相同则跳过去重。 - Exec 函数:遍历每个类的 Native 函数,名称按 Itanium C++ ABI 修饰为
_ZN{len}{Class}{len+4}exec{Func}Ev,偏移取Func.GetExecFunction();同址只保留一个。
例:ACharacter::Jump → _ZN10ACharacter8execJumpEv。
12.3 DumpspaceGenerator(JSON)
用途:为 Dumpspace 在线查看器提供结构化数据,支持网页浏览类/结构体/枚举/函数。
基于 DSGen 数据模型(DSGen.h):
enum EType { ET_Default, ET_Struct, ET_Class, ET_Enum, ET_Function };
struct MemberType { // 类型的递归表示
EType type; std::string typeName, extendedType; // extendedType 存 "*"/"&"
bool reference; std::vector<MemberType> subTypes; // 模板子类型
};
类型被序列化成数组 [typeName, 类型简写, 修饰符, 子类型数组],例如:
FVector*→["FVector", "S", "*", []]TArray<int>→["TArray", "C", "", [["int", "D", "", []]]]
ClassHolder/EnumHolder/FunctionHolder 分别承载类、枚举、函数;GeneratedStaticOffsets() 还把 OFFSET_GOBJECTS/GNAMES/GWORLD/PROCESSEVENT 等关键偏移写入元数据。最终 DSGen::dump() 输出整个 JSON。
三种格式都复用了同一套 Wrapper + Manager 数据:
MappingGenerator关心序列化类型,IDAMappingGenerator关心地址,DumpspaceGenerator关心可读结构——正是抽象层让同一份数据服务于完全不同的消费者。
13. 关键函数索引
| 部分 | 函数 | 说明 |
|---|---|---|
| 入口 | CppGenerator::Generate() |
SDK 生成主流程 |
| 结构体 | GenerateStruct() |
单个 struct/class 声明 + 断言 |
| 成员 | GenerateMembers() |
成员排列与 padding |
| 填充 | GenerateBytePadding() / GenerateBitPadding() |
字节 / 位域填充 |
| 枚举 | GenerateEnum() |
enum class 生成 |
| 函数 | GenerateSingleFunction() |
函数声明 + ProcessEvent 实现 |
| 签名 | GenerateFunctionInfo() |
UFunction → C++ 签名解析 |
| 类型 | GetMemberTypeStringWithoutConst() |
UE 属性 → C++ 类型字符串 |
| 基础 | GenerateBasicFiles() |
Basic.hpp / Basic.cpp |
| 预定义 | InitPredefinedMembers() / InitPredefinedFunctions() |
注入 UObject 等核心成员/方法 |
| 文件头 | WriteFileHead() / WriteFileEnd() |
头部注释 + 依赖 include / 闭合 |
| 容器 | GenerateUnrealContainers() |
TArray/TMap/TSet/FString 实现 |
| 自测 | GenerateSDKTestScript() / ExecuteSDKCompilationTestScript() |
生成 / 执行编译测试 |
| USMAP | MappingGenerator::GeneratePropertyType() 等 |
.usmap 属性递归编码 |
| IDA | IDAMappingGenerator::MangleFunctionName() 等 |
Exec 函数名修饰 / 虚表导出 |
| Dumpspace | DumpspaceGenerator::GenerateStruct() 等 |
JSON 数据模型构建 |
文档版本: 1.0 最后更新: 2026-06-30 基于 Dumper-7 源码分析(CppGenerator / MappingGenerator / IDAMappingGenerator / DumpspaceGenerator)
评论
- 还没有评论,来说点什么吧。