C++: Implement bitfields

This commit is contained in:
Katy Coe
2020-06-27 17:09:05 +02:00
parent 29c9514fdf
commit 630b0a771d
2 changed files with 62 additions and 32 deletions

View File

@@ -13,11 +13,10 @@ using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Il2CppInspector.Outputs; using Il2CppInspector.CppUtils.UnityHeaders;
using Il2CppInspector.Outputs.UnityHeaders;
using Il2CppInspector.Reflection; using Il2CppInspector.Reflection;
namespace Il2CppInspector.Cpp namespace Il2CppInspector.CppUtils
{ {
// Compound type // Compound type
public enum CompoundType public enum CompoundType
@@ -32,9 +31,12 @@ namespace Il2CppInspector.Cpp
// The name of the type // The name of the type
public virtual string Name { get; set; } public virtual string Name { get; set; }
// The size of the C++ type in bytes // The size of the C++ type in bits
public virtual int Size { get; set; } public virtual int Size { get; set; }
// The size of the C++ type in bytes
public virtual int SizeBytes => (Size / 8) + (Size % 8 > 0 ? 1 : 0);
public CppType(string name = null, int size = 0) { public CppType(string name = null, int size = 0) {
Name = name; Name = name;
Size = size; Size = size;
@@ -46,7 +48,7 @@ namespace Il2CppInspector.Cpp
// Generate typedef to this type // Generate typedef to this type
public CppAlias AsAlias(string Name) => new CppAlias(Name, this); public CppAlias AsAlias(string Name) => new CppAlias(Name, this);
public override string ToString() => $"/* {Size:x2} - {Name} */"; public override string ToString() => $"/* {SizeBytes:x2} - {Name} */";
} }
// A pointer type // A pointer type
@@ -66,6 +68,8 @@ namespace Il2CppInspector.Cpp
public override int Size => ElementType.Size; public override int Size => ElementType.Size;
public override int SizeBytes => ElementType.SizeBytes;
public CppAlias(string name, CppType elementType) : base(name) => ElementType = elementType; public CppAlias(string name, CppType elementType) : base(name) => ElementType = elementType;
public override string ToString() => $"typedef {ElementType.Name} {Name};"; public override string ToString() => $"typedef {ElementType.Name} {Name};";
@@ -95,6 +99,10 @@ namespace Il2CppInspector.Cpp
// TODO: Use InheritanceStyleEnum to determine whether the field is embedded or a pointer // TODO: Use InheritanceStyleEnum to determine whether the field is embedded or a pointer
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 (field.BitfieldSize == 0 && field.Offset % 8 != 0)
field.Offset = (field.Offset / 8) * 8 + 8;
if (Fields.ContainsKey(field.Offset)) if (Fields.ContainsKey(field.Offset))
Fields[field.Offset].Add(field); Fields[field.Offset].Add(field);
else else
@@ -117,7 +125,7 @@ namespace Il2CppInspector.Cpp
foreach (var field in Fields.Values.SelectMany(f => f)) foreach (var field in Fields.Values.SelectMany(f => f))
sb.AppendLine(" " + field); sb.AppendLine(" " + field);
sb.Append($"}} {Name}{(Name.Length > 0 ? " " : "")}/* Size: 0x{Size:x2} */;"); sb.Append($"}} {Name}{(Name.Length > 0 ? " " : "")}/* Size: 0x{SizeBytes:x2} */;");
} }
// Forward declaration // Forward declaration
else { else {
@@ -137,8 +145,22 @@ namespace Il2CppInspector.Cpp
// The offset of the field into the type // The offset of the field into the type
public int Offset { get; set; } public int Offset { get; set; }
// The offset of the field into the type in bytes
public int OffsetBytes => Offset / 8;
// The size of the field // The size of the field
public int Size => Type.Size; public int Size => (BitfieldSize > 0 ? BitfieldSize : Type.Size);
public int SizeBytes => (Size / 8) + (Size % 8 > 0 ? 1 : 0);
// The size of the field in bits
public int BitfieldSize { get; set; }
// The LSB of the bitfield
public int BitfieldLSB => Offset % 8;
// The MSB of the bitfield
public int BitfieldMSB => BitfieldLSB + Size - 1;
// The type of the field // The type of the field
// This type will be wrong (by design) for function pointers, pointers to pointers etc. // This type will be wrong (by design) for function pointers, pointers to pointers etc.
@@ -146,12 +168,13 @@ namespace Il2CppInspector.Cpp
internal CppType Type { get; set; } internal CppType Type { get; set; }
// C++ representation of field (might be incorrect due to the above) // C++ representation of field (might be incorrect due to the above)
public override string ToString() => $"/* 0x{Offset:x2} - 0x{Offset + Size - 1:x2} (0x{Size:x2}) */" public override string ToString() => $"/* 0x{OffsetBytes:x2} - 0x{OffsetBytes + SizeBytes - 1:x2} (0x{SizeBytes:x2}) */"
// nested anonymous types // nested anonymous types
+ (Type is CppComplexType && Type.Name == "" ? "\n" + Type.ToString()[..^1] + " " + Name : + (Type is CppComplexType && Type.Name == "" ? "\n" + Type.ToString()[..^1] + " " + Name :
// regular fields // regular fields
$" {Type.Name} {Name}") $" {Type.Name} {Name}" + (BitfieldSize > 0? $" : {BitfieldSize}" : ""))
+ ";"; + ";"
+ (BitfieldSize > 0? $" // bits {BitfieldLSB} - {BitfieldMSB}" : "");
} }
// A collection of C++ types // A collection of C++ types
@@ -165,27 +188,27 @@ namespace Il2CppInspector.Cpp
public IEnumerator<CppType> GetEnumerator() => Types.Values.GetEnumerator(); public IEnumerator<CppType> GetEnumerator() => Types.Values.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
// Architecture width in bytes (4 bytes for 32-bit or 8 bytes for 64-bit, to determine pointer sizes) // Architecture width in bits (32/64) - to determine pointer sizes
public int WordSize { get; } public int WordSize { get; }
private static readonly List<CppType> primitiveTypes = new List<CppType> { private static readonly List<CppType> primitiveTypes = new List<CppType> {
new CppType("uint8_t", 1), new CppType("uint8_t", 8),
new CppType("uint16_t", 2), new CppType("uint16_t", 16),
new CppType("uint32_t", 4), new CppType("uint32_t", 32),
new CppType("uint64_t", 8), new CppType("uint64_t", 64),
new CppType("int8_t", 1), new CppType("int8_t", 8),
new CppType("int16_t", 2), new CppType("int16_t", 16),
new CppType("int32_t", 4), new CppType("int32_t", 32),
new CppType("int64_t", 8), new CppType("int64_t", 64),
new CppType("char", 1), new CppType("char", 8),
new CppType("int", 4), new CppType("int", 32),
new CppType("float", 4), new CppType("float", 32),
new CppType("double", 8), new CppType("double", 64),
new CppType("void", 0) new CppType("void", 0)
}; };
public CppTypes(int wordSize) { public CppTypes(int wordSize) {
WordSize = wordSize; WordSize = wordSize * 8;
Types = primitiveTypes.ToDictionary(t => t.Name, t => t); Types = primitiveTypes.ToDictionary(t => t.Name, t => t);
// This is all compiler-dependent, let's hope for the best! // This is all compiler-dependent, let's hope for the best!
@@ -197,12 +220,13 @@ namespace Il2CppInspector.Cpp
public void AddFromDeclarationText(string text) { public void AddFromDeclarationText(string text) {
using StringReader lines = new StringReader(text); using StringReader lines = new StringReader(text);
var fnPtr = @"\S+\s*\(\s*\*(\S+)\)\s*\(.*\)";
var rgxExternDecl = new Regex(@"struct (\S+);"); var rgxExternDecl = new Regex(@"struct (\S+);");
var rgxTypedefForwardDecl = new Regex(@"typedef struct (\S+) (\S+);"); var rgxTypedefForwardDecl = new Regex(@"typedef struct (\S+) (\S+);");
var rgxTypedefFnPtr = new Regex(@"typedef\s+(?:struct )?\S+\s*\(\s*\*(\S+)\)\s*\(.*\);"); var rgxTypedefFnPtr = new Regex(@"typedef\s+(?:struct )?" + fnPtr + ";");
var rgxTypedef = new Regex(@"typedef (\S+?)\s*\**\s*(\S+);"); var rgxTypedef = new Regex(@"typedef (\S+?)\s*\**\s*(\S+);");
var rgxFieldFnPtr = new Regex(@"\S+\s*\(\s*\*(\S+)\)\s*\(.*\);"); var rgxFieldFnPtr = new Regex(fnPtr + @";");
var rgxField = new Regex(@"^(?:struct )?(\S+?)\s*\**\s*(\S+);"); var rgxField = new Regex(@"^(?:struct )?(\S+?)\s*\**\s*(\S+)(?:\s*:\s*([0-9]+))?;");
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+\*");
@@ -211,7 +235,6 @@ namespace Il2CppInspector.Cpp
bool inEnum = false; bool inEnum = false;
string line; string line;
// TODO: Bitfields
// TODO: Arrays // TODO: Arrays
// TODO: Alignment directives // TODO: Alignment directives
// TODO: enum prefix in field (Il2CppWindowsRuntimeTypeName) // TODO: enum prefix in field (Il2CppWindowsRuntimeTypeName)
@@ -410,6 +433,10 @@ namespace Il2CppInspector.Cpp
var name = field.Groups[2].Captures[0].ToString(); var name = field.Groups[2].Captures[0].ToString();
var typeName = field.Groups[1].Captures[0].ToString(); var typeName = field.Groups[1].Captures[0].ToString();
int bitfield = 0;
if (field.Groups[3].Captures.Count > 0)
bitfield = int.Parse(field.Groups[3].Captures[0].ToString());
// Potential multiple indirection // Potential multiple indirection
var type = Types[typeName]; var type = Types[typeName];
var pointers = line.Count(c => c == '*'); var pointers = line.Count(c => c == '*');
@@ -417,9 +444,12 @@ namespace Il2CppInspector.Cpp
type = type.AsPointer(WordSize); type = type.AsPointer(WordSize);
var ct = currentType.Peek(); var ct = currentType.Peek();
ct.AddField(new CppField {Name = name, Type = type}); ct.AddField(new CppField {Name = name, Type = type, BitfieldSize = bitfield});
if (bitfield == 0)
Debug.WriteLine($"[FIELD {(pointers > 0? "PTR":"VAL")} ] {line} -- {name}"); Debug.WriteLine($"[FIELD {(pointers > 0? "PTR":"VAL")} ] {line} -- {name}");
else
Debug.WriteLine($"[BITFIELD ] {line} -- {name} : {bitfield}");
continue; continue;
} }

View File

@@ -7,8 +7,8 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using Il2CppInspector.Cpp; using Il2CppInspector.CppUtils;
using Il2CppInspector.Outputs.UnityHeaders; using Il2CppInspector.CppUtils.UnityHeaders;
using Il2CppInspector.Reflection; using Il2CppInspector.Reflection;
using NUnit.Framework; using NUnit.Framework;