Add --suppress-metadata option

This commit is contained in:
Katy Coe
2019-11-13 15:55:18 +01:00
parent 69551c7f39
commit 7c64e0a09c
7 changed files with 59 additions and 37 deletions

View File

@@ -23,6 +23,9 @@ namespace Il2CppInspector
// Suppress types, fields and methods with the CompilerGenerated attribute; suppress the attribute itself from property getters and setters // Suppress types, fields and methods with the CompilerGenerated attribute; suppress the attribute itself from property getters and setters
public bool SuppressGenerated { get; set; } public bool SuppressGenerated { get; set; }
// Suppress binary metadata in code comments
public bool SuppressMetadata { get; set; }
private const string CGAttribute = "System.Runtime.CompilerServices.CompilerGeneratedAttribute"; private const string CGAttribute = "System.Runtime.CompilerServices.CompilerGeneratedAttribute";
private const string FBAttribute = "System.Runtime.CompilerServices.FixedBufferAttribute"; private const string FBAttribute = "System.Runtime.CompilerServices.FixedBufferAttribute";
private const string ExtAttribute = "System.Runtime.CompilerServices.ExtensionAttribute"; private const string ExtAttribute = "System.Runtime.CompilerServices.ExtensionAttribute";
@@ -158,7 +161,7 @@ namespace Il2CppInspector
text.Append($"// Image {asm.Index}: {asm.FullName} - {asm.ImageDefinition.typeStart}\n"); text.Append($"// Image {asm.Index}: {asm.FullName} - {asm.ImageDefinition.typeStart}\n");
// Assembly-level attributes // Assembly-level attributes
text.Append(asm.CustomAttributes.Where(a => a.AttributeType.FullName != ExtAttribute).OrderBy(a => a.AttributeType.Name).ToString(attributePrefix: "assembly: ")); text.Append(asm.CustomAttributes.Where(a => a.AttributeType.FullName != ExtAttribute).OrderBy(a => a.AttributeType.Name).ToString(attributePrefix: "assembly: ", emitPointer: !SuppressMetadata));
if (asm.CustomAttributes.Any()) if (asm.CustomAttributes.Any())
text.Append("\n"); text.Append("\n");
} }
@@ -185,14 +188,16 @@ namespace Il2CppInspector
sb.Append(prefix + "\t[NonSerialized]\n"); sb.Append(prefix + "\t[NonSerialized]\n");
// Attributes // Attributes
sb.Append(field.CustomAttributes.Where(a => a.AttributeType.FullName != FBAttribute).OrderBy(a => a.AttributeType.Name).ToString(prefix + "\t")); sb.Append(field.CustomAttributes.Where(a => a.AttributeType.FullName != FBAttribute).OrderBy(a => a.AttributeType.Name).ToString(prefix + "\t", emitPointer: !SuppressMetadata));
sb.Append(prefix + "\t"); sb.Append(prefix + "\t");
sb.Append(field.GetModifierString()); sb.Append(field.GetModifierString());
// Fixed buffers // Fixed buffers
if (field.GetCustomAttributes(FBAttribute).Any()) if (field.GetCustomAttributes(FBAttribute).Any()) {
sb.Append($"/* {((ulong) field.GetCustomAttributes(FBAttribute)[0].VirtualAddress).ToAddressString()} */" + if (!SuppressMetadata)
$" {field.FieldType.DeclaredFields[0].FieldType.CSharpName} {field.Name}[0]"); // FixedElementField sb.Append($"/* {((ulong) field.GetCustomAttributes(FBAttribute)[0].VirtualAddress).ToAddressString()} */ ");
sb.Append($"{field.FieldType.DeclaredFields[0].FieldType.CSharpName} {field.Name}[0]"); // FixedElementField
}
// Regular fields // Regular fields
else else
sb.Append($"{field.FieldType.CSharpName} {field.Name}"); sb.Append($"{field.FieldType.CSharpName} {field.Name}");
@@ -200,7 +205,7 @@ namespace Il2CppInspector
sb.Append($" = {field.DefaultValueString}"); sb.Append($" = {field.DefaultValueString}");
sb.Append(";"); sb.Append(";");
// Don't output field indices for const fields (they don't have any storage) // Don't output field indices for const fields (they don't have any storage)
if (!field.IsLiteral) if (!field.IsLiteral && !SuppressMetadata)
sb.Append($" // 0x{(uint) field.Offset:X2}"); sb.Append($" // 0x{(uint) field.Offset:X2}");
sb.Append("\n"); sb.Append("\n");
} }
@@ -211,7 +216,7 @@ namespace Il2CppInspector
sb.Clear(); sb.Clear();
foreach (var prop in type.DeclaredProperties) { foreach (var prop in type.DeclaredProperties) {
// Attributes // Attributes
sb.Append(prop.CustomAttributes.OrderBy(a => a.AttributeType.Name).ToString(prefix + "\t")); sb.Append(prop.CustomAttributes.OrderBy(a => a.AttributeType.Name).ToString(prefix + "\t", emitPointer: !SuppressMetadata));
// The access mask enum values go from 1 (private) to 6 (public) in order from most to least restrictive // The access mask enum values go from 1 (private) to 6 (public) in order from most to least restrictive
var getAccess = (prop.GetMethod?.Attributes ?? 0) & MethodAttributes.MemberAccessMask; var getAccess = (prop.GetMethod?.Attributes ?? 0) & MethodAttributes.MemberAccessMask;
@@ -225,16 +230,20 @@ namespace Il2CppInspector
sb.Append($"{prop.Name} {{ "); sb.Append($"{prop.Name} {{ ");
// Indexer // Indexer
else else
sb.Append("this[" + string.Join(", ", primary.DeclaredParameters.SkipLast(getAccess >= setAccess? 0 : 1).Select(p => p.GetParameterString())) + "] { "); sb.Append("this[" + string.Join(", ", primary.DeclaredParameters.SkipLast(getAccess >= setAccess? 0 : 1).Select(p => p.GetParameterString(!SuppressMetadata))) + "] { ");
sb.Append((prop.CanRead? prop.GetMethod.CustomAttributes.Where(a => !SuppressGenerated || a.AttributeType.FullName != CGAttribute).ToString(inline: true) sb.Append((prop.CanRead? prop.GetMethod.CustomAttributes.Where(a => !SuppressGenerated || a.AttributeType.FullName != CGAttribute).ToString(inline: true, emitPointer: !SuppressMetadata)
+ (getAccess < setAccess? prop.GetMethod.GetAccessModifierString() : "") + "get; " : "") + (getAccess < setAccess? prop.GetMethod.GetAccessModifierString() : "") + "get; " : "")
+ (prop.CanWrite? prop.SetMethod.CustomAttributes.Where(a => !SuppressGenerated || a.AttributeType.FullName != CGAttribute).ToString(inline: true) + (prop.CanWrite? prop.SetMethod.CustomAttributes.Where(a => !SuppressGenerated || a.AttributeType.FullName != CGAttribute).ToString(inline: true, emitPointer: !SuppressMetadata)
+ (setAccess < getAccess? prop.SetMethod.GetAccessModifierString() : "") + "set; " : "") + "}"); + (setAccess < getAccess? prop.SetMethod.GetAccessModifierString() : "") + "set; " : "") + "}");
if (!SuppressMetadata) {
if ((prop.CanRead && prop.GetMethod.VirtualAddress != 0) || (prop.CanWrite && prop.SetMethod.VirtualAddress != 0)) if ((prop.CanRead && prop.GetMethod.VirtualAddress != 0) || (prop.CanWrite && prop.SetMethod.VirtualAddress != 0))
sb.Append(" // "); sb.Append(" // ");
sb.Append((prop.CanRead && prop.GetMethod.VirtualAddress != 0 ? prop.GetMethod.VirtualAddress.ToAddressString() + " " : "") sb.Append((prop.CanRead && prop.GetMethod.VirtualAddress != 0 ? prop.GetMethod.VirtualAddress.ToAddressString() + " " : "")
+ (prop.CanWrite && prop.SetMethod.VirtualAddress != 0 ? prop.SetMethod.VirtualAddress.ToAddressString() : "") + "\n"); + (prop.CanWrite && prop.SetMethod.VirtualAddress != 0 ? prop.SetMethod.VirtualAddress.ToAddressString() : ""));
}
sb.Append("\n");
usedMethods.Add(prop.GetMethod); usedMethods.Add(prop.GetMethod);
usedMethods.Add(prop.SetMethod); usedMethods.Add(prop.SetMethod);
} }
@@ -244,7 +253,7 @@ namespace Il2CppInspector
sb.Clear(); sb.Clear();
foreach (var evt in type.DeclaredEvents) { foreach (var evt in type.DeclaredEvents) {
// Attributes // Attributes
sb.Append(evt.CustomAttributes.OrderBy(a => a.AttributeType.Name).ToString(prefix + "\t")); sb.Append(evt.CustomAttributes.OrderBy(a => a.AttributeType.Name).ToString(prefix + "\t", emitPointer: !SuppressMetadata));
string modifiers = evt.AddMethod?.GetModifierString(); string modifiers = evt.AddMethod?.GetModifierString();
sb.Append($"{prefix}\t{modifiers}event {evt.EventHandlerType.CSharpName} {evt.Name} {{\n"); sb.Append($"{prefix}\t{modifiers}event {evt.EventHandlerType.CSharpName} {evt.Name} {{\n");
@@ -252,7 +261,7 @@ namespace Il2CppInspector
if (evt.AddMethod != null) m.Add("add", evt.AddMethod.VirtualAddress); if (evt.AddMethod != null) m.Add("add", evt.AddMethod.VirtualAddress);
if (evt.RemoveMethod != null) m.Add("remove", evt.RemoveMethod.VirtualAddress); if (evt.RemoveMethod != null) m.Add("remove", evt.RemoveMethod.VirtualAddress);
if (evt.RaiseMethod != null) m.Add("raise", evt.RaiseMethod.VirtualAddress); if (evt.RaiseMethod != null) m.Add("raise", evt.RaiseMethod.VirtualAddress);
sb.Append(string.Join("\n", m.Select(x => $"{prefix}\t\t{x.Key}; // {x.Value.ToAddressString()}")) + "\n" + prefix + "\t}\n"); sb.Append(string.Join("\n", m.Select(x => $"{prefix}\t\t{x.Key};{(SuppressMetadata? "" : " // " + x.Value.ToAddressString())}")) + "\n" + prefix + "\t}\n");
usedMethods.Add(evt.AddMethod); usedMethods.Add(evt.AddMethod);
usedMethods.Add(evt.RemoveMethod); usedMethods.Add(evt.RemoveMethod);
usedMethods.Add(evt.RaiseMethod); usedMethods.Add(evt.RaiseMethod);
@@ -266,11 +275,11 @@ namespace Il2CppInspector
sb.Clear(); sb.Clear();
foreach (var method in type.DeclaredConstructors) { foreach (var method in type.DeclaredConstructors) {
// Attributes // Attributes
sb.Append(method.CustomAttributes.OrderBy(a => a.AttributeType.Name).ToString(prefix + "\t")); sb.Append(method.CustomAttributes.OrderBy(a => a.AttributeType.Name).ToString(prefix + "\t", emitPointer: !SuppressMetadata));
sb.Append($"{prefix}\t{method.GetModifierString()}{method.DeclaringType.UnmangledBaseName}{method.GetTypeParametersString()}("); sb.Append($"{prefix}\t{method.GetModifierString()}{method.DeclaringType.UnmangledBaseName}{method.GetTypeParametersString()}(");
sb.Append(method.GetParametersString()); sb.Append(method.GetParametersString(!SuppressMetadata) + ");");
sb.Append(");" + (method.VirtualAddress != 0 ? $" // {method.VirtualAddress.ToAddressString()}" : "") + "\n"); sb.Append((!SuppressMetadata && method.VirtualAddress != 0 ? $" // {method.VirtualAddress.ToAddressString()}" : "") + "\n");
} }
codeBlocks.Add("Constructors", sb.ToString()); codeBlocks.Add("Constructors", sb.ToString());
@@ -294,7 +303,7 @@ namespace Il2CppInspector
// TODO: DefaultMemberAttribute should be output if it is present and the type does not have an indexer, otherwise suppressed // TODO: DefaultMemberAttribute should be output if it is present and the type does not have an indexer, otherwise suppressed
// See https://docs.microsoft.com/en-us/dotnet/api/system.reflection.defaultmemberattribute?view=netframework-4.8 // See https://docs.microsoft.com/en-us/dotnet/api/system.reflection.defaultmemberattribute?view=netframework-4.8
sb.Append(type.CustomAttributes.Where(a => a.AttributeType.FullName != DMAttribute && a.AttributeType.FullName != ExtAttribute) sb.Append(type.CustomAttributes.Where(a => a.AttributeType.FullName != DMAttribute && a.AttributeType.FullName != ExtAttribute)
.OrderBy(a => a.AttributeType.Name).ToString(prefix)); .OrderBy(a => a.AttributeType.Name).ToString(prefix, emitPointer: !SuppressMetadata));
// Roll-up multicast delegates to use the 'delegate' syntactic sugar // Roll-up multicast delegates to use the 'delegate' syntactic sugar
if (type.IsClass && type.IsSealed && type.BaseType?.FullName == "System.MulticastDelegate") { if (type.IsClass && type.IsSealed && type.BaseType?.FullName == "System.MulticastDelegate") {
@@ -302,12 +311,14 @@ namespace Il2CppInspector
var del = type.GetMethod("Invoke"); var del = type.GetMethod("Invoke");
// IL2CPP doesn't seem to retain return type attributes // IL2CPP doesn't seem to retain return type attributes
//sb.Append(del.ReturnType.CustomAttributes.ToString(prefix, "return: ")); //sb.Append(del.ReturnType.CustomAttributes.ToString(prefix, "return: ", emitPointer: !SuppressMetadata));
if (del.RequiresUnsafeContext) if (del.RequiresUnsafeContext)
sb.Append("unsafe "); sb.Append("unsafe ");
sb.Append($"delegate {del.ReturnType.CSharpName} {type.CSharpTypeDeclarationName}("); sb.Append($"delegate {del.ReturnType.CSharpName} {type.CSharpTypeDeclarationName}(");
sb.Append(del.GetParametersString()); sb.Append(del.GetParametersString(!SuppressMetadata) + ");");
sb.Append($"); // TypeDefIndex: {type.Index}; {del.VirtualAddress.ToAddressString()}\n"); if (!SuppressMetadata)
sb.Append($" // TypeDefIndex: {type.Index}; {del.VirtualAddress.ToAddressString()}");
sb.Append("\n");
return sb.ToString(); return sb.ToString();
} }
@@ -320,7 +331,10 @@ namespace Il2CppInspector
@base.Insert(0, type.GetEnumUnderlyingType().CSharpName); @base.Insert(0, type.GetEnumUnderlyingType().CSharpName);
var baseText = @base.Count > 0 ? " : " + string.Join(", ", @base) : string.Empty; var baseText = @base.Count > 0 ? " : " + string.Join(", ", @base) : string.Empty;
sb.Append($"{type.CSharpTypeDeclarationName}{baseText} // TypeDefIndex: {type.Index}\n"); sb.Append($"{type.CSharpTypeDeclarationName}{baseText}");
if (!SuppressMetadata)
sb.Append($" // TypeDefIndex: {type.Index}");
sb.Append("\n");
if (type.GenericTypeParameters != null) if (type.GenericTypeParameters != null)
foreach (var gp in type.GenericTypeParameters) { foreach (var gp in type.GenericTypeParameters) {
@@ -352,16 +366,16 @@ namespace Il2CppInspector
var writer = new StringBuilder(); var writer = new StringBuilder();
// Attributes // Attributes
writer.Append(method.CustomAttributes.Where(a => a.AttributeType.FullName != ExtAttribute).OrderBy(a => a.AttributeType.Name).ToString(prefix + "\t")); writer.Append(method.CustomAttributes.Where(a => a.AttributeType.FullName != ExtAttribute).OrderBy(a => a.AttributeType.Name).ToString(prefix + "\t", emitPointer: !SuppressMetadata));
// IL2CPP doesn't seem to retain return type attributes // IL2CPP doesn't seem to retain return type attributes
//writer.Append(method.ReturnType.CustomAttributes.ToString(prefix + "\t", "return: ")); //writer.Append(method.ReturnType.CustomAttributes.ToString(prefix + "\t", "return: ", emitPointer: !SuppressMetadata));
writer.Append($"{prefix}\t{method.GetModifierString()}"); writer.Append($"{prefix}\t{method.GetModifierString()}");
if (method.Name != "op_Implicit" && method.Name != "op_Explicit") if (method.Name != "op_Implicit" && method.Name != "op_Explicit")
writer.Append($"{method.ReturnParameter.GetReturnParameterString()} {method.CSharpName}{method.GetTypeParametersString()}"); writer.Append($"{method.ReturnParameter.GetReturnParameterString()} {method.CSharpName}{method.GetTypeParametersString()}");
else else
writer.Append($"{method.CSharpName}{method.ReturnType.CSharpName}"); writer.Append($"{method.CSharpName}{method.ReturnType.CSharpName}");
writer.Append("(" + method.GetParametersString() + ")"); writer.Append("(" + method.GetParametersString(!SuppressMetadata) + ")");
if (method.GenericTypeParameters != null) if (method.GenericTypeParameters != null)
foreach (var gp in method.GenericTypeParameters) { foreach (var gp in method.GenericTypeParameters) {
@@ -370,7 +384,7 @@ namespace Il2CppInspector
writer.Append($"\n{prefix}\t\t{constraint}"); writer.Append($"\n{prefix}\t\t{constraint}");
} }
writer.Append(";" + (method.VirtualAddress != 0 ? $" // {method.VirtualAddress.ToAddressString()}" : "") + "\n"); writer.Append(";" + (!SuppressMetadata && method.VirtualAddress != 0 ? $" // {method.VirtualAddress.ToAddressString()}" : "") + "\n");
return writer.ToString(); return writer.ToString();
} }

View File

@@ -48,6 +48,9 @@ namespace Il2CppInspector
[Option('g', "no-suppress-cg", Required = false, HelpText = "Don't suppress generation of C# code for items with CompilerGenerated attribute", Default = false)] [Option('g', "no-suppress-cg", Required = false, HelpText = "Don't suppress generation of C# code for items with CompilerGenerated attribute", Default = false)]
public bool DontSuppressCompilerGenerated { get; set; } public bool DontSuppressCompilerGenerated { get; set; }
[Option('n', "suppress-metadata", Required = false, HelpText = "Suppress method pointers, field offsets and type indices from C# output. Useful for comparing two versions of a binary for changes with a diff tool", Default = false)]
public bool SuppressMetadata { get; set; }
} }
public static int Main(string[] args) => public static int Main(string[] args) =>
@@ -84,7 +87,8 @@ namespace Il2CppInspector
// C# signatures output // C# signatures output
var writer = new Il2CppCSharpDumper(model) { var writer = new Il2CppCSharpDumper(model) {
ExcludedNamespaces = options.ExcludedNamespaces.ToList(), ExcludedNamespaces = options.ExcludedNamespaces.ToList(),
SuppressGenerated = !options.DontSuppressCompilerGenerated SuppressGenerated = !options.DontSuppressCompilerGenerated,
SuppressMetadata = options.SuppressMetadata
}; };
var imageSuffix = i++ > 0 ? "-" + (i - 1) : ""; var imageSuffix = i++ > 0 ? "-" + (i - 1) : "";

View File

@@ -7,7 +7,7 @@ namespace Il2CppInspector.Reflection
public static class Extensions public static class Extensions
{ {
// Convert a list of CustomAttributeData objects into C#-friendly attribute usages // Convert a list of CustomAttributeData objects into C#-friendly attribute usages
public static string ToString(this IEnumerable<CustomAttributeData> attributes, string linePrefix = "", string attributePrefix = "", bool inline = false) { public static string ToString(this IEnumerable<CustomAttributeData> attributes, string linePrefix = "", string attributePrefix = "", bool inline = false, bool emitPointer = false) {
var sb = new StringBuilder(); var sb = new StringBuilder();
foreach (var cad in attributes) { foreach (var cad in attributes) {
@@ -15,7 +15,10 @@ namespace Il2CppInspector.Reflection
var suffix = name.LastIndexOf("Attribute", StringComparison.Ordinal); var suffix = name.LastIndexOf("Attribute", StringComparison.Ordinal);
if (suffix != -1) if (suffix != -1)
name = name[..suffix]; name = name[..suffix];
sb.Append($"{linePrefix}[{attributePrefix}{name}] {(inline? "/*" : "//")} {((ulong)cad.VirtualAddress).ToAddressString()}{(inline? " */ " : "\n")}"); sb.Append($"{linePrefix}[{attributePrefix}{name}]");
if (emitPointer)
sb.Append($" {(inline? "/*" : "//")} {((ulong)cad.VirtualAddress).ToAddressString()}{(inline? " */" : "")}");
sb.Append(inline? " ":"\n");
} }
return sb.ToString(); return sb.ToString();

View File

@@ -163,7 +163,7 @@ namespace Il2CppInspector.Reflection
} }
// Get C# syntax-friendly list of parameters // Get C# syntax-friendly list of parameters
public string GetParametersString() => string.Join(", ", DeclaredParameters.Select(p => p.GetParameterString())); public string GetParametersString(bool emitPointer = false) => string.Join(", ", DeclaredParameters.Select(p => p.GetParameterString(emitPointer)));
public string GetTypeParametersString() => GenericTypeParameters == null? "" : public string GetTypeParametersString() => GenericTypeParameters == null? "" :
"<" + string.Join(", ", GenericTypeParameters.Select(p => p.CSharpName)) + ">"; "<" + string.Join(", ", GenericTypeParameters.Select(p => p.CSharpName)) + ">";

View File

@@ -96,9 +96,9 @@ namespace Il2CppInspector.Reflection
private string getCSharpSignatureString() => $"{GetModifierString()}{ParameterType.CSharpName}"; private string getCSharpSignatureString() => $"{GetModifierString()}{ParameterType.CSharpName}";
public string GetSignatureString() => $"{GetModifierString()}{ParameterType.FullName}"; public string GetSignatureString() => $"{GetModifierString()}{ParameterType.FullName}";
public string GetParameterString() => IsRetval? null : public string GetParameterString(bool emitPointer = false) => IsRetval? null :
(Position == 0 && Member.GetCustomAttributes("System.Runtime.CompilerServices.ExtensionAttribute").Any()? "this ":"") (Position == 0 && Member.GetCustomAttributes("System.Runtime.CompilerServices.ExtensionAttribute").Any()? "this ":"")
+ $"{CustomAttributes.ToString(inline: true).Replace("[ParamArray]", "params")}" + $"{CustomAttributes.ToString(inline: true, emitPointer: emitPointer).Replace("[ParamArray]", "params")}"
+ $"{getCSharpSignatureString()} {Name}" + $"{getCSharpSignatureString()} {Name}"
+ (HasDefaultValue ? " = " + DefaultValue.ToCSharpValue() : ""); + (HasDefaultValue ? " = " + DefaultValue.ToCSharpValue() : "");

View File

@@ -54,7 +54,7 @@ namespace Il2CppInspector
// Dump each image in the binary separately // Dump each image in the binary separately
int i = 0; int i = 0;
foreach (var il2cpp in inspectors) foreach (var il2cpp in inspectors)
new Il2CppCSharpDumper(new Il2CppModel(il2cpp)) {ExcludedNamespaces = excludedNamespaces, SuppressGenerated = true} new Il2CppCSharpDumper(new Il2CppModel(il2cpp)) {ExcludedNamespaces = excludedNamespaces, SuppressGenerated = true, SuppressMetadata = false}
.WriteSingleFile(testPath + @"\test-result" + (i++ > 0 ? "-" + (i - 1) : "") + ".cs"); .WriteSingleFile(testPath + @"\test-result" + (i++ > 0 ? "-" + (i - 1) : "") + ".cs");
// Compare test result with expected result // Compare test result with expected result

View File

@@ -1,5 +1,5 @@
# Il2CppInspector # Il2CppInspector
Extract types, methods, properties and fields from Unity IL2CPP binaries. Easily extract types and metadata from IL2CPP binaries.
* **No manual reverse-engineering required; all data is calculated automatically!** * **No manual reverse-engineering required; all data is calculated automatically!**
* Supports ELF (Android .so), PE (Windows .exe), Mach-O (Apple iOS/Mac) and Universal Binary (Fat Mach-O) file formats * Supports ELF (Android .so), PE (Windows .exe), Mach-O (Apple iOS/Mac) and Universal Binary (Fat Mach-O) file formats
@@ -13,7 +13,7 @@ Extract types, methods, properties and fields from Unity IL2CPP binaries.
* Dynamic symbol table scanning for ELF binaries if present * Dynamic symbol table scanning for ELF binaries if present
* Symbol relocation handling for ELF binaries * Symbol relocation handling for ELF binaries
* **Il2CppInspector** re-usable class library for low-level access to IL2CPP binaries and metadata * **Il2CppInspector** re-usable class library for low-level access to IL2CPP binaries and metadata
* **Il2CppReflector** re-usable class library for high-level .NET Reflection-style access to IL2CPP types and data as a tree model * **Il2CppModel** re-usable class library for high-level .NET Reflection-style access to IL2CPP types and data as a tree model
* Test chassis for automated integration testing of IL2CPP binaries * Test chassis for automated integration testing of IL2CPP binaries
Class library targets .NET Standard 2.1. Application targets .NET Core 3.0. Built with Visual Studio 2019. Class library targets .NET Standard 2.1. Application targets .NET Core 3.0. Built with Visual Studio 2019.
@@ -45,6 +45,7 @@ File format and architecture are automatically detected.
-s, --sort (Default: index) Sort order of type definitions in C# output ('index' = by type definition index, 'name' = by type name). No effect when using file-per-class layout -s, --sort (Default: index) Sort order of type definitions in C# output ('index' = by type definition index, 'name' = by type name). No effect when using file-per-class layout
-f, --flatten (Default: false) Flatten the namespace hierarchy into a single folder rather than using per-namespace subfolders. Only used when layout is per-namespace or per-class -f, --flatten (Default: false) Flatten the namespace hierarchy into a single folder rather than using per-namespace subfolders. Only used when layout is per-namespace or per-class
-g, --no-suppress-cg (Default: false) Don't suppress generation of C# code for items with CompilerGenerated attribute -g, --no-suppress-cg (Default: false) Don't suppress generation of C# code for items with CompilerGenerated attribute
-n, --suppress-metadata (Default: false) Suppress method pointers, field offsets and type indices from C# output. Useful for comparing two versions of a binary for changes with a diff tool
``` ```
Defaults if not specified: Defaults if not specified: