From 8a85acb242343841afcb95affe41e8abc499e72e Mon Sep 17 00:00:00 2001 From: Katy Coe Date: Sat, 27 Jun 2020 21:47:55 +0200 Subject: [PATCH] C++: Various tweaks Indent output Handle bool fields Skip comments and methods Check that every line that matters is parsed Update test to check every Unity version --- Il2CppInspector.Common/Cpp/CppType.cs | 96 ++++++++++++++++++++++++++- Il2CppTests/TestCppTypes.cs | 18 +++-- 2 files changed, 106 insertions(+), 8 deletions(-) diff --git a/Il2CppInspector.Common/Cpp/CppType.cs b/Il2CppInspector.Common/Cpp/CppType.cs index a30898d..a29042b 100644 --- a/Il2CppInspector.Common/Cpp/CppType.cs +++ b/Il2CppInspector.Common/Cpp/CppType.cs @@ -151,7 +151,7 @@ namespace Il2CppInspector.CppUtils if (Fields.Any()) { sb.AppendLine("{"); foreach (var field in Fields.Values.SelectMany(f => f)) - sb.AppendLine(" " + field); + sb.AppendLine(" " + string.Join("\n ", field.ToString().Split('\n'))); sb.Append($"}} {Name}{(Name.Length > 0 ? " " : "")}/* Size: 0x{SizeBytes:x2} */;"); } @@ -236,6 +236,7 @@ namespace Il2CppInspector.CppUtils new CppType("int", 32), new CppType("float", 32), new CppType("double", 64), + new CppType("bool", 8), new CppType("void", 0) }; @@ -270,10 +271,13 @@ namespace Il2CppInspector.CppUtils var rgxAlignment = new Regex(@"__attribute__\(\(aligned\(([0-9]+)\)\)\)"); var rgxIsBitDirective = new Regex(@"#ifdef\s+IS_(32|64)BIT"); + var rgxSingleLineComment = new Regex(@"/\*.*?\*/"); var currentType = new Stack(); bool inEnum = false; bool falseIfBlock = false; + bool inComment = false; + bool inMethod = false; string line; // TODO: function pointer signatures @@ -281,9 +285,65 @@ namespace Il2CppInspector.CppUtils while ((line = lines.ReadLine()) != null) { // Sanitize + + // Remove comments + if (line.Contains("//")) + line = line.Substring(0, line.IndexOf("//", StringComparison.Ordinal)); + + // End of multi-line comment? + if (line.Contains("*/") && inComment) { + inComment = false; + line = line.Substring(line.IndexOf("*/", StringComparison.Ordinal) + 2); + } + + if (inComment) { + Debug.WriteLine($"[COMMENT ] {line}"); + continue; + } + + // Remove all single-line comments + line = rgxSingleLineComment.Replace(line, ""); + + // Start of multi-line comment? + if (line.Contains("/*") && !inComment) { + inComment = true; + line = line.Substring(0, line.IndexOf("/*")); + } + + // Ignore global variables + if (line.StartsWith("const ") && currentType.Count == 0) { + Debug.WriteLine($"[GLOBAL ] {line}"); + continue; + } + + // Ignore methods + // Note: This is a very lazy way of processing early version IL2CPP headers + if (line != "}" && inMethod) { + Debug.WriteLine($"[METHOD ] {line}"); + continue; + } + + if (line == "}" && inMethod) { + inMethod = false; + + Debug.WriteLine($"[METHOD END ] {line}"); + continue; + } + + if (line.StartsWith("static inline ")) { + inMethod = true; + + Debug.WriteLine($"[METHOD START ] {line}"); + continue; + } + + // Remove keywords we don't care about line = rgxStripKeywords.Replace(line, ""); + + // Remove whitespace in multiple indirections line = rgxCompressPtrs.Replace(line, "**"); + // Process __attribute((aligned(x))) var alignment = 0; var alignmentMatch = rgxAlignment.Match(line); if (alignmentMatch.Success) { @@ -293,6 +353,10 @@ namespace Il2CppInspector.CppUtils line = line.Trim(); + // Ignore blank lines + if (line.Length == 0) + continue; + // Process #ifs before anything else // Doesn't handle nesting but we probably don't need to (use a Stack if we do) var ifdef = rgxIsBitDirective.Match(line); @@ -544,16 +608,42 @@ namespace Il2CppInspector.CppUtils continue; } + // Make sure we're not ignoring anything we shouldn't Debug.WriteLine($"[IGNORE ] {line}"); + + // Block opens + if (line == "{") + continue; + + // Enum values + if (inEnum) + continue; + + // Global variables + if (line.StartsWith("static")) + continue; + + // Pragma directives + if (line.StartsWith("#pragma")) + continue; + + // Imports + if (line.StartsWith("extern")) + continue; + + throw new InvalidOperationException("Could not understand C++ code: " + line); } } // Generate a populated CppTypes object from a set of Unity headers - public static CppTypes FromUnityHeaders(UnityVersion version, int wordSize = 32) { + public static CppTypes FromUnityVersion(UnityVersion version, int wordSize = 32) + => FromUnityHeaders(UnityHeader.GetHeaderForVersion(version)); + + public static CppTypes FromUnityHeaders(UnityHeader header, int wordSize = 32) { var cppTypes = new CppTypes(wordSize); // Process Unity headers - var headers = UnityHeader.GetHeaderForVersion(version).GetHeaderText(); + var headers = header.GetHeaderText(); cppTypes.AddFromDeclarationText(headers); return cppTypes; diff --git a/Il2CppTests/TestCppTypes.cs b/Il2CppTests/TestCppTypes.cs index 5732bd6..a489ebb 100644 --- a/Il2CppTests/TestCppTypes.cs +++ b/Il2CppTests/TestCppTypes.cs @@ -7,6 +7,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Linq; using Il2CppInspector.CppUtils; using Il2CppInspector.CppUtils.UnityHeaders; using Il2CppInspector.Reflection; @@ -19,14 +20,21 @@ namespace Il2CppInspector { [Test] public void TestCppTypes() { - // TODO: Flesh out CppTypes test + // NOTE: This test doesn't check for correct results, only that parsing doesn't fail! - var cppTypes = CppTypes.FromUnityHeaders(new UnityVersion("2019.3.1f1")); + var unityAllHeaders = UnityHeader.GetAllHeaders(); - foreach (var cppType in cppTypes.Types) - Debug.WriteLine(cppType.Key + ":\n" + cppType.Value + "\n"); + // Ensure we have read the embedded assembly resources + Assert.IsTrue(unityAllHeaders.Any()); - throw new NotImplementedException(); + // Ensure we can interpret every header from every version of Unity without errors + // This will throw InvalidOperationException if there is a problem + foreach (var unityHeader in unityAllHeaders) { + var cppTypes = CppTypes.FromUnityHeaders(unityHeader); + + foreach (var cppType in cppTypes.Types) + Debug.WriteLine("// " + cppType.Key + "\n" + cppType.Value + "\n"); + } } } }