Skip to content

Commit

Permalink
Massive speed improvement (30 sec -> 10 sec on TLD)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sam Byass committed Jan 22, 2021
1 parent c097e6f commit 52d8e71
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 72 deletions.
48 changes: 19 additions & 29 deletions Cpp2IL/AssemblyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,10 @@ private static void InjectCustomAttributes(AssemblyDefinition imageDef)
return methods;
}

private static Dictionary<ModuleDefinition, (MethodDefinition, MethodDefinition, MethodDefinition)> _attributesByModule = new Dictionary<ModuleDefinition, (MethodDefinition, MethodDefinition, MethodDefinition)>();

private static List<CppMethodData> 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")
Expand All @@ -236,11 +237,23 @@ private static List<CppMethodData> 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
Expand All @@ -251,29 +264,6 @@ private static List<CppMethodData> ProcessTypeContents(Il2CppMetadata metadata,
//field
var fields = new List<FieldInType>();

var baseFields = new List<FieldDefinition>();

var current = ilTypeDefinition;
while (current.BaseType != null)
{
var targetName = current.BaseType.FullName;
if (targetName.Contains("<")) // types with generic parameters (Type'1<T>) 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)
{
Expand Down
7 changes: 4 additions & 3 deletions Cpp2IL/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
22 changes: 17 additions & 5 deletions LibCpp2IL/ClassReadingBinaryReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ public long Position
}
}

private Dictionary<FieldInfo, VersionAttribute?> _cachedVersionAttributes = new Dictionary<FieldInfo, VersionAttribute?>();
private Dictionary<FieldInfo, bool> _cachedNoSerialize = new Dictionary<FieldInfo, bool>();

public T ReadClass<T>(long offset) where T : new()
{
var t = new T();
Expand All @@ -65,20 +68,29 @@ 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!;
}

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)
{
Expand Down
29 changes: 13 additions & 16 deletions LibCpp2IL/Metadata/Il2CppMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Il2CppAssemblyDefinition, Il2CppCustomAttributeTypeRange[]> _typeRangesByAssembly = new Dictionary<Il2CppAssemblyDefinition, Il2CppCustomAttributeTypeRange[]>();
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)
Expand Down
13 changes: 12 additions & 1 deletion LibCpp2IL/Metadata/Il2CppMethodDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
13 changes: 5 additions & 8 deletions LibCpp2IL/Metadata/Il2CppTypeDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}

Expand Down
2 changes: 1 addition & 1 deletion LibCpp2IL/PlusSearch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ private IEnumerable<ulong> FindAllMappedWords(ulong va)
}

// Find all valid virtual address pointers to a set of virtual addresses
private IEnumerable<ulong> FindAllMappedWords(IEnumerable<ulong> va) => va.SelectMany(a => FindAllMappedWords(a));
private IEnumerable<ulong> FindAllMappedWords(IEnumerable<ulong> va) => va.SelectMany(FindAllMappedWords);

internal ulong FindCodeRegistrationUsingMscorlib()
{
Expand Down
51 changes: 42 additions & 9 deletions LibCpp2IL/Reflection/LibCpp2IlReflection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, Il2CppTypeDefinition> _cachedTypesByFullName = new Dictionary<string, Il2CppTypeDefinition>();
private static Dictionary<Il2CppTypeDefinition, int> _typeIndexes = new Dictionary<Il2CppTypeDefinition, int>();
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)
{
Expand All @@ -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)
Expand Down

0 comments on commit 52d8e71

Please sign in to comment.