private Type PrepareFieldType(Type originalType) { var cppType = CppTypes.GetCppType(originalType); if (originalType.IsGenericType) { return(cppType.GetGenericTypeDefinition().MakeGenericType(cppType.GenericTypeArguments.Select(gp => PrepareFieldType(gp)).ToArray())); } if (cppType.Assembly == Assembly.GetExecutingAssembly() && !cppType.Assembly.IsDynamic) { return(CppTypes.CreateType("UF::" + RenameType(cppType))); } return(cppType); }
private void WriteFieldDeclaration(string name, UpdateField declarationType) { var fieldGeneratedType = CppTypes.GetCppType(declarationType.Type); string typeName; if (_writeUpdateMasks) { var bit = CppTypes.CreateConstantForTemplateParameter(_fieldBitIndex[name][0]); if (fieldGeneratedType.IsArray) { if (typeof(DynamicUpdateField).IsAssignableFrom(fieldGeneratedType.GetElementType())) { var elementType = PrepareFieldType(fieldGeneratedType.GetElementType().GenericTypeArguments[0]); typeName = TypeHandler.GetFriendlyName(elementType); fieldGeneratedType = _arrayUpdateFieldType.MakeGenericType( _dynamicUpdateFieldBaseType.MakeGenericType(elementType), CppTypes.CreateConstantForTemplateParameter(declarationType.Size), bit, CppTypes.CreateConstantForTemplateParameter(_fieldBitIndex[name][1])); } else { var elementType = PrepareFieldType(fieldGeneratedType.GetElementType()); typeName = TypeHandler.GetFriendlyName(elementType); fieldGeneratedType = _arrayUpdateFieldType.MakeGenericType(elementType, CppTypes.CreateConstantForTemplateParameter(declarationType.Size), bit, CppTypes.CreateConstantForTemplateParameter(_fieldBitIndex[name][1])); } } else if (typeof(DynamicUpdateField).IsAssignableFrom(declarationType.Type)) { var elementType = PrepareFieldType(fieldGeneratedType.GenericTypeArguments[0]); typeName = TypeHandler.GetFriendlyName(elementType); fieldGeneratedType = _dynamicUpdateFieldType.MakeGenericType(PrepareFieldType(fieldGeneratedType.GenericTypeArguments[0]), CppTypes.CreateConstantForTemplateParameter(_fieldBitIndex[name][1]), bit); } else if (typeof(BlzOptionalField).IsAssignableFrom(declarationType.Type)) { var elementType = PrepareFieldType(fieldGeneratedType.GenericTypeArguments[0]); typeName = TypeHandler.GetFriendlyName(elementType); fieldGeneratedType = _optionalUpdateFieldType.MakeGenericType(elementType, CppTypes.CreateConstantForTemplateParameter(_fieldBitIndex[name][1]), bit); } else { var elementType = PrepareFieldType(declarationType.Type); typeName = TypeHandler.GetFriendlyName(elementType); fieldGeneratedType = _updateFieldType.MakeGenericType(PrepareFieldType(declarationType.Type), CppTypes.CreateConstantForTemplateParameter(_fieldBitIndex[name][1]), bit); } _header.WriteLine($" {TypeHandler.GetFriendlyName(fieldGeneratedType)} {name};"); } else if (fieldGeneratedType.IsArray) { typeName = TypeHandler.GetFriendlyName(fieldGeneratedType.GetElementType()); _header.WriteLine($" {typeName} {name}[{declarationType.Size}];"); } else { typeName = TypeHandler.GetFriendlyName(fieldGeneratedType); _header.WriteLine($" {typeName} {name};"); } if ((declarationType.CustomFlag & CustomUpdateFieldFlag.ViewerDependent) != CustomUpdateFieldFlag.None) { _header.WriteLine($" struct {name}Tag : ViewerDependentValueTag<{typeName}> {{}};"); } }
public void TestCppTypes() { // NOTE: This test doesn't check for correct results, only that parsing doesn't fail! var unityAllHeaders = UnityHeader.GetAllHeaders(); // Ensure we have read the embedded assembly resources Assert.IsTrue(unityAllHeaders.Any()); // Ensure we can interpret every header from every version of Unity without errors // This will throw InvalidOperationException if there is a problem foreach (var unityHeader in unityAllHeaders) { var cppTypes = CppTypes.FromUnityHeaders(unityHeader); foreach (var cppType in cppTypes.Types) { Debug.WriteLine("// " + cppType.Key + "\n" + cppType.Value.ToString("o") + "\n"); } } // Do a few sanity checks taken from real applications // NOTE: Does not provide full code coverage! var cppTypes2 = CppTypes.FromUnityVersion(new UnityVersion("2019.3.1f1"), 64); CppComplexType ct; CppField field; // Un-nested class ct = (CppComplexType)cppTypes2["Il2CppClass"]; field = ct[0xD8].First(); Assert.AreEqual(field.Name, "cctor_finished"); field = ct[0x128].First(); Assert.AreEqual(field.Name, "vtable"); field = ct["cctor_finished"]; Assert.AreEqual(field.OffsetBytes, 0xD8); field = ct["vtable"]; Assert.AreEqual(field.OffsetBytes, 0x128); // Nested class ct = (CppComplexType)cppTypes2["Il2CppClass_Merged"]; var fields = ct.Flattened; field = fields[0xD8].First(); Assert.AreEqual(field.Name, "cctor_finished"); field = fields[0x128].First(); Assert.AreEqual(field.Name, "vtable"); field = fields["cctor_finished"]; Assert.AreEqual(field.OffsetBytes, 0xD8); field = fields["vtable"]; Assert.AreEqual(field.OffsetBytes, 0x128); }
string GetTypeCode(CppType mangleType, Dictionary <string, int> compressMap) { CppTypes element = mangleType.ElementType; IEnumerable <CppModifiers> modifiers = mangleType.Modifiers; StringBuilder code = new StringBuilder(); var ptrOrRef = For.AnyInputIn(CppModifiers.Pointer, CppModifiers.Reference); var modifierCode = modifiers.Reverse().Transform( For.AnyInputIn(CppModifiers.Pointer, CppModifiers.Array).Emit("P"), For.AnyInputIn(CppModifiers.Reference).Emit("R"), // Itanium mangled names do not include const or volatile unless // they modify the type pointed to by pointer or reference. Choose.TopOne( For.AllInputsIn(CppModifiers.Volatile, CppModifiers.Const).InAnyOrder().After(ptrOrRef).Emit("VK"), For.AnyInputIn(CppModifiers.Volatile).After(ptrOrRef).Emit("V"), For.AnyInputIn(CppModifiers.Const).After(ptrOrRef).Emit("K") ) ); code.Append(string.Join(string.Empty, modifierCode.ToArray())); int modifierLength = code.Length; switch (element) { case CppTypes.Int: code.Append(modifiers.Transform( For.AllInputsIn(CppModifiers.Unsigned, CppModifiers.Short).InAnyOrder().Emit('t'), For.AnyInputIn(CppModifiers.Short).Emit('s'), For.AllInputsIn(CppModifiers.Unsigned, CppModifiers.Long, CppModifiers.Long).InAnyOrder().Emit('y'), For.AllInputsIn(CppModifiers.Long, CppModifiers.Long).InAnyOrder().Emit('x'), For.AllInputsIn(CppModifiers.Unsigned, CppModifiers.Long).InAnyOrder().Emit('m'), For.AnyInputIn(CppModifiers.Long).Emit('l'), For.AnyInputIn(CppModifiers.Unsigned).Emit('j') ).DefaultIfEmpty('i').ToArray()); break; case CppTypes.Bool: code.Append('b'); break; case CppTypes.Char: if (modifiers.Contains(CppModifiers.Signed)) { code.Append('a'); } else if (modifiers.Contains(CppModifiers.Unsigned)) { code.Append('h'); } else { code.Append('c'); } break; case CppTypes.Float: code.Append('f'); break; case CppTypes.Double: if (modifiers.Contains(CppModifiers.Long)) { code.Append('e'); } else { code.Append('d'); } break; case CppTypes.Class: case CppTypes.Struct: case CppTypes.Union: case CppTypes.Enum: // TODO: This is getting a little ridiculous and should probably be refactored... // Determine if we have any namespaces to print out bool hasNamespace = (mangleType.Namespaces != null); if (hasNamespace) { code.Append('N'); foreach (var ns in mangleType.Namespaces) { code.Append(GetIdentifier(compressMap, ns)); } } // Look up the type by itself first // NOTE: Order here is important so that they get sequenced properly bool foundType; string value = GetIdentifier(compressMap, mangleType.ElementTypeName, 0, out foundType); if (modifierLength > 0) { // Next lookup the type with modifiers for a match bool foundExact; string exact = GetIdentifier(compressMap, mangleType.ToString(), modifierLength, out foundExact); if (foundExact) { // Use the exact values sequence ID and remove all modifiers and namespaces code.Length = 0; hasNamespace = false; value = exact; } else if (foundType) { // We didn't get an exact match but we know the type // so remove the namespaces, but not the modifiers code.Length = modifierLength; hasNamespace = false; } } // Print out our class identifier code.Append(value); // If we're printing out namespaces then signal the end of the namespace if (hasNamespace) { code.Append('E'); } // Since we handle the modifier compression in here make sure we skip the logic below modifierLength = 0; break; } // If there were modifiers then always add it to the compression map // NOTE: If there were multiple modifiers this causes us to skip sequence numbers if (modifierLength > 0) { bool found; string value = GetIdentifier(compressMap, mangleType.ToString(), modifierLength, out found); if (found) { return(value); } } return(code.ToString()); }
string GetTypeCode(CppType mangleType, Dictionary <string, int> compressMap) { CppTypes element = mangleType.ElementType; IEnumerable <CppModifiers> modifiers = mangleType.Modifiers; StringBuilder code = new StringBuilder(); var ptrOrRef = For.AnyInputIn(CppModifiers.Pointer, CppModifiers.Reference); var modifierCode = modifiers.Reverse().Transform( For.AnyInputIn(CppModifiers.Pointer, CppModifiers.Array).Emit("P"), For.AnyInputIn(CppModifiers.Reference).Emit("R"), // Itanium mangled names do not include const or volatile unless // they modify the type pointed to by pointer or reference. Choose.TopOne( For.AllInputsIn(CppModifiers.Volatile, CppModifiers.Const).InAnyOrder().After(ptrOrRef).Emit("VK"), For.AnyInputIn(CppModifiers.Volatile).After(ptrOrRef).Emit("V"), For.AnyInputIn(CppModifiers.Const).After(ptrOrRef).Emit("K") ) ); code.Append(string.Join(string.Empty, modifierCode.ToArray())); switch (element) { case CppTypes.Int: code.Append(modifiers.Transform( For.AllInputsIn(CppModifiers.Unsigned, CppModifiers.Short).InAnyOrder().Emit('t'), For.AnyInputIn(CppModifiers.Short).Emit('s'), For.AllInputsIn(CppModifiers.Unsigned, CppModifiers.Long, CppModifiers.Long).InAnyOrder().Emit('y'), For.AllInputsIn(CppModifiers.Long, CppModifiers.Long).InAnyOrder().Emit('x'), For.AllInputsIn(CppModifiers.Unsigned, CppModifiers.Long).InAnyOrder().Emit('m'), For.AnyInputIn(CppModifiers.Long).Emit('l'), For.AnyInputIn(CppModifiers.Unsigned).Emit('j') ).DefaultIfEmpty('i').ToArray()); break; case CppTypes.Bool: code.Append('b'); break; case CppTypes.Char: if (modifiers.Contains(CppModifiers.Signed)) { code.Append('a'); } else if (modifiers.Contains(CppModifiers.Unsigned)) { code.Append('h'); } else { code.Append('c'); } break; case CppTypes.Float: code.Append('f'); break; case CppTypes.Double: if (modifiers.Contains(CppModifiers.Long)) { code.Append('e'); } else { code.Append('d'); } break; case CppTypes.Class: case CppTypes.Struct: case CppTypes.Union: case CppTypes.Enum: if (mangleType.Namespaces != null) { code.Append('N'); foreach (var ns in mangleType.Namespaces) { code.Append(GetIdentifier(compressMap, ns)); } } code.Append(GetIdentifier(compressMap, mangleType.ElementTypeName)); if (mangleType.Namespaces != null) { code.Append('E'); } break; } return(code.ToString()); }
public virtual string GetTypeCode(CppType mangleType) { CppTypes element = mangleType.ElementType; IEnumerable <CppModifiers> modifiers = mangleType.Modifiers; StringBuilder code = new StringBuilder(); var ptr = For.AnyInputIn(CppModifiers.Pointer); var ptrRefOrArray = For.AnyInputIn(CppModifiers.Pointer, CppModifiers.Reference, CppModifiers.Array); var modifierCode = modifiers.Reverse().Transform( Choose.TopOne( For.AllInputsIn(CppModifiers.Const, CppModifiers.Volatile).InAnyOrder().After(ptrRefOrArray).Emit('D'), For.AnyInputIn(CppModifiers.Const).After(ptrRefOrArray).Emit('B'), For.AnyInputIn(CppModifiers.Volatile).After(ptrRefOrArray).Emit('C'), For.AnyInput <CppModifiers> ().After(ptrRefOrArray).Emit('A') ), For.AnyInputIn(CppModifiers.Array).Emit('Q'), For.AnyInputIn(CppModifiers.Reference).Emit('A'), Choose.TopOne( ptr.After().AllInputsIn(CppModifiers.Const, CppModifiers.Volatile).InAnyOrder().Emit('S'), ptr.After().AnyInputIn(CppModifiers.Const).Emit('Q'), ptr.After().AnyInputIn(CppModifiers.Volatile).Emit('R'), ptr.Emit('P') ), ptrRefOrArray.AtEnd().Emit('A') ); code.Append(modifierCode.ToArray()); switch (element) { case CppTypes.Void: code.Append('X'); break; case CppTypes.Int: code.Append(modifiers.Transform( For.AllInputsIn(CppModifiers.Unsigned, CppModifiers.Short).InAnyOrder().Emit('G') ).DefaultIfEmpty('H').ToArray()); break; case CppTypes.Char: code.Append('D'); break; case CppTypes.Class: code.Append('V'); code.Append(mangleType.ElementTypeName); code.Append("@@"); break; case CppTypes.Struct: code.Append('U'); code.Append(mangleType.ElementTypeName); code.Append("@@"); break; case CppTypes.Union: code.Append('T'); code.Append(mangleType.ElementTypeName); code.Append("@@"); break; case CppTypes.Enum: code.Append("W4"); code.Append(mangleType.ElementTypeName); code.Append("@@"); break; } return(code.ToString()); }
public virtual string GetTypeCode(CppType mangleType, BackReferenceList backReferences) { CppTypes element = mangleType.ElementType; IEnumerable <CppModifiers> modifiers = mangleType.Modifiers; StringBuilder code = new StringBuilder(); var ptr = For.AnyInputIn(CppModifiers.Pointer); var ptrRefOrArray = For.AnyInputIn(CppModifiers.Pointer, CppModifiers.Reference, CppModifiers.Array); var modifierCode = modifiers.Reverse().Transform( Choose.TopOne( For.AllInputsIn(CppModifiers.Const, CppModifiers.Volatile).InAnyOrder().After(ptrRefOrArray).Emit('D'), For.AnyInputIn(CppModifiers.Const).After(ptrRefOrArray).Emit('B'), For.AnyInputIn(CppModifiers.Volatile).After(ptrRefOrArray).Emit('C'), For.AnyInput <CppModifiers> ().After(ptrRefOrArray).Emit('A') ), For.AnyInputIn(CppModifiers.Array).Emit('Q'), For.AnyInputIn(CppModifiers.Reference).Emit('A'), Choose.TopOne( ptr.After().AllInputsIn(CppModifiers.Const, CppModifiers.Volatile).InAnyOrder().Emit('S'), ptr.After().AnyInputIn(CppModifiers.Const).Emit('Q'), ptr.After().AnyInputIn(CppModifiers.Volatile).Emit('R'), ptr.Emit('P') ), ptrRefOrArray.AtEnd().Emit('A') ); code.Append(modifierCode.ToArray()); // Type codes taken from http://www.kegel.com/mangle.html#operators switch (element) { case CppTypes.Void: code.Append('X'); break; case CppTypes.Int: code.Append(modifiers.Transform(Choose.TopOne( For.AllInputsIn(CppModifiers.Unsigned, CppModifiers.Short).InAnyOrder().Emit('G'), For.AllInputsIn(CppModifiers.Short).InAnyOrder().Emit('F'), For.AllInputsIn(CppModifiers.Unsigned, CppModifiers.Long).InAnyOrder().Emit('K'), For.AllInputsIn(CppModifiers.Long).InAnyOrder().Emit('J'), For.AllInputsIn(CppModifiers.Unsigned).InAnyOrder().Emit('I') )).DefaultIfEmpty('H').ToArray()); break; case CppTypes.Float: code.Append('M'); break; case CppTypes.Double: code.Append(modifiers.Transform( For.AllInputsIn(CppModifiers.Long).InAnyOrder().Emit('O') ).DefaultIfEmpty('N').ToArray()); break; case CppTypes.Char: code.Append(modifiers.Transform(Choose.TopOne( For.AllInputsIn(CppModifiers.Unsigned).InAnyOrder().Emit('E'), For.AllInputsIn(CppModifiers.Signed).InAnyOrder().Emit('C') )).DefaultIfEmpty('D').ToArray()); break; case CppTypes.Class: code.Append('V'); code.Append(GetTypeNameWithBackReferences(mangleType, backReferences)); code.Append("@"); break; case CppTypes.Struct: code.Append('U'); code.Append(GetTypeNameWithBackReferences(mangleType, backReferences)); code.Append("@"); break; case CppTypes.Union: code.Append('T'); code.Append(GetTypeNameWithBackReferences(mangleType, backReferences)); code.Append("@"); break; case CppTypes.Enum: code.Append("W4"); code.Append(GetTypeNameWithBackReferences(mangleType, backReferences)); code.Append("@"); break; case CppTypes.Bool: code.Append("_N"); break; } return(code.ToString()); }