C++: Implement enums
This commit is contained in:
@@ -20,7 +20,8 @@ namespace Il2CppInspector.Cpp
|
|||||||
public enum CompoundType
|
public enum CompoundType
|
||||||
{
|
{
|
||||||
Struct,
|
Struct,
|
||||||
Union
|
Union,
|
||||||
|
Enum
|
||||||
}
|
}
|
||||||
|
|
||||||
// A type with no fields
|
// A type with no fields
|
||||||
@@ -114,7 +115,7 @@ namespace Il2CppInspector.Cpp
|
|||||||
public override string ToString() => $"typedef {ElementType.Name} {Name};";
|
public override string ToString() => $"typedef {ElementType.Name} {Name};";
|
||||||
}
|
}
|
||||||
|
|
||||||
// A struct, union or class type (type with fields)
|
// A struct, union, enum or class type (type with fields)
|
||||||
public class CppComplexType : CppType, IEnumerable<CppField>
|
public class CppComplexType : CppType, IEnumerable<CppField>
|
||||||
{
|
{
|
||||||
// Various enumerators
|
// Various enumerators
|
||||||
@@ -193,6 +194,7 @@ namespace Il2CppInspector.Cpp
|
|||||||
|
|
||||||
// Add a field to the type. Returns the offset of the field in the type
|
// Add a field to the type. Returns the offset of the field in the type
|
||||||
public int AddField(CppField field, int alignmentBytes = 0) {
|
public int AddField(CppField field, int alignmentBytes = 0) {
|
||||||
|
// Unions and enums always have an offset of zero
|
||||||
field.Offset = CompoundType == CompoundType.Struct ? Size : 0;
|
field.Offset = CompoundType == CompoundType.Struct ? Size : 0;
|
||||||
|
|
||||||
// If we just came out of a bitfield, move to the next byte if necessary
|
// If we just came out of a bitfield, move to the next byte if necessary
|
||||||
@@ -217,15 +219,21 @@ namespace Il2CppInspector.Cpp
|
|||||||
|
|
||||||
if (Name.Length > 0)
|
if (Name.Length > 0)
|
||||||
sb.Append("typedef ");
|
sb.Append("typedef ");
|
||||||
sb.Append(CompoundType == CompoundType.Struct ? "struct " : "union ");
|
sb.Append(CompoundType == CompoundType.Struct ? "struct " : CompoundType == CompoundType.Enum? "enum " : "union ");
|
||||||
sb.Append(Name + (Name.Length > 0 ? " " : ""));
|
sb.Append(Name + (Name.Length > 0 ? " " : ""));
|
||||||
|
|
||||||
if (Fields.Any()) {
|
var delimiter = CompoundType == CompoundType.Enum ? "," : ";";
|
||||||
sb.AppendLine("{");
|
|
||||||
foreach (var field in Fields.Values.SelectMany(f => f))
|
|
||||||
sb.AppendLine("\t" + string.Join("\n\t", field.ToString().Split('\n')));
|
|
||||||
|
|
||||||
sb.Append($"}} {Name}{(Name.Length > 0 ? " " : "")}/* Size: 0x{SizeBytes:x2} */;");
|
if (Fields.Any()) {
|
||||||
|
sb.Append("{");
|
||||||
|
foreach (var field in Fields.Values.SelectMany(f => f))
|
||||||
|
sb.Append("\n\t" + string.Join("\n\t", field.ToString().Split('\n')) + delimiter);
|
||||||
|
|
||||||
|
// Chop off final comma
|
||||||
|
if (CompoundType == CompoundType.Enum)
|
||||||
|
sb = sb.Remove(sb.Length - 1, 1);
|
||||||
|
|
||||||
|
sb.Append($"\n}} {Name}{(Name.Length > 0 ? " " : "")}/* Size: 0x{SizeBytes:x2} */;");
|
||||||
}
|
}
|
||||||
// Forward declaration
|
// Forward declaration
|
||||||
else {
|
else {
|
||||||
@@ -237,7 +245,7 @@ namespace Il2CppInspector.Cpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A field in a C++ type
|
// A field in a C++ type
|
||||||
public struct CppField
|
public class CppField
|
||||||
{
|
{
|
||||||
// The name of the field
|
// The name of the field
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
@@ -286,16 +294,23 @@ namespace Il2CppInspector.Cpp
|
|||||||
if (Type is CppArrayType a)
|
if (Type is CppArrayType a)
|
||||||
suffix += "[" + a.Length + "]";
|
suffix += "[" + a.Length + "]";
|
||||||
|
|
||||||
suffix += ";";
|
|
||||||
|
|
||||||
// bitfields
|
// bitfields
|
||||||
if (BitfieldSize > 0)
|
if (BitfieldSize > 0)
|
||||||
suffix += $" // bits {BitfieldLSB} - {BitfieldMSB}";
|
suffix += $" /* bits {BitfieldLSB} - {BitfieldMSB} */";
|
||||||
|
|
||||||
return offset + field + suffix;
|
return offset + field + suffix;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An enum key and value pair
|
||||||
|
public class CppEnumField : CppField
|
||||||
|
{
|
||||||
|
// The value of this key name
|
||||||
|
public ulong Value { get; set; }
|
||||||
|
|
||||||
|
public override string ToString() => Name + " = " + Value;
|
||||||
|
}
|
||||||
|
|
||||||
// A collection of C++ types
|
// A collection of C++ types
|
||||||
public class CppTypes : IEnumerable<CppType>
|
public class CppTypes : IEnumerable<CppType>
|
||||||
{
|
{
|
||||||
@@ -350,6 +365,7 @@ namespace Il2CppInspector.Cpp
|
|||||||
var rgxTypedef = new Regex(@"typedef (\S+?)\s*\**\s*(\S+);");
|
var rgxTypedef = new Regex(@"typedef (\S+?)\s*\**\s*(\S+);");
|
||||||
var rgxFieldFnPtr = new Regex(fnPtr + @";");
|
var rgxFieldFnPtr = new Regex(fnPtr + @";");
|
||||||
var rgxField = new Regex(@"^(?:struct |enum )?(\S+?)\s*\**\s*((?:\S|\s*,\s*)+)(?:\s*:\s*([0-9]+))?;");
|
var rgxField = new Regex(@"^(?:struct |enum )?(\S+?)\s*\**\s*((?:\S|\s*,\s*)+)(?:\s*:\s*([0-9]+))?;");
|
||||||
|
var rgxEnumValue = new Regex(@"^\s*([A-Za-z0-9_]+)(?:\s*=\s*(.+?))?,?\s*$");
|
||||||
|
|
||||||
var rgxStripKeywords = new Regex(@"\b(?:const|unsigned|volatile)\b");
|
var rgxStripKeywords = new Regex(@"\b(?:const|unsigned|volatile)\b");
|
||||||
var rgxCompressPtrs = new Regex(@"\*\s+\*");
|
var rgxCompressPtrs = new Regex(@"\*\s+\*");
|
||||||
@@ -361,10 +377,10 @@ namespace Il2CppInspector.Cpp
|
|||||||
var rgxSingleLineComment = new Regex(@"/\*.*?\*/");
|
var rgxSingleLineComment = new Regex(@"/\*.*?\*/");
|
||||||
|
|
||||||
var currentType = new Stack<CppComplexType>();
|
var currentType = new Stack<CppComplexType>();
|
||||||
bool inEnum = false;
|
|
||||||
bool falseIfBlock = false;
|
bool falseIfBlock = false;
|
||||||
bool inComment = false;
|
bool inComment = false;
|
||||||
bool inMethod = false;
|
bool inMethod = false;
|
||||||
|
var nextEnumValue = 0ul;
|
||||||
string line;
|
string line;
|
||||||
|
|
||||||
while ((line = lines.ReadLine()) != null) {
|
while ((line = lines.ReadLine()) != null) {
|
||||||
@@ -558,7 +574,8 @@ namespace Il2CppInspector.Cpp
|
|||||||
// Start of enum
|
// Start of enum
|
||||||
// typedef enum <optional-type-name>
|
// typedef enum <optional-type-name>
|
||||||
if (line.StartsWith("typedef enum") && line.IndexOf(";", StringComparison.Ordinal) == -1) {
|
if (line.StartsWith("typedef enum") && line.IndexOf(";", StringComparison.Ordinal) == -1) {
|
||||||
inEnum = true;
|
currentType.Push(new CppComplexType(CompoundType.Enum));
|
||||||
|
nextEnumValue = 0;
|
||||||
|
|
||||||
Debug.WriteLine($"\n[ENUM START ] {line}");
|
Debug.WriteLine($"\n[ENUM START ] {line}");
|
||||||
continue;
|
continue;
|
||||||
@@ -591,21 +608,13 @@ namespace Il2CppInspector.Cpp
|
|||||||
// end of [typedef] struct/union/enum
|
// end of [typedef] struct/union/enum
|
||||||
if (line.StartsWith("}") && line.EndsWith(";")) {
|
if (line.StartsWith("}") && line.EndsWith(";")) {
|
||||||
// We need this to avoid false positives on field matches below
|
// We need this to avoid false positives on field matches below
|
||||||
if (currentType.Count == 0 && !inEnum) {
|
/*if (currentType.Count == 0 && !inEnum) {
|
||||||
Debug.WriteLine($"[IGNORE ] {line}");
|
Debug.WriteLine($"[IGNORE ] {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
var name = line[1..^1].Trim();
|
var name = line[1..^1].Trim();
|
||||||
|
|
||||||
if (inEnum) {
|
|
||||||
Types.Add(name, new CppType(name, WordSize));
|
|
||||||
inEnum = false;
|
|
||||||
|
|
||||||
Debug.WriteLine($"[ENUM END ] {line} -- {name}\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ct = currentType.Pop();
|
var ct = currentType.Pop();
|
||||||
|
|
||||||
// End of top-level typedef, so it's a type name
|
// End of top-level typedef, so it's a type name
|
||||||
@@ -695,6 +704,32 @@ namespace Il2CppInspector.Cpp
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enum value field
|
||||||
|
var enumValue = rgxEnumValue.Match(line);
|
||||||
|
if (enumValue.Success) {
|
||||||
|
var name = enumValue.Groups[1].Captures[0].ToString();
|
||||||
|
|
||||||
|
var value = nextEnumValue++;
|
||||||
|
if (enumValue.Groups[2].Captures.Count > 0) {
|
||||||
|
// Convert the text to a ulong even if it's hexadecimal with a 0x prefix
|
||||||
|
var valueText = enumValue.Groups[2].Captures[0].ToString();
|
||||||
|
|
||||||
|
var conv = new System.ComponentModel.UInt64Converter();
|
||||||
|
|
||||||
|
// Handle bit shift operator
|
||||||
|
var values = valueText.Split("<<").Select(t => (ulong) conv.ConvertFromInvariantString(t.Trim())).ToArray();
|
||||||
|
|
||||||
|
value = values.Length == 1 ? values[0] : values[0] << (int)values[1];
|
||||||
|
nextEnumValue = value + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ct = currentType.Peek();
|
||||||
|
ct.AddField(new CppEnumField {Name = name, Type = WordSize == 32 ? Types["uint32_t"] : Types["uint64_t"], Value = value});
|
||||||
|
|
||||||
|
Debug.WriteLine($"[ENUM VALUE ] {line} -- {name} = {value}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure we're not ignoring anything we shouldn't
|
// Make sure we're not ignoring anything we shouldn't
|
||||||
Debug.WriteLine($"[IGNORE ] {line}");
|
Debug.WriteLine($"[IGNORE ] {line}");
|
||||||
|
|
||||||
@@ -702,10 +737,6 @@ namespace Il2CppInspector.Cpp
|
|||||||
if (line == "{")
|
if (line == "{")
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Enum values
|
|
||||||
if (inEnum)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Global variables
|
// Global variables
|
||||||
if (line.StartsWith("static"))
|
if (line.StartsWith("static"))
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user