C++: Parse typedef struct definitions and nested types as fields
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -18,52 +19,56 @@ using Il2CppInspector.Reflection;
|
|||||||
|
|
||||||
namespace Il2CppInspector.Cpp
|
namespace Il2CppInspector.Cpp
|
||||||
{
|
{
|
||||||
public abstract class CppType
|
// A type with no fields
|
||||||
|
public class CppType
|
||||||
{
|
{
|
||||||
// The name of the type
|
// The name of the type
|
||||||
public string Name { get; }
|
public string Name { get; internal set; }
|
||||||
|
|
||||||
// Calculate the size of the C++ type in bytes
|
// The size of the C++ type in bytes
|
||||||
public abstract int GetSize();
|
public int Size { get; protected set; }
|
||||||
|
|
||||||
protected CppType(string name) => Name = name;
|
public CppType(string name, int size) {
|
||||||
|
Name = name;
|
||||||
|
Size = size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A non-struct non-class type
|
// A struct, union or class type (type with fields)
|
||||||
public class CppPrimitiveType : CppType
|
|
||||||
{
|
|
||||||
private readonly int size;
|
|
||||||
|
|
||||||
public override int GetSize() => size;
|
|
||||||
|
|
||||||
public CppPrimitiveType(string name, int size) : base(name) => this.size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A struct or class type
|
|
||||||
public class CppComplexType : CppType
|
public class CppComplexType : CppType
|
||||||
{
|
{
|
||||||
// Dictionary of byte offset in the type to each field
|
// Dictionary of byte offset in the type to each field
|
||||||
public Dictionary<int, CppField> Fields { get; } = new Dictionary<int, CppField>();
|
// Unions and bitfields can have more than one field at the same offset
|
||||||
|
public Dictionary<int, List<CppField>> Fields { get; } = new Dictionary<int, List<CppField>>();
|
||||||
|
|
||||||
// Calculate the size by summing the size of each field's type
|
public CppComplexType() : base("", 0) {}
|
||||||
public override int GetSize() => Fields.Values.Select(f => f.Type.GetSize()).Sum();
|
|
||||||
|
|
||||||
public CppComplexType(string name) : base(name) {}
|
// Add a field to the type. Returns the offset of the field in the type
|
||||||
|
public int AddField(CppField field) {
|
||||||
|
// TODO: Use InheritanceStyleEnum to determine whether the field is embedded or a pointer
|
||||||
|
field.Offset = Size;
|
||||||
|
|
||||||
// TODO: Add fields
|
if (Fields.ContainsKey(Size))
|
||||||
|
Fields[Size].Add(field);
|
||||||
|
else
|
||||||
|
Fields.Add(Size, new List<CppField> { field });
|
||||||
|
|
||||||
|
Size += field.Type.Size;
|
||||||
|
return Size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A field in a C++ type
|
// A field in a C++ type
|
||||||
public struct CppField
|
public struct CppField
|
||||||
{
|
{
|
||||||
// The name of the field
|
// The name of the field
|
||||||
public string Name { get; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
// The offset of the field into the type
|
// The offset of the field into the type
|
||||||
public int Offset { get; }
|
public int Offset { get; set; }
|
||||||
|
|
||||||
// The type of the field
|
// The type of the field
|
||||||
public CppType Type { get; }
|
public CppType Type { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// A collection of C++ types
|
// A collection of C++ types
|
||||||
@@ -77,16 +82,16 @@ namespace Il2CppInspector.Cpp
|
|||||||
// Architecture width in bytes (4 bytes for 32-bit or 8 bytes for 64-bit, to determine pointer sizes)
|
// Architecture width in bytes (4 bytes for 32-bit or 8 bytes for 64-bit, to determine pointer sizes)
|
||||||
public int WordSize { get; }
|
public int WordSize { get; }
|
||||||
|
|
||||||
private static readonly List<CppPrimitiveType> primitiveTypes = new List<CppPrimitiveType> {
|
private static readonly List<CppType> primitiveTypes = new List<CppType> {
|
||||||
new CppPrimitiveType("uint8_t", 1),
|
new CppType("uint8_t", 1),
|
||||||
new CppPrimitiveType("uint16_t", 2),
|
new CppType("uint16_t", 2),
|
||||||
new CppPrimitiveType("uint32_t", 4),
|
new CppType("uint32_t", 4),
|
||||||
new CppPrimitiveType("uint64_t", 8),
|
new CppType("uint64_t", 8),
|
||||||
new CppPrimitiveType("int8_t", 1),
|
new CppType("int8_t", 1),
|
||||||
new CppPrimitiveType("int16_t", 2),
|
new CppType("int16_t", 2),
|
||||||
new CppPrimitiveType("int32_t", 4),
|
new CppType("int32_t", 4),
|
||||||
new CppPrimitiveType("int64_t", 8),
|
new CppType("int64_t", 8),
|
||||||
new CppPrimitiveType("char", 1)
|
new CppType("char", 1)
|
||||||
};
|
};
|
||||||
|
|
||||||
public CppTypes(int wordSize) {
|
public CppTypes(int wordSize) {
|
||||||
@@ -94,8 +99,8 @@ namespace Il2CppInspector.Cpp
|
|||||||
types = primitiveTypes.ToDictionary(t => t.Name, t => (CppType) t);
|
types = primitiveTypes.ToDictionary(t => t.Name, t => (CppType) t);
|
||||||
|
|
||||||
// This is all compiler-dependent, let's hope for the best!
|
// This is all compiler-dependent, let's hope for the best!
|
||||||
types.Add("uintptr_t", new CppPrimitiveType("uintptr_t", WordSize));
|
types.Add("uintptr_t", new CppType("uintptr_t", WordSize));
|
||||||
types.Add("size_t", new CppPrimitiveType("size_t", WordSize));
|
types.Add("size_t", new CppType("size_t", WordSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse a block of C++ source code, adding any types found
|
// Parse a block of C++ source code, adding any types found
|
||||||
@@ -105,24 +110,29 @@ namespace Il2CppInspector.Cpp
|
|||||||
var rgxTypedef = new Regex(@"typedef (\S+) (\S+);");
|
var rgxTypedef = new Regex(@"typedef (\S+) (\S+);");
|
||||||
var rgxTypedefFnPtr = new Regex(@"typedef\s+\S+\s*\(\s*\*(\S+)\)\s*\(.*\);");
|
var rgxTypedefFnPtr = new Regex(@"typedef\s+\S+\s*\(\s*\*(\S+)\)\s*\(.*\);");
|
||||||
|
|
||||||
|
var currentType = new Stack<CppComplexType>();
|
||||||
string line;
|
string line;
|
||||||
|
|
||||||
while ((line = lines.ReadLine()) != null) {
|
while ((line = lines.ReadLine()) != null) {
|
||||||
|
|
||||||
// Sanitize
|
// Sanitize
|
||||||
line = line.Trim();
|
line = line.Trim();
|
||||||
line = line.Replace(" const ", " ");
|
line = line.Replace(" const ", " ");
|
||||||
|
if (line.StartsWith("const "))
|
||||||
|
line = line.Substring(6);
|
||||||
|
|
||||||
|
// Function pointer
|
||||||
// typedef <retType> (*<alias>)(<args>);
|
// typedef <retType> (*<alias>)(<args>);
|
||||||
var typedef = rgxTypedefFnPtr.Match(line);
|
var typedef = rgxTypedefFnPtr.Match(line);
|
||||||
if (typedef.Success) {
|
if (typedef.Success) {
|
||||||
var alias = typedef.Groups[1].Captures[0].ToString();
|
var alias = typedef.Groups[1].Captures[0].ToString();
|
||||||
|
|
||||||
Debug.WriteLine($"[TYPEDEF] {line} -- Adding method pointer typedef to {alias}");
|
|
||||||
|
|
||||||
types.Add(alias, types["uintptr_t"]);
|
types.Add(alias, types["uintptr_t"]);
|
||||||
|
|
||||||
|
Debug.WriteLine($"[TYPEDEF PTR ] {line} -- Adding method pointer typedef to {alias}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Alias
|
||||||
// typedef <targetType> <alias>;
|
// typedef <targetType> <alias>;
|
||||||
typedef = rgxTypedef.Match(line);
|
typedef = rgxTypedef.Match(line);
|
||||||
if (typedef.Success) {
|
if (typedef.Success) {
|
||||||
@@ -135,18 +145,63 @@ namespace Il2CppInspector.Cpp
|
|||||||
}
|
}
|
||||||
// Regular aliases
|
// Regular aliases
|
||||||
else {
|
else {
|
||||||
Debug.WriteLine($"[TYPEDEF] {line} -- Adding typedef from {existingType} to {alias}");
|
|
||||||
|
|
||||||
types.Add(alias, types[existingType]);
|
types.Add(alias, types[existingType]);
|
||||||
|
|
||||||
|
Debug.WriteLine($"[TYPEDEF ALIAS] {line} -- Adding typedef from {existingType} to {alias}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.WriteLine("[IGNORE ] " + line);
|
// Start of struct
|
||||||
|
// typedef struct <optional-type-name>
|
||||||
|
if (line.StartsWith("typedef struct") && line.IndexOf(";", StringComparison.Ordinal) == -1) {
|
||||||
|
currentType.Push(new CppComplexType());
|
||||||
|
|
||||||
|
Debug.WriteLine($"[STRUCT START ] {line}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nested complex field
|
||||||
|
// struct <optional-type-name>
|
||||||
|
// union <optional-type-name>
|
||||||
|
var words = line.Split(' ');
|
||||||
|
if ((words[0] == "union" || words[0] == "struct") && words.Length <= 2) {
|
||||||
|
currentType.Push(new CppComplexType());
|
||||||
|
|
||||||
|
Debug.WriteLine($"[FIELD START ] {line}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// End of complex field or complex type
|
||||||
|
// end of [typedef] struct/union
|
||||||
|
if (line.StartsWith("}") && line.EndsWith(";") && currentType.Count > 0) {
|
||||||
|
var ct = currentType.Pop();
|
||||||
|
var name = line[1..^1].Trim();
|
||||||
|
|
||||||
|
// End of top-level typedef, so it's a type name
|
||||||
|
if (currentType.Count == 0) {
|
||||||
|
ct.Name = name;
|
||||||
|
types.Add(ct.Name, ct);
|
||||||
|
|
||||||
|
Debug.WriteLine($"[STRUCT END ] {line} -- {name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise it's a field name in the current type
|
||||||
|
else {
|
||||||
|
var parent = currentType.Peek();
|
||||||
|
parent.AddField(new CppField { Name = name, Type = ct });
|
||||||
|
|
||||||
|
Debug.WriteLine($"[FIELD END ] {line} -- {ct.Name} {name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.WriteLine("[IGNORE ] " + line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a populated CppTypes object from an
|
// Generate a populated CppTypes object from a set of Unity headers
|
||||||
public static CppTypes FromUnityHeaders(UnityVersion version) {
|
public static CppTypes FromUnityHeaders(UnityVersion version) {
|
||||||
var cppTypes = new CppTypes(8);
|
var cppTypes = new CppTypes(8);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user