Comments and minor refactoring into lambdas
This commit is contained in:
@@ -15,14 +15,19 @@ using System.Text.RegularExpressions;
|
|||||||
|
|
||||||
namespace Il2CppInspector.Outputs
|
namespace Il2CppInspector.Outputs
|
||||||
{
|
{
|
||||||
|
// Class for generating C header declarations from Reflection objects (TypeInfo, etc.)
|
||||||
public class CppDeclarations
|
public class CppDeclarations
|
||||||
{
|
{
|
||||||
private readonly Il2CppModel model;
|
private readonly Il2CppModel model;
|
||||||
|
|
||||||
|
// Version number and header file to generate structures for
|
||||||
public UnityVersion UnityVersion { get; }
|
public UnityVersion UnityVersion { get; }
|
||||||
public UnityHeader UnityHeader { get; }
|
public UnityHeader UnityHeader { get; }
|
||||||
|
|
||||||
// How inheritance of type structs should be represented.
|
// How inheritance of type structs should be represented.
|
||||||
|
// Different C++ compilers lay out C++ class structures differently,
|
||||||
|
// meaning that the compiler must be known in order to generate class type structures
|
||||||
|
// with the correct layout.
|
||||||
public enum InheritanceStyleEnum
|
public enum InheritanceStyleEnum
|
||||||
{
|
{
|
||||||
C, // Inheritance structs use C syntax, and will automatically choose MSVC or GCC based on inferred compiler.
|
C, // Inheritance structs use C syntax, and will automatically choose MSVC or GCC based on inferred compiler.
|
||||||
@@ -59,6 +64,7 @@ namespace Il2CppInspector.Outputs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// C type declaration used to name variables of the given C# type
|
||||||
public string AsCType(TypeInfo ti) {
|
public string AsCType(TypeInfo ti) {
|
||||||
// IsArray case handled by TypeNamer.GetName
|
// IsArray case handled by TypeNamer.GetName
|
||||||
if (ti.IsByRef || ti.IsPointer) {
|
if (ti.IsByRef || ti.IsPointer) {
|
||||||
@@ -90,9 +96,19 @@ namespace Il2CppInspector.Outputs
|
|||||||
}
|
}
|
||||||
|
|
||||||
#region Field Struct Generation
|
#region Field Struct Generation
|
||||||
|
/* Generating field structures (structures for the fields of a given type) occurs in two passes.
|
||||||
|
* In the first pass (VisitFieldStructs), we walk over a type and all of the types that the resulting structure would depend on.
|
||||||
|
* In the second pass (GenerateVisitedFieldStructs), we generate all type structures in the necessary order.
|
||||||
|
* (For example: structures for value types must precede any usage of those value types for layout reasons).
|
||||||
|
*/
|
||||||
|
|
||||||
|
// A cache of field structures that have already been generated, to eliminate duplicate definitions
|
||||||
private readonly HashSet<TypeInfo> VisitedFieldStructs = new HashSet<TypeInfo>();
|
private readonly HashSet<TypeInfo> VisitedFieldStructs = new HashSet<TypeInfo>();
|
||||||
|
|
||||||
|
// A queue of field structures that need to be generated.
|
||||||
private readonly List<TypeInfo> TodoFieldStructs = new List<TypeInfo>();
|
private readonly List<TypeInfo> TodoFieldStructs = new List<TypeInfo>();
|
||||||
|
|
||||||
|
// Walk over dependencies of the given type, to figure out what field structures it depends on
|
||||||
private void VisitFieldStructs(TypeInfo ti) {
|
private void VisitFieldStructs(TypeInfo ti) {
|
||||||
if (VisitedFieldStructs.Contains(ti))
|
if (VisitedFieldStructs.Contains(ti))
|
||||||
return;
|
return;
|
||||||
@@ -116,12 +132,15 @@ namespace Il2CppInspector.Outputs
|
|||||||
TodoFieldStructs.Add(ti);
|
TodoFieldStructs.Add(ti);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate the fields for the base class of all objects (Il2CppObject)
|
||||||
|
// The two fields are inlined so that we can specialize the klass member for each type object
|
||||||
private void GenerateObjectFields(StringBuilder csrc, TypeInfo ti) {
|
private void GenerateObjectFields(StringBuilder csrc, TypeInfo ti) {
|
||||||
csrc.Append(
|
csrc.Append(
|
||||||
$" struct {TypeNamer.GetName(ti)}__Class *klass;\n" +
|
$" struct {TypeNamer.GetName(ti)}__Class *klass;\n" +
|
||||||
$" struct MonitorData *monitor;\n");
|
$" struct MonitorData *monitor;\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate structure fields for each field of a given type
|
||||||
private void GenerateFieldList(StringBuilder csrc, Namespace ns, TypeInfo ti) {
|
private void GenerateFieldList(StringBuilder csrc, Namespace ns, TypeInfo ti) {
|
||||||
var namer = ns.MakeNamer<FieldInfo>((field) => sanitizeIdentifier(field.Name));
|
var namer = ns.MakeNamer<FieldInfo>((field) => sanitizeIdentifier(field.Name));
|
||||||
foreach (var field in ti.DeclaredFields) {
|
foreach (var field in ti.DeclaredFields) {
|
||||||
@@ -131,6 +150,7 @@ namespace Il2CppInspector.Outputs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate the C structure for a value type, such as an enum or struct
|
||||||
private void GenerateValueFieldStruct(StringBuilder csrc, TypeInfo ti) {
|
private void GenerateValueFieldStruct(StringBuilder csrc, TypeInfo ti) {
|
||||||
string name = TypeNamer.GetName(ti);
|
string name = TypeNamer.GetName(ti);
|
||||||
if (ti.IsEnum) {
|
if (ti.IsEnum) {
|
||||||
@@ -163,6 +183,7 @@ namespace Il2CppInspector.Outputs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate the C structure for a reference type, such as a class or array
|
||||||
private void GenerateRefFieldStruct(StringBuilder csrc, TypeInfo ti) {
|
private void GenerateRefFieldStruct(StringBuilder csrc, TypeInfo ti) {
|
||||||
var name = TypeNamer.GetName(ti);
|
var name = TypeNamer.GetName(ti);
|
||||||
if (ti.IsArray || ti.FullName == "System.Array") {
|
if (ti.IsArray || ti.FullName == "System.Array") {
|
||||||
@@ -242,6 +263,7 @@ namespace Il2CppInspector.Outputs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "Flush" the list of visited types, generating C structures for each one
|
||||||
private void GenerateVisitedFieldStructs(StringBuilder csrc) {
|
private void GenerateVisitedFieldStructs(StringBuilder csrc) {
|
||||||
foreach (var ti in TodoFieldStructs) {
|
foreach (var ti in TodoFieldStructs) {
|
||||||
if (ti.IsEnum || ti.IsValueType)
|
if (ti.IsEnum || ti.IsValueType)
|
||||||
@@ -254,7 +276,9 @@ namespace Il2CppInspector.Outputs
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Class Struct Generation
|
#region Class Struct Generation
|
||||||
private Dictionary<TypeInfo, TypeInfo> ConcreteImplementations = new Dictionary<TypeInfo, TypeInfo>();
|
|
||||||
|
// Concrete implementations for abstract classes, for use in looking up VTable signatures and names
|
||||||
|
private readonly Dictionary<TypeInfo, TypeInfo> ConcreteImplementations = new Dictionary<TypeInfo, TypeInfo>();
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// VTables for abstract types have "null" in place of abstract functions.
|
/// VTables for abstract types have "null" in place of abstract functions.
|
||||||
/// This function searches for concrete implementations so that we can properly
|
/// This function searches for concrete implementations so that we can properly
|
||||||
@@ -347,6 +371,7 @@ namespace Il2CppInspector.Outputs
|
|||||||
TodoTypeStructs.Add(ti);
|
TodoTypeStructs.Add(ti);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate the C structure for virtual function calls in a given type (the VTable)
|
||||||
private void GenerateVTableStruct(StringBuilder csrc, TypeInfo ti) {
|
private void GenerateVTableStruct(StringBuilder csrc, TypeInfo ti) {
|
||||||
MethodBase[] vtable;
|
MethodBase[] vtable;
|
||||||
if (ti.IsInterface) {
|
if (ti.IsInterface) {
|
||||||
@@ -380,6 +405,7 @@ namespace Il2CppInspector.Outputs
|
|||||||
csrc.Append($"}};\n");
|
csrc.Append($"}};\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate the overall Il2CppClass-shaped structure for the given type
|
||||||
private void GenerateTypeStruct(StringBuilder csrc, TypeInfo ti) {
|
private void GenerateTypeStruct(StringBuilder csrc, TypeInfo ti) {
|
||||||
var name = TypeNamer.GetName(ti);
|
var name = TypeNamer.GetName(ti);
|
||||||
GenerateVTableStruct(csrc, ti);
|
GenerateVTableStruct(csrc, ti);
|
||||||
@@ -452,6 +478,7 @@ namespace Il2CppInspector.Outputs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate a C declaration for a method
|
||||||
private string GenerateMethodDeclaration(MethodBase method, string name, TypeInfo declaringType) {
|
private string GenerateMethodDeclaration(MethodBase method, string name, TypeInfo declaringType) {
|
||||||
string retType;
|
string retType;
|
||||||
if (method is MethodInfo mi) {
|
if (method is MethodInfo mi) {
|
||||||
|
|||||||
@@ -16,8 +16,14 @@ namespace Il2CppInspector.Outputs
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class Namespace
|
public class Namespace
|
||||||
{
|
{
|
||||||
|
// The central data structure that keeps track of which names have been generated
|
||||||
|
// The value for any given key K is the number of unique objects originally named K, minus 1.
|
||||||
|
// Each time we see a particular name assigned to a new, different object, we bump its rename count
|
||||||
|
// and give it a suffix. For example, if we have three different objects all named X,
|
||||||
|
// we'd name them X, X_1, and X_2, and renameCount["X"] would be 2.
|
||||||
private readonly Dictionary<string, int> renameCount = new Dictionary<string, int>();
|
private readonly Dictionary<string, int> renameCount = new Dictionary<string, int>();
|
||||||
|
|
||||||
|
// Mark a name as reserved without assigning an object to it (e.g. for keywords and built-in names)
|
||||||
public void ReserveName(string name) {
|
public void ReserveName(string name) {
|
||||||
if (renameCount.ContainsKey(name)) {
|
if (renameCount.ContainsKey(name)) {
|
||||||
throw new Exception($"Can't reserve {name}: already taken!");
|
throw new Exception($"Can't reserve {name}: already taken!");
|
||||||
@@ -25,6 +31,7 @@ namespace Il2CppInspector.Outputs
|
|||||||
renameCount[name] = 0;
|
renameCount[name] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a Namer object which will give names to objects of type T which are unique within this namespace
|
||||||
public Namer<T> MakeNamer<T>(Namer<T>.KeyFunc keyFunc) {
|
public Namer<T> MakeNamer<T>(Namer<T>.KeyFunc keyFunc) {
|
||||||
return new Namer<T>(this, keyFunc);
|
return new Namer<T>(this, keyFunc);
|
||||||
}
|
}
|
||||||
@@ -35,8 +42,14 @@ namespace Il2CppInspector.Outputs
|
|||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
public class Namer<T>
|
public class Namer<T>
|
||||||
{
|
{
|
||||||
|
// Parent namespace
|
||||||
private Namespace ns;
|
private Namespace ns;
|
||||||
|
|
||||||
|
// Names given out by this Namer.
|
||||||
private readonly Dictionary<T, string> names = new Dictionary<T, string>();
|
private readonly Dictionary<T, string> names = new Dictionary<T, string>();
|
||||||
|
|
||||||
|
// The function which maps a T object to a suitably mangled name
|
||||||
|
// That name might be further mangled by the Namer to make the name unique within the namespace
|
||||||
public delegate string KeyFunc(T t);
|
public delegate string KeyFunc(T t);
|
||||||
private readonly KeyFunc keyFunc;
|
private readonly KeyFunc keyFunc;
|
||||||
|
|
||||||
@@ -45,12 +58,20 @@ namespace Il2CppInspector.Outputs
|
|||||||
this.keyFunc = keyFunc;
|
this.keyFunc = keyFunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uniquely name an object within the parent namespace
|
||||||
public string GetName(T t) {
|
public string GetName(T t) {
|
||||||
|
// If we've named this particular object before, just return that name
|
||||||
string name;
|
string name;
|
||||||
if (names.TryGetValue(t, out name))
|
if (names.TryGetValue(t, out name))
|
||||||
return name;
|
return name;
|
||||||
|
// Obtain the mangled name for the object
|
||||||
name = keyFunc(t);
|
name = keyFunc(t);
|
||||||
// This approach avoids linear scan (quadratic blowup) if there are a lot of similarly-named objects.
|
// Check if the mangled name has been given to another object - if it has,
|
||||||
|
// we need to give the object a new suffixed name (e.g. X_1).
|
||||||
|
// We might need to repeat this process if the new suffixed name also exists.
|
||||||
|
// Each iteration tacks on another suffix - so we normally expect this to only take
|
||||||
|
// a single iteration. (It might take multiple iterations in rare cases, e.g.
|
||||||
|
// another object had the mangled name X_1).
|
||||||
if (ns.renameCount.ContainsKey(name)) {
|
if (ns.renameCount.ContainsKey(name)) {
|
||||||
int v = ns.renameCount[name] + 1;
|
int v = ns.renameCount[name] + 1;
|
||||||
while (ns.renameCount.ContainsKey(name + "_" + v))
|
while (ns.renameCount.ContainsKey(name + "_" + v))
|
||||||
|
|||||||
@@ -9,16 +9,22 @@ using System.Reflection;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Il2CppInspector.Outputs.UnityHeaders
|
namespace Il2CppInspector.Outputs.UnityHeaders
|
||||||
{
|
{
|
||||||
|
// Each instance of UnityHeader represents one header file which potentially covers multiple versions of Unity.
|
||||||
public class UnityHeader
|
public class UnityHeader
|
||||||
{
|
{
|
||||||
|
// Metadata version of this header. Multiple headers may have the same metadata version
|
||||||
public double MetadataVersion { get; }
|
public double MetadataVersion { get; }
|
||||||
|
|
||||||
|
// Minimum and maximum Unity version numbers corresponding to this header. Both endpoints are inclusive
|
||||||
public UnityVersion MinVersion { get; }
|
public UnityVersion MinVersion { get; }
|
||||||
public UnityVersion MaxVersion { get; }
|
public UnityVersion MaxVersion { get; }
|
||||||
|
|
||||||
|
// Filename for the embedded .h resource file containing the header
|
||||||
public string HeaderFilename { get; }
|
public string HeaderFilename { get; }
|
||||||
|
|
||||||
private UnityHeader(string headerFilename) {
|
private UnityHeader(string headerFilename) {
|
||||||
HeaderFilename = headerFilename;
|
HeaderFilename = headerFilename;
|
||||||
var bits = headerFilename.Replace(".h", "").Split("-");
|
var bits = headerFilename.Replace(".h", "").Split("-");
|
||||||
@@ -39,10 +45,10 @@ namespace Il2CppInspector.Outputs.UnityHeaders
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Contains(UnityVersion version) {
|
// Determine if this header supports the given version of Unity
|
||||||
return version.CompareTo(MinVersion) >= 0 && (MaxVersion == null || version.CompareTo(MaxVersion) <= 0);
|
public bool Contains(UnityVersion version) => version.CompareTo(MinVersion) >= 0 && (MaxVersion == null || version.CompareTo(MaxVersion) <= 0);
|
||||||
}
|
|
||||||
|
|
||||||
|
// Return the contents of this header file as a string
|
||||||
public string GetHeaderText() {
|
public string GetHeaderText() {
|
||||||
string resourceName = typeof(UnityHeader).Namespace + "." + HeaderFilename;
|
string resourceName = typeof(UnityHeader).Namespace + "." + HeaderFilename;
|
||||||
Assembly assembly = Assembly.GetCallingAssembly();
|
Assembly assembly = Assembly.GetCallingAssembly();
|
||||||
@@ -55,6 +61,7 @@ namespace Il2CppInspector.Outputs.UnityHeaders
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List all header files embedded into this build of Il2Cpp
|
||||||
public static IEnumerable<UnityHeader> GetAllHeaders() {
|
public static IEnumerable<UnityHeader> GetAllHeaders() {
|
||||||
string prefix = typeof(UnityHeader).Namespace + ".";
|
string prefix = typeof(UnityHeader).Namespace + ".";
|
||||||
Assembly assembly = Assembly.GetCallingAssembly();
|
Assembly assembly = Assembly.GetCallingAssembly();
|
||||||
@@ -63,11 +70,13 @@ namespace Il2CppInspector.Outputs.UnityHeaders
|
|||||||
.Select(s => new UnityHeader(s.Substring(prefix.Length)));
|
.Select(s => new UnityHeader(s.Substring(prefix.Length)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the header file which supports the given version of Unity
|
||||||
public static UnityHeader GetHeaderForVersion(string version) => GetHeaderForVersion(new UnityVersion(version));
|
public static UnityHeader GetHeaderForVersion(string version) => GetHeaderForVersion(new UnityVersion(version));
|
||||||
public static UnityHeader GetHeaderForVersion(UnityVersion version) {
|
public static UnityHeader GetHeaderForVersion(UnityVersion version) => GetAllHeaders().Where(v => v.Contains(version)).First();
|
||||||
return GetAllHeaders().Where(v => v.Contains(version)).First();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Guess which header file(s) correspond to the given metadata+binary.
|
||||||
|
// Note that this may match multiple headers due to structural changes between versions
|
||||||
|
// that are not reflected in the metadata version.
|
||||||
public static List<UnityHeader> GuessHeadersForModel(Reflection.Il2CppModel model) {
|
public static List<UnityHeader> GuessHeadersForModel(Reflection.Il2CppModel model) {
|
||||||
List<UnityHeader> result = new List<UnityHeader>();
|
List<UnityHeader> result = new List<UnityHeader>();
|
||||||
foreach (var v in GetAllHeaders()) {
|
foreach (var v in GetAllHeaders()) {
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ using System.Text.RegularExpressions;
|
|||||||
|
|
||||||
namespace Il2CppInspector.Outputs.UnityHeaders
|
namespace Il2CppInspector.Outputs.UnityHeaders
|
||||||
{
|
{
|
||||||
|
// Parsed representation of a Unity version number, such as 5.3.0f1 or 2019.3.7.
|
||||||
public class UnityVersion : IComparable<UnityVersion>, IEquatable<UnityVersion>
|
public class UnityVersion : IComparable<UnityVersion>, IEquatable<UnityVersion>
|
||||||
{
|
{
|
||||||
|
// A sorted enumeration of build types, in order of maturity
|
||||||
public enum BuildTypeEnum
|
public enum BuildTypeEnum
|
||||||
{
|
{
|
||||||
Unspecified,
|
Unspecified,
|
||||||
@@ -44,6 +46,7 @@ namespace Il2CppInspector.Outputs.UnityHeaders
|
|||||||
_ => throw new ArgumentException("Unknown build type " + s),
|
_ => throw new ArgumentException("Unknown build type " + s),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Unity version number is of the form <Major>.<Minor>.<Update>[<BuildType><BuildNumber>]
|
||||||
public int Major { get; }
|
public int Major { get; }
|
||||||
public int Minor { get; }
|
public int Minor { get; }
|
||||||
public int Update { get; }
|
public int Update { get; }
|
||||||
@@ -70,6 +73,7 @@ namespace Il2CppInspector.Outputs.UnityHeaders
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compare two version numbers, intransitively (due to the Unspecified build type)
|
||||||
public int CompareTo(UnityVersion other) {
|
public int CompareTo(UnityVersion other) {
|
||||||
int res;
|
int res;
|
||||||
if (0 != (res = Major.CompareTo(other.Major)))
|
if (0 != (res = Major.CompareTo(other.Major)))
|
||||||
|
|||||||
Reference in New Issue
Block a user