diff --git a/Il2CppInspector/Il2CppModel.cs b/Il2CppInspector/Il2CppModel.cs index 35c3419..25aadc1 100644 --- a/Il2CppInspector/Il2CppModel.cs +++ b/Il2CppInspector/Il2CppModel.cs @@ -31,8 +31,6 @@ namespace Il2CppInspector.Reflection // List of all methods ordered by their MethodDefinitionIndex public MethodBase[] MethodsByDefinitionIndex { get; } - // List of all types - public Il2CppModel(Il2CppInspector package) { Package = package; TypesByDefinitionIndex = new TypeInfo[package.TypeDefinitions.Length]; @@ -108,5 +106,19 @@ namespace Il2CppInspector.Reflection TypesByVirtualAddress.Add(ptr, newUsage); return newUsage; } + + // Attribute management + private int getCustomAttributeIndex(Assembly asm, uint token, int customAttributeIndex) { + var image = asm.Definition; + + throw new NotImplementedException(); + } + + public int GetCustomAttributeIndex(Assembly asm) => getCustomAttributeIndex(asm, asm.Definition.token, -1); + public int GetCustomAttributeIndex(EventInfo evt) => getCustomAttributeIndex(evt.Assembly, evt.Definition.token, evt.Definition.customAttributeIndex); + public int GetCustomAttributeIndex(FieldInfo field) => getCustomAttributeIndex(field.Assembly, field.Definition.token, field.Definition.customAttributeIndex); + public int GetCustomAttributeIndex(MethodBase method) => getCustomAttributeIndex(method.Assembly, method.Definition.token, method.Definition.customAttributeIndex); + public int GetCustomAttributeIndex(ParameterInfo param) => getCustomAttributeIndex(param.Member.Assembly, param.Definition.token, param.Definition.customAttributeIndex); + public int GetCustomAttributeIndex(PropertyInfo prop) => getCustomAttributeIndex(prop.Assembly, prop.Definition.token, prop.Definition.customAttributeIndex); } } \ No newline at end of file diff --git a/Il2CppInspector/Metadata.cs b/Il2CppInspector/Metadata.cs index 934799a..d7af7af 100644 --- a/Il2CppInspector/Metadata.cs +++ b/Il2CppInspector/Metadata.cs @@ -28,9 +28,11 @@ namespace Il2CppInspector public Il2CppEventDefinition[] Events { get; } public Il2CppGenericContainer[] GenericContainers { get; } public Il2CppGenericParameter[] GenericParameters { get; } + public Il2CppCustomAttributeTypeRange[] AttributeTypeRanges { get; } public int[] InterfaceUsageIndices { get; } public int[] NestedTypeIndices { get; } + public int[] AttributeRangeIndices { get; } public Dictionary Strings { get; } = new Dictionary(); @@ -102,7 +104,12 @@ namespace Il2CppInspector NestedTypeIndices = ReadArray(Header.nestedTypesOffset, Header.nestedTypesCount / sizeof(int)); GenericContainers = ReadArray(Header.genericContainersOffset, Header.genericContainersCount / Sizeof(typeof(Il2CppGenericContainer))); GenericParameters = ReadArray(Header.genericParametersOffset, Header.genericParametersCount / Sizeof(typeof(Il2CppGenericParameter))); - // TODO: ParameterDefaultValue, ParameterConstraints, MetadataUsage, CustomAttributes + + if (Version >= 21) { + AttributeRangeIndices = ReadArray(Header.attributeTypesOffset, Header.attributeTypesCount / sizeof(int)); + AttributeTypeRanges = ReadArray(Header.attributesInfoOffset, Header.attributesInfoCount / Sizeof(typeof(Il2CppCustomAttributeTypeRange))); + } + // TODO: ParameterDefaultValue, ParameterConstraints, MetadataUsage // Get all string literals Position = Header.stringOffset; diff --git a/Il2CppInspector/MetadataClasses.cs b/Il2CppInspector/MetadataClasses.cs index f8f58f0..08482fd 100644 --- a/Il2CppInspector/MetadataClasses.cs +++ b/Il2CppInspector/MetadataClasses.cs @@ -322,4 +322,13 @@ namespace Il2CppInspector public ushort num; public ushort flags; } + + public class Il2CppCustomAttributeTypeRange + { + [Version(Min = 24.1)] + public uint token; + + public int start; + public int count; + } } diff --git a/Il2CppInspector/Reflection/ReflectionClasses.cs b/Il2CppInspector/Reflection/CustomAttributeData.cs similarity index 57% rename from Il2CppInspector/Reflection/ReflectionClasses.cs rename to Il2CppInspector/Reflection/CustomAttributeData.cs index e28378c..bc10b04 100644 --- a/Il2CppInspector/Reflection/ReflectionClasses.cs +++ b/Il2CppInspector/Reflection/CustomAttributeData.cs @@ -6,8 +6,10 @@ namespace Il2CppInspector.Reflection { + // See: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.customattributedata?view=netframework-4.8 + public class CustomAttributeData { - // TODO + // TODO: CustomAttributeData } } diff --git a/Il2CppTests/Il2CppTests.csproj b/Il2CppTests/Il2CppTests.csproj index 64a9d14..2ca7903 100644 --- a/Il2CppTests/Il2CppTests.csproj +++ b/Il2CppTests/Il2CppTests.csproj @@ -27,6 +27,7 @@ + @@ -35,6 +36,7 @@ + diff --git a/Il2CppTests/TestSources/CustomAttributeData.cs b/Il2CppTests/TestSources/CustomAttributeData.cs new file mode 100644 index 0000000..04e45d3 --- /dev/null +++ b/Il2CppTests/TestSources/CustomAttributeData.cs @@ -0,0 +1,80 @@ +/* + Copyright 2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com + + All rights reserved. +*/ + +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Il2CppTests.TestSources; + +// This code is adapted from https://docs.microsoft.com/en-us/dotnet/api/system.reflection.customattributedata?view=netframework-4.8 + +// The example attribute is applied to the assembly. +[assembly: Example(ExampleKind.ThirdKind, Note = "This is a note on the assembly.")] + +namespace Il2CppTests.TestSources +{ + // An enumeration used by the ExampleAttribute class. + public enum ExampleKind + { + FirstKind, + SecondKind, + ThirdKind, + FourthKind + }; + + // An example attribute. The attribute can be applied to all + // targets, from assemblies to parameters. + [AttributeUsage(AttributeTargets.All)] + public class ExampleAttribute : Attribute + { + // Data for properties. + private ExampleKind kindValue; + private string noteValue; + private string[] arrayStrings; + private int[] arrayNumbers; + + // Constructors. The parameterless constructor (.ctor) calls + // the constructor that specifies ExampleKind and an array of + // strings, and supplies the default values. + public ExampleAttribute(ExampleKind initKind, string[] initStrings) { + kindValue = initKind; + arrayStrings = initStrings; + } + public ExampleAttribute(ExampleKind initKind) : this(initKind, null) { } + public ExampleAttribute() : this(ExampleKind.FirstKind, null) { } + + // Properties. The Note and Numbers properties must be read/write, so they + // can be used as named parameters. + public ExampleKind Kind => kindValue; + public string[] Strings => arrayStrings; + + public string Note { + get { return noteValue; } + set { noteValue = value; } + } + public int[] Numbers { + get { return arrayNumbers; } + set { arrayNumbers = value; } + } + } + + // The example attribute is applied to the test class. + [Example(ExampleKind.SecondKind, + new[] { "String array argument, line 1", + "String array argument, line 2", + "String array argument, line 3" }, + Note = "This is a note on the class.", + Numbers = new[] { 53, 57, 59 })] + public class Test + { + // The example attribute is applied to a method, using the + // parameterless constructor and supplying a named argument. + // The attribute is also applied to the method parameter. + [Example(Note = "This is a note on a method.")] + public void TestMethod([Example] object arg) { } + } +}