Comments and minor refactoring into lambdas
This commit is contained in:
@@ -15,14 +15,19 @@ using System.Text.RegularExpressions;
|
||||
|
||||
namespace Il2CppInspector.Outputs
|
||||
{
|
||||
// Class for generating C header declarations from Reflection objects (TypeInfo, etc.)
|
||||
public class CppDeclarations
|
||||
{
|
||||
private readonly Il2CppModel model;
|
||||
|
||||
// Version number and header file to generate structures for
|
||||
public UnityVersion UnityVersion { get; }
|
||||
public UnityHeader UnityHeader { get; }
|
||||
|
||||
// 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
|
||||
{
|
||||
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) {
|
||||
// IsArray case handled by TypeNamer.GetName
|
||||
if (ti.IsByRef || ti.IsPointer) {
|
||||
@@ -90,9 +96,19 @@ namespace Il2CppInspector.Outputs
|
||||
}
|
||||
|
||||
#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>();
|
||||
|
||||
// A queue of field structures that need to be generated.
|
||||
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) {
|
||||
if (VisitedFieldStructs.Contains(ti))
|
||||
return;
|
||||
@@ -116,12 +132,15 @@ namespace Il2CppInspector.Outputs
|
||||
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) {
|
||||
csrc.Append(
|
||||
$" struct {TypeNamer.GetName(ti)}__Class *klass;\n" +
|
||||
$" struct MonitorData *monitor;\n");
|
||||
}
|
||||
|
||||
// Generate structure fields for each field of a given type
|
||||
private void GenerateFieldList(StringBuilder csrc, Namespace ns, TypeInfo ti) {
|
||||
var namer = ns.MakeNamer<FieldInfo>((field) => sanitizeIdentifier(field.Name));
|
||||
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) {
|
||||
string name = TypeNamer.GetName(ti);
|
||||
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) {
|
||||
var name = TypeNamer.GetName(ti);
|
||||
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) {
|
||||
foreach (var ti in TodoFieldStructs) {
|
||||
if (ti.IsEnum || ti.IsValueType)
|
||||
@@ -254,7 +276,9 @@ namespace Il2CppInspector.Outputs
|
||||
#endregion
|
||||
|
||||
#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>
|
||||
/// VTables for abstract types have "null" in place of abstract functions.
|
||||
/// This function searches for concrete implementations so that we can properly
|
||||
@@ -347,6 +371,7 @@ namespace Il2CppInspector.Outputs
|
||||
TodoTypeStructs.Add(ti);
|
||||
}
|
||||
|
||||
// Generate the C structure for virtual function calls in a given type (the VTable)
|
||||
private void GenerateVTableStruct(StringBuilder csrc, TypeInfo ti) {
|
||||
MethodBase[] vtable;
|
||||
if (ti.IsInterface) {
|
||||
@@ -380,6 +405,7 @@ namespace Il2CppInspector.Outputs
|
||||
csrc.Append($"}};\n");
|
||||
}
|
||||
|
||||
// Generate the overall Il2CppClass-shaped structure for the given type
|
||||
private void GenerateTypeStruct(StringBuilder csrc, TypeInfo ti) {
|
||||
var name = TypeNamer.GetName(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) {
|
||||
string retType;
|
||||
if (method is MethodInfo mi) {
|
||||
|
||||
@@ -16,8 +16,14 @@ namespace Il2CppInspector.Outputs
|
||||
/// </summary>
|
||||
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>();
|
||||
|
||||
// Mark a name as reserved without assigning an object to it (e.g. for keywords and built-in names)
|
||||
public void ReserveName(string name) {
|
||||
if (renameCount.ContainsKey(name)) {
|
||||
throw new Exception($"Can't reserve {name}: already taken!");
|
||||
@@ -25,6 +31,7 @@ namespace Il2CppInspector.Outputs
|
||||
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) {
|
||||
return new Namer<T>(this, keyFunc);
|
||||
}
|
||||
@@ -35,8 +42,14 @@ namespace Il2CppInspector.Outputs
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class Namer<T>
|
||||
{
|
||||
// Parent namespace
|
||||
private Namespace ns;
|
||||
|
||||
// Names given out by this Namer.
|
||||
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);
|
||||
private readonly KeyFunc keyFunc;
|
||||
|
||||
@@ -45,12 +58,20 @@ namespace Il2CppInspector.Outputs
|
||||
this.keyFunc = keyFunc;
|
||||
}
|
||||
|
||||
// Uniquely name an object within the parent namespace
|
||||
public string GetName(T t) {
|
||||
// If we've named this particular object before, just return that name
|
||||
string name;
|
||||
if (names.TryGetValue(t, out name))
|
||||
return name;
|
||||
// Obtain the mangled name for the object
|
||||
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)) {
|
||||
int v = ns.renameCount[name] + 1;
|
||||
while (ns.renameCount.ContainsKey(name + "_" + v))
|
||||
|
||||
@@ -9,16 +9,22 @@ using System.Reflection;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace Il2CppInspector.Outputs.UnityHeaders
|
||||
{
|
||||
// Each instance of UnityHeader represents one header file which potentially covers multiple versions of Unity.
|
||||
public class UnityHeader
|
||||
{
|
||||
// Metadata version of this header. Multiple headers may have the same metadata version
|
||||
public double MetadataVersion { get; }
|
||||
|
||||
// Minimum and maximum Unity version numbers corresponding to this header. Both endpoints are inclusive
|
||||
public UnityVersion MinVersion { get; }
|
||||
public UnityVersion MaxVersion { get; }
|
||||
|
||||
// Filename for the embedded .h resource file containing the header
|
||||
public string HeaderFilename { get; }
|
||||
|
||||
private UnityHeader(string headerFilename) {
|
||||
HeaderFilename = headerFilename;
|
||||
var bits = headerFilename.Replace(".h", "").Split("-");
|
||||
@@ -39,10 +45,10 @@ namespace Il2CppInspector.Outputs.UnityHeaders
|
||||
return res;
|
||||
}
|
||||
|
||||
public bool Contains(UnityVersion version) {
|
||||
return version.CompareTo(MinVersion) >= 0 && (MaxVersion == null || version.CompareTo(MaxVersion) <= 0);
|
||||
}
|
||||
// Determine if this header supports the given version of Unity
|
||||
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() {
|
||||
string resourceName = typeof(UnityHeader).Namespace + "." + HeaderFilename;
|
||||
Assembly assembly = Assembly.GetCallingAssembly();
|
||||
@@ -55,6 +61,7 @@ namespace Il2CppInspector.Outputs.UnityHeaders
|
||||
return result;
|
||||
}
|
||||
|
||||
// List all header files embedded into this build of Il2Cpp
|
||||
public static IEnumerable<UnityHeader> GetAllHeaders() {
|
||||
string prefix = typeof(UnityHeader).Namespace + ".";
|
||||
Assembly assembly = Assembly.GetCallingAssembly();
|
||||
@@ -63,11 +70,13 @@ namespace Il2CppInspector.Outputs.UnityHeaders
|
||||
.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(UnityVersion version) {
|
||||
return GetAllHeaders().Where(v => v.Contains(version)).First();
|
||||
}
|
||||
public static UnityHeader GetHeaderForVersion(UnityVersion version) => 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) {
|
||||
List<UnityHeader> result = new List<UnityHeader>();
|
||||
foreach (var v in GetAllHeaders()) {
|
||||
|
||||
@@ -10,8 +10,10 @@ using System.Text.RegularExpressions;
|
||||
|
||||
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>
|
||||
{
|
||||
// A sorted enumeration of build types, in order of maturity
|
||||
public enum BuildTypeEnum
|
||||
{
|
||||
Unspecified,
|
||||
@@ -44,6 +46,7 @@ namespace Il2CppInspector.Outputs.UnityHeaders
|
||||
_ => 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 Minor { get; }
|
||||
public int Update { get; }
|
||||
@@ -70,6 +73,7 @@ namespace Il2CppInspector.Outputs.UnityHeaders
|
||||
return res;
|
||||
}
|
||||
|
||||
// Compare two version numbers, intransitively (due to the Unspecified build type)
|
||||
public int CompareTo(UnityVersion other) {
|
||||
int res;
|
||||
if (0 != (res = Major.CompareTo(other.Major)))
|
||||
|
||||
Reference in New Issue
Block a user