diff --git a/Cpp2IL/AssemblyBuilder.cs b/Cpp2IL/AssemblyBuilder.cs index ac2b7697..f65e2a75 100644 --- a/Cpp2IL/AssemblyBuilder.cs +++ b/Cpp2IL/AssemblyBuilder.cs @@ -213,9 +213,10 @@ private static void InjectCustomAttributes(AssemblyDefinition imageDef) return methods; } + private static Dictionary _attributesByModule = new Dictionary(); + private static List ProcessTypeContents(Il2CppMetadata metadata, PE cppAssembly, Il2CppTypeDefinition cppTypeDefinition, TypeDefinition ilTypeDefinition, Il2CppAssemblyDefinition imageDef) { - var imageName = metadata.GetStringFromIndex(imageDef.nameIndex); var typeMetaText = new StringBuilder(); typeMetaText.Append($"Type: {ilTypeDefinition.FullName}:") .Append($"\n\tBase Class: \n\t\t{ilTypeDefinition.BaseType}\n") @@ -236,11 +237,23 @@ private static List ProcessTypeContents(Il2CppMetadata metadata, } } - var addressAttribute = ilTypeDefinition.Module.Types.First(x => x.Name == "AddressAttribute").Methods[0]; - var fieldOffsetAttribute = ilTypeDefinition.Module.Types.First(x => x.FullName == "Cpp2IlInjected.FieldOffsetAttribute").Methods[0]; - var attributeAttribute = ilTypeDefinition.Module.Types.First(x => x.Name == "AttributeAttribute").Methods[0]; - var metadataOffsetAttribute = ilTypeDefinition.Module.Types.First(x => x.Name == "MetadataOffsetAttribute").Methods[0]; - var tokenAttribute = ilTypeDefinition.Module.Types.First(x => x.Name == "TokenAttribute").Methods[0]; + + MethodDefinition addressAttribute; + MethodDefinition fieldOffsetAttribute; + MethodDefinition tokenAttribute; + + if (!_attributesByModule.ContainsKey(ilTypeDefinition.Module)) + { + addressAttribute = ilTypeDefinition.Module.Types.First(x => x.Name == "AddressAttribute").Methods[0]; + fieldOffsetAttribute = ilTypeDefinition.Module.Types.First(x => x.FullName == "Cpp2IlInjected.FieldOffsetAttribute").Methods[0]; + tokenAttribute = ilTypeDefinition.Module.Types.First(x => x.Name == "TokenAttribute").Methods[0]; + _attributesByModule[ilTypeDefinition.Module] = (addressAttribute, fieldOffsetAttribute, tokenAttribute); + } + else + { + (addressAttribute, fieldOffsetAttribute, tokenAttribute) = _attributesByModule[ilTypeDefinition.Module]; + } + var stringType = ilTypeDefinition.Module.ImportReference(Utils.TryLookupTypeDefByName("System.String").Item1); //Token attribute @@ -251,29 +264,6 @@ private static List ProcessTypeContents(Il2CppMetadata metadata, //field var fields = new List(); - var baseFields = new List(); - - var current = ilTypeDefinition; - while (current.BaseType != null) - { - var targetName = current.BaseType.FullName; - if (targetName.Contains("<")) // types with generic parameters (Type'1) are stored as Type'1, so I just removed the part that causes trouble and called it a day - targetName = targetName.Substring(0, targetName.IndexOf("<")); - - current = SharedState.AllTypeDefinitions.Find(t => t.FullName == targetName); - - if (current == null) - { - typeMetaText.Append("WARN: Type " + targetName + " is not defined yet\n"); - break; - } - - baseFields.InsertRange(0, current.Fields.Where(f => !f.IsStatic)); // each loop we go one inheritage level deeper, so these "new" fields should be inserted before the previous ones - } - - //Handle base fields - // var fieldOffset = baseFields.Aggregate((ulong) (ilTypeDefinition.MetadataType == MetadataType.Class ? 0x10 : 0x0), (currentOffset, baseField) => HandleField(baseField.FieldType, currentOffset, baseField.Name, baseField, ref fields, typeMetaText)); - var lastFieldIdx = cppTypeDefinition.firstFieldIdx + cppTypeDefinition.field_count; for (var fieldIdx = cppTypeDefinition.firstFieldIdx; fieldIdx < lastFieldIdx; ++fieldIdx) { diff --git a/Cpp2IL/Program.cs b/Cpp2IL/Program.cs index 0987c8fc..594bddf2 100644 --- a/Cpp2IL/Program.cs +++ b/Cpp2IL/Program.cs @@ -242,9 +242,10 @@ public static int Main(string[] args) var fieldDefinition = typeDefinition.Fields.First(x => x.Name == fieldName); //Get attributes and look for the serialize field attribute. - var attributeIndex = LibCpp2IlMain.TheMetadata.GetCustomAttributeIndex(imageDef, fieldDef.customAttributeIndex, fieldDef.token); - if (attributeIndex < 0) continue; - var attributeTypeRange = LibCpp2IlMain.TheMetadata.attributeTypeRanges[attributeIndex]; + var attributeTypeRange = LibCpp2IlMain.TheMetadata.GetCustomAttributeIndex(imageDef, fieldDef.customAttributeIndex, fieldDef.token); + + if(attributeTypeRange == null) continue; + for (var attributeIdxIdx = 0; attributeIdxIdx < attributeTypeRange.count; attributeIdxIdx++) { var attributeTypeIndex = LibCpp2IlMain.TheMetadata.attributeTypes[attributeTypeRange.start + attributeIdxIdx]; diff --git a/LibCpp2IL/ClassReadingBinaryReader.cs b/LibCpp2IL/ClassReadingBinaryReader.cs index c3ad46cf..dae8dbbb 100644 --- a/LibCpp2IL/ClassReadingBinaryReader.cs +++ b/LibCpp2IL/ClassReadingBinaryReader.cs @@ -52,6 +52,9 @@ public long Position } } + private Dictionary _cachedVersionAttributes = new Dictionary(); + private Dictionary _cachedNoSerialize = new Dictionary(); + public T ReadClass(long offset) where T : new() { var t = new T(); @@ -65,9 +68,9 @@ public long Position var value = ReadPrimitive(type); //32-bit fixes... - if (value is uint && typeof(T).Name == "UInt64") + if (value is uint && typeof(T) == typeof(ulong)) value = Convert.ToUInt64(value); - if (value is int && typeof(T).Name == "Int64") + if (value is int && typeof(T) == typeof(long)) value = Convert.ToInt64(value); return (T) value!; @@ -75,10 +78,19 @@ public long Position foreach (var i in t.GetType().GetFields()) { - var attr = (VersionAttribute?) Attribute.GetCustomAttribute(i, typeof(VersionAttribute)); - var nonSerializedAttribute = (NonSerializedAttribute?) Attribute.GetCustomAttribute(i, typeof(NonSerializedAttribute)); + VersionAttribute? attr; + if (!_cachedVersionAttributes.ContainsKey(i)) + { + attr = (VersionAttribute?) Attribute.GetCustomAttribute(i, typeof(VersionAttribute)); + _cachedVersionAttributes[i] = attr; + } + else + attr = _cachedVersionAttributes[i]; + + if(!_cachedNoSerialize.ContainsKey(i)) + _cachedNoSerialize[i] = Attribute.GetCustomAttribute(i, typeof(NonSerializedAttribute)) != null; - if (nonSerializedAttribute != null) continue; + if (_cachedNoSerialize[i]) continue; if (attr != null) { diff --git a/LibCpp2IL/Metadata/Il2CppMetadata.cs b/LibCpp2IL/Metadata/Il2CppMetadata.cs index eb16c382..3bd67a19 100644 --- a/LibCpp2IL/Metadata/Il2CppMetadata.cs +++ b/LibCpp2IL/Metadata/Il2CppMetadata.cs @@ -257,25 +257,22 @@ public string GetStringFromIndex(int index) return ReadStringToNull(metadataHeader.stringOffset + index); } - public int GetCustomAttributeIndex(Il2CppAssemblyDefinition assemblyDef, int customAttributeIndex, uint token) + private Dictionary _typeRangesByAssembly = new Dictionary(); + public Il2CppCustomAttributeTypeRange? GetCustomAttributeIndex(Il2CppAssemblyDefinition assemblyDef, int customAttributeIndex, uint token) { - if (LibCpp2IlMain.MetadataVersion > 24) + if (LibCpp2IlMain.MetadataVersion <= 24f) + return attributeTypeRanges[customAttributeIndex]; + + if (!_typeRangesByAssembly.ContainsKey(assemblyDef)) + _typeRangesByAssembly[assemblyDef] = attributeTypeRanges.SubArray(assemblyDef.customAttributeStart, (int) assemblyDef.customAttributeCount); + + foreach (var r in _typeRangesByAssembly[assemblyDef]) { - var end = assemblyDef.customAttributeStart + assemblyDef.customAttributeCount; - for (int i = assemblyDef.customAttributeStart; i < end; i++) - { - if (attributeTypeRanges[i].token == token) - { - return i; - } - } - - return -1; - } - else - { - return customAttributeIndex; + if (r.token == token) return r; } + + return null; + } public string GetStringLiteralFromIndex(uint index) diff --git a/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs b/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs index 4f67dc17..dd3e4247 100644 --- a/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs @@ -38,7 +38,18 @@ public class Il2CppMethodDefinition public Il2CppTypeDefinition? DeclaringType => LibCpp2IlMain.TheMetadata == null ? null : LibCpp2IlMain.TheMetadata.typeDefs[declaringTypeIdx]; - public ulong MethodPointer => LibCpp2IlMain.ThePe == null || LibCpp2IlMain.TheMetadata == null || DeclaringType == null ? 0 : LibCpp2IlMain.ThePe.GetMethodPointer(methodIndex, MethodIndex, DeclaringType!.DeclaringAssembly!.assemblyIndex, token); + public ulong MethodPointer + { + get + { + if(LibCpp2IlMain.ThePe == null || LibCpp2IlMain.TheMetadata == null || DeclaringType == null) + return 0; + + var asmIdx = LibCpp2IlMain.MetadataVersion >= 24.2f ? DeclaringType!.DeclaringAssembly!.assemblyIndex : 0; //Not needed below 24.2 + + return LibCpp2IlMain.ThePe.GetMethodPointer(methodIndex, MethodIndex, asmIdx, token); + } + } public long MethodOffsetInFile => MethodPointer == 0 || LibCpp2IlMain.ThePe == null ? 0 : LibCpp2IlMain.ThePe.MapVirtualAddressToRaw(MethodPointer); diff --git a/LibCpp2IL/Metadata/Il2CppTypeDefinition.cs b/LibCpp2IL/Metadata/Il2CppTypeDefinition.cs index 25ab3619..c2f8d2b7 100644 --- a/LibCpp2IL/Metadata/Il2CppTypeDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppTypeDefinition.cs @@ -72,14 +72,11 @@ public Il2CppAssemblyDefinition? DeclaringAssembly if (LibCpp2IlMain.TheMetadata == null) return null; var typeIdx = TypeIndex; - foreach (var assemblyDefinition in LibCpp2IlMain.TheMetadata.assemblyDefinitions) - { - var lastIdx = assemblyDefinition.firstTypeIndex + assemblyDefinition.typeCount - 1; - if (assemblyDefinition.firstTypeIndex <= typeIdx && typeIdx <= lastIdx) - return assemblyDefinition; - } - - return null; + return LibCpp2IlMain.TheMetadata.assemblyDefinitions + .Select(assemblyDefinition => new {assemblyDefinition, lastIdx = assemblyDefinition.firstTypeIndex + assemblyDefinition.typeCount - 1}) + .Where(t => t.assemblyDefinition.firstTypeIndex <= typeIdx && typeIdx <= t.lastIdx) + .Select(t => t.assemblyDefinition) + .FirstOrDefault(); } } diff --git a/LibCpp2IL/PlusSearch.cs b/LibCpp2IL/PlusSearch.cs index cdef4b02..760aaf1a 100644 --- a/LibCpp2IL/PlusSearch.cs +++ b/LibCpp2IL/PlusSearch.cs @@ -149,7 +149,7 @@ private IEnumerable FindAllMappedWords(ulong va) } // Find all valid virtual address pointers to a set of virtual addresses - private IEnumerable FindAllMappedWords(IEnumerable va) => va.SelectMany(a => FindAllMappedWords(a)); + private IEnumerable FindAllMappedWords(IEnumerable va) => va.SelectMany(FindAllMappedWords); internal ulong FindCodeRegistrationUsingMscorlib() { diff --git a/LibCpp2IL/Reflection/LibCpp2IlReflection.cs b/LibCpp2IL/Reflection/LibCpp2IlReflection.cs index f8ceaad6..61d8f070 100644 --- a/LibCpp2IL/Reflection/LibCpp2IlReflection.cs +++ b/LibCpp2IL/Reflection/LibCpp2IlReflection.cs @@ -6,17 +6,41 @@ namespace LibCpp2IL { public static class LibCpp2IlReflection { + private static Dictionary<(string, string?), Il2CppTypeDefinition> _cachedTypes = new Dictionary<(string, string?), Il2CppTypeDefinition>(); + private static Dictionary _cachedTypesByFullName = new Dictionary(); + private static Dictionary _typeIndexes = new Dictionary(); public static Il2CppTypeDefinition? GetType(string name, string? @namespace = null) { if (LibCpp2IlMain.TheMetadata == null) return null; - var typeDef = LibCpp2IlMain.TheMetadata.typeDefs.FirstOrDefault(td => - td.Name == name && - (@namespace == null || @namespace == td.Namespace) - ); + var key = (name, @namespace); + if (!_cachedTypes.ContainsKey(key)) + { + var typeDef = LibCpp2IlMain.TheMetadata.typeDefs.FirstOrDefault(td => + td.Name == name && + (@namespace == null || @namespace == td.Namespace) + ); + _cachedTypes[key] = typeDef; + } + + return _cachedTypes[key]; + } + + public static Il2CppTypeDefinition? GetTypeByFullName(string fullName) + { + if (LibCpp2IlMain.TheMetadata == null) return null; - return typeDef; + if (!_cachedTypesByFullName.ContainsKey(fullName)) + { + var typeDef = LibCpp2IlMain.TheMetadata.typeDefs.FirstOrDefault(td => + td.FullName == fullName + ); + _cachedTypesByFullName[fullName] = typeDef; + } + + return _cachedTypesByFullName[fullName]; } + public static Il2CppTypeDefinition? GetTypeDefinitionByTypeIndex(int index) { @@ -33,12 +57,21 @@ public static int GetTypeIndexFromType(Il2CppTypeDefinition typeDefinition) { if (LibCpp2IlMain.TheMetadata == null) return -1; - for (var i = 0; i < LibCpp2IlMain.TheMetadata.typeDefs.Length; i++) + lock (_typeIndexes) { - if (LibCpp2IlMain.TheMetadata.typeDefs[i] == typeDefinition) return i; - } + if (!_typeIndexes.ContainsKey(typeDefinition)) + { + for (var i = 0; i < LibCpp2IlMain.TheMetadata.typeDefs.Length; i++) + { + if (LibCpp2IlMain.TheMetadata.typeDefs[i] == typeDefinition) + { + _typeIndexes[typeDefinition] = i; + } + } + } - return -1; + return _typeIndexes.GetValueOrDefault(typeDefinition, -1); + } } public static int GetMethodIndexFromMethod(Il2CppMethodDefinition methodDefinition)