diff --git a/Il2CppDumper/Il2CppDumper.cs b/Il2CppDumper/Il2CppDumper.cs index c07f8c5..06bbbc7 100644 --- a/Il2CppDumper/Il2CppDumper.cs +++ b/Il2CppDumper/Il2CppDumper.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Security.Cryptography.X509Certificates; using System.Text; using Il2CppInspector.Reflection; @@ -50,60 +51,70 @@ namespace Il2CppInspector else { if (type.IsAbstract && !type.IsInterface) writer.Write("abstract "); - if (type.IsSealed && !type.IsValueType) + if (type.IsSealed && !type.IsValueType && !type.IsEnum) writer.Write("sealed "); } if (type.IsInterface) writer.Write("interface "); else if (type.IsValueType) writer.Write("struct "); + else if (type.IsEnum) + writer.Write("enum "); else writer.Write("class "); var @base = type.ImplementedInterfaces.Select(x => x.CSharpName).ToList(); - if (type.BaseType != null && type.BaseType.FullName != "System.Object" && type.BaseType.FullName != "System.ValueType") + if (type.BaseType != null && type.BaseType.FullName != "System.Object" && type.BaseType.FullName != "System.ValueType" && !type.IsEnum) @base.Insert(0, type.BaseType.CSharpName); + if (type.IsEnum && type.ElementType.CSharpName != "int") // enums derive from int by default + @base.Insert(0, type.ElementType.CSharpName); var baseText = @base.Count > 0 ? " : " + string.Join(", ", @base) : string.Empty; writer.Write($"{type.Name}{baseText} // TypeDefIndex: {type.Index}\n{{\n"); // Fields - if (type.DeclaredFields.Count > 0) - writer.Write("\t// Fields\n"); + if (!type.IsEnum) { + if (type.DeclaredFields.Count > 0) + writer.Write("\t// Fields\n"); - foreach (var field in type.DeclaredFields) { - writer.Write("\t"); - if (field.IsNotSerialized) - writer.Write("[NonSerialized]\t"); + foreach (var field in type.DeclaredFields) { + writer.Write("\t"); + if (field.IsNotSerialized) + writer.Write("[NonSerialized]\t"); - if (field.IsPrivate) - writer.Write("private "); - if (field.IsPublic) - writer.Write("public "); - if (field.IsFamily) - writer.Write("protected "); - if (field.IsAssembly) - writer.Write("internal "); - if (field.IsFamilyOrAssembly) - writer.Write("protected internal "); - if (field.IsFamilyAndAssembly) - writer.Write("[family and assembly] "); - if (field.IsLiteral) - writer.Write("const "); - // All const fields are also static by implication - else if (field.IsStatic) - writer.Write("static "); - if (field.IsInitOnly) - writer.Write("readonly "); - if (field.IsPinvokeImpl) - writer.Write("extern "); - writer.Write($"{field.FieldType.CSharpName} {field.Name}"); - if (field.HasDefaultValue) - writer.Write($" = {field.DefaultValueString}"); - writer.Write("; // 0x{0:X}\n", field.Offset); + if (field.IsPrivate) + writer.Write("private "); + if (field.IsPublic) + writer.Write("public "); + if (field.IsFamily) + writer.Write("protected "); + if (field.IsAssembly) + writer.Write("internal "); + if (field.IsFamilyOrAssembly) + writer.Write("protected internal "); + if (field.IsFamilyAndAssembly) + writer.Write("[family and assembly] "); + if (field.IsLiteral) + writer.Write("const "); + // All const fields are also static by implication + else if (field.IsStatic) + writer.Write("static "); + if (field.IsInitOnly) + writer.Write("readonly "); + if (field.IsPinvokeImpl) + writer.Write("extern "); + writer.Write($"{field.FieldType.CSharpName} {field.Name}"); + if (field.HasDefaultValue) + writer.Write($" = {field.DefaultValueString}"); + writer.Write("; // 0x{0:X}\n", field.Offset); + } + if (type.DeclaredFields.Count > 0) + writer.Write("\n"); + } + else { + writer.Write(string.Join(",\n", type.GetEnumNames().Zip(type.GetEnumValues().OfType(), + (k, v) => new { k, v }).OrderBy(x => x.v).Select(x => $"\t{x.k} = {x.v}")) + "\n"); } - if (type.DeclaredFields.Count > 0) - writer.Write("\n"); // Properties if (type.DeclaredProperties.Count > 0) diff --git a/Il2CppInspector/Il2CppReflector.cs b/Il2CppInspector/Il2CppReflector.cs index bef5d35..8e09716 100644 --- a/Il2CppInspector/Il2CppReflector.cs +++ b/Il2CppInspector/Il2CppReflector.cs @@ -48,12 +48,16 @@ namespace Il2CppInspector.Reflection return new TypeInfo(this, pType, memberType); default: - // Basic primitive types - if ((int) pType.type >= Il2CppConstants.FullNameTypeString.Count) - return null; - - return Assemblies.SelectMany(x => x.DefinedTypes).First(x => x.FullName == Il2CppConstants.FullNameTypeString[(int)pType.type]); + return GetTypeFromEnum(pType.type); } } + + // Basic primitive types + public TypeInfo GetTypeFromEnum(Il2CppTypeEnum t) { + if ((int)t >= Il2CppConstants.FullNameTypeString.Count) + return null; + + return Assemblies.SelectMany(x => x.DefinedTypes).First(x => x.FullName == Il2CppConstants.FullNameTypeString[(int)t]); + } } } \ No newline at end of file diff --git a/Il2CppInspector/Reflection/TypeInfo.cs b/Il2CppInspector/Reflection/TypeInfo.cs index 5d9e3e8..d1b85eb 100644 --- a/Il2CppInspector/Reflection/TypeInfo.cs +++ b/Il2CppInspector/Reflection/TypeInfo.cs @@ -52,7 +52,15 @@ 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 - public TypeInfo ElementType { get; } + private Il2CppType enumElementType; + private TypeInfo elementType; + public TypeInfo ElementType { + get { + if (IsEnum && elementType == null) + elementType = Assembly.Model.GetType(enumElementType, MemberTypes.TypeInfo); + return elementType; + } + } // Type name including namespace public string FullName => (IsPointer? "void *" : "") @@ -75,7 +83,7 @@ namespace Il2CppInspector.Reflection { public bool IsArray { get; } public bool IsByRef => throw new NotImplementedException(); public bool IsClass => (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Class; - public bool IsEnum => throw new NotImplementedException(); + public bool IsEnum { get; } public bool IsGenericParameter { get; } public bool IsGenericType => throw new NotImplementedException(); public bool IsGenericTypeDefinition => throw new NotImplementedException(); @@ -115,11 +123,11 @@ namespace Il2CppInspector.Reflection { // TODO: Custom attribute stuff - public string[] GetEnumNames() => throw new NotImplementedException(); + public string[] GetEnumNames() => IsEnum? DeclaredFields.Where(x => x.Name != "value__").Select(x => x.Name).ToArray() : throw new InvalidOperationException("Type is not an enumeration"); - public TypeInfo GetEnumUnderlyingType() => throw new NotImplementedException(); + public TypeInfo GetEnumUnderlyingType() => ElementType; - public Array GetEnumValues() => throw new NotImplementedException(); + public Array GetEnumValues() => IsEnum? DeclaredFields.Where(x => x.Name != "value__").Select(x => x.DefaultValue).ToArray() : throw new InvalidOperationException("Type is not an enumeration"); // TODO: Event stuff @@ -167,6 +175,12 @@ 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) + if (((Definition.bitfield >> 1) & 1) == 1) { + IsEnum = true; + enumElementType = pkg.TypeUsages[Definition.elementTypeIndex]; + } + // Add all implemented interfaces implementedInterfaces = new Il2CppType[Definition.interfaces_count]; for (var i = 0; i < Definition.interfaces_count; i++) @@ -224,7 +238,7 @@ namespace Il2CppInspector.Reflection { if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_ARRAY) { var descriptor = image.ReadMappedObject(pType.datapoint); var elementType = image.ReadMappedObject(descriptor.etype); - ElementType = model.GetType(elementType); + this.elementType = model.GetType(elementType); Namespace = ElementType.Namespace; Name = ElementType.Name; @@ -235,7 +249,7 @@ namespace Il2CppInspector.Reflection { // Dynamically allocated array if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY) { var elementType = image.ReadMappedObject(pType.datapoint); - ElementType = model.GetType(elementType); + this.elementType = model.GetType(elementType); Namespace = ElementType.Namespace; Name = ElementType.Name;