diff --git a/Cpp2IL.Core/Cpp2IL.Core.csproj b/Cpp2IL.Core/Cpp2IL.Core.csproj index 02d1ec3f..1e6a8412 100644 --- a/Cpp2IL.Core/Cpp2IL.Core.csproj +++ b/Cpp2IL.Core/Cpp2IL.Core.csproj @@ -60,6 +60,7 @@ + diff --git a/Cpp2IL.Core/OutputFormats/Disassembly/HeaderConstants.cs b/Cpp2IL.Core/OutputFormats/Disassembly/HeaderConstants.cs new file mode 100644 index 00000000..9053d55b --- /dev/null +++ b/Cpp2IL.Core/OutputFormats/Disassembly/HeaderConstants.cs @@ -0,0 +1,696 @@ +namespace Il2CppDumper; + +public static class HeaderConstants +{ + public const string GenericHeader = + """ + typedef void(*Il2CppMethodPointer)(); + + struct MethodInfo; + + struct VirtualInvokeData + { + Il2CppMethodPointer methodPtr; + const MethodInfo* method; + }; + + struct Il2CppType + { + void* data; + unsigned int bits; + }; + + struct Il2CppClass; + + struct Il2CppObject + { + Il2CppClass *klass; + void *monitor; + }; + + union Il2CppRGCTXData + { + void* rgctxDataDummy; + const MethodInfo* method; + const Il2CppType* type; + Il2CppClass* klass; + }; + + struct Il2CppRuntimeInterfaceOffsetPair + { + Il2CppClass* interfaceType; + int32_t offset; + }; + + """; + + public const string HeaderV29 = + """ + struct Il2CppClass_1 + { + void* image; + void* gc_desc; + const char* name; + const char* namespaze; + Il2CppType byval_arg; + Il2CppType this_arg; + Il2CppClass* element_class; + Il2CppClass* castClass; + Il2CppClass* declaringType; + Il2CppClass* parent; + void *generic_class; + void* typeMetadataHandle; + void* interopData; + Il2CppClass* klass; + void* fields; + void* events; + void* properties; + void* methods; + Il2CppClass** nestedTypes; + Il2CppClass** implementedInterfaces; + Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; + }; + + struct Il2CppClass_2 + { + Il2CppClass** typeHierarchy; + void *unity_user_data; + uint32_t initializationExceptionGCHandle; + uint32_t cctor_started; + uint32_t cctor_finished; + size_t cctor_thread; + void* genericContainerHandle; + uint32_t instance_size; + uint32_t actualSize; + uint32_t element_size; + int32_t native_size; + uint32_t static_fields_size; + uint32_t thread_static_fields_size; + int32_t thread_static_fields_offset; + uint32_t flags; + uint32_t token; + uint16_t method_count; + uint16_t property_count; + uint16_t field_count; + uint16_t event_count; + uint16_t nested_type_count; + uint16_t vtable_count; + uint16_t interfaces_count; + uint16_t interface_offsets_count; + uint8_t typeHierarchyDepth; + uint8_t genericRecursionDepth; + uint8_t rank; + uint8_t minimumAlignment; + uint8_t naturalAligment; + uint8_t packingSize; + uint8_t bitflags1; + uint8_t bitflags2; + }; + + struct Il2CppClass + { + Il2CppClass_1 _1; + void* static_fields; + Il2CppRGCTXData* rgctx_data; + Il2CppClass_2 _2; + VirtualInvokeData vtable[255]; + }; + + typedef uintptr_t il2cpp_array_size_t; + typedef int32_t il2cpp_array_lower_bound_t; + struct Il2CppArrayBounds + { + il2cpp_array_size_t length; + il2cpp_array_lower_bound_t lower_bound; + }; + + typedef void (*InvokerMethod)(Il2CppMethodPointer, const MethodInfo*, void*, void**, void*); + struct MethodInfo + { + Il2CppMethodPointer methodPointer; + Il2CppMethodPointer virtualMethodPointer; + InvokerMethod invoker_method; + const char* name; + Il2CppClass *klass; + const Il2CppType *return_type; + const Il2CppType** parameters; + union + { + const Il2CppRGCTXData* rgctx_data; + const void* methodMetadataHandle; + }; + union + { + const void* genericMethod; + const void* genericContainerHandle; + }; + uint32_t token; + uint16_t flags; + uint16_t iflags; + uint16_t slot; + uint8_t parameters_count; + uint8_t bitflags; + }; + + + """; + + public const string HeaderV27 = + """ + struct Il2CppClass_1 + { + void* image; + void* gc_desc; + const char* name; + const char* namespaze; + Il2CppType byval_arg; + Il2CppType this_arg; + Il2CppClass* element_class; + Il2CppClass* castClass; + Il2CppClass* declaringType; + Il2CppClass* parent; + void *generic_class; + void* typeMetadataHandle; + void* interopData; + Il2CppClass* klass; + void* fields; + void* events; + void* properties; + void* methods; + Il2CppClass** nestedTypes; + Il2CppClass** implementedInterfaces; + Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; + }; + + struct Il2CppClass_2 + { + Il2CppClass** typeHierarchy; + void *unity_user_data; + uint32_t initializationExceptionGCHandle; + uint32_t cctor_started; + uint32_t cctor_finished; + size_t cctor_thread; + void* genericContainerHandle; + uint32_t instance_size; + uint32_t actualSize; + uint32_t element_size; + int32_t native_size; + uint32_t static_fields_size; + uint32_t thread_static_fields_size; + int32_t thread_static_fields_offset; + uint32_t flags; + uint32_t token; + uint16_t method_count; + uint16_t property_count; + uint16_t field_count; + uint16_t event_count; + uint16_t nested_type_count; + uint16_t vtable_count; + uint16_t interfaces_count; + uint16_t interface_offsets_count; + uint8_t typeHierarchyDepth; + uint8_t genericRecursionDepth; + uint8_t rank; + uint8_t minimumAlignment; + uint8_t naturalAligment; + uint8_t packingSize; + uint8_t bitflags1; + uint8_t bitflags2; + }; + + struct Il2CppClass + { + Il2CppClass_1 _1; + void* static_fields; + Il2CppRGCTXData* rgctx_data; + Il2CppClass_2 _2; + VirtualInvokeData vtable[255]; + }; + + typedef uintptr_t il2cpp_array_size_t; + typedef int32_t il2cpp_array_lower_bound_t; + struct Il2CppArrayBounds + { + il2cpp_array_size_t length; + il2cpp_array_lower_bound_t lower_bound; + }; + + struct MethodInfo + { + Il2CppMethodPointer methodPointer; + void* invoker_method; + const char* name; + Il2CppClass *klass; + const Il2CppType *return_type; + const void* parameters; + union + { + const Il2CppRGCTXData* rgctx_data; + const void* methodMetadataHandle; + }; + union + { + const void* genericMethod; + const void* genericContainerHandle; + }; + uint32_t token; + uint16_t flags; + uint16_t iflags; + uint16_t slot; + uint8_t parameters_count; + uint8_t bitflags; + }; + + + """; + + public const string HeaderV242 = + """ + struct Il2CppClass_1 + { + void* image; + void* gc_desc; + const char* name; + const char* namespaze; + Il2CppType byval_arg; + Il2CppType this_arg; + Il2CppClass* element_class; + Il2CppClass* castClass; + Il2CppClass* declaringType; + Il2CppClass* parent; + void *generic_class; + void* typeDefinition; + void* interopData; + Il2CppClass* klass; + void* fields; + void* events; + void* properties; + void* methods; + Il2CppClass** nestedTypes; + Il2CppClass** implementedInterfaces; + Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; + }; + + struct Il2CppClass_2 + { + Il2CppClass** typeHierarchy; + void *unity_user_data; + uint32_t initializationExceptionGCHandle; + uint32_t cctor_started; + uint32_t cctor_finished; + size_t cctor_thread; + int32_t genericContainerIndex; + uint32_t instance_size; + uint32_t actualSize; + uint32_t element_size; + int32_t native_size; + uint32_t static_fields_size; + uint32_t thread_static_fields_size; + int32_t thread_static_fields_offset; + uint32_t flags; + uint32_t token; + uint16_t method_count; + uint16_t property_count; + uint16_t field_count; + uint16_t event_count; + uint16_t nested_type_count; + uint16_t vtable_count; + uint16_t interfaces_count; + uint16_t interface_offsets_count; + uint8_t typeHierarchyDepth; + uint8_t genericRecursionDepth; + uint8_t rank; + uint8_t minimumAlignment; + uint8_t naturalAligment; + uint8_t packingSize; + uint8_t bitflags1; + uint8_t bitflags2; + }; + + struct Il2CppClass + { + Il2CppClass_1 _1; + void* static_fields; + Il2CppRGCTXData* rgctx_data; + Il2CppClass_2 _2; + VirtualInvokeData vtable[255]; + }; + + typedef uintptr_t il2cpp_array_size_t; + typedef int32_t il2cpp_array_lower_bound_t; + struct Il2CppArrayBounds + { + il2cpp_array_size_t length; + il2cpp_array_lower_bound_t lower_bound; + }; + + struct MethodInfo + { + Il2CppMethodPointer methodPointer; + void* invoker_method; + const char* name; + Il2CppClass *klass; + const Il2CppType *return_type; + const void* parameters; + union + { + const Il2CppRGCTXData* rgctx_data; + const void* methodDefinition; + }; + union + { + const void* genericMethod; + const void* genericContainer; + }; + uint32_t token; + uint16_t flags; + uint16_t iflags; + uint16_t slot; + uint8_t parameters_count; + uint8_t bitflags; + }; + + + """; + + public const string HeaderV241 = + """ + struct Il2CppClass_1 + { + void* image; + void* gc_desc; + const char* name; + const char* namespaze; + Il2CppType byval_arg; + Il2CppType this_arg; + Il2CppClass* element_class; + Il2CppClass* castClass; + Il2CppClass* declaringType; + Il2CppClass* parent; + void *generic_class; + void* typeDefinition; + void* interopData; + Il2CppClass* klass; + void* fields; + void* events; + void* properties; + void* methods; + Il2CppClass** nestedTypes; + Il2CppClass** implementedInterfaces; + Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; + }; + + struct Il2CppClass_2 + { + Il2CppClass** typeHierarchy; + uint32_t initializationExceptionGCHandle; + uint32_t cctor_started; + uint32_t cctor_finished; + uint64_t cctor_thread; + int32_t genericContainerIndex; + uint32_t instance_size; + uint32_t actualSize; + uint32_t element_size; + int32_t native_size; + uint32_t static_fields_size; + uint32_t thread_static_fields_size; + int32_t thread_static_fields_offset; + uint32_t flags; + uint32_t token; + uint16_t method_count; + uint16_t property_count; + uint16_t field_count; + uint16_t event_count; + uint16_t nested_type_count; + uint16_t vtable_count; + uint16_t interfaces_count; + uint16_t interface_offsets_count; + uint8_t typeHierarchyDepth; + uint8_t genericRecursionDepth; + uint8_t rank; + uint8_t minimumAlignment; + uint8_t naturalAligment; + uint8_t packingSize; + uint8_t bitflags1; + uint8_t bitflags2; + }; + + struct Il2CppClass + { + Il2CppClass_1 _1; + void* static_fields; + Il2CppRGCTXData* rgctx_data; + Il2CppClass_2 _2; + VirtualInvokeData vtable[255]; + }; + + typedef uintptr_t il2cpp_array_size_t; + typedef int32_t il2cpp_array_lower_bound_t; + struct Il2CppArrayBounds + { + il2cpp_array_size_t length; + il2cpp_array_lower_bound_t lower_bound; + }; + + struct MethodInfo + { + Il2CppMethodPointer methodPointer; + void* invoker_method; + const char* name; + Il2CppClass *klass; + const Il2CppType *return_type; + const void* parameters; + union + { + const Il2CppRGCTXData* rgctx_data; + const void* methodDefinition; + }; + union + { + const void* genericMethod; + const void* genericContainer; + }; + uint32_t token; + uint16_t flags; + uint16_t iflags; + uint16_t slot; + uint8_t parameters_count; + uint8_t bitflags; + }; + + + """; + + public const string HeaderV240 = + """ + struct Il2CppClass_1 + { + void* image; + void* gc_desc; + const char* name; + const char* namespaze; + Il2CppType* byval_arg; + Il2CppType* this_arg; + Il2CppClass* element_class; + Il2CppClass* castClass; + Il2CppClass* declaringType; + Il2CppClass* parent; + void *generic_class; + void* typeDefinition; + void* interopData; + void* fields; + void* events; + void* properties; + void* methods; + Il2CppClass** nestedTypes; + Il2CppClass** implementedInterfaces; + Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; + }; + + struct Il2CppClass_2 + { + Il2CppClass** typeHierarchy; + uint32_t cctor_started; + uint32_t cctor_finished; + uint64_t cctor_thread; + int32_t genericContainerIndex; + int32_t customAttributeIndex; + uint32_t instance_size; + uint32_t actualSize; + uint32_t element_size; + int32_t native_size; + uint32_t static_fields_size; + uint32_t thread_static_fields_size; + int32_t thread_static_fields_offset; + uint32_t flags; + uint32_t token; + uint16_t method_count; + uint16_t property_count; + uint16_t field_count; + uint16_t event_count; + uint16_t nested_type_count; + uint16_t vtable_count; + uint16_t interfaces_count; + uint16_t interface_offsets_count; + uint8_t typeHierarchyDepth; + uint8_t genericRecursionDepth; + uint8_t rank; + uint8_t minimumAlignment; + uint8_t packingSize; + uint8_t bitflags1; + uint8_t bitflags2; + }; + + struct Il2CppClass + { + Il2CppClass_1 _1; + void* static_fields; + Il2CppRGCTXData* rgctx_data; + Il2CppClass_2 _2; + VirtualInvokeData vtable[255]; + }; + + typedef int32_t il2cpp_array_size_t; + typedef int32_t il2cpp_array_lower_bound_t; + struct Il2CppArrayBounds + { + il2cpp_array_size_t length; + il2cpp_array_lower_bound_t lower_bound; + }; + + struct MethodInfo + { + Il2CppMethodPointer methodPointer; + void* invoker_method; + const char* name; + Il2CppClass *declaring_type; + const Il2CppType *return_type; + const void* parameters; + union + { + const Il2CppRGCTXData* rgctx_data; + const void* methodDefinition; + }; + union + { + const void* genericMethod; + const void* genericContainer; + }; + int32_t customAttributeIndex; + uint32_t token; + uint16_t flags; + uint16_t iflags; + uint16_t slot; + uint8_t parameters_count; + uint8_t bitflags; + }; + + + """; + + public const string HeaderV22 = + """ + struct Il2CppClass_1 + { + void* image; + void* gc_desc; + const char* name; + const char* namespaze; + Il2CppType* byval_arg; + Il2CppType* this_arg; + Il2CppClass* element_class; + Il2CppClass* castClass; + Il2CppClass* declaringType; + Il2CppClass* parent; + void *generic_class; + void* typeDefinition; + void* fields; + void* events; + void* properties; + void* methods; + Il2CppClass** nestedTypes; + Il2CppClass** implementedInterfaces; + Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; + }; + + struct Il2CppClass_2 + { + Il2CppClass** typeHierarchy; + uint32_t cctor_started; + uint32_t cctor_finished; + uint64_t cctor_thread; + int32_t genericContainerIndex; + int32_t customAttributeIndex; + uint32_t instance_size; + uint32_t actualSize; + uint32_t element_size; + int32_t native_size; + uint32_t static_fields_size; + uint32_t thread_static_fields_size; + int32_t thread_static_fields_offset; + uint32_t flags; + uint32_t token; + uint16_t method_count; + uint16_t property_count; + uint16_t field_count; + uint16_t event_count; + uint16_t nested_type_count; + uint16_t vtable_count; + uint16_t interfaces_count; + uint16_t interface_offsets_count; + uint8_t typeHierarchyDepth; + uint8_t genericRecursionDepth; + uint8_t rank; + uint8_t minimumAlignment; + uint8_t packingSize; + uint8_t bitflags1; + uint8_t bitflags2; + }; + + struct Il2CppClass + { + Il2CppClass_1 _1; + void* static_fields; + Il2CppRGCTXData* rgctx_data; + Il2CppClass_2 _2; + VirtualInvokeData vtable[255]; + }; + + typedef int32_t il2cpp_array_size_t; + typedef int32_t il2cpp_array_lower_bound_t; + struct Il2CppArrayBounds + { + il2cpp_array_size_t length; + il2cpp_array_lower_bound_t lower_bound; + }; + + struct MethodInfo + { + Il2CppMethodPointer methodPointer; + void* invoker_method; + const char* name; + Il2CppClass *declaring_type; + const Il2CppType *return_type; + const void* parameters; + union + { + const Il2CppRGCTXData* rgctx_data; + const void* methodDefinition; + }; + union + { + const void* genericMethod; + const void* genericContainer; + }; + int32_t customAttributeIndex; + uint32_t token; + uint16_t flags; + uint16_t iflags; + uint16_t slot; + uint8_t parameters_count; + uint8_t bitflags; + }; + + + """; +} diff --git a/Cpp2IL.Core/OutputFormats/Disassembly/Il2CppConstants.cs b/Cpp2IL.Core/OutputFormats/Disassembly/Il2CppConstants.cs new file mode 100644 index 00000000..f8bb86ac --- /dev/null +++ b/Cpp2IL.Core/OutputFormats/Disassembly/Il2CppConstants.cs @@ -0,0 +1,73 @@ +namespace Il2CppDumper +{ + class Il2CppConstants + { + /* + * Field Attributes (21.1.5). + */ + public const int FIELD_ATTRIBUTE_FIELD_ACCESS_MASK = 0x0007; + public const int FIELD_ATTRIBUTE_COMPILER_CONTROLLED = 0x0000; + public const int FIELD_ATTRIBUTE_PRIVATE = 0x0001; + public const int FIELD_ATTRIBUTE_FAM_AND_ASSEM = 0x0002; + public const int FIELD_ATTRIBUTE_ASSEMBLY = 0x0003; + public const int FIELD_ATTRIBUTE_FAMILY = 0x0004; + public const int FIELD_ATTRIBUTE_FAM_OR_ASSEM = 0x0005; + public const int FIELD_ATTRIBUTE_PUBLIC = 0x0006; + + public const int FIELD_ATTRIBUTE_STATIC = 0x0010; + public const int FIELD_ATTRIBUTE_INIT_ONLY = 0x0020; + public const int FIELD_ATTRIBUTE_LITERAL = 0x0040; + + /* + * Method Attributes (22.1.9) + */ + public const int METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK = 0x0007; + public const int METHOD_ATTRIBUTE_COMPILER_CONTROLLED = 0x0000; + public const int METHOD_ATTRIBUTE_PRIVATE = 0x0001; + public const int METHOD_ATTRIBUTE_FAM_AND_ASSEM = 0x0002; + public const int METHOD_ATTRIBUTE_ASSEM = 0x0003; + public const int METHOD_ATTRIBUTE_FAMILY = 0x0004; + public const int METHOD_ATTRIBUTE_FAM_OR_ASSEM = 0x0005; + public const int METHOD_ATTRIBUTE_PUBLIC = 0x0006; + + public const int METHOD_ATTRIBUTE_STATIC = 0x0010; + public const int METHOD_ATTRIBUTE_FINAL = 0x0020; + public const int METHOD_ATTRIBUTE_VIRTUAL = 0x0040; + + public const int METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK = 0x0100; + public const int METHOD_ATTRIBUTE_REUSE_SLOT = 0x0000; + public const int METHOD_ATTRIBUTE_NEW_SLOT = 0x0100; + + public const int METHOD_ATTRIBUTE_ABSTRACT = 0x0400; + + public const int METHOD_ATTRIBUTE_PINVOKE_IMPL = 0x2000; + + /* + * Type Attributes (21.1.13). + */ + public const int TYPE_ATTRIBUTE_VISIBILITY_MASK = 0x00000007; + public const int TYPE_ATTRIBUTE_NOT_PUBLIC = 0x00000000; + public const int TYPE_ATTRIBUTE_PUBLIC = 0x00000001; + public const int TYPE_ATTRIBUTE_NESTED_PUBLIC = 0x00000002; + public const int TYPE_ATTRIBUTE_NESTED_PRIVATE = 0x00000003; + public const int TYPE_ATTRIBUTE_NESTED_FAMILY = 0x00000004; + public const int TYPE_ATTRIBUTE_NESTED_ASSEMBLY = 0x00000005; + public const int TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM = 0x00000006; + public const int TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM = 0x00000007; + + + public const int TYPE_ATTRIBUTE_INTERFACE = 0x00000020; + + public const int TYPE_ATTRIBUTE_ABSTRACT = 0x00000080; + public const int TYPE_ATTRIBUTE_SEALED = 0x00000100; + + public const int TYPE_ATTRIBUTE_SERIALIZABLE = 0x00002000; + + /* + * Flags for Params (22.1.12) + */ + public const int PARAM_ATTRIBUTE_IN = 0x0001; + public const int PARAM_ATTRIBUTE_OUT = 0x0002; + public const int PARAM_ATTRIBUTE_OPTIONAL = 0x0010; + } +} diff --git a/Cpp2IL.Core/OutputFormats/Disassembly/ScriptJson.cs b/Cpp2IL.Core/OutputFormats/Disassembly/ScriptJson.cs new file mode 100644 index 00000000..e91cd6b3 --- /dev/null +++ b/Cpp2IL.Core/OutputFormats/Disassembly/ScriptJson.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; + +namespace Il2CppDumper +{ + public class ScriptJson + { + public List ScriptMethod = new(); + public List ScriptString = new(); + public List ScriptMetadata = new(); + public List ScriptMetadataMethod = new(); + public ulong[] Addresses; + } + + public class ScriptMethod + { + public ulong Address; + public string Name; + public string Signature; + public string TypeSignature; + } + + public class ScriptString + { + public ulong Address; + public string Value; + } + + public class ScriptMetadata + { + public ulong Address; + public string Name; + public string Signature; + } + + public class ScriptMetadataMethod + { + public ulong Address; + public string Name; + public ulong MethodAddress; + } +} diff --git a/Cpp2IL.Core/OutputFormats/Disassembly/StructGenerator.cs b/Cpp2IL.Core/OutputFormats/Disassembly/StructGenerator.cs new file mode 100644 index 00000000..67725a6d --- /dev/null +++ b/Cpp2IL.Core/OutputFormats/Disassembly/StructGenerator.cs @@ -0,0 +1,1604 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; +using Cpp2IL.Core.Utils; +using LibCpp2IL; +using LibCpp2IL.BinaryStructures; +using LibCpp2IL.Metadata; +using LibCpp2IL.PE; +using static Il2CppDumper.Il2CppConstants; + +namespace Il2CppDumper +{ + public class StructGenerator + { + private Il2CppMetadata metadata => LibCpp2IlMain.TheMetadata; + private Il2CppBinary il2Cpp => LibCpp2IlMain.Binary; + private readonly Dictionary typeDefImageNames = new(); + private readonly HashSet structNameHashSet = new(StringComparer.Ordinal); + private readonly List structInfoList = new(); + private readonly Dictionary structInfoWithStructName = new(); + private readonly HashSet structCache = new(); + private readonly Dictionary structNameDic = new(); + private readonly Dictionary genericClassStructNameDic = new(); + private readonly Dictionary nameGenericClassDic = new(); + private readonly List genericClassList = new(); + private readonly StringBuilder arrayClassHeader = new(); + private readonly StringBuilder methodInfoHeader = new(); + private static readonly HashSet methodInfoCache = new(); + private static readonly HashSet keyword = new(StringComparer.Ordinal) + { "klass", "monitor", "register", "_cs", "auto", "friend", "template", "flat", "default", "_ds", "interrupt", + "unsigned", "signed", "asm", "if", "case", "break", "continue", "do", "new", "_", "short", "union", "class", "namespace"}; + private static readonly HashSet specialKeywords = new(StringComparer.Ordinal) + { "inline", "near", "far" }; + + public void WriteScript(string outputDir) + { + var json = new ScriptJson(); + // Generate a unique name + for (var imageIndex = 0; imageIndex < metadata.imageDefinitions.Length; imageIndex++) + { + var imageDef = metadata.imageDefinitions[imageIndex]; + var imageName = metadata.GetStringFromIndex(imageDef.nameIndex); + var typeEnd = imageDef.firstTypeIndex + imageDef.typeCount; + for (int typeIndex = imageDef.firstTypeIndex; typeIndex < typeEnd; typeIndex++) + { + var typeDef = metadata.typeDefs[typeIndex]; + typeDefImageNames.Add(typeDef, imageName); + CreateStructNameDic(typeDef); + } + } + // Generate a dictionary that will be used later to process the generalized instances. + foreach (var il2CppType in il2Cpp.AllTypes.Where(x => x.Type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST)) + { + var genericClass = il2Cpp.ReadReadableAtVirtualAddress(il2CppType.Data.GenericClass); + var typeDef = genericClass.TypeDefinition; + if (typeDef == null) + { + continue; + } + var typeBaseName = structNameDic[typeDef]; + var typeToReplaceName = FixName(GetTypeDefName(typeDef, true, true)); + var typeReplaceName = FixName(GetTypeName(il2CppType, true, false)); + var typeStructName = typeBaseName.Replace(typeToReplaceName, typeReplaceName); + nameGenericClassDic[typeStructName] = il2CppType; + genericClassStructNameDic[il2CppType.Data.GenericClass] = typeStructName; + } + // handler + foreach (var imageDef in metadata.imageDefinitions) + { + var imageName = metadata.GetStringFromIndex(imageDef.nameIndex); + var typeEnd = imageDef.firstTypeIndex + imageDef.typeCount; + for (int typeIndex = imageDef.firstTypeIndex; typeIndex < typeEnd; typeIndex++) + { + var typeDef = metadata.typeDefs[typeIndex]; + AddStruct(typeDef); + var typeName = GetTypeDefName(typeDef, true, true); + var methodEnd = typeDef.FirstMethodIdx + typeDef.MethodCount; + for (var i = typeDef.FirstMethodIdx; i < methodEnd; ++i) + { + var methodDef = metadata.methodDefs[i]; + var methodName = metadata.GetStringFromIndex(methodDef.nameIndex); + var methodPointer = methodDef.MethodPointer; + if (methodPointer > 0) + { + var methodTypeSignature = new List(); + var scriptMethod = new ScriptMethod(); + json.ScriptMethod.Add(scriptMethod); + scriptMethod.Address = il2Cpp.GetRva(methodPointer); + var methodFullName = typeName + "$$" + methodName; + scriptMethod.Name = methodFullName; + + var methodReturnType = methodDef.RawReturnType; + var returnType = ParseType(methodReturnType); + if (methodReturnType.Byref == 1) + { + returnType += "*"; + } + methodTypeSignature.Add(methodReturnType.Byref == 1 ? Il2CppTypeEnum.IL2CPP_TYPE_PTR : methodReturnType.Type); + var signature = $"{returnType} {FixName(methodFullName)} ("; + var parameterStrs = new List(); + if ((methodDef.flags & METHOD_ATTRIBUTE_STATIC) == 0) + { + var thisType = ParseType(il2Cpp.AllTypes[typeDef.ByvalTypeIndex]); + methodTypeSignature.Add(il2Cpp.AllTypes[typeDef.ByvalTypeIndex].Type); + parameterStrs.Add($"{thisType} __this"); + } + else if (LibCpp2IlMain.MetadataVersion <= 24) + { + methodTypeSignature.Add(Il2CppTypeEnum.IL2CPP_TYPE_PTR); + parameterStrs.Add($"Il2CppObject* __this"); + } + for (var j = 0; j < methodDef.parameterCount; j++) + { + var parameterDef = metadata.parameterDefs[methodDef.parameterStart + j]; + var parameterName = metadata.GetStringFromIndex(parameterDef.nameIndex); + var parameterType = il2Cpp.AllTypes[parameterDef.typeIndex]; + var parameterCType = ParseType(parameterType); + if (parameterType.Byref == 1) + { + parameterCType += "*"; + } + methodTypeSignature.Add(parameterType.Byref == 1 ? Il2CppTypeEnum.IL2CPP_TYPE_PTR : parameterType.Type); + parameterStrs.Add($"{parameterCType} {FixName(parameterName)}"); + } + methodTypeSignature.Add(Il2CppTypeEnum.IL2CPP_TYPE_PTR); + parameterStrs.Add("const MethodInfo* method"); + signature += string.Join(", ", parameterStrs); + signature += ");"; + scriptMethod.Signature = signature; + scriptMethod.TypeSignature = GetMethodTypeSignature(methodTypeSignature); + } + //Generic Instance Functions + if (il2Cpp.ConcreteGenericMethods.TryGetValue(methodDef, out var methodSpecs)) + { + foreach (var methodSpec in methodSpecs) + { + var genericMethodPointer = il2Cpp.methodSpecGenericMethodPointers[methodSpec]; + if (genericMethodPointer > 0) + { + var methodTypeSignature = new List(); + var scriptMethod = new ScriptMethod(); + json.ScriptMethod.Add(scriptMethod); + scriptMethod.Address = il2Cpp.GetRva(genericMethodPointer); + var methodInfoName = $"MethodInfo_{scriptMethod.Address:X}"; + var structTypeName = structNameDic[typeDef]; + var rgctxs = GenerateRGCTX(imageName, methodDef); + if (methodInfoCache.Add(genericMethodPointer)) + { + GenerateMethodInfo(methodInfoName, structTypeName, rgctxs); + } + (var methodSpecTypeName, var methodSpecMethodName) = GetMethodSpecName(methodSpec, true); + var methodFullName = methodSpecTypeName + "$$" + methodSpecMethodName; + scriptMethod.Name = methodFullName; + + var genericContext = GetMethodSpecGenericContext(methodSpec); + var methodReturnType = methodDef.RawReturnType; + var returnType = ParseType(methodReturnType, genericContext); + if (methodReturnType.Byref == 1) + { + returnType += "*"; + } + methodTypeSignature.Add(methodReturnType.Byref == 1 ? Il2CppTypeEnum.IL2CPP_TYPE_PTR : methodReturnType.Type); + var signature = $"{returnType} {FixName(methodFullName)} ("; + var parameterStrs = new List(); + if ((methodDef.flags & METHOD_ATTRIBUTE_STATIC) == 0) + { + string thisType; + if (methodSpec.classIndexIndex != -1) + { + var typeBaseName = structNameDic[typeDef]; + var typeToReplaceName = FixName(typeName); + var typeReplaceName = FixName(methodSpecTypeName); + var typeStructName = typeBaseName.Replace(typeToReplaceName, typeReplaceName); + if (nameGenericClassDic.TryGetValue(typeStructName, out var il2CppType)) + { + thisType = ParseType(il2CppType); + methodTypeSignature.Add(il2CppType.Type); + } + else + { + //There is no separate generic instance class + thisType = ParseType(il2Cpp.AllTypes[typeDef.ByvalTypeIndex]); + methodTypeSignature.Add(il2Cpp.AllTypes[typeDef.ByvalTypeIndex].Type); + } + } + else + { + thisType = ParseType(il2Cpp.AllTypes[typeDef.ByvalTypeIndex]); + methodTypeSignature.Add(il2Cpp.AllTypes[typeDef.ByvalTypeIndex].Type); + } + parameterStrs.Add($"{thisType} __this"); + } + else if (LibCpp2IlMain.MetadataVersion <= 24) + { + methodTypeSignature.Add(Il2CppTypeEnum.IL2CPP_TYPE_PTR); + parameterStrs.Add($"Il2CppObject* __this"); + } + for (var j = 0; j < methodDef.parameterCount; j++) + { + var parameterDef = metadata.parameterDefs[methodDef.parameterStart + j]; + var parameterName = metadata.GetStringFromIndex(parameterDef.nameIndex); + var parameterType = il2Cpp.AllTypes[parameterDef.typeIndex]; + var parameterCType = ParseType(parameterType, genericContext); + if (parameterType.Byref == 1) + { + parameterCType += "*"; + } + methodTypeSignature.Add(parameterType.Byref == 1 ? Il2CppTypeEnum.IL2CPP_TYPE_PTR : parameterType.Type); + parameterStrs.Add($"{parameterCType} {FixName(parameterName)}"); + } + methodTypeSignature.Add(Il2CppTypeEnum.IL2CPP_TYPE_PTR); + parameterStrs.Add($"const {methodInfoName}* method"); + signature += string.Join(", ", parameterStrs); + signature += ");"; + scriptMethod.Signature = signature; + scriptMethod.TypeSignature = GetMethodTypeSignature(methodTypeSignature); + } + } + } + } + } + } + //Handling function range + List orderedPointers; + if (LibCpp2IlMain.MetadataVersion >= 24.2) + { + orderedPointers = new List(); + foreach (var pair in il2Cpp.codeGenModuleMethodPointers) + { + orderedPointers.AddRange(pair.Value); + } + } + else + { + orderedPointers = il2Cpp.methodPointers.ToList(); + } + orderedPointers.AddRange(il2Cpp.genericMethodPointers); + orderedPointers.AddRange(il2Cpp.invokerPointers); + if (LibCpp2IlMain.MetadataVersion < 29) + { + orderedPointers.AddRange(il2Cpp.AllCustomAttributeGenerators); + } + if (LibCpp2IlMain.MetadataVersion >= 22) + { + if (il2Cpp.reversePInvokeWrappers != null) + orderedPointers.AddRange(il2Cpp.reversePInvokeWrappers); + if (il2Cpp.unresolvedVirtualCallPointers != null) + orderedPointers.AddRange(il2Cpp.unresolvedVirtualCallPointers); + } + //TODO interopData also contains the function + orderedPointers = orderedPointers.Distinct().OrderBy(x => x).ToList(); + orderedPointers.Remove(0); + json.Addresses = new ulong[orderedPointers.Count]; + for (int i = 0; i < orderedPointers.Count; i++) + { + json.Addresses[i] = il2Cpp.GetRva(orderedPointers[i]); + } + // Processing MetadataUsage + if (LibCpp2IlMain.MetadataVersion >= 27) + { + var sectionHelper = GetSectionHelper(); + foreach (var sec in sectionHelper.Data) + { + il2Cpp.Position = sec.offset; + var end = Math.Min(sec.offsetEnd, il2Cpp.Length) - il2Cpp.PointerSize; + while (il2Cpp.Position < end) + { + var addr = il2Cpp.Position; + var metadataValue = il2Cpp.ReadNUint(); + var position = il2Cpp.Position; + if (metadataValue < uint.MaxValue) + { + var encodedToken = (uint)metadataValue; + var usage = metadata.GetEncodedIndexType(encodedToken); + if (usage > 0 && usage <= 6) + { + var decodedIndex = metadata.GetDecodedMethodIndex(encodedToken); + if (metadataValue == ((usage << 29) | (decodedIndex << 1)) + 1) + { + var va = il2Cpp.MapRTVA(addr); + if (va > 0) + { + switch ((MetadataUsageType)usage) + { + case 0: + break; + case MetadataUsageType.TypeInfo: + if (decodedIndex < il2Cpp.AllTypes.Length) + { + AddMetadataUsageTypeInfo(json, decodedIndex, va); + } + break; + case MetadataUsageType.Type: + if (decodedIndex < il2Cpp.AllTypes.Length) + { + AddMetadataUsageIl2CppType(json, decodedIndex, va); + } + break; + case MetadataUsageType.MethodDef: + if (decodedIndex < metadata.methodDefs.Length) + { + AddMetadataUsageMethodDef(json, decodedIndex, va); + } + break; + case MetadataUsageType.FieldInfo: + if (decodedIndex < metadata.fieldRefs.Length) + { + AddMetadataUsageFieldInfo(json, decodedIndex, va); + } + break; + case MetadataUsageType.StringLiteral: + //if (decodedIndex < metadata.stringLiterals.Length) + { + AddMetadataUsageStringLiteral(json, decodedIndex, va); + } + break; + case MetadataUsageType.MethodRef: + if (decodedIndex < il2Cpp.AllGenericMethodSpecs.Length) + { + AddMetadataUsageMethodRef(json, decodedIndex, va); + } + break; + } + if (il2Cpp.Position != position) + { + il2Cpp.Position = position; + } + } + } + } + } + } + } + } + else if (LibCpp2IlMain.MetadataVersion > 16 && LibCpp2IlMain.MetadataVersion < 27) + { + foreach (var i in metadata.metadataUsageDic[(uint)MetadataUsageType.TypeInfo]) + { + AddMetadataUsageTypeInfo(json, i.Value, il2Cpp.metadataUsages[i.Key]); + } + foreach (var i in metadata.metadataUsageDic[(uint)MetadataUsageType.Type]) + { + AddMetadataUsageIl2CppType(json, i.Value, il2Cpp.metadataUsages[i.Key]); + } + foreach (var i in metadata.metadataUsageDic[(uint)MetadataUsageType.MethodDef]) + { + AddMetadataUsageMethodDef(json, i.Value, il2Cpp.metadataUsages[i.Key]); + } + foreach (var i in metadata.metadataUsageDic[(uint)MetadataUsageType.FieldInfo]) + { + AddMetadataUsageFieldInfo(json, i.Value, il2Cpp.metadataUsages[i.Key]); + } + foreach (var i in metadata.metadataUsageDic[(uint)MetadataUsageType.StringLiteral]) + { + AddMetadataUsageStringLiteral(json, i.Value, il2Cpp.metadataUsages[i.Key]); + } + foreach (var i in metadata.metadataUsageDic[(uint)MetadataUsageType.MethodRef]) + { + AddMetadataUsageMethodRef(json, i.Value, il2Cpp.metadataUsages[i.Key]); + } + } + //Outputs a separate StringLiteral + var stringLiterals = json.ScriptString.Select(x => new + { + value = x.Value, + address = $"0x{x.Address:X}" + }).ToArray(); + var jsonOptions = new JsonSerializerOptions() { WriteIndented = true, IncludeFields = true }; + File.WriteAllText(outputDir + "stringliteral.json", JsonSerializer.Serialize(stringLiterals, jsonOptions), new UTF8Encoding(false)); + //write to a file + File.WriteAllText(outputDir + "script.json", JsonSerializer.Serialize(json, jsonOptions)); + //il2cpp.h + for (int i = 0; i < genericClassList.Count; i++) + { + var pointer = genericClassList[i]; + AddGenericClassStruct(pointer); + } + var headerStruct = new StringBuilder(); + foreach (var info in structInfoList) + { + structInfoWithStructName.Add(info.TypeName + "_o", info); + } + foreach (var info in structInfoList) + { + headerStruct.Append(RecursionStructInfo(info)); + } + var sb = new StringBuilder(); + sb.Append(HeaderConstants.GenericHeader); + switch (LibCpp2IlMain.MetadataVersion) + { + case 22f: + sb.Append(HeaderConstants.HeaderV22); + break; + case 23f: + case 24f: + sb.Append(HeaderConstants.HeaderV240); + break; + case 24.1f: + sb.Append(HeaderConstants.HeaderV241); + break; + case 24.2f: + case 24.3f: + case 24.4f: + case 24.5f: + sb.Append(HeaderConstants.HeaderV242); + break; + case 27f: + case 27.1f: + case 27.2f: + sb.Append(HeaderConstants.HeaderV27); + break; + case 29f: + case 29.1f: + case 31f: + sb.Append(HeaderConstants.HeaderV29); + break; + default: + Console.WriteLine($"WARNING: This il2cpp version [{LibCpp2IlMain.MetadataVersion}] does not support generating .h files"); + return; + } + sb.Append(headerStruct); + sb.Append(arrayClassHeader); + sb.Append(methodInfoHeader); + File.WriteAllText(outputDir + "il2cpp.h", sb.ToString()); + } + + private void AddMetadataUsageTypeInfo(ScriptJson json, uint index, ulong address) + { + var type = il2Cpp.AllTypes[index]; + var typeName = GetTypeName(type, true, false); + var scriptMetadata = new ScriptMetadata(); + json.ScriptMetadata.Add(scriptMetadata); + scriptMetadata.Address = il2Cpp.GetRva(address); + scriptMetadata.Name = typeName + "_TypeInfo"; + var signature = GetIl2CppStructName(type); + if (signature.EndsWith("_array")) + { + scriptMetadata.Signature = "Il2CppClass*"; + } + else + { + scriptMetadata.Signature = FixName(signature) + "_c*"; + } + } + + private void AddMetadataUsageIl2CppType(ScriptJson json, uint index, ulong address) + { + var type = il2Cpp.AllTypes[index]; + var typeName = GetTypeName(type, true, false); + var scriptMetadata = new ScriptMetadata(); + json.ScriptMetadata.Add(scriptMetadata); + scriptMetadata.Address = il2Cpp.GetRva(address); + scriptMetadata.Name = typeName + "_var"; + scriptMetadata.Signature = "Il2CppType*"; + } + + private void AddMetadataUsageMethodDef(ScriptJson json, uint index, ulong address) + { + var methodDef = metadata.methodDefs[index]; + var typeDef = methodDef.DeclaringType; + var typeName = GetTypeDefName(typeDef, true, true); + var methodName = typeName + "." + metadata.GetStringFromIndex(methodDef.nameIndex) + "()"; + var scriptMetadataMethod = new ScriptMetadataMethod(); + json.ScriptMetadataMethod.Add(scriptMetadataMethod); + scriptMetadataMethod.Address = il2Cpp.GetRva(address); + scriptMetadataMethod.Name = "Method$" + methodName; + var imageName = typeDefImageNames[typeDef]; + var methodPointer = methodDef.MethodPointer; + if (methodPointer > 0) + { + scriptMetadataMethod.MethodAddress = il2Cpp.GetRva(methodPointer); + } + } + + private void AddMetadataUsageFieldInfo(ScriptJson json, uint index, ulong address) + { + var fieldRef = metadata.fieldRefs[index]; + var type = il2Cpp.AllTypes[fieldRef.typeIndex]; + var typeDef = GetTypeDefinition(type); + var fieldDef = metadata.fieldDefs[typeDef.FirstFieldIdx + fieldRef.fieldIndex]; + var fieldName = GetTypeName(type, true, false) + "." + metadata.GetStringFromIndex(fieldDef.nameIndex); + var scriptMetadata = new ScriptMetadata(); + json.ScriptMetadata.Add(scriptMetadata); + scriptMetadata.Address = il2Cpp.GetRva(address); + scriptMetadata.Name = "Field$" + fieldName; + } + + private void AddMetadataUsageStringLiteral(ScriptJson json, uint index, ulong address) + { + var scriptString = new ScriptString(); + json.ScriptString.Add(scriptString); + scriptString.Address = il2Cpp.GetRva(address); + scriptString.Value = metadata.GetStringLiteralFromIndex(index); + } + + private void AddMetadataUsageMethodRef(ScriptJson json, uint index, ulong address) + { + var methodSpec = il2Cpp.AllGenericMethodSpecs[index]; + var scriptMetadataMethod = new ScriptMetadataMethod(); + json.ScriptMetadataMethod.Add(scriptMetadataMethod); + scriptMetadataMethod.Address = il2Cpp.GetRva(address); + (var methodSpecTypeName, var methodSpecMethodName) = GetMethodSpecName(methodSpec, true); + scriptMetadataMethod.Name = "Method$" + methodSpecTypeName + "." + methodSpecMethodName + "()"; + if (il2Cpp.methodSpecGenericMethodPointers.ContainsKey(methodSpec)) + { + var genericMethodPointer = il2Cpp.methodSpecGenericMethodPointers[methodSpec]; + if (genericMethodPointer > 0) + { + scriptMetadataMethod.MethodAddress = il2Cpp.GetRva(genericMethodPointer); + } + } + } + + private static string FixName(string str) + { + if (keyword.Contains(str)) + { + str = "_" + str; + } + else if (specialKeywords.Contains(str)) + { + str = "_" + str + "_"; + } + + if (Regex.IsMatch(str, "^[0-9]")) + { + return "_" + str; + } + else + { + return Regex.Replace(str, "[^a-zA-Z0-9_]", "_"); + } + } + + private string ParseType(Il2CppType il2CppType, Il2CppGenericContext? context = null) + { + switch (il2CppType.Type) + { + case Il2CppTypeEnum.IL2CPP_TYPE_VOID: + return "void"; + case Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN: + return "bool"; + case Il2CppTypeEnum.IL2CPP_TYPE_CHAR: + return "uint16_t"; //Il2CppChar + case Il2CppTypeEnum.IL2CPP_TYPE_I1: + return "int8_t"; + case Il2CppTypeEnum.IL2CPP_TYPE_U1: + return "uint8_t"; + case Il2CppTypeEnum.IL2CPP_TYPE_I2: + return "int16_t"; + case Il2CppTypeEnum.IL2CPP_TYPE_U2: + return "uint16_t"; + case Il2CppTypeEnum.IL2CPP_TYPE_I4: + return "int32_t"; + case Il2CppTypeEnum.IL2CPP_TYPE_U4: + return "uint32_t"; + case Il2CppTypeEnum.IL2CPP_TYPE_I8: + return "int64_t"; + case Il2CppTypeEnum.IL2CPP_TYPE_U8: + return "uint64_t"; + case Il2CppTypeEnum.IL2CPP_TYPE_R4: + return "float"; + case Il2CppTypeEnum.IL2CPP_TYPE_R8: + return "double"; + case Il2CppTypeEnum.IL2CPP_TYPE_STRING: + return "System_String_o*"; + case Il2CppTypeEnum.IL2CPP_TYPE_PTR: + { + var oriType = il2Cpp.GetIl2CppTypeFromPointer(il2CppType.Data.Type); + return ParseType(oriType) + "*"; + } + case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE: + { + var typeDef = il2CppType.CoerceToUnderlyingTypeDefinition(); + if (typeDef.IsEnumType) + { + return ParseType(typeDef.EnumUnderlyingType); + } + return structNameDic[typeDef] + "_o"; + } + case Il2CppTypeEnum.IL2CPP_TYPE_CLASS: + { + var typeDef = il2CppType.CoerceToUnderlyingTypeDefinition(); + return structNameDic[typeDef] + "_o*"; + } + case Il2CppTypeEnum.IL2CPP_TYPE_VAR: + { + if (context != null) + { + var genericParameter = il2CppType.GetGenericParameterDef(); + var genericInst = context.ClassInst; + var pointers = genericInst.Pointers; + var pointer = pointers[genericParameter.Index]; + var type = il2Cpp.GetIl2CppTypeFromPointer(pointer); + return ParseType(type); + } + return "Il2CppObject*"; + } + case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: + { + var arrayType = il2CppType.GetArrayType(); + var elementType = il2Cpp.GetIl2CppTypeFromPointer(arrayType.etype); + var elementStructName = GetIl2CppStructName(elementType, context); + var typeStructName = elementStructName + "_array"; + if (structNameHashSet.Add(typeStructName)) + { + ParseArrayClassStruct(elementType, context); + } + return typeStructName + "*"; + } + case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: + { + var genericClass = il2CppType.GetGenericClass(); + var typeDef = genericClass.TypeDefinition;//genericClass.TypeDefinition; + var typeStructName = genericClassStructNameDic[il2CppType.Data.GenericClass]; + if (structNameHashSet.Add(typeStructName)) + { + genericClassList.Add(il2CppType.Data.GenericClass); + } + if (typeDef.IsValueType) + { + if (typeDef.IsEnumType) + { + return ParseType(typeDef.EnumUnderlyingType); + } + return typeStructName + "_o"; + } + return typeStructName + "_o*"; + } + case Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF: + return "Il2CppObject*"; + case Il2CppTypeEnum.IL2CPP_TYPE_I: + return "intptr_t"; + case Il2CppTypeEnum.IL2CPP_TYPE_U: + return "uintptr_t"; + case Il2CppTypeEnum.IL2CPP_TYPE_OBJECT: + return "Il2CppObject*"; + case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY: + { + var elementType = il2Cpp.GetIl2CppTypeFromPointer(il2CppType.Data.Type); + var elementStructName = GetIl2CppStructName(elementType, context); + var typeStructName = elementStructName + "_array"; + if (structNameHashSet.Add(typeStructName)) + { + ParseArrayClassStruct(elementType, context); + } + return typeStructName + "*"; + } + case Il2CppTypeEnum.IL2CPP_TYPE_MVAR: + { + if (context != null) + { + var genericParameter = il2CppType.GetGenericParameterDef(); + //https://github.com/Perfare/Il2CppDumper/issues/687 + if (context.method_inst == 0 && context.class_inst != 0) + { + goto case Il2CppTypeEnum.IL2CPP_TYPE_VAR; + } + var genericInst = il2Cpp.ReadReadableAtVirtualAddress(context.method_inst); + var pointers = genericInst.Pointers; + var pointer = pointers[genericParameter.Index]; + var type = il2Cpp.GetIl2CppTypeFromPointer(pointer); + return ParseType(type); + } + return "Il2CppObject*"; + } + default: + throw new NotSupportedException(); + } + } + public static string GetMethodTypeSignature(List types) + { + string signature = string.Empty; + foreach (Il2CppTypeEnum type in types) + { + signature += type switch + { + Il2CppTypeEnum.IL2CPP_TYPE_VOID => "v", + Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN or Il2CppTypeEnum.IL2CPP_TYPE_CHAR or Il2CppTypeEnum.IL2CPP_TYPE_I1 or Il2CppTypeEnum.IL2CPP_TYPE_U1 or Il2CppTypeEnum.IL2CPP_TYPE_I2 or Il2CppTypeEnum.IL2CPP_TYPE_U2 or Il2CppTypeEnum.IL2CPP_TYPE_I4 or Il2CppTypeEnum.IL2CPP_TYPE_U4 => "i", + Il2CppTypeEnum.IL2CPP_TYPE_I8 or Il2CppTypeEnum.IL2CPP_TYPE_U8 => "j", + Il2CppTypeEnum.IL2CPP_TYPE_R4 => "f", + Il2CppTypeEnum.IL2CPP_TYPE_R8 => "d", + Il2CppTypeEnum.IL2CPP_TYPE_STRING or Il2CppTypeEnum.IL2CPP_TYPE_PTR or Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE or Il2CppTypeEnum.IL2CPP_TYPE_CLASS or Il2CppTypeEnum.IL2CPP_TYPE_VAR or Il2CppTypeEnum.IL2CPP_TYPE_ARRAY or Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST or Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF or Il2CppTypeEnum.IL2CPP_TYPE_I or Il2CppTypeEnum.IL2CPP_TYPE_U or Il2CppTypeEnum.IL2CPP_TYPE_OBJECT or Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY or Il2CppTypeEnum.IL2CPP_TYPE_MVAR => "i", + _ => throw new NotSupportedException(), + }; + } + return signature; + } + + private void AddStruct(Il2CppTypeDefinition typeDef) + { + var structInfo = new StructInfo(); + structInfoList.Add(structInfo); + structInfo.TypeName = structNameDic[typeDef]; + structInfo.IsValueType = typeDef.IsValueType; + AddParents(typeDef, structInfo); + AddFields(typeDef, structInfo, null); + AddVTableMethod(structInfo, typeDef); + AddRGCTX(structInfo, typeDef); + } + + private void AddGenericClassStruct(ulong pointer) + { + var genericClass = il2Cpp.ReadReadableAtVirtualAddress(pointer); + var typeDef = genericClass.TypeDefinition; + var structInfo = new StructInfo(); + structInfoList.Add(structInfo); + structInfo.TypeName = genericClassStructNameDic[pointer]; + structInfo.IsValueType = typeDef.IsValueType; + AddParents(typeDef, structInfo); + AddFields(typeDef, structInfo, genericClass.Context); + AddVTableMethod(structInfo, typeDef); + } + + private void AddParents(Il2CppTypeDefinition typeDef, StructInfo structInfo) + { + if (!typeDef.IsValueType && !typeDef.IsEnumType) + { + if (typeDef.ParentIndex >= 0) + { + var parent = il2Cpp.AllTypes[typeDef.ParentIndex]; + if (parent.Type != Il2CppTypeEnum.IL2CPP_TYPE_OBJECT) + { + structInfo.Parent = GetIl2CppStructName(parent); + } + } + } + } + + private void AddFields(Il2CppTypeDefinition typeDef, StructInfo structInfo, Il2CppGenericContext context) + { + if (typeDef.FieldCount > 0) + { + var fieldEnd = typeDef.FirstFieldIdx + typeDef.FieldCount; + var cache = new HashSet(StringComparer.Ordinal); + for (var i = typeDef.FirstFieldIdx; i < fieldEnd; ++i) + { + var fieldDef = metadata.fieldDefs[i]; + var fieldType = il2Cpp.AllTypes[fieldDef.typeIndex]; + if ((fieldType.Attrs & FIELD_ATTRIBUTE_LITERAL) != 0) + { + continue; + } + var structFieldInfo = new StructFieldInfo + { + FieldTypeName = ParseType(fieldType, context) + }; + var fieldName = FixName(metadata.GetStringFromIndex(fieldDef.nameIndex)); + if (!cache.Add(fieldName)) + { + fieldName = $"_{i - typeDef.FirstFieldIdx}_{fieldName}"; + } + structFieldInfo.FieldName = fieldName; + structFieldInfo.IsValueType = IsValueType(fieldType, context); + structFieldInfo.IsCustomType = IsCustomType(fieldType, context); + if ((fieldType.Attrs & FIELD_ATTRIBUTE_STATIC) != 0) + { + structInfo.StaticFields.Add(structFieldInfo); + } + else + { + structInfo.Fields.Add(structFieldInfo); + } + } + } + } + + private void AddVTableMethod(StructInfo structInfo, Il2CppTypeDefinition typeDef) + { + var dic = new SortedDictionary(); + for (int i = 0; i < typeDef.VtableCount; i++) + { + var vTableIndex = typeDef.VtableStart + i; + var encodedMethodIndex = metadata.VTableMethodIndices[vTableIndex]; + var usage = metadata.GetEncodedIndexType(encodedMethodIndex); + var index = metadata.GetDecodedMethodIndex(encodedMethodIndex); + Il2CppMethodDefinition methodDef; + if (usage == 6) //MethodRef + { + var methodSpec = il2Cpp.AllGenericMethodSpecs[index]; + methodDef = metadata.methodDefs[methodSpec.methodDefinitionIndex]; + } + else + { + methodDef = metadata.methodDefs[index]; + } + if (methodDef.slot != ushort.MaxValue) + { + dic[methodDef.slot] = methodDef; + } + } + if (dic.Count > 0) + { + structInfo.VTableMethod = new StructVTableMethodInfo[dic.Last().Key + 1]; + foreach (var i in dic) + { + var methodInfo = new StructVTableMethodInfo(); + structInfo.VTableMethod[i.Key] = methodInfo; + var methodDef = i.Value; + methodInfo.MethodName = $"{FixName(metadata.GetStringFromIndex(methodDef.nameIndex))}"; + } + } + } + + private void AddRGCTX(StructInfo structInfo, Il2CppTypeDefinition typeDef) + { + var imageName = typeDefImageNames[typeDef]; + var collection = typeDef.RgctXs; + if (collection != null) + { + foreach (var definitionData in collection) + { + var structRGCTXInfo = new StructRGCTXInfo(); + structInfo.RGCTXs.Add(structRGCTXInfo); + structRGCTXInfo.Type = definitionData.type; + Il2CppRGCTXDefinition rgctxDefData; + if (LibCpp2IlMain.MetadataVersion >= 27.2) + { + rgctxDefData = il2Cpp.ReadReadableAtVirtualAddress(definitionData._data); + } + else + { + rgctxDefData = definitionData.data; + } + switch (definitionData.type) + { + case Il2CppRGCTXDataType.IL2CPP_RGCTX_DATA_TYPE: + { + var il2CppType = il2Cpp.AllTypes[rgctxDefData.TypeIndex]; + structRGCTXInfo.TypeName = FixName(GetTypeName(il2CppType, true, false)); + break; + } + case Il2CppRGCTXDataType.IL2CPP_RGCTX_DATA_CLASS: + { + var il2CppType = il2Cpp.AllTypes[rgctxDefData.TypeIndex]; + structRGCTXInfo.ClassName = FixName(GetTypeName(il2CppType, true, false)); + break; + } + case Il2CppRGCTXDataType.IL2CPP_RGCTX_DATA_METHOD: + { + var methodSpec = il2Cpp.AllGenericMethodSpecs[rgctxDefData.MethodIndex]; + (var methodSpecTypeName, var methodSpecMethodName) = GetMethodSpecName(methodSpec, true); + structRGCTXInfo.MethodName = FixName(methodSpecTypeName + "." + methodSpecMethodName); + break; + } + } + } + } + } + + private List GenerateRGCTX(string imageName, Il2CppMethodDefinition methodDef) + { + var rgctxs = new List(); + var collection = GetRGCTXDefinition(imageName, methodDef); + if (collection != null) + { + foreach (var definitionData in collection) + { + var structRGCTXInfo = new StructRGCTXInfo(); + rgctxs.Add(structRGCTXInfo); + structRGCTXInfo.Type = definitionData.type; + Il2CppRGCTXDefinition rgctxDefData; + if (LibCpp2IlMain.MetadataVersion >= 27.2) + { + rgctxDefData = il2Cpp.ReadReadableAtVirtualAddress(definitionData._data); + } + else + { + rgctxDefData = definitionData.data; + } + switch (definitionData.type) + { + case Il2CppRGCTXDataType.IL2CPP_RGCTX_DATA_TYPE: + { + var il2CppType = il2Cpp.AllTypes[rgctxDefData.TypeIndex]; + structRGCTXInfo.TypeName = FixName(GetTypeName(il2CppType, true, false)); + break; + } + case Il2CppRGCTXDataType.IL2CPP_RGCTX_DATA_CLASS: + { + var il2CppType = il2Cpp.AllTypes[rgctxDefData.TypeIndex]; + structRGCTXInfo.ClassName = FixName(GetTypeName(il2CppType, true, false)); + break; + } + case Il2CppRGCTXDataType.IL2CPP_RGCTX_DATA_METHOD: + { + var methodSpec = il2Cpp.AllGenericMethodSpecs[rgctxDefData.MethodIndex]; + (var methodSpecTypeName, var methodSpecMethodName) = GetMethodSpecName(methodSpec, true); + structRGCTXInfo.MethodName = FixName(methodSpecTypeName + "." + methodSpecMethodName); + break; + } + } + } + } + return rgctxs; + } + + private void ParseArrayClassStruct(Il2CppType il2CppType, Il2CppGenericContext context) + { + var structName = GetIl2CppStructName(il2CppType, context); + arrayClassHeader.Append($"struct {structName}_array {{\n" + + $"\tIl2CppObject obj;\n" + + $"\tIl2CppArrayBounds *bounds;\n" + + $"\til2cpp_array_size_t max_length;\n" + + $"\t{ParseType(il2CppType, context)} m_Items[65535];\n" + + $"}};\n"); + } + + private Il2CppTypeDefinition GetTypeDefinition(Il2CppType il2CppType) + { + switch (il2CppType.Type) + { + case Il2CppTypeEnum.IL2CPP_TYPE_VOID: + case Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN: + case Il2CppTypeEnum.IL2CPP_TYPE_CHAR: + case Il2CppTypeEnum.IL2CPP_TYPE_I1: + case Il2CppTypeEnum.IL2CPP_TYPE_U1: + case Il2CppTypeEnum.IL2CPP_TYPE_I2: + case Il2CppTypeEnum.IL2CPP_TYPE_U2: + case Il2CppTypeEnum.IL2CPP_TYPE_I4: + case Il2CppTypeEnum.IL2CPP_TYPE_U4: + case Il2CppTypeEnum.IL2CPP_TYPE_I8: + case Il2CppTypeEnum.IL2CPP_TYPE_U8: + case Il2CppTypeEnum.IL2CPP_TYPE_R4: + case Il2CppTypeEnum.IL2CPP_TYPE_R8: + case Il2CppTypeEnum.IL2CPP_TYPE_STRING: + case Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF: + case Il2CppTypeEnum.IL2CPP_TYPE_I: + case Il2CppTypeEnum.IL2CPP_TYPE_U: + case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE: + case Il2CppTypeEnum.IL2CPP_TYPE_CLASS: + case Il2CppTypeEnum.IL2CPP_TYPE_OBJECT: + return il2CppType.CoerceToUnderlyingTypeDefinition(); + case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: + var genericClass = il2Cpp.ReadReadableAtVirtualAddress(il2CppType.Data.GenericClass); + return genericClass.TypeDefinition; + default: + throw new NotSupportedException(); + } + } + + private void CreateStructNameDic(Il2CppTypeDefinition typeDef) + { + var typeName = GetTypeDefName(typeDef, true, true); + var typeStructName = FixName(typeName); + var uniqueName = GetUniqueName(typeStructName); + structNameDic.Add(typeDef, uniqueName); + } + + private string GetUniqueName(string name) + { + var fixName = name; + int i = 1; + while (!structNameHashSet.Add(fixName)) + { + fixName = $"{name}_{i++}"; + } + return fixName; + } + + private string RecursionStructInfo(StructInfo info) + { + if (!structCache.Add(info)) + { + return string.Empty; + } + + var sb = new StringBuilder(); + var pre = new StringBuilder(); + + if (info.Parent != null) + { + var parentStructName = info.Parent + "_o"; + pre.Append(RecursionStructInfo(structInfoWithStructName[parentStructName])); + sb.Append($"struct {info.TypeName}_Fields : {info.Parent}_Fields {{\n"); + // C style + //sb.Append($"struct {info.TypeName}_Fields {{\n"); + //sb.Append($"\t{info.Parent}_Fields _;\n"); + } + else + { + if (LibCpp2IlMain.Binary is PE && !info.IsValueType) + { + if (LibCpp2IlMain.Binary.is32Bit) + { + sb.Append($"struct __declspec(align(4)) {info.TypeName}_Fields {{\n"); + } + else + { + sb.Append($"struct __declspec(align(8)) {info.TypeName}_Fields {{\n"); + } + } + else + { + sb.Append($"struct {info.TypeName}_Fields {{\n"); + } + } + foreach (var field in info.Fields) + { + if (field.IsValueType) + { + var fieldInfo = structInfoWithStructName[field.FieldTypeName]; + pre.Append(RecursionStructInfo(fieldInfo)); + } + if (field.IsCustomType) + { + sb.Append($"\tstruct {field.FieldTypeName} {field.FieldName};\n"); + } + else + { + sb.Append($"\t{field.FieldTypeName} {field.FieldName};\n"); + } + } + sb.Append("};\n"); + + if (info.RGCTXs.Count > 0) + { + sb.Append($"struct {info.TypeName}_RGCTXs {{\n"); + for (int i = 0; i < info.RGCTXs.Count; i++) + { + var rgctx = info.RGCTXs[i]; + switch (rgctx.Type) + { + case Il2CppRGCTXDataType.IL2CPP_RGCTX_DATA_TYPE: + sb.Append($"\tIl2CppType* _{i}_{rgctx.TypeName};\n"); + break; + case Il2CppRGCTXDataType.IL2CPP_RGCTX_DATA_CLASS: + sb.Append($"\tIl2CppClass* _{i}_{rgctx.ClassName};\n"); + break; + case Il2CppRGCTXDataType.IL2CPP_RGCTX_DATA_METHOD: + sb.Append($"\tMethodInfo* _{i}_{rgctx.MethodName};\n"); + break; + } + } + sb.Append("};\n"); + } + + if (info.VTableMethod.Length > 0) + { + sb.Append($"struct {info.TypeName}_VTable {{\n"); + for (int i = 0; i < info.VTableMethod.Length; i++) + { + sb.Append($"\tVirtualInvokeData _{i}_"); + var method = info.VTableMethod[i]; + if (method != null) + { + sb.Append(method.MethodName); + } + else + { + sb.Append("unknown"); + } + sb.Append(";\n"); + } + sb.Append("};\n"); + } + + sb.Append($"struct {info.TypeName}_c {{\n"); + sb.Append($"\tIl2CppClass_1 _1;\n"); + if (info.StaticFields.Count > 0) + { + sb.Append($"\tstruct {info.TypeName}_StaticFields* static_fields;\n"); + } + else + { + sb.Append("\tvoid* static_fields;\n"); + } + if (info.RGCTXs.Count > 0) + { + sb.Append($"\t{info.TypeName}_RGCTXs* rgctx_data;\n"); + } + else + { + sb.Append("\tIl2CppRGCTXData* rgctx_data;\n"); + } + sb.Append($"\tIl2CppClass_2 _2;\n"); + if (info.VTableMethod.Length > 0) + { + sb.Append($"\t{info.TypeName}_VTable vtable;\n"); + } + else + { + sb.Append("\tVirtualInvokeData vtable[32];\n"); + } + sb.Append($"}};\n"); + + sb.Append($"struct {info.TypeName}_o {{\n"); + if (!info.IsValueType) + { + sb.Append($"\t{info.TypeName}_c *klass;\n"); + sb.Append($"\tvoid *monitor;\n"); + } + sb.Append($"\t{info.TypeName}_Fields fields;\n"); + sb.Append("};\n"); + + if (info.StaticFields.Count > 0) + { + sb.Append($"struct {info.TypeName}_StaticFields {{\n"); + foreach (var field in info.StaticFields) + { + if (field.IsValueType) + { + var fieldInfo = structInfoWithStructName[field.FieldTypeName]; + pre.Append(RecursionStructInfo(fieldInfo)); + } + if (field.IsCustomType) + { + sb.Append($"\tstruct {field.FieldTypeName} {field.FieldName};\n"); + } + else + { + sb.Append($"\t{field.FieldTypeName} {field.FieldName};\n"); + } + } + sb.Append("};\n"); + } + + return pre.Append(sb).ToString(); + } + + private string GetIl2CppStructName(Il2CppType il2CppType, Il2CppGenericContext context = null) + { + switch (il2CppType.Type) + { + case Il2CppTypeEnum.IL2CPP_TYPE_VOID: + case Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN: + case Il2CppTypeEnum.IL2CPP_TYPE_CHAR: + case Il2CppTypeEnum.IL2CPP_TYPE_I1: + case Il2CppTypeEnum.IL2CPP_TYPE_U1: + case Il2CppTypeEnum.IL2CPP_TYPE_I2: + case Il2CppTypeEnum.IL2CPP_TYPE_U2: + case Il2CppTypeEnum.IL2CPP_TYPE_I4: + case Il2CppTypeEnum.IL2CPP_TYPE_U4: + case Il2CppTypeEnum.IL2CPP_TYPE_I8: + case Il2CppTypeEnum.IL2CPP_TYPE_U8: + case Il2CppTypeEnum.IL2CPP_TYPE_R4: + case Il2CppTypeEnum.IL2CPP_TYPE_R8: + case Il2CppTypeEnum.IL2CPP_TYPE_STRING: + case Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF: + case Il2CppTypeEnum.IL2CPP_TYPE_I: + case Il2CppTypeEnum.IL2CPP_TYPE_U: + case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE: + case Il2CppTypeEnum.IL2CPP_TYPE_CLASS: + case Il2CppTypeEnum.IL2CPP_TYPE_OBJECT: + { + var typeDef = il2CppType.CoerceToUnderlyingTypeDefinition(); + return structNameDic[typeDef]; + } + case Il2CppTypeEnum.IL2CPP_TYPE_PTR: + { + var oriType = il2Cpp.GetIl2CppTypeFromPointer(il2CppType.Data.Type); + return GetIl2CppStructName(oriType); + } + case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: + { + var arrayType = il2Cpp.ReadReadableAtVirtualAddress(il2CppType.Data.Array); + var elementType = il2Cpp.GetIl2CppTypeFromPointer(arrayType.etype); + var elementStructName = GetIl2CppStructName(elementType, context); + var typeStructName = elementStructName + "_array"; + if (structNameHashSet.Add(typeStructName)) + { + ParseArrayClassStruct(elementType, context); + } + return typeStructName; + } + case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY: + { + var elementType = il2Cpp.GetIl2CppTypeFromPointer(il2CppType.Data.Type); + var elementStructName = GetIl2CppStructName(elementType, context); + var typeStructName = elementStructName + "_array"; + if (structNameHashSet.Add(typeStructName)) + { + ParseArrayClassStruct(elementType, context); + } + return typeStructName; + } + case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: + { + var typeStructName = genericClassStructNameDic[il2CppType.Data.GenericClass]; + if (structNameHashSet.Add(typeStructName)) + { + genericClassList.Add(il2CppType.Data.GenericClass); + } + return typeStructName; + } + case Il2CppTypeEnum.IL2CPP_TYPE_VAR: + { + if (context != null) + { + var genericParameter = il2CppType.GetGenericParameterDef(); + var genericInst = il2Cpp.ReadReadableAtVirtualAddress(context.class_inst); + var pointers = genericInst.Pointers; + var pointer = pointers[genericParameter.Index]; + var type = il2Cpp.GetIl2CppTypeFromPointer(pointer); + return GetIl2CppStructName(type); + } + return "System_Object"; + } + case Il2CppTypeEnum.IL2CPP_TYPE_MVAR: + { + if (context != null) + { + var genericParameter = il2CppType.GetGenericParameterDef(); + var genericInst = il2Cpp.ReadReadableAtVirtualAddress(context.method_inst); + var pointers = genericInst.Pointers; + var pointer = pointers[genericParameter.Index]; + var type = il2Cpp.GetIl2CppTypeFromPointer(pointer); + return GetIl2CppStructName(type); + } + return "System_Object"; + } + default: + throw new NotSupportedException(); + } + } + + private bool IsValueType(Il2CppType il2CppType, Il2CppGenericContext context) + { + switch (il2CppType.Type) + { + case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE: + { + var typeDef = il2CppType.CoerceToUnderlyingTypeDefinition(); + return !typeDef.IsEnumType; + } + case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: + { + var genericClass = il2Cpp.ReadReadableAtVirtualAddress(il2CppType.Data.GenericClass); + var typeDef = genericClass.TypeDefinition; + return typeDef.IsValueType && !typeDef.IsEnumType; + } + case Il2CppTypeEnum.IL2CPP_TYPE_VAR: + { + if (context != null) + { + var genericParameter = il2CppType.GetGenericParameterDef(); + var genericInst = il2Cpp.ReadReadableAtVirtualAddress(context.class_inst); + var pointers = genericInst.Pointers; + var pointer = pointers[genericParameter.Index]; + var type = il2Cpp.GetIl2CppTypeFromPointer(pointer); + return IsValueType(type, null); + } + return false; + } + case Il2CppTypeEnum.IL2CPP_TYPE_MVAR: + { + if (context != null) + { + var genericParameter = il2CppType.GetGenericParameterDef(); + var genericInst = il2Cpp.ReadReadableAtVirtualAddress(context.method_inst); + var pointers = genericInst.Pointers; + var pointer = pointers[genericParameter.Index]; + var type = il2Cpp.GetIl2CppTypeFromPointer(pointer); + return IsValueType(type, null); + } + return false; + } + default: + return false; + } + } + + private bool IsCustomType(Il2CppType il2CppType, Il2CppGenericContext context) + { + switch (il2CppType.Type) + { + case Il2CppTypeEnum.IL2CPP_TYPE_PTR: + { + var oriType = il2Cpp.GetIl2CppTypeFromPointer(il2CppType.Data.Type); + return IsCustomType(oriType, context); + } + case Il2CppTypeEnum.IL2CPP_TYPE_STRING: + case Il2CppTypeEnum.IL2CPP_TYPE_CLASS: + case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: + case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY: + { + return true; + } + case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE: + { + var typeDef = il2CppType.CoerceToUnderlyingTypeDefinition(); + if (typeDef.IsEnumType) + { + return IsCustomType(typeDef.EnumUnderlyingType, context); + } + return true; + } + case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: + { + var genericClass = il2CppType.GetGenericClass(); + var typeDef = genericClass.TypeDefinition; + if (typeDef.IsEnumType) + { + return IsCustomType(typeDef.EnumUnderlyingType, context); + } + return true; + } + case Il2CppTypeEnum.IL2CPP_TYPE_VAR: + { + if (context != null) + { + var genericParameter = il2CppType.GetGenericParameterDef(); + var genericInst = il2Cpp.ReadReadableAtVirtualAddress(context.class_inst); + var pointers = genericInst.Pointers; + var pointer = pointers[genericParameter.Index]; + var type = il2Cpp.GetIl2CppTypeFromPointer(pointer); + return IsCustomType(type, null); + } + return false; + } + case Il2CppTypeEnum.IL2CPP_TYPE_MVAR: + { + if (context != null) + { + var genericParameter = il2CppType.GetGenericParameterDef(); + var genericInst = context.MethodInst; + var pointers = genericInst.Pointers; + var pointer = pointers[genericParameter.Index]; + var type = il2Cpp.GetIl2CppTypeFromPointer(pointer); + return IsCustomType(type, null); + } + return false; + } + default: + return false; + } + } + + private static readonly Dictionary TypeString = new() + { + {1,"void"}, + {2,"bool"}, + {3,"char"}, + {4,"sbyte"}, + {5,"byte"}, + {6,"short"}, + {7,"ushort"}, + {8,"int"}, + {9,"uint"}, + {10,"long"}, + {11,"ulong"}, + {12,"float"}, + {13,"double"}, + {14,"string"}, + {22,"TypedReference"}, + {24,"IntPtr"}, + {25,"UIntPtr"}, + {28,"object"}, + }; + + public string GetTypeName(Il2CppType il2CppType, bool addNamespace, bool is_nested) + { + switch (il2CppType.Type) + { + case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: + { + var arrayType = il2CppType.GetArrayType(); + var elementType = il2CppType.GetArrayElementType(); + return $"{GetTypeName(elementType, addNamespace, false)}[{new string(',', arrayType.rank - 1)}]"; + } + case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY: + { + var elementType = il2Cpp.GetIl2CppTypeFromPointer(il2CppType.Data.Type); + return $"{GetTypeName(elementType, addNamespace, false)}[]"; + } + case Il2CppTypeEnum.IL2CPP_TYPE_PTR: + { + var oriType = il2Cpp.GetIl2CppTypeFromPointer(il2CppType.Data.Type); + return $"{GetTypeName(oriType, addNamespace, false)}*"; + } + case Il2CppTypeEnum.IL2CPP_TYPE_VAR: + case Il2CppTypeEnum.IL2CPP_TYPE_MVAR: + { + var param = il2CppType.GetGenericParameterDef(); + return metadata.GetStringFromIndex(param.nameIndex); + } + case Il2CppTypeEnum.IL2CPP_TYPE_CLASS: + case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE: + case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: + { + string str = string.Empty; + Il2CppTypeDefinition typeDef; + Il2CppGenericClass genericClass = null; + if (il2CppType.Type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) + { + genericClass = il2CppType.GetGenericClass(); + typeDef = genericClass.TypeDefinition; + } + else + { + typeDef = il2CppType.CoerceToUnderlyingTypeDefinition(); + } + if (typeDef.DeclaringTypeIndex != -1) + { + str += GetTypeName(il2Cpp.AllTypes[typeDef.DeclaringTypeIndex], addNamespace, true); + str += '.'; + } + else if (addNamespace) + { + var @namespace = metadata.GetStringFromIndex(typeDef.NamespaceIndex); + if (@namespace != "") + { + str += @namespace + "."; + } + } + + var typeName = metadata.GetStringFromIndex(typeDef.NameIndex); + var index = typeName.IndexOf("`"); + if (index != -1) + { + str += typeName[..index]; + } + else + { + str += typeName; + } + + if (is_nested) + return str; + + if (genericClass != null) + { + var genericInst = il2Cpp.ReadReadableAtVirtualAddress(genericClass.Context.class_inst); + str += GetGenericInstParams(genericInst); + } + else if (typeDef.GenericContainerIndex >= 0) + { + var genericContainer = metadata.genericContainers[typeDef.GenericContainerIndex]; + str += GetGenericContainerParams(genericContainer); + } + + return str; + } + default: + return TypeString[(int)il2CppType.Type]; + } + } + + public string GetTypeDefName(Il2CppTypeDefinition typeDef, bool addNamespace, bool genericParameter) + { + var prefix = string.Empty; + if (typeDef.DeclaringTypeIndex != -1) + { + prefix = GetTypeName(il2Cpp.AllTypes[typeDef.DeclaringTypeIndex], addNamespace, true) + "."; + } + else if (addNamespace) + { + var @namespace = metadata.GetStringFromIndex(typeDef.NamespaceIndex); + if (@namespace != "") + { + prefix = @namespace + "."; + } + } + var typeName = metadata.GetStringFromIndex(typeDef.NameIndex); + if (typeDef.GenericContainerIndex >= 0) + { + var index = typeName.IndexOf("`"); + if (index != -1) + { + typeName = typeName[..index]; + } + if (genericParameter) + { + var genericContainer = metadata.genericContainers[typeDef.GenericContainerIndex]; + typeName += GetGenericContainerParams(genericContainer); + } + } + return prefix + typeName; + } + + public string GetGenericInstParams(Il2CppGenericInst genericInst) + { + var genericParameterNames = new List(); + var pointers = genericInst.Pointers; + for (int i = 0; i < pointers.Length; i++) + { + var il2CppType = il2Cpp.GetIl2CppTypeFromPointer(pointers[i]); + genericParameterNames.Add(GetTypeName(il2CppType, false, false)); + } + return $"<{string.Join(", ", genericParameterNames)}>"; + } + + public string GetGenericContainerParams(Il2CppGenericContainer genericContainer) + { + var genericParameterNames = new List(); + for (int i = 0; i < genericContainer.genericParameterCount; i++) + { + var genericParameterIndex = genericContainer.genericParameterStart + i; + var genericParameter = metadata.genericParameters[genericParameterIndex]; + genericParameterNames.Add(metadata.GetStringFromIndex(genericParameter.nameIndex)); + } + return $"<{string.Join(", ", genericParameterNames)}>"; + } + + public (string, string) GetMethodSpecName(Il2CppMethodSpec methodSpec, bool addNamespace = false) + { + var methodDef = methodSpec.MethodDefinition; + var typeDef = methodDef.DeclaringType; + var typeName = GetTypeDefName(typeDef, addNamespace, false); + if (methodSpec.classIndexIndex != -1) + { + var classInst = methodSpec.GenericClassInst; + typeName += GetGenericInstParams(classInst); + } + var methodName = metadata.GetStringFromIndex(methodDef.nameIndex); + if (methodSpec.methodIndexIndex != -1) + { + var methodInst = methodSpec.GenericMethodInst; + methodName += GetGenericInstParams(methodInst); + } + return (typeName, methodName); + } + + private void GenerateMethodInfo(string methodInfoName, string structTypeName, List rgctxs) + { + if (rgctxs.Count > 0) + { + methodInfoHeader.Append($"struct {methodInfoName}_RGCTXs {{\n"); + for (int i = 0; i < rgctxs.Count; i++) + { + var rgctx = rgctxs[i]; + switch (rgctx.Type) + { + case Il2CppRGCTXDataType.IL2CPP_RGCTX_DATA_TYPE: + methodInfoHeader.Append($"\tIl2CppType* _{i}_{rgctx.TypeName};\n"); + break; + case Il2CppRGCTXDataType.IL2CPP_RGCTX_DATA_CLASS: + methodInfoHeader.Append($"\tIl2CppClass* _{i}_{rgctx.ClassName};\n"); + break; + case Il2CppRGCTXDataType.IL2CPP_RGCTX_DATA_METHOD: + methodInfoHeader.Append($"\tMethodInfo* _{i}_{rgctx.MethodName};\n"); + break; + } + } + methodInfoHeader.Append("};\n"); + } + + methodInfoHeader.Append($"struct {methodInfoName} {{\n"); + methodInfoHeader.Append($"\tIl2CppMethodPointer methodPointer;\n"); + if (LibCpp2IlMain.MetadataVersion >= 29) + { + methodInfoHeader.Append($"\tIl2CppMethodPointer virtualMethodPointer;\n"); + methodInfoHeader.Append($"\tInvokerMethod invoker_method;\n"); + } + else + { + methodInfoHeader.Append($"\tvoid* invoker_method;\n"); //TODO + } + methodInfoHeader.Append($"\tconst char* name;\n"); + if (LibCpp2IlMain.MetadataVersion <= 24) + { + methodInfoHeader.Append($"\t{structTypeName}_c *declaring_type;\n"); + } + else + { + methodInfoHeader.Append($"\t{structTypeName}_c *klass;\n"); + } + methodInfoHeader.Append($"\tconst Il2CppType *return_type;\n"); + if (LibCpp2IlMain.MetadataVersion >= 29) + { + methodInfoHeader.Append($"\tconst Il2CppType** parameters;\n"); + } + else + { + methodInfoHeader.Append($"\tconst void* parameters;\n"); //ParameterInfo* + } + if (rgctxs.Count > 0) + { + methodInfoHeader.Append($"\tconst {methodInfoName}_RGCTXs* rgctx_data;\n"); + } + else + { + methodInfoHeader.Append($"\tconst Il2CppRGCTXData* rgctx_data;\n"); + } + methodInfoHeader.Append($"\tunion\n"); + methodInfoHeader.Append($"\t{{\n"); + methodInfoHeader.Append($"\t\tconst void* genericMethod;\n"); + if (LibCpp2IlMain.MetadataVersion >= 27) + { + methodInfoHeader.Append($"\t\tconst void* genericContainerHandle;\n"); + } + else + { + methodInfoHeader.Append($"\t\tconst void* genericContainer;\n"); + } + methodInfoHeader.Append($"\t}};\n"); + if (LibCpp2IlMain.MetadataVersion <= 24) + { + methodInfoHeader.Append($"\tint32_t customAttributeIndex;\n"); + } + methodInfoHeader.Append($"\tuint32_t token;\n"); + methodInfoHeader.Append($"\tuint16_t flags;\n"); + methodInfoHeader.Append($"\tuint16_t iflags;\n"); + methodInfoHeader.Append($"\tuint16_t slot;\n"); + methodInfoHeader.Append($"\tuint8_t parameters_count;\n"); + methodInfoHeader.Append($"\tuint8_t bitflags;\n"); + methodInfoHeader.Append($"}};\n"); + } + } +} diff --git a/Cpp2IL.Core/OutputFormats/Disassembly/StructInfo.cs b/Cpp2IL.Core/OutputFormats/Disassembly/StructInfo.cs new file mode 100644 index 00000000..422531eb --- /dev/null +++ b/Cpp2IL.Core/OutputFormats/Disassembly/StructInfo.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; + +namespace Il2CppDumper +{ + public class StructInfo + { + public string TypeName; + public bool IsValueType; + public string Parent; + public List Fields = new(); + public List StaticFields = new(); + public StructVTableMethodInfo[] VTableMethod = Array.Empty(); + public List RGCTXs = new(); + } + + public class StructFieldInfo + { + public string FieldTypeName; + public string FieldName; + public bool IsValueType; + public bool IsCustomType; + } + + public class StructVTableMethodInfo + { + public string MethodName; + } + + public class StructRGCTXInfo + { + public Il2CppRGCTXDataType Type; + public string TypeName; + public string ClassName; + public string MethodName; + } +}