diff --git a/Il2CppInspector/Il2CppInspector.cs b/Il2CppInspector/Il2CppInspector.cs index 6e98b4b..288a9d8 100644 --- a/Il2CppInspector/Il2CppInspector.cs +++ b/Il2CppInspector/Il2CppInspector.cs @@ -31,6 +31,7 @@ namespace Il2CppInspector public Il2CppPropertyDefinition[] Properties => Metadata.Properties; public Il2CppEventDefinition[] Events => Metadata.Events; public int[] InterfaceUsageIndices => Metadata.InterfaceUsageIndices; + public int[] NestedTypeIndices => Metadata.NestedTypeIndices; public Dictionary FieldDefaultValue { get; } = new Dictionary(); public List FieldOffsets { get; } public List TypeUsages => Binary.Types; diff --git a/Il2CppInspector/Il2CppModel.cs b/Il2CppInspector/Il2CppModel.cs index 5eca24e..bb1c154 100644 --- a/Il2CppInspector/Il2CppModel.cs +++ b/Il2CppInspector/Il2CppModel.cs @@ -15,20 +15,18 @@ namespace Il2CppInspector.Reflection public Il2CppInspector Package { get; } public List Assemblies { get; } = new List(); + // List of all types ordered by their TypeDefinitionIndex + public TypeInfo[] TypesByIndex { get; } + public Il2CppModel(Il2CppInspector package) { Package = package; + TypesByIndex = new TypeInfo[package.TypeDefinitions.Length]; // Create Assembly objects from Il2Cpp package for (var image = 0; image < package.Images.Length; image++) Assemblies.Add(new Assembly(this, image)); } - // Get the assembly in which a type is defined - public Assembly GetAssembly(TypeInfo type) => Assemblies.FirstOrDefault(x => x.DefinedTypes.Contains(type)); - - // Get a type from its IL2CPP type index - public TypeInfo GetTypeFromIndex(long 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) { @@ -36,7 +34,7 @@ namespace Il2CppInspector.Reflection case Il2CppTypeEnum.IL2CPP_TYPE_CLASS: case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE: // Classes defined in the metadata - return GetTypeFromIndex((int) pType.datapoint); + return TypesByIndex[(int) pType.datapoint]; case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: @@ -57,7 +55,8 @@ namespace Il2CppInspector.Reflection if ((int)t >= Il2CppConstants.FullNameTypeString.Count) return null; - return Assemblies.SelectMany(x => x.DefinedTypes).First(x => x.FullName == Il2CppConstants.FullNameTypeString[(int)t]); + var fqn = Il2CppConstants.FullNameTypeString[(int) t]; + return TypesByIndex.First(x => x.FullName == fqn); } } } \ No newline at end of file diff --git a/Il2CppInspector/Metadata.cs b/Il2CppInspector/Metadata.cs index cf610ef..b9f020b 100644 --- a/Il2CppInspector/Metadata.cs +++ b/Il2CppInspector/Metadata.cs @@ -28,6 +28,7 @@ namespace Il2CppInspector public Il2CppEventDefinition[] Events { get; } public int[] InterfaceUsageIndices { get; } + public int[] NestedTypeIndices { get; } public Dictionary Strings { get; } = new Dictionary(); @@ -96,6 +97,7 @@ namespace Il2CppInspector Properties = ReadArray(Header.propertiesOffset, Header.propertiesCount / Sizeof(typeof(Il2CppPropertyDefinition))); Events = ReadArray(Header.eventsOffset, Header.eventsCount / Sizeof(typeof(Il2CppEventDefinition))); InterfaceUsageIndices = ReadArray(Header.interfacesOffset, Header.interfacesCount / sizeof(int)); + NestedTypeIndices = ReadArray(Header.nestedTypesOffset, Header.nestedTypesCount / sizeof(int)); // TODO: ParameterDefaultValue, GenericParameters, ParameterConstraints, GenericContainers, MetadataUsage, CustomAttributes // Get all string literals diff --git a/Il2CppInspector/Reflection/Assembly.cs b/Il2CppInspector/Reflection/Assembly.cs index b2b9156..3853947 100644 --- a/Il2CppInspector/Reflection/Assembly.cs +++ b/Il2CppInspector/Reflection/Assembly.cs @@ -47,7 +47,7 @@ namespace Il2CppInspector.Reflection { // Generate types in DefinedTypes from typeStart to typeStart+typeCount-1 for (var t = Definition.typeStart; t < Definition.typeStart + Definition.typeCount; t++) { - var type = new TypeInfo(Model.Package, t, this); + var type = new TypeInfo(t, this); // Don't add empty module definitions if (type.Name != "") diff --git a/Il2CppInspector/Reflection/MemberInfo.cs b/Il2CppInspector/Reflection/MemberInfo.cs index 3855209..464b8db 100644 --- a/Il2CppInspector/Reflection/MemberInfo.cs +++ b/Il2CppInspector/Reflection/MemberInfo.cs @@ -18,7 +18,8 @@ namespace Il2CppInspector.Reflection { public IEnumerable CustomAttributes => throw new NotImplementedException(); // Type that this type is declared in for nested types - public TypeInfo DeclaringType { get; } + protected int declaringTypeDefinitionIndex { private get; set; } = -1; + public TypeInfo DeclaringType => declaringTypeDefinitionIndex != -1? Assembly.Model.TypesByIndex[declaringTypeDefinitionIndex] : null; // What sort of member this is, eg. method, field etc. public abstract MemberTypes MemberType { get; } @@ -29,16 +30,13 @@ namespace Il2CppInspector.Reflection { // 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; - } + protected MemberInfo(Assembly asm) => Assembly = asm; // For lower level members, eg. fields, properties etc. and nested types - protected MemberInfo(TypeInfo declaringType) { + protected MemberInfo(TypeInfo declaringType = null) { if (declaringType != null) { Assembly = declaringType.Assembly; - DeclaringType = declaringType; + declaringTypeDefinitionIndex = declaringType.Index; } } diff --git a/Il2CppInspector/Reflection/ParameterInfo.cs b/Il2CppInspector/Reflection/ParameterInfo.cs index a411802..bc36294 100644 --- a/Il2CppInspector/Reflection/ParameterInfo.cs +++ b/Il2CppInspector/Reflection/ParameterInfo.cs @@ -38,7 +38,7 @@ namespace Il2CppInspector.Reflection public TypeInfo ParameterType => Member.Assembly.Model.GetType(paramType, MemberTypes.TypeInfo); // Zero-indexed position of the parameter in parameter list - public int Position { get; private set; } + public int Position { get; } // Create a parameter. Specify paramIndex == -1 for a return type parameter public ParameterInfo(Il2CppInspector pkg, int paramIndex, MethodBase declaringMethod) { diff --git a/Il2CppInspector/Reflection/TypeInfo.cs b/Il2CppInspector/Reflection/TypeInfo.cs index 3d2c3f8..812e674 100644 --- a/Il2CppInspector/Reflection/TypeInfo.cs +++ b/Il2CppInspector/Reflection/TypeInfo.cs @@ -47,7 +47,10 @@ namespace Il2CppInspector.Reflection { public List DeclaredFields { get; } = new List(); public List DeclaredMembers => throw new NotImplementedException(); public List DeclaredMethods { get; } = new List(); - public List DeclaredNestedTypes => throw new NotImplementedException(); + + private int[] declaredNestedTypes; + public IEnumerable DeclaredNestedTypes => declaredNestedTypes.Select(x => Assembly.Model.TypesByIndex[x]); + public List DeclaredProperties { get; } = new List(); // Method that the type is declared in if this is a type parameter of a generic method @@ -68,6 +71,7 @@ namespace Il2CppInspector.Reflection { public string FullName => (IsPointer? "void *" : "") + Namespace + (Namespace.Length > 0? "." : "") + + (DeclaringType != null? DeclaringType.Name + "." : "") + base.Name + (GenericTypeParameters != null ? "<" + string.Join(", ", GenericTypeParameters.Select(x => x.Name)) + ">" : "") + (IsArray? "[]" : ""); @@ -91,7 +95,7 @@ namespace Il2CppInspector.Reflection { public bool IsGenericTypeDefinition => throw new NotImplementedException(); public bool IsImport => (Attributes & TypeAttributes.Import) == TypeAttributes.Import; public bool IsInterface => (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface; - public bool IsNested { get; } // TODO: Partially implemented + public bool IsNested => DeclaringType != null; public bool IsNestedAssembly => (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedAssembly; public bool IsNestedFamANDAssem => (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamANDAssem; public bool IsNestedFamily => (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily; @@ -108,7 +112,8 @@ namespace Il2CppInspector.Reflection { public bool IsSpecialName => (Attributes & TypeAttributes.SpecialName) == TypeAttributes.SpecialName; public bool IsValueType => BaseType?.FullName == "System.ValueType"; - public override MemberTypes MemberType { get; } + // May get overridden by Il2CppType-based constructor below + public override MemberTypes MemberType { get; } = MemberTypes.TypeInfo; public override string Name { get => (IsPointer ? "void *" : "") @@ -118,7 +123,11 @@ namespace Il2CppInspector.Reflection { protected set => base.Name = value; } - public string Namespace { get; } + private string @namespace; + public string Namespace { + get => !string.IsNullOrEmpty(@namespace) ? @namespace : DeclaringType?.Namespace ?? ""; + set => @namespace = value; + } // Number of dimensions of an array private readonly int arrayRank; @@ -135,8 +144,11 @@ namespace Il2CppInspector.Reflection { // TODO: Generic stuff // Initialize from specified type index in metadata - public TypeInfo(Il2CppInspector pkg, int typeIndex, Assembly owner) : - base(owner) { + + // Top-level types + public TypeInfo(int typeIndex, Assembly owner) : base(owner) { + var pkg = Assembly.Model.Package; + Definition = pkg.TypeDefinitions[typeIndex]; Index = typeIndex; Namespace = pkg.Strings[Definition.namespaceIndex]; @@ -145,6 +157,14 @@ namespace Il2CppInspector.Reflection { if (Definition.parentIndex >= 0) baseType = pkg.TypeUsages[Definition.parentIndex]; + // Nested type? + if (Definition.declaringTypeIndex >= 0) { + declaringTypeDefinitionIndex = (int) pkg.TypeUsages[Definition.declaringTypeIndex].datapoint; + } + + // Add to global type definition list + Assembly.Model.TypesByIndex[Index] = this; + if ((Definition.flags & Il2CppConstants.TYPE_ATTRIBUTE_SERIALIZABLE) != 0) Attributes |= TypeAttributes.Serializable; if ((Definition.flags & Il2CppConstants.TYPE_ATTRIBUTE_VISIBILITY_MASK) == Il2CppConstants.TYPE_ATTRIBUTE_PUBLIC) @@ -187,6 +207,11 @@ namespace Il2CppInspector.Reflection { for (var i = 0; i < Definition.interfaces_count; i++) implementedInterfaces[i] = pkg.TypeUsages[pkg.InterfaceUsageIndices[Definition.interfacesStart + i]]; + // Add all nested types + declaredNestedTypes = new int[Definition.nested_type_count]; + for (var n = 0; n < Definition.nested_type_count; n++) + declaredNestedTypes[n] = pkg.NestedTypeIndices[Definition.nestedTypesStart + n]; + // Add all fields for (var f = Definition.fieldStart; f < Definition.fieldStart + Definition.field_count; f++) DeclaredFields.Add(new FieldInfo(pkg, f, this)); @@ -207,20 +232,21 @@ namespace Il2CppInspector.Reflection { // Add all events for (var e = Definition.eventStart; e < Definition.eventStart + Definition.event_count; e++) DeclaredEvents.Add(new EventInfo(pkg, e, this)); - MemberType = MemberTypes.TypeInfo; } // Initialize type from binary usage - public TypeInfo(Il2CppModel model, Il2CppType pType, MemberTypes memberType) : base(null) { + public TypeInfo(Il2CppModel model, Il2CppType pType, MemberTypes memberType) { var image = model.Package.BinaryImage; - IsNested = true; + // TODO: IsNested = true; MemberType = memberType; + // TODO: Set Assembly and DeclaringType + // 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); + var genericTypeDef = model.TypesByIndex[generic.typeDefinitionIndex]; Namespace = genericTypeDef.Namespace; Name = genericTypeDef.Name;