diff --git a/Il2CppInspector.Common/Architectures/Il2CppBinaryARM.cs b/Il2CppInspector.Common/Architectures/Il2CppBinaryARM.cs index 508f1ac..415811a 100644 --- a/Il2CppInspector.Common/Architectures/Il2CppBinaryARM.cs +++ b/Il2CppInspector.Common/Architectures/Il2CppBinaryARM.cs @@ -5,6 +5,7 @@ All rights reserved. */ +using Il2CppInspector.Next; using System; using System.Collections.Generic; using System.Linq; @@ -342,10 +343,10 @@ namespace Il2CppInspector // In v21 and later, R0-R2 + PC will be set and they will be the only registers set // Pre-v21, R0-R1 + PC will be the only registers set - if (image.Version >= 21 && regs.Count == 4 && regs.TryGetValue(0, out r0) && regs.TryGetValue(1, out r1) && regs.TryGetValue(2, out uint _)) + if (image.Version >= MetadataVersions.V210 && regs.Count == 4 && regs.TryGetValue(0, out r0) && regs.TryGetValue(1, out r1) && regs.TryGetValue(2, out uint _)) return (r0 & 0xffff_fffe, r1 & 0xffff_fffe); - if (image.Version < 21 && regs.Count == 3 && regs.TryGetValue(0, out r0) && regs.TryGetValue(1, out r1)) + if (image.Version < MetadataVersions.V210 && regs.Count == 3 && regs.TryGetValue(0, out r0) && regs.TryGetValue(1, out r1)) return (r0 & 0xffff_fffe, r1 & 0xffff_fffe); return (0, 0); diff --git a/Il2CppInspector.Common/Architectures/Il2CppBinaryARM64.cs b/Il2CppInspector.Common/Architectures/Il2CppBinaryARM64.cs index 7891d45..692a28f 100644 --- a/Il2CppInspector.Common/Architectures/Il2CppBinaryARM64.cs +++ b/Il2CppInspector.Common/Architectures/Il2CppBinaryARM64.cs @@ -4,6 +4,7 @@ All rights reserved. */ +using Il2CppInspector.Next; using System; using System.Collections.Generic; @@ -168,10 +169,10 @@ namespace Il2CppInspector // Is it Il2CppCodegenRegistration(void)? // In v21 and later, X0-X2 will be set and they will be the only registers set // Pre-v21, X0-X1 will be the only registers set - if (image.Version >= 21 && regs.Count == 3 && regs.TryGetValue(0, out ulong x0) && regs.TryGetValue(1, out x1) && regs.TryGetValue(2, out ulong _)) + if (image.Version >= MetadataVersions.V210 && regs.Count == 3 && regs.TryGetValue(0, out ulong x0) && regs.TryGetValue(1, out x1) && regs.TryGetValue(2, out ulong _)) return (x0, x1); - if (image.Version < 21 && regs.Count == 2 && regs.TryGetValue(0, out x0) && regs.TryGetValue(1, out x1)) + if (image.Version < MetadataVersions.V210 && regs.Count == 2 && regs.TryGetValue(0, out x0) && regs.TryGetValue(1, out x1)) return (x0, x1); return (0, 0); diff --git a/Il2CppInspector.Common/Architectures/Il2CppBinaryX64.cs b/Il2CppInspector.Common/Architectures/Il2CppBinaryX64.cs index 6505314..efe00d3 100644 --- a/Il2CppInspector.Common/Architectures/Il2CppBinaryX64.cs +++ b/Il2CppInspector.Common/Architectures/Il2CppBinaryX64.cs @@ -4,6 +4,7 @@ All rights reserved. */ +using Il2CppInspector.Next; using System; using System.Collections.Generic; using System.Collections.Specialized; @@ -216,7 +217,7 @@ namespace Il2CppInspector offset = nextLea?.foundOffset + leaSize ?? buff2Size; } - if ((image.Version < 21 && leas.Count == 2) || (image.Version >= 21 && leas.Count == 3)) { + if ((image.Version < MetadataVersions.V210 && leas.Count == 2) || (image.Version >= MetadataVersions.V210 && leas.Count == 3)) { // Register-based argument passing? var leaRSI = leas.FirstOrDefault(l => l.Value == RSI).Key.address; var leaRDI = leas.FirstOrDefault(l => l.Value == RDI).Key.address; diff --git a/Il2CppInspector.Common/Architectures/Il2CppBinaryX86.cs b/Il2CppInspector.Common/Architectures/Il2CppBinaryX86.cs index 00fa3df..fb4bd76 100644 --- a/Il2CppInspector.Common/Architectures/Il2CppBinaryX86.cs +++ b/Il2CppInspector.Common/Architectures/Il2CppBinaryX86.cs @@ -4,6 +4,7 @@ All rights reserved. */ +using Il2CppInspector.Next; using System; using System.Linq; @@ -34,7 +35,7 @@ namespace Il2CppInspector return (0, 0); // Jump to Il2CppCodegenRegistration - if(image.Version < 21) { + if(image.Version < MetadataVersions.V210) { image.Position = image.MapVATR((ulong)pCgr + 1); metadata = image.ReadUInt32(); image.Position = image.MapVATR((ulong)pCgr + 6); diff --git a/Il2CppInspector.Common/Cpp/UnityHeaders/UnityHeaders.cs b/Il2CppInspector.Common/Cpp/UnityHeaders/UnityHeaders.cs index 9c6f4cd..f0045bf 100644 --- a/Il2CppInspector.Common/Cpp/UnityHeaders/UnityHeaders.cs +++ b/Il2CppInspector.Common/Cpp/UnityHeaders/UnityHeaders.cs @@ -11,6 +11,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Text.RegularExpressions; +using Il2CppInspector.Next; +using VersionedSerialization; namespace Il2CppInspector.Cpp.UnityHeaders { @@ -19,7 +21,7 @@ namespace Il2CppInspector.Cpp.UnityHeaders public class UnityHeaders : IEquatable { // Metadata version for which this group of headers are valid. Multiple headers may have the same metadata version - public double MetadataVersion { get; } + public StructVersion MetadataVersion { get; } // Range of Unity versions for which this group of headers are valid public UnityVersionRange VersionRange { get; } @@ -114,7 +116,7 @@ namespace Il2CppInspector.Cpp.UnityHeaders if (metadataVersion != binary.Image.Version) continue; - if (metadataVersion == 21) { + if (metadataVersion == MetadataVersions.V210) { /* Special version logic for metadata version 21 based on the Il2CppMetadataRegistration.fieldOffsets field */ var headerFieldOffsetsArePointers = r.VersionRange.Min.CompareTo("5.3.7") >= 0 && r.VersionRange.Min.CompareTo("5.4.0") != 0; var binaryFieldOffsetsArePointers = binary.FieldOffsets == null; @@ -194,8 +196,8 @@ namespace Il2CppInspector.Cpp.UnityHeaders } // Get the metadata version from a type header resource name - private static double GetMetadataVersionFromFilename(string resourceName) - => double.Parse(resourceName.Substring(typeof(UnityHeaders).Namespace.Length + 1).Split('-')[0], NumberFormatInfo.InvariantInfo); + private static StructVersion GetMetadataVersionFromFilename(string resourceName) + => resourceName[(typeof(UnityHeaders).Namespace!.Length + 1)..].Split('-')[0]; // Equality comparisons public static bool operator ==(UnityHeaders first, UnityHeaders second) { diff --git a/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs b/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs index f26186c..74a68dd 100644 --- a/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs +++ b/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs @@ -11,12 +11,13 @@ using System.Linq; using System.Reflection; using System.Text; using NoisyCowStudios.Bin2Object; +using VersionedSerialization; namespace Il2CppInspector { public interface IFileFormatStream { - double Version { get; set; } + StructVersion Version { get; set; } long Length { get; } uint NumImages { get; } string DefaultFilename { get; } diff --git a/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs b/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs index f07220b..586a3e3 100644 --- a/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs +++ b/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs @@ -5,6 +5,7 @@ All rights reserved. */ +using Il2CppInspector.Next; using System; using System.Collections.Generic; using System.Diagnostics; @@ -12,6 +13,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; +using VersionedSerialization; namespace Il2CppInspector { @@ -135,7 +137,7 @@ namespace Il2CppInspector } // Load binary without a global-metadata.dat available - public static Il2CppBinary Load(IFileFormatStream stream, double metadataVersion, EventHandler statusCallback = null) { + public static Il2CppBinary Load(IFileFormatStream stream, StructVersion metadataVersion, EventHandler statusCallback = null) { foreach (var loadedImage in stream.TryNextLoadStrategy()) { var inst = LoadImpl(stream, statusCallback); if (inst.FindRegistrationStructs(metadataVersion)) @@ -167,7 +169,7 @@ namespace Il2CppInspector } // Initialize binary without a global-metadata.dat available - public bool FindRegistrationStructs(double metadataVersion) { + public bool FindRegistrationStructs(StructVersion metadataVersion) { Image.Version = metadataVersion; StatusUpdate("Searching for binary metadata"); @@ -282,21 +284,22 @@ namespace Il2CppInspector // genericAdjustorThunks was inserted before invokerPointersCount in 24.5 and 27.1 // pointer expected if we need to bump version - if (Image.Version == 24.4 && CodeRegistration.invokerPointersCount > 0x50000) + if (Image.Version == MetadataVersions.V244 && CodeRegistration.invokerPointersCount > 0x50000) { - Image.Version = 24.5; + Image.Version = MetadataVersions.V245; CodeRegistration = Image.ReadMappedObject(codeRegistration); } - if (Image.Version == 24.4 && CodeRegistration.reversePInvokeWrapperCount > 0x50000) { - Image.Version = 24.5; + if (Image.Version == MetadataVersions.V244 && CodeRegistration.reversePInvokeWrapperCount > 0x50000) { + Image.Version = MetadataVersions.V245; codeRegistration -= 1 * pointerSize; CodeRegistration = Image.ReadMappedObject(codeRegistration); } - if (Image.Version == 29 && (long)CodeRegistration.genericMethodPointersCount - MetadataRegistration.genericMethodTableCount > 0x10000) + if ((Image.Version == MetadataVersions.V290 || Image.Version == MetadataVersions.V310) && + (long)CodeRegistration.genericMethodPointersCount - MetadataRegistration.genericMethodTableCount > 0x10000) { - Image.Version = 29.1; + Image.Version = new StructVersion(Image.Version.Major, 1, Image.Version.Tag); codeRegistration -= 2 * pointerSize; CodeRegistration = Image.ReadMappedObject(codeRegistration); } @@ -317,21 +320,21 @@ namespace Il2CppInspector || CodeRegistration.reversePInvokeWrapperCount > 0x10000 || CodeRegistration.unresolvedVirtualCallCount > 0x4000 // >= 22 || CodeRegistration.interopDataCount > 0x1000 // >= 23 - || (Image.Version <= 24.1 && CodeRegistration.invokerPointersCount > CodeRegistration.methodPointersCount)) + || (Image.Version <= MetadataVersions.V241 && CodeRegistration.invokerPointersCount > CodeRegistration.methodPointersCount)) throw new NotSupportedException("The detected Il2CppCodeRegistration / Il2CppMetadataRegistration structs do not pass validation. This may mean that their fields have been re-ordered as a form of obfuscation and Il2CppInspector has not been able to restore the original order automatically. Consider re-ordering the fields in Il2CppBinaryClasses.cs and try again."); // The global method pointer list was deprecated in v24.2 in favour of Il2CppCodeGenModule - if (Image.Version <= 24.1) + if (Image.Version <= MetadataVersions.V241) GlobalMethodPointers = Image.ReadMappedArray(CodeRegistration.pmethodPointers, (int) CodeRegistration.methodPointersCount); // After v24 method pointers and RGCTX data were stored in Il2CppCodeGenModules - if (Image.Version >= 24.2) { + if (Image.Version >= MetadataVersions.V242) { Modules = new Dictionary(); // In v24.3, windowsRuntimeFactoryTable collides with codeGenModules. So far no samples have had windowsRuntimeFactoryCount > 0; // if this changes we'll have to get smarter about disambiguating these two. if (CodeRegistration.codeGenModulesCount == 0) { - Image.Version = 24.3; + Image.Version = MetadataVersions.V243; CodeRegistration = Image.ReadMappedObject(codeRegistration); } @@ -364,10 +367,10 @@ namespace Il2CppInspector // Field offset data. Metadata <=21.x uses a value-type array; >=21.x uses a pointer array // Versions from 22 onwards use an array of pointers in Binary.FieldOffsetData - bool fieldOffsetsArePointers = (Image.Version >= 22); + bool fieldOffsetsArePointers = (Image.Version >= MetadataVersions.V220); // Some variants of 21 also use an array of pointers - if (Image.Version == 21) { + if (Image.Version == MetadataVersions.V210) { var fieldTest = Image.ReadMappedWordArray(MetadataRegistration.pfieldOffsets, 6); // We detect this by relying on the fact Module, Object, ValueType, Attribute, _Attribute and Int32 @@ -386,7 +389,7 @@ namespace Il2CppInspector TypeReferenceIndicesByAddress = typeRefPointers.Zip(Enumerable.Range(0, typeRefPointers.Length), (a, i) => new { a, i }).ToDictionary(x => x.a, x => x.i); TypeReferences = - Image.Version >= 27.2 + Image.Version >= MetadataVersions.V272 ? Image.ReadMappedObjectPointerArray(MetadataRegistration.ptypes, (int) MetadataRegistration.typesCount) .Cast() .ToList() @@ -394,7 +397,7 @@ namespace Il2CppInspector // Custom attribute constructors (function pointers) // This is managed in Il2CppInspector for metadata >= 27 - if (Image.Version < 27) { + if (Image.Version < MetadataVersions.V270) { CustomAttributeGenerators = Image.ReadMappedArray(CodeRegistration.customAttributeGenerators, (int) CodeRegistration.customAttributeCount); } @@ -408,7 +411,7 @@ namespace Il2CppInspector // >=22: unresolvedVirtualCallPointers // >=23: interopData - if (Image.Version < 19) { + if (Image.Version < MetadataVersions.V190) { VTableMethodReferences = Image.ReadMappedArray(MetadataRegistration.methodReferences, (int)MetadataRegistration.methodReferencesCount); } diff --git a/Il2CppInspector.Common/IL2CPP/Il2CppBinaryClasses.cs b/Il2CppInspector.Common/IL2CPP/Il2CppBinaryClasses.cs index dbfe032..fee8920 100644 --- a/Il2CppInspector.Common/IL2CPP/Il2CppBinaryClasses.cs +++ b/Il2CppInspector.Common/IL2CPP/Il2CppBinaryClasses.cs @@ -60,15 +60,19 @@ namespace Il2CppInspector [Version(Min = 22, Max = 29)] public ulong unresolvedVirtualCallCount; - [Version(Min = 29.1)] + [Version(Min = 29.1, Max = 29.2)] + [Version(Min = 31.1, Max = 31.2)] public ulong unresolvedIndirectCallCount; [Version(Min = 22)] public ulong unresolvedVirtualCallPointers; - [Version(Min = 29.1)] + [Version(Min = 29.1, Max = 29.2)] + [Version(Min = 31.1, Max = 31.2)] public ulong unresolvedInstanceCallPointers; - [Version(Min = 29.1)] + + [Version(Min = 29.1, Max = 29.2)] + [Version(Min = 31.1, Max = 31.2)] public ulong unresolvedStaticCallPointers; // Added in metadata v23 diff --git a/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs b/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs index e332342..9702702 100644 --- a/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs +++ b/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs @@ -4,6 +4,7 @@ All rights reserved. */ +using Il2CppInspector.Next; using Il2CppInspector.Utils; using NoisyCowStudios.Bin2Object; using System; @@ -12,6 +13,7 @@ using System.IO; using System.IO.Compression; using System.Linq; using System.Text; +using VersionedSerialization; namespace Il2CppInspector { @@ -31,7 +33,9 @@ namespace Il2CppInspector public List MetadataUsages { get; } // Shortcuts - public double Version => Math.Max(Metadata.Version, Binary.Image.Version); + public StructVersion Version => Metadata.Version > Binary.Image.Version + ? Metadata.Version + : Binary.Image.Version; public Dictionary Strings => Metadata.Strings; public string[] StringLiterals => Metadata.StringLiterals; @@ -93,11 +97,11 @@ namespace Il2CppInspector private List buildMetadataUsages() { // No metadata usages for versions < 19 - if (Version < 19) + if (Version < MetadataVersions.V190) return null; // Metadata usages are lazily initialized during runtime for versions >= 27 - if (Version >= 27) + if (Version >= MetadataVersions.V270) return buildLateBindingMetadataUsages(); // Version >= 19 && < 27 @@ -217,9 +221,9 @@ namespace Il2CppInspector } // Build list of custom attribute generators - if (Version < 27) + if (Version < MetadataVersions.V270) CustomAttributeGenerators = Binary.CustomAttributeGenerators; - else if (Version < 29) + else if (Version < MetadataVersions.V290) { var cagCount = Images.Sum(i => i.customAttributeCount); CustomAttributeGenerators = new ulong[cagCount]; @@ -243,7 +247,7 @@ namespace Il2CppInspector // Get sorted list of function pointers from all sources // TODO: This does not include IL2CPP API functions - var sortedFunctionPointers = (Version <= 24.1)? + var sortedFunctionPointers = (Version <= MetadataVersions.V241) ? Binary.GlobalMethodPointers.Select(getDecodedAddress).ToList() : Binary.ModuleMethodPointers.SelectMany(module => module.Value).Select(getDecodedAddress).ToList(); @@ -261,7 +265,7 @@ namespace Il2CppInspector FunctionAddresses.Add(sortedFunctionPointers[^1], sortedFunctionPointers[^1]); // Organize custom attribute indices - if (Version >= 24.1) { + if (Version >= MetadataVersions.V241) { AttributeIndicesByToken = []; foreach (var image in Images) { @@ -269,7 +273,7 @@ namespace Il2CppInspector for (int i = 0; i < image.customAttributeCount; i++) { var index = image.customAttributeStart + i; - var token = Version >= 29 ? AttributeDataRanges[index].token : AttributeTypeRanges[index].token; + var token = Version >= MetadataVersions.V290 ? AttributeDataRanges[index].token : AttributeTypeRanges[index].token; attsByToken.Add(token, index); } @@ -294,13 +298,13 @@ namespace Il2CppInspector ulong start = 0; // Global method pointer array - if (Version <= 24.1) { + if (Version <= MetadataVersions.V241) { start = Binary.GlobalMethodPointers[methodDef.methodIndex]; } // Per-module method pointer array uses the bottom 24 bits of the method's metadata token // Derived from il2cpp::vm::MetadataCache::GetMethodPointer - if (Version >= 24.2) { + if (Version >= MetadataVersions.V242) { var method = (methodDef.token & 0xffffff); if (method == 0) return null; @@ -335,7 +339,7 @@ namespace Il2CppInspector // Get a method invoker index from a method definition public int GetInvokerIndex(Il2CppCodeGenModule module, Il2CppMethodDefinition methodDef) { - if (Version <= 24.1) { + if (Version <= MetadataVersions.V241) { return methodDef.invokerIndex; } diff --git a/Il2CppInspector.Common/IL2CPP/ImageScan.cs b/Il2CppInspector.Common/IL2CPP/ImageScan.cs index d494537..91a05af 100644 --- a/Il2CppInspector.Common/IL2CPP/ImageScan.cs +++ b/Il2CppInspector.Common/IL2CPP/ImageScan.cs @@ -9,6 +9,7 @@ using System.Buffers; using System.Collections.Generic; using System.Linq; using System.Text; +using Il2CppInspector.Next; namespace Il2CppInspector { @@ -120,7 +121,7 @@ namespace Il2CppInspector // Find CodeRegistration // >= 24.2 - if (metadata.Version >= 24.2) { + if (metadata.Version >= MetadataVersions.V242) { // < 27: mscorlib.dll is always the first CodeGenModule // >= 27: mscorlib.dll is always the last CodeGenModule (Assembly-CSharp.dll is always the first but non-Unity builds don't have this DLL) @@ -137,7 +138,7 @@ namespace Il2CppInspector // Unwind from string pointer -> CodeGenModule -> CodeGenModules + x foreach (var potentialCodeGenModules in FindAllPointerChains(imageBytes, va, 2)) { - if (metadata.Version >= 27) + if (metadata.Version >= MetadataVersions.V270) { for (int i = imagesCount - 1; i >= 0; i--) { @@ -210,16 +211,16 @@ namespace Il2CppInspector // if this changes we'll have to get smarter about disambiguating these two. var cr = Image.ReadMappedObject(codeRegistration); - if (Image.Version == 24.2 && cr.interopDataCount == 0) { - Image.Version = 24.3; + if (Image.Version == MetadataVersions.V242 && cr.interopDataCount == 0) { + Image.Version = MetadataVersions.V243; codeRegistration -= ptrSize * 2; // two extra words for WindowsRuntimeFactory } - if (Image.Version == 27 && cr.reversePInvokeWrapperCount > 0x30000) + if (Image.Version == MetadataVersions.V270 && cr.reversePInvokeWrapperCount > 0x30000) { // If reversePInvokeWrapperCount is a pointer, then it's because we're actually on 27.1 and there's a genericAdjustorThunks pointer interfering. // We need to bump version to 27.1 and back up one more pointer. - Image.Version = 27.1; + Image.Version = MetadataVersions.V271; codeRegistration -= ptrSize; } } @@ -259,7 +260,7 @@ namespace Il2CppInspector vas = FindAllMappedWords(imageBytes, typesLength).Select(a => a - mrSize + ptrSize * 4); // >= 19 && < 27 - if (Image.Version < 27) + if (Image.Version < MetadataVersions.V270) foreach (var va in vas) { var mr = Image.ReadMappedObject(va); if (mr.metadataUsagesCount == (ulong) metadata.MetadataUsageLists.Length) diff --git a/Il2CppInspector.Common/IL2CPP/Metadata.cs b/Il2CppInspector.Common/IL2CPP/Metadata.cs index 57841d8..a814585 100644 --- a/Il2CppInspector.Common/IL2CPP/Metadata.cs +++ b/Il2CppInspector.Common/IL2CPP/Metadata.cs @@ -10,7 +10,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using Il2CppInspector.Next; using NoisyCowStudios.Bin2Object; +using VersionedSerialization; namespace Il2CppInspector { @@ -86,9 +88,9 @@ namespace Il2CppInspector } // Set object versioning for Bin2Object from metadata version - Version = Header.version; + Version = new StructVersion(Header.version); - if (Version < 16 || Version > 31) { + if (Version < MetadataVersions.V160 || Version > MetadataVersions.V310) { throw new InvalidOperationException($"The supplied metadata file is not of a supported version ({Header.version})."); } @@ -108,8 +110,8 @@ namespace Il2CppInspector var realHeaderLength = Header.stringLiteralOffset; if (realHeaderLength != Sizeof(typeof(Il2CppGlobalMetadataHeader))) { - if (Version == 24.0) { - Version = 24.2; + if (Version == MetadataVersions.V240) { + Version = MetadataVersions.V242; Header = ReadObject(0); } } @@ -120,16 +122,16 @@ namespace Il2CppInspector } // Load all the relevant metadata using offsets provided in the header - if (Version >= 16) + if (Version >= MetadataVersions.V160) Images = ReadArray(Header.imagesOffset, Header.imagesCount / Sizeof(typeof(Il2CppImageDefinition))); // As an additional sanity check, all images in the metadata should have Mono.Cecil.MetadataToken == 1 // In metadata v24.1, two extra fields were added which will cause the below test to fail. // In that case, we can then adjust the version number and reload // Tokens were introduced in v19 - we don't bother testing earlier versions - if (Version >= 19 && Images.Any(x => x.token != 1)) - if (Version == 24.0) { - Version = 24.1; + if (Version >= MetadataVersions.V190 && Images.Any(x => x.token != 1)) + if (Version == MetadataVersions.V240) { + Version = MetadataVersions.V241; // No need to re-read the header, it's the same for both sub-versions Images = ReadArray(Header.imagesOffset, Header.imagesCount / Sizeof(typeof(Il2CppImageDefinition))); @@ -153,44 +155,63 @@ namespace Il2CppInspector InterfaceOffsets = ReadArray(Header.interfaceOffsetsOffset, Header.interfaceOffsetsCount / Sizeof(typeof(Il2CppInterfaceOffsetPair))); VTableMethodIndices = ReadArray(Header.vtableMethodsOffset, Header.vtableMethodsCount / sizeof(uint)); - if (Version >= 16) { + if (Version >= MetadataVersions.V160) { // In v24.4 hashValueIndex was removed from Il2CppAssemblyNameDefinition, which is a field in Il2CppAssemblyDefinition // The number of images and assemblies should be the same. If they are not, we deduce that we are using v24.4 // Note the version comparison matches both 24.2 and 24.3 here since 24.3 is tested for during binary loading var assemblyCount = Header.assembliesCount / Sizeof(typeof(Il2CppAssemblyDefinition)); var changedAssemblyDefStruct = false; - if ((Version == 24.1 || Version == 24.2 || Version == 24.3) && assemblyCount < Images.Length) + if ((Version == MetadataVersions.V241 || Version == MetadataVersions.V242 || Version == MetadataVersions.V243) && assemblyCount < Images.Length) { - if (Version == 24.1) + if (Version == MetadataVersions.V241) changedAssemblyDefStruct = true; - Version = 24.4; + Version = MetadataVersions.V244; } Assemblies = ReadArray(Header.assembliesOffset, Images.Length); if (changedAssemblyDefStruct) - Version = 24.1; + Version = MetadataVersions.V241; ParameterDefaultValues = ReadArray(Header.parameterDefaultValuesOffset, Header.parameterDefaultValuesCount / Sizeof(typeof(Il2CppParameterDefaultValue))); } - if (Version >= 19 && Version < 27) { + if (Version >= MetadataVersions.V190 && Version < MetadataVersions.V270) { MetadataUsageLists = ReadArray(Header.metadataUsageListsOffset, Header.metadataUsageListsCount / Sizeof(typeof(Il2CppMetadataUsageList))); MetadataUsagePairs = ReadArray(Header.metadataUsagePairsOffset, Header.metadataUsagePairsCount / Sizeof(typeof(Il2CppMetadataUsagePair))); } - if (Version >= 19) { + if (Version >= MetadataVersions.V190) { FieldRefs = ReadArray(Header.fieldRefsOffset, Header.fieldRefsCount / Sizeof(typeof(Il2CppFieldRef))); } - if (Version >= 21 && Version < 29) { + if (Version >= MetadataVersions.V210 && Version < MetadataVersions.V290) { AttributeTypeIndices = ReadArray(Header.attributeTypesOffset, Header.attributeTypesCount / sizeof(int)); AttributeTypeRanges = ReadArray(Header.attributesInfoOffset, Header.attributesInfoCount / Sizeof(typeof(Il2CppCustomAttributeTypeRange))); } - if (Version >= 29) + if (Version >= MetadataVersions.V290) { AttributeDataRanges = ReadArray(Header.attributeDataRangeOffset, Header.attributeDataRangeSize / Sizeof(typeof(Il2CppCustomAttributeDataRange))); } + if (Version == MetadataVersions.V290 || Version == MetadataVersions.V310) + { + // 29.2/31.2 added a new isUnmanagedCallersOnly flag to Il2CppMethodDefinition. + // This offsets all subsequent entries by one - we can detect this by checking the + // top token byte (which should always be 0x06). + + if (Methods.Length >= 2) + { + var secondToken = Methods[1].token; + if (secondToken >> 24 != 0x6) + { + Version = new StructVersion(Version.Major, 1, Version.Tag); + + Methods = ReadArray(Header.methodsOffset, + Header.methodsCount / Sizeof(typeof(Il2CppMethodDefinition))); + } + } + } + // Get all metadata strings var pluginGetStringsResult = PluginHooks.GetStrings(this); if (pluginGetStringsResult.IsDataModified && !pluginGetStringsResult.IsInvalid) @@ -229,7 +250,9 @@ namespace Il2CppInspector public int Sizeof(Type type) => Sizeof(type, Version); - public int Sizeof(Type type, double metadataVersion, int longSizeBytes = 8) { + public int Sizeof(Type type, StructVersion metadataVersion, int longSizeBytes = 8) + { + var doubleRepresentation = metadataVersion.AsDouble; if (Reader.ObjectMappings.TryGetValue(type, out var streamType)) type = streamType; @@ -239,7 +262,7 @@ namespace Il2CppInspector { // Only process fields for our selected object versioning (always process if none supplied) var versions = i.GetCustomAttributes(false).Select(v => (v.Min, v.Max)).ToList(); - if (versions.Any() && !versions.Any(v => (v.Min <= metadataVersion || v.Min == -1) && (v.Max >= metadataVersion || v.Max == -1))) + if (versions.Any() && !versions.Any(v => (v.Min <= doubleRepresentation || v.Min == -1) && (v.Max >= doubleRepresentation || v.Max == -1))) continue; if (i.FieldType == typeof(long) || i.FieldType == typeof(ulong)) diff --git a/Il2CppInspector.Common/IL2CPP/MetadataClasses.cs b/Il2CppInspector.Common/IL2CPP/MetadataClasses.cs index 2d1ef86..e2c3cfc 100644 --- a/Il2CppInspector.Common/IL2CPP/MetadataClasses.cs +++ b/Il2CppInspector.Common/IL2CPP/MetadataClasses.cs @@ -342,7 +342,8 @@ namespace Il2CppInspector public ushort slot; public ushort parameterCount; - [Version(Min = 29.2, Max = 31)] + [Version(Min = 29.2, Max = 29.2)] + [Version(Min = 31.2, Max = 31.2)] public bool isUnmanagedCallersOnly; } diff --git a/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs b/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs index f30df66..2c4d765 100644 --- a/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs +++ b/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs @@ -6,6 +6,8 @@ All rights reserved. */ +using Il2CppInspector.Next; + namespace Il2CppInspector { public enum MetadataUsageType @@ -34,7 +36,7 @@ namespace Il2CppInspector public static MetadataUsage FromEncodedIndex(Il2CppInspector package, uint encodedIndex, ulong virtualAddress = 0) { uint index; MetadataUsageType usageType; - if (package.Version < 19) { + if (package.Version < MetadataVersions.V190) { /* These encoded indices appear only in vtables, and are decoded by IsGenericMethodIndex/GetDecodedMethodIndex */ var isGeneric = encodedIndex & 0x80000000; index = package.Binary.VTableMethodReferences[encodedIndex & 0x7FFFFFFF]; @@ -46,7 +48,7 @@ namespace Il2CppInspector index = encodedIndex & 0x1FFFFFFF; // From v27 the bottom bit is set to indicate the usage token hasn't been replaced with a pointer at runtime yet - if (package.Version >= 27) + if (package.Version >= MetadataVersions.V270) index >>= 1; } return new MetadataUsage(usageType, (int)index, virtualAddress); diff --git a/Il2CppInspector.Common/Il2CppInspector.csproj b/Il2CppInspector.Common/Il2CppInspector.csproj index c7e69ca..bf4f2d7 100644 --- a/Il2CppInspector.Common/Il2CppInspector.csproj +++ b/Il2CppInspector.Common/Il2CppInspector.csproj @@ -47,6 +47,8 @@ + + diff --git a/Il2CppInspector.Common/Model/AddressMap.cs b/Il2CppInspector.Common/Model/AddressMap.cs index d212291..2fa7fde 100644 --- a/Il2CppInspector.Common/Model/AddressMap.cs +++ b/Il2CppInspector.Common/Model/AddressMap.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using Il2CppInspector.Cpp; +using Il2CppInspector.Next; using Il2CppInspector.Reflection; namespace Il2CppInspector.Model @@ -123,7 +124,7 @@ namespace Il2CppInspector.Model Add(binary.CodeRegistrationPointer, binary.CodeRegistration); Add(binary.MetadataRegistrationPointer, binary.MetadataRegistration); - if (Model.Package.Version >= 24.2) { + if (Model.Package.Version >= MetadataVersions.V242) { // TODO: Add some kind of AppArray composite type for arrays as we'll be adding more later Add(binary.CodeRegistration.pcodeGenModules, binary.CodeGenModulePointers); diff --git a/Il2CppInspector.Common/Model/AppModel.cs b/Il2CppInspector.Common/Model/AppModel.cs index e16c0c7..65adac9 100644 --- a/Il2CppInspector.Common/Model/AppModel.cs +++ b/Il2CppInspector.Common/Model/AppModel.cs @@ -12,6 +12,7 @@ using System.Linq; using Aron.Weiler; using Il2CppInspector.Cpp; using Il2CppInspector.Cpp.UnityHeaders; +using Il2CppInspector.Next; using Il2CppInspector.Reflection; namespace Il2CppInspector.Model @@ -54,7 +55,7 @@ namespace Il2CppInspector.Model public Dictionary Fields { get; } = []; public Dictionary FieldRvas { get; } = []; - public bool StringIndexesAreOrdinals => Package.Version < 19; + public bool StringIndexesAreOrdinals => Package.Version < MetadataVersions.V190; // The .NET type model for the application public TypeModel TypeModel { get; } @@ -270,7 +271,7 @@ namespace Il2CppInspector.Model } // Add string literals for metadata <19 to the model - if (Package.Version < 19) { + if (Package.Version < MetadataVersions.V190) { /* Version < 19 calls `il2cpp_codegen_string_literal_from_index` to get string literals. * Unfortunately, metadata references are just loose globals in Il2CppMetadataUsage.cpp * so we can't automatically name those. Next best thing is to define an enum for the strings. */ diff --git a/Il2CppInspector.Common/Next/MetadataVersions.cs b/Il2CppInspector.Common/Next/MetadataVersions.cs new file mode 100644 index 0000000..992f636 --- /dev/null +++ b/Il2CppInspector.Common/Next/MetadataVersions.cs @@ -0,0 +1,33 @@ +using VersionedSerialization; + +namespace Il2CppInspector.Next; + +public static class MetadataVersions +{ + public static readonly StructVersion V160 = new(16); + + public static readonly StructVersion V190 = new(19); + + public static readonly StructVersion V210 = new(21); + public static readonly StructVersion V220 = new(22); + + public static readonly StructVersion V240 = new(24); + public static readonly StructVersion V241 = new(24, 1); + public static readonly StructVersion V242 = new(24, 2); + public static readonly StructVersion V243 = new(24, 3); + public static readonly StructVersion V244 = new(24, 4); + public static readonly StructVersion V245 = new(24, 5); + + public static readonly StructVersion V270 = new(27); + public static readonly StructVersion V271 = new(27, 1); + public static readonly StructVersion V272 = new(27, 2); + + public static readonly StructVersion V290 = new(29); + public static readonly StructVersion V310 = new(31); + + public static readonly StructVersion V291 = new(29, 1); + public static readonly StructVersion V311 = new(31, 1); + + public static readonly StructVersion V292 = new(29, 2); + public static readonly StructVersion V312 = new(31, 2); +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Outputs/AssemblyShims.cs b/Il2CppInspector.Common/Outputs/AssemblyShims.cs index 4dda8f7..f54fa25 100644 --- a/Il2CppInspector.Common/Outputs/AssemblyShims.cs +++ b/Il2CppInspector.Common/Outputs/AssemblyShims.cs @@ -12,6 +12,7 @@ using System.IO; using System.Linq; using dnlib.DotNet; using dnlib.DotNet.Emit; +using Il2CppInspector.Next; using Il2CppInspector.Reflection; namespace Il2CppInspector.Outputs @@ -591,7 +592,7 @@ namespace Il2CppInspector.Outputs // Create folder for DLLs Directory.CreateDirectory(outputPath); - if (model.Package.Version >= 29) + if (model.Package.Version >= MetadataVersions.V290) { // We can now apply all attributes directly. directApplyAttributes = model.TypesByDefinitionIndex diff --git a/Il2CppInspector.Common/Outputs/CppScaffolding.cs b/Il2CppInspector.Common/Outputs/CppScaffolding.cs index f7795f0..d19f84e 100644 --- a/Il2CppInspector.Common/Outputs/CppScaffolding.cs +++ b/Il2CppInspector.Common/Outputs/CppScaffolding.cs @@ -248,7 +248,7 @@ namespace Il2CppInspector.Outputs using (_writer) { writeHeader(); - writeCode($"#define __IL2CPP_METADATA_VERSION {_model.Package.Version * 10:F0}"); + writeCode($"#define __IL2CPP_METADATA_VERSION {_model.Package.Version.Major * 10 + _model.Package.Version.Minor * 10:F0}"); } // Write boilerplate code diff --git a/Il2CppInspector.Common/Outputs/JSONMetadata.cs b/Il2CppInspector.Common/Outputs/JSONMetadata.cs index 846ab89..42df77c 100644 --- a/Il2CppInspector.Common/Outputs/JSONMetadata.cs +++ b/Il2CppInspector.Common/Outputs/JSONMetadata.cs @@ -8,6 +8,7 @@ using System.IO; using System.Text.Json; using Il2CppInspector.Reflection; using Il2CppInspector.Model; +using Il2CppInspector.Next; namespace Il2CppInspector.Outputs { @@ -182,7 +183,7 @@ namespace Il2CppInspector.Outputs // TODO: In the future, add data ranges for the entire IL2CPP metadata tree writeArray("arrayMetadata", () => { - if (model.Package.Version >= 24.2) { + if (model.Package.Version >= MetadataVersions.V242) { writeObject(() => writeTypedArray(binary.CodeRegistration.pcodeGenModules, binary.Modules.Count, "struct Il2CppCodeGenModule *", "g_CodeGenModules")); } }, "IL2CPP Array Metadata"); diff --git a/Il2CppInspector.Common/Reflection/CustomAttributeData.cs b/Il2CppInspector.Common/Reflection/CustomAttributeData.cs index 60e9c6e..511544b 100644 --- a/Il2CppInspector.Common/Reflection/CustomAttributeData.cs +++ b/Il2CppInspector.Common/Reflection/CustomAttributeData.cs @@ -4,6 +4,7 @@ All rights reserved. */ +using Il2CppInspector.Next; using System; using System.Collections.Generic; using System.Linq; @@ -88,10 +89,10 @@ namespace Il2CppInspector.Reflection var pkg = asm.Model.Package; // Attribute type ranges weren't included before v21 (customASttributeGenerators was though) - if (pkg.Version < 21) + if (pkg.Version < MetadataVersions.V210) yield break; - if (pkg.Version < 29) + if (pkg.Version < MetadataVersions.V290) { var range = pkg.AttributeTypeRanges[customAttributeIndex]; for (var i = range.start; i < range.start + range.count; i++) diff --git a/Il2CppInspector.Common/Reflection/TypeModel.cs b/Il2CppInspector.Common/Reflection/TypeModel.cs index 35bb8db..cf0b6ee 100644 --- a/Il2CppInspector.Common/Reflection/TypeModel.cs +++ b/Il2CppInspector.Common/Reflection/TypeModel.cs @@ -5,6 +5,7 @@ All rights reserved. */ +using Il2CppInspector.Next; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -214,7 +215,7 @@ namespace Il2CppInspector.Reflection // Get generic type definition TypeInfo genericTypeDef; - if (Package.Version < 27) { + if (Package.Version < MetadataVersions.V270) { // It appears that TypeRef can be -1 if the generic depth recursion limit // (--maximum-recursive-generic-depth=) is reached in Il2Cpp. In this case, // no generic instance type is generated, so we just produce a null TypeInfo here. @@ -318,7 +319,7 @@ namespace Il2CppInspector.Reflection // The attribute index is an index into AttributeTypeRanges, each of which is a start-end range index into AttributeTypeIndices, each of which is a TypeIndex public int GetCustomAttributeIndex(Assembly asm, int token, int customAttributeIndex) { // Prior to v24.1, Type, Field, Parameter, Method, Event, Property, Assembly definitions had their own customAttributeIndex field - if (Package.Version <= 24.0) + if (Package.Version <= MetadataVersions.V240) return customAttributeIndex; // From v24.1 onwards, token was added to Il2CppCustomAttributeTypeRange and each Il2CppImageDefinition noted the CustomAttributeTypeRanges for the image diff --git a/Il2CppInspector.Common/Utils/BlobReader.cs b/Il2CppInspector.Common/Utils/BlobReader.cs index f4936f2..f2c367e 100644 --- a/Il2CppInspector.Common/Utils/BlobReader.cs +++ b/Il2CppInspector.Common/Utils/BlobReader.cs @@ -3,6 +3,7 @@ using System.Text; using System; using System.Diagnostics; using System.IO; +using Il2CppInspector.Next; namespace Il2CppInspector.Utils; @@ -111,7 +112,7 @@ public static class BlobReader int ReadInt32() { - if (blob.Version >= 29) + if (blob.Version >= MetadataVersions.V290) { var address = blob.Position; @@ -131,7 +132,7 @@ public static class BlobReader uint ReadUInt32() { - if (blob.Version >= 29) + if (blob.Version >= MetadataVersions.V290) { var address = blob.Position; diff --git a/VersionedSerialization/StructVersion.cs b/VersionedSerialization/StructVersion.cs index 23a1c82..c7a9f17 100644 --- a/VersionedSerialization/StructVersion.cs +++ b/VersionedSerialization/StructVersion.cs @@ -6,6 +6,8 @@ public readonly struct StructVersion(int major = 0, int minor = 0, string? tag = public readonly int Minor = minor; public readonly string? Tag = tag; + public double AsDouble => Major + Minor / 10.0; + #region Equality operators public static bool operator ==(StructVersion left, StructVersion right) @@ -42,9 +44,17 @@ public readonly struct StructVersion(int major = 0, int minor = 0, string? tag = public static implicit operator StructVersion(string value) { var versionParts = value.Split('.'); - if (versionParts.Length is 1 or > 2) + if (versionParts.Length > 2) throw new InvalidOperationException("Invalid version string."); + if (versionParts.Length == 1) + { + if (!int.TryParse(versionParts[0], out var version)) + throw new InvalidOperationException("Invalid single-number version string."); + + return new StructVersion(version); + } + var tagParts = versionParts[1].Split("-"); if (tagParts.Length > 2) throw new InvalidOperationException("Invalid version string.");