From f0adf416e4a35a8b23f0239cba5c8f9883b72e5b Mon Sep 17 00:00:00 2001 From: Katy Coe Date: Fri, 10 Nov 2017 16:30:11 +0100 Subject: [PATCH] Initial implementation and output of properties --- Il2CppDumper/Il2CppDumper.cs | 46 ++++++++----------- Il2CppInspector/Metadata.cs | 5 +- Il2CppInspector/MetadataClasses.cs | 10 ++++ Il2CppInspector/Reflection/MethodBase.cs | 33 +++++++++++++ Il2CppInspector/Reflection/PropertyInfo.cs | 40 ++++++++++++++++ .../Reflection/ReflectionClasses.cs | 8 ---- Il2CppInspector/Reflection/TypeInfo.cs | 6 ++- 7 files changed, 110 insertions(+), 38 deletions(-) create mode 100644 Il2CppInspector/Reflection/PropertyInfo.cs diff --git a/Il2CppDumper/Il2CppDumper.cs b/Il2CppDumper/Il2CppDumper.cs index ec6b3b5..c07f8c5 100644 --- a/Il2CppDumper/Il2CppDumper.cs +++ b/Il2CppDumper/Il2CppDumper.cs @@ -24,6 +24,8 @@ namespace Il2CppInspector } foreach (var type in model.Assemblies.SelectMany(x => x.DefinedTypes)) { + + // Type declaration writer.Write($"\n// Namespace: {type.Namespace}\n"); if (type.IsImport) @@ -65,6 +67,7 @@ namespace Il2CppInspector writer.Write($"{type.Name}{baseText} // TypeDefIndex: {type.Index}\n{{\n"); + // Fields if (type.DeclaredFields.Count > 0) writer.Write("\t// Fields\n"); @@ -102,38 +105,25 @@ namespace Il2CppInspector if (type.DeclaredFields.Count > 0) writer.Write("\n"); + // Properties + if (type.DeclaredProperties.Count > 0) + writer.Write("\t// Properties\n"); + + foreach (var prop in type.DeclaredProperties) { + string modifiers = prop.GetMethod?.GetModifierString() ?? prop.SetMethod.GetModifierString(); + writer.Write($"\t{modifiers} {prop.PropertyType.CSharpName} {prop.Name} {{ "); + writer.Write((prop.GetMethod != null ? "get; " : "") + (prop.SetMethod != null ? "set; " : "")); + writer.Write("}\n"); + } + if (type.DeclaredProperties.Count > 0) + writer.Write("\n"); + + // Methods if (type.DeclaredMethods.Count > 0) writer.Write("\t// Methods\n"); foreach (var method in type.DeclaredMethods) { - writer.Write("\t"); - if (method.IsPrivate) - writer.Write("private "); - if (method.IsPublic) - writer.Write("public "); - if (method.IsFamily) - writer.Write("protected "); - if (method.IsAssembly) - writer.Write("internal "); - if (method.IsFamilyOrAssembly) - writer.Write("protected internal "); - if (method.IsFamilyAndAssembly) - writer.Write("[family and assembly] "); - - if (method.IsAbstract) - writer.Write("abstract "); - // Methods that implement interfaces are IsVirtual && IsFinal with MethodAttributes.NewSlot (don't show 'virtual sealed' for these) - if (method.IsFinal && (method.Attributes & MethodAttributes.VtableLayoutMask) == MethodAttributes.ReuseSlot) - writer.Write("sealed override "); - // All abstract, override and sealed methods are also virtual by nature - if (method.IsVirtual && !method.IsAbstract && !method.IsFinal) - writer.Write((method.Attributes & MethodAttributes.VtableLayoutMask) == MethodAttributes.NewSlot? "virtual " : "override "); - if (method.IsStatic) - writer.Write("static "); - if ((method.Attributes & MethodAttributes.PinvokeImpl) != 0) - writer.Write("extern "); - - writer.Write($"{method.ReturnType.CSharpName} {method.Name}("); + writer.Write($"\t{method.GetModifierString()} {method.ReturnType.CSharpName} {method.Name}("); bool first = true; foreach (var param in method.DeclaredParameters) { diff --git a/Il2CppInspector/Metadata.cs b/Il2CppInspector/Metadata.cs index 9024dc9..75ce515 100644 --- a/Il2CppInspector/Metadata.cs +++ b/Il2CppInspector/Metadata.cs @@ -23,6 +23,8 @@ namespace Il2CppInspector public Il2CppParameterDefinition[] Params { get; } public Il2CppFieldDefinition[] Fields { get; } public Il2CppFieldDefaultValue[] FieldDefaultValues { get; } + public Il2CppPropertyDefinition[] Properties { get; } + public int[] InterfaceUsageIndices { get; } public Dictionary Strings { get; } = new Dictionary(); @@ -52,8 +54,9 @@ namespace Il2CppInspector Params = ReadArray(Header.parametersOffset, Header.parametersCount / Sizeof(typeof(Il2CppParameterDefinition))); Fields = ReadArray(Header.fieldsOffset, Header.fieldsCount / Sizeof(typeof(Il2CppFieldDefinition))); FieldDefaultValues = ReadArray(Header.fieldDefaultValuesOffset, Header.fieldDefaultValuesCount / Sizeof(typeof(Il2CppFieldDefaultValue))); + Properties = ReadArray(Header.propertiesOffset, Header.propertiesOffset / Sizeof(typeof(Il2CppPropertyDefinition))); InterfaceUsageIndices = ReadArray(Header.interfacesOffset, Header.interfacesCount / sizeof(int)); - // TODO: Events, Properties, ParameterDefaultValue, GenericParameters, ParameterConstraints, GenericContainers, MetadataUsage, CustomAttributes + // TODO: Events, ParameterDefaultValue, GenericParameters, ParameterConstraints, GenericContainers, MetadataUsage, CustomAttributes // Get all string literals Position = Header.stringOffset; diff --git a/Il2CppInspector/MetadataClasses.cs b/Il2CppInspector/MetadataClasses.cs index a1ccef8..6b59710 100644 --- a/Il2CppInspector/MetadataClasses.cs +++ b/Il2CppInspector/MetadataClasses.cs @@ -216,4 +216,14 @@ namespace Il2CppInspector public int typeIndex; public int dataIndex; } + + public class Il2CppPropertyDefinition + { + public int nameIndex; + public int get; + public int set; + public uint attrs; + public int customAttributeIndex; + public uint token; + } } diff --git a/Il2CppInspector/Reflection/MethodBase.cs b/Il2CppInspector/Reflection/MethodBase.cs index b269d70..eda463c 100644 --- a/Il2CppInspector/Reflection/MethodBase.cs +++ b/Il2CppInspector/Reflection/MethodBase.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using System.Text; namespace Il2CppInspector.Reflection { @@ -41,5 +42,37 @@ namespace Il2CppInspector.Reflection // TODO: GetMethodBody() protected MethodBase(TypeInfo declaringType) : base(declaringType) { } + + public string GetModifierString() { + StringBuilder modifiers = new StringBuilder(); + + if (IsPrivate) + modifiers.Append("private "); + if (IsPublic) + modifiers.Append("public "); + if (IsFamily) + modifiers.Append("protected "); + if (IsAssembly) + modifiers.Append("internal "); + if (IsFamilyOrAssembly) + modifiers.Append("protected internal "); + if (IsFamilyAndAssembly) + modifiers.Append("[family and assembly] "); + + if (IsAbstract) + modifiers.Append("abstract "); + // Methods that implement interfaces are IsVirtual && IsFinal with MethodAttributes.NewSlot (don't show 'virtual sealed' for these) + if (IsFinal && (Attributes & MethodAttributes.VtableLayoutMask) == MethodAttributes.ReuseSlot) + modifiers.Append("sealed override "); + // All abstract, override and sealed methods are also virtual by nature + if (IsVirtual && !IsAbstract && !IsFinal) + modifiers.Append((Attributes & MethodAttributes.VtableLayoutMask) == MethodAttributes.NewSlot ? "virtual " : "override "); + if (IsStatic) + modifiers.Append("static "); + if ((Attributes & MethodAttributes.PinvokeImpl) != 0) + modifiers.Append("extern "); + + return modifiers.ToString().Trim(); + } } } \ No newline at end of file diff --git a/Il2CppInspector/Reflection/PropertyInfo.cs b/Il2CppInspector/Reflection/PropertyInfo.cs new file mode 100644 index 0000000..0456050 --- /dev/null +++ b/Il2CppInspector/Reflection/PropertyInfo.cs @@ -0,0 +1,40 @@ +/* + Copyright 2017 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com + + All rights reserved. +*/ + +using System.Reflection; + +namespace Il2CppInspector.Reflection { + public class PropertyInfo : MemberInfo + { + public bool CanRead => GetMethod != null; + public bool CanWrite => SetMethod != null; + + // TODO: CustomAttributes + + public MethodInfo GetMethod { get; } + public MethodInfo SetMethod { get; } + + public string Name { get; } + + public TypeInfo PropertyType => GetMethod?.ReturnType ?? SetMethod.DeclaredParameters[0].ParameterType; + + public override MemberTypes MemberType => MemberTypes.Property; + + public PropertyInfo(Il2CppInspector pkg, int propIndex, TypeInfo declaringType) : + base(declaringType) { + var prop = pkg.Metadata.Properties[propIndex]; + + Name = pkg.Strings[prop.nameIndex]; + + // NOTE: This relies on methods being added to TypeInfo.DeclaredMethods in the same order they are defined in the Il2Cpp metadata + // prop.get and prop.set are method indices from the first method of the declaring type + if (prop.get >= 0) + GetMethod = declaringType.DeclaredMethods[prop.get]; + if (prop.set >= 0) + SetMethod = declaringType.DeclaredMethods[prop.set]; + } + } +} \ No newline at end of file diff --git a/Il2CppInspector/Reflection/ReflectionClasses.cs b/Il2CppInspector/Reflection/ReflectionClasses.cs index 8970082..5011ead 100644 --- a/Il2CppInspector/Reflection/ReflectionClasses.cs +++ b/Il2CppInspector/Reflection/ReflectionClasses.cs @@ -17,14 +17,6 @@ namespace Il2CppInspector.Reflection base(declaringType) { } } - public class PropertyInfo : MemberInfo - { - // TODO - public override MemberTypes MemberType => MemberTypes.Property | MemberTypes.Method; - public PropertyInfo(Il2CppInspector pkg, int methodIndex, TypeInfo declaringType) : - base(declaringType) { } - } - public class CustomAttributeData { // TODO diff --git a/Il2CppInspector/Reflection/TypeInfo.cs b/Il2CppInspector/Reflection/TypeInfo.cs index d3e958d..5d9e3e8 100644 --- a/Il2CppInspector/Reflection/TypeInfo.cs +++ b/Il2CppInspector/Reflection/TypeInfo.cs @@ -46,7 +46,7 @@ namespace Il2CppInspector.Reflection { public List DeclaredMembers => throw new NotImplementedException(); public List DeclaredMethods { get; } = new List(); public List DeclaredNestedTypes => throw new NotImplementedException(); - public List DeclaredProperties => throw new NotImplementedException(); + public List DeclaredProperties { get; } = new List(); // Method that the type is declared in if this is a type parameter of a generic method public MethodBase DeclaringMethod => throw new NotImplementedException(); @@ -180,6 +180,10 @@ namespace Il2CppInspector.Reflection { for (var m = Definition.methodStart; m < Definition.methodStart + Definition.method_count; m++) DeclaredMethods.Add(new MethodInfo(pkg, m, this)); + // Add all properties + for (var p = Definition.propertyStart; p < Definition.propertyStart + Definition.property_count; p++) + DeclaredProperties.Add(new PropertyInfo(pkg, p, this)); + MemberType = MemberTypes.TypeInfo; }