C++: Implement enums

This commit is contained in:
Katy Coe
2020-07-04 13:03:23 +02:00
parent a9d002fd78
commit 91b93f3263

View File

@@ -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;