Model: Re-implement the handling of Il2CppType
This commit is contained in:
@@ -16,25 +16,32 @@ namespace Il2CppInspector.Reflection
|
||||
public List<Assembly> Assemblies { get; } = new List<Assembly>();
|
||||
|
||||
// List of all types ordered by their TypeDefinitionIndex
|
||||
public TypeInfo[] TypesByIndex { get; }
|
||||
public TypeInfo[] TypesByDefinitionIndex { get; }
|
||||
|
||||
// List of all type usages ordered by their type usage index
|
||||
public TypeInfo[] TypesByUsageIndex { get; }
|
||||
|
||||
// List of type usages that are initialized via pointers in the image
|
||||
public Dictionary<ulong, TypeInfo> TypesByVirtualAddress { get; } = new Dictionary<ulong, TypeInfo>();
|
||||
|
||||
// List of all types
|
||||
|
||||
public Il2CppModel(Il2CppInspector package) {
|
||||
Package = package;
|
||||
TypesByIndex = new TypeInfo[package.TypeDefinitions.Length];
|
||||
TypesByDefinitionIndex = new TypeInfo[package.TypeDefinitions.Length];
|
||||
TypesByUsageIndex = new TypeInfo[package.TypeUsages.Count];
|
||||
|
||||
// Create Assembly objects from Il2Cpp package
|
||||
for (var image = 0; image < package.Images.Length; image++)
|
||||
Assemblies.Add(new Assembly(this, image));
|
||||
}
|
||||
|
||||
// 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) {
|
||||
private TypeInfo getNewTypeUsage(Il2CppType usage, MemberTypes memberType) {
|
||||
switch (usage.type) {
|
||||
case Il2CppTypeEnum.IL2CPP_TYPE_CLASS:
|
||||
case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE:
|
||||
// Classes defined in the metadata
|
||||
return TypesByIndex[(int) pType.datapoint];
|
||||
return TypesByDefinitionIndex[usage.datapoint];
|
||||
|
||||
case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
|
||||
case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY:
|
||||
@@ -43,20 +50,49 @@ namespace Il2CppInspector.Reflection
|
||||
case Il2CppTypeEnum.IL2CPP_TYPE_VAR:
|
||||
case Il2CppTypeEnum.IL2CPP_TYPE_MVAR:
|
||||
// Everything that requires special handling
|
||||
return new TypeInfo(this, pType, memberType);
|
||||
return new TypeInfo(this, usage, memberType);
|
||||
|
||||
default:
|
||||
return GetTypeFromEnum(pType.type);
|
||||
// Primitive types
|
||||
return GetTypeFromTypeEnum(usage.type);
|
||||
}
|
||||
}
|
||||
|
||||
// Get or generate a type from its IL2CPP binary type usage reference
|
||||
// (field, return type, generic type parameter etc.)
|
||||
public TypeInfo GetTypeFromUsage(int typeUsageIndex, MemberTypes memberType = MemberTypes.All) {
|
||||
|
||||
// Already generated type previously?
|
||||
if (TypesByUsageIndex[typeUsageIndex] != null)
|
||||
return TypesByUsageIndex[typeUsageIndex];
|
||||
|
||||
var usage = Package.TypeUsages[typeUsageIndex];
|
||||
var newUsage = getNewTypeUsage(usage, memberType);
|
||||
|
||||
TypesByUsageIndex[typeUsageIndex] = newUsage;
|
||||
return newUsage;
|
||||
}
|
||||
|
||||
// Basic primitive types
|
||||
public TypeInfo GetTypeFromEnum(Il2CppTypeEnum t) {
|
||||
public TypeInfo GetTypeFromTypeEnum(Il2CppTypeEnum t) {
|
||||
if ((int)t >= Il2CppConstants.FullNameTypeString.Count)
|
||||
return null;
|
||||
|
||||
var fqn = Il2CppConstants.FullNameTypeString[(int) t];
|
||||
return TypesByIndex.First(x => x.FullName == fqn);
|
||||
return TypesByDefinitionIndex.First(x => x.FullName == fqn);
|
||||
}
|
||||
|
||||
// Type from a virtual address pointer
|
||||
// These are always nested types frorm usages within another type
|
||||
public TypeInfo GetTypeFromVirtualAddress(ulong ptr) {
|
||||
if (TypesByVirtualAddress.ContainsKey(ptr))
|
||||
return TypesByVirtualAddress[ptr];
|
||||
|
||||
var type = Package.BinaryImage.ReadMappedObject<Il2CppType>(ptr);
|
||||
var newUsage = getNewTypeUsage(type, MemberTypes.NestedType);
|
||||
|
||||
TypesByVirtualAddress.Add(ptr, newUsage);
|
||||
return newUsage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ namespace Il2CppInspector.Reflection
|
||||
|
||||
public static readonly string TypeConstructorName = ".cctor";
|
||||
|
||||
// TODO
|
||||
public override MemberTypes MemberType => MemberTypes.Constructor;
|
||||
|
||||
public ConstructorInfo(Il2CppInspector pkg, int methodIndex, TypeInfo declaringType) : base(pkg, methodIndex, declaringType) { }
|
||||
|
||||
@@ -24,8 +24,8 @@ namespace Il2CppInspector.Reflection
|
||||
public MethodInfo RaiseMethod { get; }
|
||||
|
||||
// Event handler delegate type
|
||||
private Il2CppType eventType;
|
||||
public TypeInfo EventHandlerType => Assembly.Model.GetType(eventType, MemberTypes.TypeInfo);
|
||||
private int eventTypeUsage;
|
||||
public TypeInfo EventHandlerType => Assembly.Model.GetTypeFromUsage(eventTypeUsage, MemberTypes.TypeInfo);
|
||||
|
||||
// True if the event has a special name
|
||||
public bool IsSpecialName => (Attributes & EventAttributes.SpecialName) == EventAttributes.SpecialName;
|
||||
@@ -38,7 +38,9 @@ namespace Il2CppInspector.Reflection
|
||||
Index = eventIndex;
|
||||
Name = pkg.Strings[Definition.nameIndex];
|
||||
|
||||
eventType = pkg.TypeUsages[Definition.typeIndex];
|
||||
eventTypeUsage = Definition.typeIndex;
|
||||
var eventType = pkg.TypeUsages[eventTypeUsage];
|
||||
|
||||
if ((eventType.attrs & Il2CppConstants.FIELD_ATTRIBUTE_SPECIAL_NAME) == Il2CppConstants.FIELD_ATTRIBUTE_SPECIAL_NAME)
|
||||
Attributes |= EventAttributes.SpecialName;
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@ namespace Il2CppInspector.Reflection {
|
||||
public FieldAttributes Attributes { get; }
|
||||
|
||||
// Type of field
|
||||
private readonly Il2CppType fieldType;
|
||||
public TypeInfo FieldType => Assembly.Model.GetType(fieldType, MemberTypes.Field);
|
||||
private readonly int fieldTypeUsage;
|
||||
public TypeInfo FieldType => Assembly.Model.GetTypeFromUsage(fieldTypeUsage, 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
|
||||
@@ -88,7 +88,9 @@ namespace Il2CppInspector.Reflection {
|
||||
Offset = pkg.FieldOffsets[fieldIndex];
|
||||
Name = pkg.Strings[Definition.nameIndex];
|
||||
|
||||
fieldType = pkg.TypeUsages[Definition.typeIndex];
|
||||
fieldTypeUsage = Definition.typeIndex;
|
||||
var fieldType = pkg.TypeUsages[fieldTypeUsage];
|
||||
|
||||
if ((fieldType.attrs & Il2CppConstants.FIELD_ATTRIBUTE_FIELD_ACCESS_MASK) == Il2CppConstants.FIELD_ATTRIBUTE_PRIVATE)
|
||||
Attributes |= FieldAttributes.Private;
|
||||
if ((fieldType.attrs & Il2CppConstants.FIELD_ATTRIBUTE_FIELD_ACCESS_MASK) == Il2CppConstants.FIELD_ATTRIBUTE_PUBLIC)
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Il2CppInspector.Reflection {
|
||||
|
||||
// Type that this type is declared in for nested types
|
||||
protected int declaringTypeDefinitionIndex { private get; set; } = -1;
|
||||
public TypeInfo DeclaringType => declaringTypeDefinitionIndex != -1? Assembly.Model.TypesByIndex[declaringTypeDefinitionIndex] : null;
|
||||
public TypeInfo DeclaringType => declaringTypeDefinitionIndex != -1? Assembly.Model.TypesByDefinitionIndex[declaringTypeDefinitionIndex] : null;
|
||||
|
||||
// What sort of member this is, eg. method, field etc.
|
||||
public abstract MemberTypes MemberType { get; }
|
||||
|
||||
@@ -17,14 +17,14 @@ namespace Il2CppInspector.Reflection
|
||||
public ParameterInfo ReturnParameter { get; }
|
||||
|
||||
// Return type of the method
|
||||
private readonly Il2CppType returnType;
|
||||
public TypeInfo ReturnType => Assembly.Model.GetType(returnType, MemberTypes.TypeInfo);
|
||||
private readonly int returnTypeUsage;
|
||||
public TypeInfo ReturnType => Assembly.Model.GetTypeFromUsage(returnTypeUsage, MemberTypes.TypeInfo);
|
||||
|
||||
// TODO: ReturnTypeCustomAttributes
|
||||
|
||||
public MethodInfo(Il2CppInspector pkg, int methodIndex, TypeInfo declaringType) : base(pkg, methodIndex, declaringType) {
|
||||
// Add return parameter
|
||||
returnType = pkg.TypeUsages[Definition.returnType];
|
||||
returnTypeUsage = Definition.returnType;
|
||||
ReturnParameter = new ParameterInfo(pkg, -1, this);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@ namespace Il2CppInspector.Reflection
|
||||
public string Name { get; }
|
||||
|
||||
// Type of this parameter
|
||||
private readonly Il2CppType paramType;
|
||||
public TypeInfo ParameterType => Member.Assembly.Model.GetType(paramType, MemberTypes.TypeInfo);
|
||||
private readonly int paramTypeUsage;
|
||||
public TypeInfo ParameterType => Member.Assembly.Model.GetTypeFromUsage(paramTypeUsage, MemberTypes.TypeInfo);
|
||||
|
||||
// Zero-indexed position of the parameter in parameter list
|
||||
public int Position { get; }
|
||||
@@ -46,7 +46,7 @@ namespace Il2CppInspector.Reflection
|
||||
|
||||
if (paramIndex == -1) {
|
||||
Position = -1;
|
||||
paramType = pkg.TypeUsages[declaringMethod.Definition.returnType];
|
||||
paramTypeUsage = declaringMethod.Definition.returnType;
|
||||
Attributes |= ParameterAttributes.Retval;
|
||||
return;
|
||||
}
|
||||
@@ -54,7 +54,8 @@ namespace Il2CppInspector.Reflection
|
||||
var param = pkg.Params[paramIndex];
|
||||
Name = pkg.Strings[param.nameIndex];
|
||||
Position = paramIndex - declaringMethod.Definition.parameterStart;
|
||||
paramType = pkg.TypeUsages[param.typeIndex];
|
||||
paramTypeUsage = param.typeIndex;
|
||||
var paramType = pkg.TypeUsages[paramTypeUsage];
|
||||
|
||||
if ((paramType.attrs & Il2CppConstants.PARAM_ATTRIBUTE_OPTIONAL) != 0)
|
||||
Attributes |= ParameterAttributes.Optional;
|
||||
|
||||
@@ -21,8 +21,8 @@ namespace Il2CppInspector.Reflection {
|
||||
public TypeAttributes Attributes { get; }
|
||||
|
||||
// Type that this type inherits from
|
||||
private Il2CppType baseType;
|
||||
public TypeInfo BaseType => baseType != null? Assembly.Model.GetType(baseType, MemberTypes.TypeInfo) : null;
|
||||
private int baseTypeUsage;
|
||||
public TypeInfo BaseType => Assembly.Model.GetTypeFromUsage(baseTypeUsage, MemberTypes.TypeInfo);
|
||||
|
||||
// True if the type contains unresolved generic type parameters
|
||||
public bool ContainsGenericParameters { get; }
|
||||
@@ -49,7 +49,7 @@ namespace Il2CppInspector.Reflection {
|
||||
public List<MethodInfo> DeclaredMethods { get; } = new List<MethodInfo>();
|
||||
|
||||
private int[] declaredNestedTypes;
|
||||
public IEnumerable<TypeInfo> DeclaredNestedTypes => declaredNestedTypes.Select(x => Assembly.Model.TypesByIndex[x]);
|
||||
public IEnumerable<TypeInfo> DeclaredNestedTypes => declaredNestedTypes.Select(x => Assembly.Model.TypesByDefinitionIndex[x]);
|
||||
|
||||
public List<PropertyInfo> DeclaredProperties { get; } = new List<PropertyInfo>();
|
||||
|
||||
@@ -57,12 +57,12 @@ namespace Il2CppInspector.Reflection {
|
||||
public MethodBase DeclaringMethod => throw new NotImplementedException();
|
||||
|
||||
// Gets the type of the object encompassed or referred to by the current array, pointer or reference type
|
||||
private Il2CppType enumElementType;
|
||||
private int enumElementTypeUsage;
|
||||
private TypeInfo elementType;
|
||||
public TypeInfo ElementType {
|
||||
get {
|
||||
if (IsEnum && elementType == null)
|
||||
elementType = Assembly.Model.GetType(enumElementType, MemberTypes.TypeInfo);
|
||||
elementType = Assembly.Model.GetTypeFromUsage(enumElementTypeUsage, MemberTypes.TypeInfo);
|
||||
return elementType;
|
||||
}
|
||||
}
|
||||
@@ -82,8 +82,8 @@ namespace Il2CppInspector.Reflection {
|
||||
|
||||
public bool HasElementType => ElementType != null;
|
||||
|
||||
private Il2CppType[] implementedInterfaces;
|
||||
public IEnumerable<TypeInfo> ImplementedInterfaces => implementedInterfaces.Select(x => Assembly.Model.GetType(x, MemberTypes.TypeInfo));
|
||||
private int[] implementedInterfaceUsages;
|
||||
public IEnumerable<TypeInfo> ImplementedInterfaces => implementedInterfaceUsages.Select(x => Assembly.Model.GetTypeFromUsage(x, MemberTypes.TypeInfo));
|
||||
|
||||
public bool IsAbstract => (Attributes & TypeAttributes.Abstract) == TypeAttributes.Abstract;
|
||||
public bool IsArray { get; }
|
||||
@@ -155,15 +155,16 @@ namespace Il2CppInspector.Reflection {
|
||||
Name = pkg.Strings[Definition.nameIndex];
|
||||
|
||||
if (Definition.parentIndex >= 0)
|
||||
baseType = pkg.TypeUsages[Definition.parentIndex];
|
||||
baseTypeUsage = Definition.parentIndex;
|
||||
|
||||
// Nested type?
|
||||
if (Definition.declaringTypeIndex >= 0) {
|
||||
declaringTypeDefinitionIndex = (int) pkg.TypeUsages[Definition.declaringTypeIndex].datapoint;
|
||||
MemberType |= MemberTypes.NestedType;
|
||||
}
|
||||
|
||||
// Add to global type definition list
|
||||
Assembly.Model.TypesByIndex[Index] = this;
|
||||
Assembly.Model.TypesByDefinitionIndex[Index] = this;
|
||||
|
||||
if ((Definition.flags & Il2CppConstants.TYPE_ATTRIBUTE_SERIALIZABLE) != 0)
|
||||
Attributes |= TypeAttributes.Serializable;
|
||||
@@ -196,16 +197,16 @@ namespace Il2CppInspector.Reflection {
|
||||
if ((Definition.flags & Il2CppConstants.TYPE_ATTRIBUTE_INTERFACE) != 0)
|
||||
Attributes |= TypeAttributes.Interface;
|
||||
|
||||
// Enumerations - bit 1 of bitfield indicates this (also the baseType will be System.Enum)
|
||||
// Enumerations - bit 1 of bitfield indicates this (also the baseTypeUsage will be System.Enum)
|
||||
if (((Definition.bitfield >> 1) & 1) == 1) {
|
||||
IsEnum = true;
|
||||
enumElementType = pkg.TypeUsages[Definition.elementTypeIndex];
|
||||
enumElementTypeUsage = Definition.elementTypeIndex;
|
||||
}
|
||||
|
||||
// Add all implemented interfaces
|
||||
implementedInterfaces = new Il2CppType[Definition.interfaces_count];
|
||||
implementedInterfaceUsages = new int[Definition.interfaces_count];
|
||||
for (var i = 0; i < Definition.interfaces_count; i++)
|
||||
implementedInterfaces[i] = pkg.TypeUsages[pkg.InterfaceUsageIndices[Definition.interfacesStart + i]];
|
||||
implementedInterfaceUsages[i] = pkg.InterfaceUsageIndices[Definition.interfacesStart + i];
|
||||
|
||||
// Add all nested types
|
||||
declaredNestedTypes = new int[Definition.nested_type_count];
|
||||
@@ -246,7 +247,7 @@ namespace Il2CppInspector.Reflection {
|
||||
// Generic type unresolved and concrete instance types
|
||||
if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) {
|
||||
var generic = image.ReadMappedObject<Il2CppGenericClass>(pType.datapoint);
|
||||
var genericTypeDef = model.TypesByIndex[generic.typeDefinitionIndex];
|
||||
var genericTypeDef = model.TypesByDefinitionIndex[generic.typeDefinitionIndex];
|
||||
|
||||
Namespace = genericTypeDef.Namespace;
|
||||
Name = genericTypeDef.Name;
|
||||
@@ -261,10 +262,10 @@ namespace Il2CppInspector.Reflection {
|
||||
|
||||
GenericTypeParameters = new List<TypeInfo>();
|
||||
foreach (var pArg in genericTypeParameters) {
|
||||
var argType = image.ReadMappedObject<Il2CppType>((ulong) pArg);
|
||||
var argType = model.GetTypeFromVirtualAddress((ulong) 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
|
||||
GenericTypeParameters.Add(argType); // TODO: Fix MemberType here
|
||||
}
|
||||
Attributes |= TypeAttributes.Class;
|
||||
}
|
||||
@@ -272,8 +273,7 @@ namespace Il2CppInspector.Reflection {
|
||||
// Array with known dimensions and bounds
|
||||
if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_ARRAY) {
|
||||
var descriptor = image.ReadMappedObject<Il2CppArrayType>(pType.datapoint);
|
||||
var elementType = image.ReadMappedObject<Il2CppType>(descriptor.etype);
|
||||
this.elementType = model.GetType(elementType);
|
||||
elementType = model.GetTypeFromVirtualAddress(descriptor.etype);
|
||||
Namespace = ElementType.Namespace;
|
||||
Name = ElementType.Name;
|
||||
|
||||
@@ -283,8 +283,7 @@ namespace Il2CppInspector.Reflection {
|
||||
|
||||
// Dynamically allocated array
|
||||
if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY) {
|
||||
var elementType = image.ReadMappedObject<Il2CppType>(pType.datapoint);
|
||||
this.elementType = model.GetType(elementType);
|
||||
elementType = model.GetTypeFromVirtualAddress(pType.datapoint);
|
||||
Namespace = ElementType.Namespace;
|
||||
Name = ElementType.Name;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user