diff --git a/Il2CppInspector/DefineConstants.cs b/Il2CppInspector/DefineConstants.cs index 01bea56..43d7d99 100644 --- a/Il2CppInspector/DefineConstants.cs +++ b/Il2CppInspector/DefineConstants.cs @@ -1,4 +1,6 @@ -public static class DefineConstants +using System.Collections.Generic; + +public static class DefineConstants { public const int FIELD_ATTRIBUTE_PRIVATE = 0x0001; public const int FIELD_ATTRIBUTE_PUBLIC = 0x0006; @@ -17,4 +19,80 @@ public const int TYPE_ATTRIBUTE_SERIALIZABLE = 0x00002000; public const int PARAM_ATTRIBUTE_OUT = 0x0002; public const int PARAM_ATTRIBUTE_OPTIONAL = 0x0010; + + public static List CSharpTypeString = new List + { + "END", + "void", + "bool", + "char", + "sbyte", + "byte", + "short", + "ushort", + "int", + "uint", + "long", + "ulong", + "float", + "double", + "string", + "PTR", // Processed separately + "BYREF", + "VALUETYPE", // Processed separately + "CLASS", // Processed separately + "T", + "Array", // Processed separately + "GENERICINST", // Processed separately + "TYPEDBYREF", + "None", + "IntPtr", + "UIntPtr", + "None", + "delegate", + "object", + "SZARRAY", // Processed separately + "T", + "CMOD_REQD", + "CMOD_OPT", + "INTERNAL", + }; + + public static List FullNameTypeString = new List + { + "END", + "System.Void", + "System.Boolean", + "System.Char", + "System.SByte", + "System.Byte", + "System.Int16", + "System.UInt16", + "System.Int32", + "System.UInt32", + "System.Int64", + "System.UInt64", + "System.Single", + "System.Double", + "System.String", + "PTR", // Processed separately + "BYREF", + "System.ValueType", // Processed separately + "CLASS", // Processed separately + "T", + "System.Array", // Processed separately + "GENERICINST", // Processed separately + "TYPEDBYREF", + "None", + "System.IntPtr", + "System.UIntPtr", + "None", + "System.Delegate", + "System.Object", + "SZARRAY", // Processed separately + "T", + "CMOD_REQD", + "CMOD_OPT", + "INTERNAL", + }; } \ No newline at end of file diff --git a/Il2CppInspector/Il2CppInspector.cs b/Il2CppInspector/Il2CppInspector.cs index dcc0ce3..6ac872e 100644 --- a/Il2CppInspector/Il2CppInspector.cs +++ b/Il2CppInspector/Il2CppInspector.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; namespace Il2CppInspector @@ -103,18 +104,13 @@ namespace Il2CppInspector ret = $"{GetTypeName(type)}[]"; } else { - if ((int)pType.type >= szTypeString.Length) + if ((int)pType.type >= DefineConstants.CSharpTypeString.Count) ret = "unknow"; else - ret = szTypeString[(int)pType.type]; + ret = DefineConstants.CSharpTypeString[(int)pType.type]; } return ret; } - - public Il2CppType GetTypeFromTypeIndex(int idx) { - return Binary.Types[idx]; - } - public int GetFieldOffsetFromIndex(int typeIndex, int fieldIndexInType) { // Versions from 22 onwards use an array of pointers in fieldOffsets bool fieldOffsetsArePointers = (Metadata.Version >= 22); @@ -144,7 +140,7 @@ namespace Il2CppInspector return null; var pValue = Metadata.Header.fieldAndParameterDefaultValueDataOffset + def.dataIndex; - Il2CppType type = GetTypeFromTypeIndex(def.typeIndex); + Il2CppType type = Binary.Types[def.typeIndex]; if (pValue == 0) return null; @@ -192,43 +188,5 @@ namespace Il2CppInspector } return value; } - - private readonly string[] szTypeString = - { - "END", - "void", - "bool", - "char", - "sbyte", - "byte", - "short", - "ushort", - "int", - "uint", - "long", - "ulong", - "float", - "double", - "string", - "PTR",//eg. void* - "BYREF", - "VALUETYPE", - "CLASS", - "T", - "ARRAY", - "GENERICINST", - "TYPEDBYREF", - "None", - "IntPtr", - "UIntPtr", - "None", - "FNPTR", - "object", - "SZARRAY", - "T", - "CMOD_REQD", - "CMOD_OPT", - "INTERNAL", - }; } } diff --git a/Il2CppInspector/Il2CppReflector.cs b/Il2CppInspector/Il2CppReflector.cs index 1a38ff1..26522fe 100644 --- a/Il2CppInspector/Il2CppReflector.cs +++ b/Il2CppInspector/Il2CppReflector.cs @@ -1,23 +1,53 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; namespace Il2CppInspector.Reflection { public class Il2CppReflector { + public Il2CppInspector Package { get; } public List Assemblies { get; } = new List(); public Il2CppReflector(Il2CppInspector package) { + Package = package; + // Create Assembly objects from Il2Cpp package for (var image = 0; image < package.Metadata.Images.Length; image++) - Assemblies.Add(new Assembly(package, image)); + Assemblies.Add(new Assembly(this, image)); } // Get the assembly in which a type is defined - public Assembly GetAssembly(Type type) => Assemblies.FirstOrDefault(x => x.DefinedTypes.Contains(type)); + public Assembly GetAssembly(TypeInfo type) => Assemblies.FirstOrDefault(x => x.DefinedTypes.Contains(type)); // Get a type from its IL2CPP type index - public Type GetTypeFromIndex(int typeIndex) => Assemblies.SelectMany(x => x.DefinedTypes).FirstOrDefault(x => x.Index == typeIndex); + public TypeInfo GetTypeFromIndex(int typeIndex) => Assemblies.SelectMany(x => x.DefinedTypes).FirstOrDefault(x => x.Index == typeIndex); + + // Get or generate a type from its IL2CPP binary type usage reference + // (field, return type, generic type parameter etc.) + public TypeInfo GetType(Il2CppType pType, MemberTypes memberType = MemberTypes.All) { + switch (pType.type) { + case Il2CppTypeEnum.IL2CPP_TYPE_CLASS: + case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE: + // Classes defined in the metadata + return GetTypeFromIndex((int) pType.datapoint); + + case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: + case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: + case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY: + case Il2CppTypeEnum.IL2CPP_TYPE_PTR: + case Il2CppTypeEnum.IL2CPP_TYPE_VAR: + // Everything that requires special handling + return new TypeInfo(this, pType, memberType); + + default: + // Basic primitive types + if ((int) pType.type >= DefineConstants.FullNameTypeString.Count) + return null; + + return Assemblies.SelectMany(x => x.DefinedTypes).First(x => x.FullName == DefineConstants.FullNameTypeString[(int)pType.type]); + } + } } -} +} \ No newline at end of file diff --git a/Il2CppInspector/Reflection/Assembly.cs b/Il2CppInspector/Reflection/Assembly.cs index bbdb70e..708a459 100644 --- a/Il2CppInspector/Reflection/Assembly.cs +++ b/Il2CppInspector/Reflection/Assembly.cs @@ -6,6 +6,7 @@ namespace Il2CppInspector.Reflection { public class Assembly { // IL2CPP-specific data + public Il2CppReflector Model { get; } public Il2CppImageDefinition Definition { get; } public int Index { get; } @@ -15,27 +16,28 @@ namespace Il2CppInspector.Reflection { public string FullName { get; } // Entry point method for the assembly - public MethodInfo EntryPoint { get; } + //public MethodInfo EntryPoint { get; } // TODO // List of types defined in the assembly - public List DefinedTypes { get; } = new List(); + public List DefinedTypes { get; } = new List(); // Get a type from its string name (including namespace) - public Type GetType(string typeName) => DefinedTypes.FirstOrDefault(x => x.FullName == typeName); + public TypeInfo GetType(string typeName) => DefinedTypes.FirstOrDefault(x => x.FullName == typeName); // Initialize from specified assembly index in package - public Assembly(Il2CppInspector pkg, int imageIndex) { - Definition = pkg.Metadata.Images[imageIndex]; + public Assembly(Il2CppReflector model, int imageIndex) { + Model = model; + Definition = Model.Package.Metadata.Images[imageIndex]; Index = Definition.assemblyIndex; - FullName = pkg.Metadata.Strings[Definition.nameIndex]; + FullName = Model.Package.Metadata.Strings[Definition.nameIndex]; if (Definition.entryPointIndex != -1) { // TODO: Generate EntryPoint method from entryPointIndex } // Generate types in DefinedTypes from typeStart to typeStart+typeCount-1 - for (int t = Definition.typeStart; t < Definition.typeStart + Definition.typeCount; t++) - DefinedTypes.Add(new Type(pkg, t, this)); + for (var t = Definition.typeStart; t < Definition.typeStart + Definition.typeCount; t++) + DefinedTypes.Add(new TypeInfo(Model.Package, t, this)); } } } \ No newline at end of file diff --git a/Il2CppInspector/Reflection/FieldInfo.cs b/Il2CppInspector/Reflection/FieldInfo.cs new file mode 100644 index 0000000..6c35ec2 --- /dev/null +++ b/Il2CppInspector/Reflection/FieldInfo.cs @@ -0,0 +1,63 @@ +using System.Reflection; + +namespace Il2CppInspector.Reflection { + public class FieldInfo : MemberInfo + { + // IL2CPP-specific data + public Il2CppFieldDefinition Definition { get; } + public int Index { get; } + + // Information/flags about the field + public FieldAttributes Attributes { get; } + + // Type of field + private readonly Il2CppType fieldType; + public TypeInfo FieldType => Assembly.Model.GetType(fieldType, MemberTypes.Field); + + // For the Is* definitions below, see: + // https://docs.microsoft.com/en-us/dotnet/api/system.reflection.fieldinfo.isfamilyandassembly?view=netframework-4.7.1#System_Reflection_FieldInfo_IsFamilyAndAssembly + + // True if the field is declared as internal + public bool IsAssembly { get; } // TODO + + // True if the field is declared as protected + public bool IsFamily { get; } // TODO + + // True if the field is declared as 'protected private' (always false) + public bool IsFamilyAndAssembly => false; + + // True if the field is declared as protected public + public bool IsFamilyOrAssembly { get; } // TODO + + // True if the field is declared as readonly + public bool IsInitOnly => (Attributes & FieldAttributes.InitOnly) == FieldAttributes.InitOnly; + + // True if the field is declared a private + public bool IsPrivate => (Attributes & FieldAttributes.Private) == FieldAttributes.Private; + + // True if the field is declared as public + public bool IsPublic => (Attributes & FieldAttributes.Public) == FieldAttributes.Public; + + // True if the field is declared as static + public bool IsStatic => (Attributes & FieldAttributes.Static) == FieldAttributes.Static; + + public override MemberTypes MemberType { get; } + + public FieldInfo(Il2CppInspector pkg, int fieldIndex, TypeInfo declaringType) : + base(declaringType) { + Definition = pkg.Metadata.Fields[fieldIndex]; + Index = fieldIndex; + Name = pkg.Metadata.Strings[pkg.Metadata.Fields[fieldIndex].nameIndex]; + + fieldType = pkg.Binary.Types[Definition.typeIndex]; + if ((fieldType.attrs & DefineConstants.FIELD_ATTRIBUTE_PRIVATE) == DefineConstants.FIELD_ATTRIBUTE_PRIVATE) + Attributes |= FieldAttributes.Private; + if ((fieldType.attrs & DefineConstants.FIELD_ATTRIBUTE_PUBLIC) == DefineConstants.FIELD_ATTRIBUTE_PUBLIC) + Attributes |= FieldAttributes.Public; + if ((fieldType.attrs & DefineConstants.FIELD_ATTRIBUTE_STATIC) == DefineConstants.FIELD_ATTRIBUTE_STATIC) + Attributes |= FieldAttributes.Static; + if ((fieldType.attrs & DefineConstants.FIELD_ATTRIBUTE_INIT_ONLY) == DefineConstants.FIELD_ATTRIBUTE_INIT_ONLY) + Attributes |= FieldAttributes.InitOnly; + } + } +} \ No newline at end of file diff --git a/Il2CppInspector/Reflection/MemberInfo.cs b/Il2CppInspector/Reflection/MemberInfo.cs index 29c82d1..75ca8c9 100644 --- a/Il2CppInspector/Reflection/MemberInfo.cs +++ b/Il2CppInspector/Reflection/MemberInfo.cs @@ -4,21 +4,35 @@ using System.Reflection; namespace Il2CppInspector.Reflection { public abstract class MemberInfo { - // Assembly that this member is defined in - public Assembly Assembly { get; set; } + // Assembly that this member is defined in. Only set when MemberType == TypeInfo + public Assembly Assembly { get; } // Custom attributes for this member - public IEnumerable CustomAttributes { get; set; } // TODO + public IEnumerable CustomAttributes { get; } // TODO // Type that this type is declared in for nested types - public Type DeclaringType { get; set; } // TODO + public TypeInfo DeclaringType { get; } // What sort of member this is, eg. method, field etc. - public MemberTypes MemberType { get; set; } // TODO + public abstract MemberTypes MemberType { get; } // Name of the member - public string Name { get; set; } + public virtual string Name { get; protected set; } // TODO: GetCustomAttributes etc. + + // For top-level members in an assembly (ie. non-nested types) + protected MemberInfo(Assembly asm, TypeInfo declaringType = null) { + Assembly = asm; + DeclaringType = declaringType; + } + + // For lower level members, eg. fields, properties etc. and nested types + protected MemberInfo(TypeInfo declaringType) { + if (declaringType != null) { + Assembly = declaringType.Assembly; + DeclaringType = declaringType; + } + } } } \ No newline at end of file diff --git a/Il2CppInspector/Reflection/ReflectionClasses.cs b/Il2CppInspector/Reflection/ReflectionClasses.cs index f6d0446..56f0a01 100644 --- a/Il2CppInspector/Reflection/ReflectionClasses.cs +++ b/Il2CppInspector/Reflection/ReflectionClasses.cs @@ -3,6 +3,7 @@ using System.Reflection; namespace Il2CppInspector.Reflection { + /* public abstract class MethodBase : MemberInfo { // (not code attributes) @@ -21,11 +22,6 @@ namespace Il2CppInspector.Reflection // TODO } - public class FieldInfo : MemberInfo - { - // TODO - } - public class PropertyInfo : MemberInfo { // TODO @@ -35,4 +31,5 @@ namespace Il2CppInspector.Reflection { // TODO } + */ } diff --git a/Il2CppInspector/Reflection/Type.cs b/Il2CppInspector/Reflection/Type.cs deleted file mode 100644 index 703f0cd..0000000 --- a/Il2CppInspector/Reflection/Type.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; - -namespace Il2CppInspector.Reflection { - public class Type : MemberInfo - { - // IL2CPP-specific data - public Il2CppTypeDefinition Definition { get; } - public int Index { get; } - - // (not code attributes) - // Undefined if the Type represents a generic type parameter - public TypeAttributes Attributes { get; } // TODO - - // Type that this type inherits from - public Type BaseType { get; } // TODO - - // TODO: ContainsGenericParameters - - // Method that the type is declared in if this is a type parameter of a generic method - public MethodBase DeclaringMethod { get; } // TODO - - // Gets the type of the object encompassed or referred to by the current array, pointer or reference type - public Type ElementType { get; } // TODO - - // Type name including namespace - public string FullName => Namespace + "." + Name; - - // TODO: Generic stuff - - public bool HasElementType { get; } // TODO - public bool IsAbstract { get; } - public bool IsArray { get; } // TODO - public bool IsByRef { get; } // TODO - public bool IsClass { get; } - public bool IsEnum { get; } // TODO - public bool IsGenericParameter { get; } // TODO - public bool IsGenericType { get; } // TODO - public bool IsGenericTypeDefinition { get; } // TODO - public bool IsInterface { get; } - public bool IsNested { get; } // TODO - public bool IsNestedPrivate { get; } // TODO - public bool IsNestedPublic { get; } // TODO - public bool IsPointer { get; } // TODO - public bool IsPrimitive { get; } // TODO - public bool IsPublic { get; } - public bool IsSealed { get; } - public bool IsSerializable { get; } - public bool IsValueType { get; } // TODO - - public string Namespace { get; } - - // Number of dimensions of an array - public int GetArrayRank() => throw new NotImplementedException(); - - public List Constructors { get; } // TODO - - public List Inerfaces { get; } // TODO - - public List Members { get; } // TODO - - public List Methods { get; } // TODO - - public List Fields { get; } // TODO - - public List NestedTypes { get; } // TODO - - public List Properties { get; } // TODO - - // TODO: Custom attribute stuff - - public string[] GetEnumNames() => throw new NotImplementedException(); - - public Type GetEnumUnderlyingType() => throw new NotImplementedException(); - - public Array GetEnumValues() => throw new NotImplementedException(); - - // TODO: Event stuff - - // TODO: Generic stuff - - // Initialize from specified type index in package - public Type(Il2CppInspector pkg, int typeIndex, Assembly owner) { - Assembly = owner; - Definition = pkg.Metadata.Types[typeIndex]; - Index = typeIndex; - Name = pkg.Metadata.Strings[Definition.nameIndex]; - Namespace = pkg.Metadata.Strings[Definition.namespaceIndex]; - - IsSerializable = (Definition.flags & DefineConstants.TYPE_ATTRIBUTE_SERIALIZABLE) != 0; - IsPublic = (Definition.flags & DefineConstants.TYPE_ATTRIBUTE_VISIBILITY_MASK) == DefineConstants.TYPE_ATTRIBUTE_PUBLIC; - IsAbstract = (Definition.flags & DefineConstants.TYPE_ATTRIBUTE_ABSTRACT) != 0; - IsSealed = (Definition.flags & DefineConstants.TYPE_ATTRIBUTE_SEALED) != 0; - IsInterface = (Definition.flags & DefineConstants.TYPE_ATTRIBUTE_INTERFACE) != 0; - IsClass = !IsInterface; - } - } -} \ No newline at end of file diff --git a/Il2CppInspector/Reflection/TypeInfo.cs b/Il2CppInspector/Reflection/TypeInfo.cs new file mode 100644 index 0000000..593b494 --- /dev/null +++ b/Il2CppInspector/Reflection/TypeInfo.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Il2CppInspector.Reflection { + public class TypeInfo : MemberInfo + { + // IL2CPP-specific data + public Il2CppTypeDefinition Definition { get; } + public int Index { get; } + + // Information/flags about the type + // Undefined if the Type represents a generic type parameter + public TypeAttributes Attributes { get; } // TODO + + // Type that this type inherits from + public TypeInfo BaseType { get; } // TODO + + // True if the type contains unresolved generic type parameters + public bool ContainsGenericParameters { get; } + + // C# colloquial name of the type (if available) + public string CSharpName { + get { + var s = Namespace + "." + base.Name; + var i = DefineConstants.FullNameTypeString.IndexOf(s); + var n = (i != -1 ? DefineConstants.CSharpTypeString[i] : base.Name); + if (IsArray) + n = ElementType.CSharpName; + var g = (GenericTypeParameters != null ? "<" + string.Join(", ", GenericTypeParameters.Select(x => x.CSharpName)) + ">" : ""); + return (IsPointer ? "void *" : "") + n + g + (IsArray ? "[]" : ""); + } + } + + public List DeclaredConstructors { get; } // TODO + public List DeclaredEvents { get; } // TODO + public List DeclaredFields { get; } = new List(); + public List DeclaredMembers { get; } // TODO + public List DeclaredMethods { get; } // TODO + public List DeclaredNestedTypes { get; } // TODO + public List DeclaredProperties { get; } // TODO + + // Method that the type is declared in if this is a type parameter of a generic method + public MethodBase DeclaringMethod { get; } // TODO + + // Gets the type of the object encompassed or referred to by the current array, pointer or reference type + public TypeInfo ElementType { get; } + + // Type name including namespace + public string FullName => (IsPointer? "void *" : "") + + Namespace + + (Namespace.Length > 0? "." : "") + + base.Name + + (GenericTypeParameters != null ? "<" + string.Join(", ", GenericTypeParameters.Select(x => x.Name)) + ">" : "") + + (IsArray? "[]" : ""); + + // TODO: Alot of other generics stuff + + public List GenericTypeParameters { get; } + + public bool HasElementType => ElementType != null; + public bool IsAbstract { get; } + public bool IsArray { get; } + public bool IsByRef { get; } // TODO + public bool IsClass { get; } + public bool IsEnum { get; } // TODO + public bool IsGenericParameter { get; } + public bool IsGenericType { get; } // TODO + public bool IsGenericTypeDefinition { get; } // TODO + public bool IsInterface { get; } + public bool IsNested { get; } // TODO + public bool IsNestedPrivate { get; } // TODO + public bool IsNestedPublic { get; } // TODO + public bool IsPointer { get; } // TODO + public bool IsPrimitive { get; } // TODO + public bool IsPublic { get; } + public bool IsSealed { get; } + public bool IsSerializable { get; } + public bool IsValueType { get; } // TODO + + public override MemberTypes MemberType { get; } + + public override string Name { + get => (IsPointer ? "void *" : "") + + base.Name + + (GenericTypeParameters != null? "<" + string.Join(", ", GenericTypeParameters.Select(x => x.Name)) + ">" : "") + + (IsArray ? "[]" : ""); + protected set => base.Name = value; + } + + public string Namespace { get; } + + // Number of dimensions of an array + private readonly int arrayRank; + public int GetArrayRank() => arrayRank; + + // TODO: Custom attribute stuff + + public string[] GetEnumNames() => throw new NotImplementedException(); + + public TypeInfo GetEnumUnderlyingType() => throw new NotImplementedException(); + + public Array GetEnumValues() => throw new NotImplementedException(); + + // TODO: Event stuff + + // TODO: Generic stuff + + // Initialize from specified type index in metadata + public TypeInfo(Il2CppInspector pkg, int typeIndex, Assembly owner) : + base(owner) { + Definition = pkg.Metadata.Types[typeIndex]; + Index = typeIndex; + Namespace = pkg.Metadata.Strings[Definition.namespaceIndex]; + Name = pkg.Metadata.Strings[pkg.Metadata.Types[typeIndex].nameIndex]; + + IsSerializable = (Definition.flags & DefineConstants.TYPE_ATTRIBUTE_SERIALIZABLE) != 0; + IsPublic = (Definition.flags & DefineConstants.TYPE_ATTRIBUTE_VISIBILITY_MASK) == DefineConstants.TYPE_ATTRIBUTE_PUBLIC; + IsAbstract = (Definition.flags & DefineConstants.TYPE_ATTRIBUTE_ABSTRACT) != 0; + IsSealed = (Definition.flags & DefineConstants.TYPE_ATTRIBUTE_SEALED) != 0; + IsInterface = (Definition.flags & DefineConstants.TYPE_ATTRIBUTE_INTERFACE) != 0; + IsClass = !IsInterface; + + for (var f = Definition.fieldStart; f < Definition.fieldStart + Definition.field_count; f++) + DeclaredFields.Add(new FieldInfo(pkg, f, this)); + + MemberType = MemberTypes.TypeInfo; + } + + // Initialize type from binary usage + public TypeInfo(Il2CppReflector model, Il2CppType pType, MemberTypes memberType) : base(null) { + var image = model.Package.Binary.Image; + + IsNested = true; + MemberType = memberType; + + // Generic type unresolved and concrete instance types + if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) { + var generic = image.ReadMappedObject(pType.datapoint); + var genericTypeDef = model.GetTypeFromIndex(generic.typeDefinitionIndex); + + Namespace = genericTypeDef.Namespace; + Name = genericTypeDef.Name; + + // TODO: Generic* properties and ContainsGenericParameters + + // Get the instantiation + var genericInstance = image.ReadMappedObject(generic.context.class_inst); + + // Get list of pointers to type parameters (both unresolved and concrete) + var genericTypeParameters = image.ReadMappedArray(genericInstance.type_argv, (int)genericInstance.type_argc); + + GenericTypeParameters = new List(); + foreach (var pArg in genericTypeParameters) { + var argType = image.ReadMappedObject(pArg); + // TODO: Detect whether unresolved or concrete (add concrete to GenericTypeArguments instead) + // TODO: GenericParameterPosition etc. in types we generate here + GenericTypeParameters.Add(model.GetType(argType)); // TODO: Fix MemberType here + } + + IsClass = true; + IsInterface = !IsClass; + } + + // Array with known dimensions and bounds + if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_ARRAY) { + var descriptor = image.ReadMappedObject(pType.datapoint); + var elementType = image.ReadMappedObject(descriptor.etype); + ElementType = model.GetType(elementType); + Namespace = ElementType.Namespace; + Name = ElementType.Name; + + IsArray = true; + arrayRank = descriptor.rank; + } + + // Dynamically allocated array + if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY) { + var elementType = image.ReadMappedObject(pType.datapoint); + ElementType = model.GetType(elementType); + Namespace = ElementType.Namespace; + Name = ElementType.Name; + + IsArray = true; + } + + // Unresolved generic type variable + if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_VAR) { + ContainsGenericParameters = true; + IsClass = true; + IsGenericParameter = true; + Name = "T"; // TODO: Don't hardcode parameter name + + // TODO: GenericTypeParameters? + } + + // Pointer type + IsPointer = (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_PTR); + } + } +} \ No newline at end of file