From 30c019c4ef24b77c2ec900ca01870b9f48deeb9f Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Tue, 13 Aug 2024 04:23:49 +0200 Subject: [PATCH 01/27] Bump projects to .net 9 and update nugets --- .github/workflows/build.yml | 8 ++++---- Il2CppInspector.CLI/Il2CppInspector.CLI.csproj | 4 ++-- Il2CppInspector.Common/Il2CppInspector.csproj | 6 +++--- Il2CppInspector.GUI/Il2CppInspector.GUI.csproj | 9 ++++----- Il2CppTests/Il2CppTests.csproj | 4 ++-- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9dc498b1..98dc063b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,10 +28,10 @@ jobs: run: nuget restore - name: Build GUI - run: msbuild /t:Il2CppInspector_GUI:publish /p:Configuration=Release /p:Platform="Any CPU" /p:TargetFramework=net8.0-windows /p:SelfContained=false /verbosity:minimal + run: msbuild /t:Il2CppInspector_GUI:publish /p:Configuration=Release /p:Platform="Any CPU" /p:TargetFramework=net9.0-windows /p:SelfContained=false /verbosity:minimal - name: Build CLI - run: msbuild /t:Il2CppInspector_CLI:publish /p:Configuration=Release /p:Platform="Any CPU" /p:TargetFramework=net8.0 /p:SelfContained=false /verbosity:minimal + run: msbuild /t:Il2CppInspector_CLI:publish /p:Configuration=Release /p:Platform="Any CPU" /p:TargetFramework=net9.0 /p:SelfContained=false /verbosity:minimal - name: Add Plugins folder (GUI) shell: pwsh @@ -47,11 +47,11 @@ jobs: uses: actions/upload-artifact@v3 with: name: Il2CppInspectorRedux.GUI - path: Il2CppInspector.GUI/bin/Release/net8.0-windows/win-x64/publish + path: Il2CppInspector.GUI/bin/Release/net9.0-windows/win-x64/publish - name: Upload CLI Artifact uses: actions/upload-artifact@v2 with: name: Il2CppInspectorRedux.CLI - path: Il2CppInspector.CLI/bin/Release/net8.0/win-x64/publish + path: Il2CppInspector.CLI/bin/Release/net9.0/win-x64/publish diff --git a/Il2CppInspector.CLI/Il2CppInspector.CLI.csproj b/Il2CppInspector.CLI/Il2CppInspector.CLI.csproj index 5f42189f..09f3a1d0 100644 --- a/Il2CppInspector.CLI/Il2CppInspector.CLI.csproj +++ b/Il2CppInspector.CLI/Il2CppInspector.CLI.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 true @@ -32,7 +32,7 @@ - + diff --git a/Il2CppInspector.Common/Il2CppInspector.csproj b/Il2CppInspector.Common/Il2CppInspector.csproj index a595ee88..c7e69caa 100644 --- a/Il2CppInspector.Common/Il2CppInspector.csproj +++ b/Il2CppInspector.Common/Il2CppInspector.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 Il2CppInspector.Common Katy Coe 2023.1 @@ -38,8 +38,8 @@ - - + + NU1605 diff --git a/Il2CppInspector.GUI/Il2CppInspector.GUI.csproj b/Il2CppInspector.GUI/Il2CppInspector.GUI.csproj index dbbdd5dc..841e45af 100644 --- a/Il2CppInspector.GUI/Il2CppInspector.GUI.csproj +++ b/Il2CppInspector.GUI/Il2CppInspector.GUI.csproj @@ -2,7 +2,7 @@ WinExe - net8.0-windows + net9.0-windows true false @@ -33,10 +33,9 @@ - - - - + + + NU1701 diff --git a/Il2CppTests/Il2CppTests.csproj b/Il2CppTests/Il2CppTests.csproj index f2a43c58..26ebca8d 100644 --- a/Il2CppTests/Il2CppTests.csproj +++ b/Il2CppTests/Il2CppTests.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 false @@ -215,7 +215,7 @@ - + From 22ecdc36121fee5bad2cfa2c4b9ca44ffc5c04a6 Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Tue, 13 Aug 2024 04:27:23 +0200 Subject: [PATCH 02/27] add VersionedSerialization + source generator --- Il2CppInspector.sln | 12 + .../Analyzer/InvalidVersionAnalyzer.cs | 68 +++ .../AnalyzerReleases.Shipped.md | 3 + .../AnalyzerReleases.Unshipped.md | 8 + .../Models/ObjectSerializationInfo.cs | 13 + .../Models/PropertySerializationInfo.cs | 11 + .../Models/PropertyType.cs | 48 ++ .../Models/VersionCondition.cs | 3 + .../ObjectSerializationGenerator.cs | 356 ++++++++++++++ .../StructVersion.cs | 71 +++ .../Utils/CodeGenerator.cs | 54 +++ .../Utils/Constants.cs | 17 + .../Utils/HashCode.cs | 454 ++++++++++++++++++ .../Utils/ImmutableEquatableArray.cs | 115 +++++ .../VersionedSerialization.Generator.csproj | 20 + .../Attributes/AlignedAttribute.cs | 6 + .../CustomSerializationAttribute.cs | 6 + .../Attributes/VersionConditionAttribute.cs | 11 + .../Attributes/VersionedStructAttribute.cs | 4 + VersionedSerialization/IReadable.cs | 7 + VersionedSerialization/IReader.cs | 22 + VersionedSerialization/ReaderExtensions.cs | 62 +++ VersionedSerialization/SpanReader.cs | 146 ++++++ VersionedSerialization/StructVersion.cs | 58 +++ .../VersionedSerialization.csproj | 10 + 25 files changed, 1585 insertions(+) create mode 100644 VersionedSerialization.Generator/Analyzer/InvalidVersionAnalyzer.cs create mode 100644 VersionedSerialization.Generator/AnalyzerReleases.Shipped.md create mode 100644 VersionedSerialization.Generator/AnalyzerReleases.Unshipped.md create mode 100644 VersionedSerialization.Generator/Models/ObjectSerializationInfo.cs create mode 100644 VersionedSerialization.Generator/Models/PropertySerializationInfo.cs create mode 100644 VersionedSerialization.Generator/Models/PropertyType.cs create mode 100644 VersionedSerialization.Generator/Models/VersionCondition.cs create mode 100644 VersionedSerialization.Generator/ObjectSerializationGenerator.cs create mode 100644 VersionedSerialization.Generator/StructVersion.cs create mode 100644 VersionedSerialization.Generator/Utils/CodeGenerator.cs create mode 100644 VersionedSerialization.Generator/Utils/Constants.cs create mode 100644 VersionedSerialization.Generator/Utils/HashCode.cs create mode 100644 VersionedSerialization.Generator/Utils/ImmutableEquatableArray.cs create mode 100644 VersionedSerialization.Generator/VersionedSerialization.Generator.csproj create mode 100644 VersionedSerialization/Attributes/AlignedAttribute.cs create mode 100644 VersionedSerialization/Attributes/CustomSerializationAttribute.cs create mode 100644 VersionedSerialization/Attributes/VersionConditionAttribute.cs create mode 100644 VersionedSerialization/Attributes/VersionedStructAttribute.cs create mode 100644 VersionedSerialization/IReadable.cs create mode 100644 VersionedSerialization/IReader.cs create mode 100644 VersionedSerialization/ReaderExtensions.cs create mode 100644 VersionedSerialization/SpanReader.cs create mode 100644 VersionedSerialization/StructVersion.cs create mode 100644 VersionedSerialization/VersionedSerialization.csproj diff --git a/Il2CppInspector.sln b/Il2CppInspector.sln index 71d053df..10087344 100644 --- a/Il2CppInspector.sln +++ b/Il2CppInspector.sln @@ -40,6 +40,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{ .github\workflows\build.yml = .github\workflows\build.yml EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VersionedSerialization", "VersionedSerialization\VersionedSerialization.csproj", "{803C3421-1907-4114-8B6B-F5E1789FD6A6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VersionedSerialization.Generator", "VersionedSerialization.Generator\VersionedSerialization.Generator.csproj", "{6FF1F0C0-374A-4B7E-B173-697605679AF6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -66,6 +70,14 @@ Global {A24D77DA-8A64-4AD3-956A-677A96F20373}.Debug|Any CPU.Build.0 = Debug|Any CPU {A24D77DA-8A64-4AD3-956A-677A96F20373}.Release|Any CPU.ActiveCfg = Release|Any CPU {A24D77DA-8A64-4AD3-956A-677A96F20373}.Release|Any CPU.Build.0 = Release|Any CPU + {803C3421-1907-4114-8B6B-F5E1789FD6A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {803C3421-1907-4114-8B6B-F5E1789FD6A6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {803C3421-1907-4114-8B6B-F5E1789FD6A6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {803C3421-1907-4114-8B6B-F5E1789FD6A6}.Release|Any CPU.Build.0 = Release|Any CPU + {6FF1F0C0-374A-4B7E-B173-697605679AF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6FF1F0C0-374A-4B7E-B173-697605679AF6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6FF1F0C0-374A-4B7E-B173-697605679AF6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6FF1F0C0-374A-4B7E-B173-697605679AF6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/VersionedSerialization.Generator/Analyzer/InvalidVersionAnalyzer.cs b/VersionedSerialization.Generator/Analyzer/InvalidVersionAnalyzer.cs new file mode 100644 index 00000000..5bb7d8c7 --- /dev/null +++ b/VersionedSerialization.Generator/Analyzer/InvalidVersionAnalyzer.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using VersionedSerialization.Generator.Utils; + +namespace VersionedSerialization.Generator.Analyzer; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public class InvalidVersionAnalyzer : DiagnosticAnalyzer +{ + private const string Identifier = "VS0001"; + + private const string Category = "Usage"; + private const string Title = "Invalid version string in attribute"; + private const string MessageFormat = "Invalid version string"; + private const string Description = + "The version needs to be specified in the following format: .. Tags are not supported."; + + private static readonly DiagnosticDescriptor Descriptor = new(Identifier, Title, MessageFormat, + Category, DiagnosticSeverity.Error, true, Description); + + public override ImmutableArray SupportedDiagnostics => [Descriptor]; + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.PropertyDeclaration, SyntaxKind.FieldDeclaration); + } + + private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context) + { + if (context.ContainingSymbol == null) + return; + + var compilation = context.Compilation; + var versionConditionAttribute = compilation.GetTypeByMetadataName(Constants.VersionConditionAttribute); + + foreach (var attribute in context.ContainingSymbol.GetAttributes()) + { + if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, versionConditionAttribute)) + { + if (attribute.ApplicationSyntaxReference == null) + continue; + + foreach (var argument in attribute.NamedArguments) + { + var name = argument.Key; + if (name is Constants.LessThan or Constants.GreaterThan or Constants.EqualTo) + { + var value = (string)argument.Value.Value!; + + if (!StructVersion.TryParse(value, out var ver) || ver.Tag != null) + { + var span = attribute.ApplicationSyntaxReference.Span; + var location = attribute.ApplicationSyntaxReference.SyntaxTree.GetLocation(span); + var diagnostic = Diagnostic.Create(Descriptor, location); + context.ReportDiagnostic(diagnostic); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/VersionedSerialization.Generator/AnalyzerReleases.Shipped.md b/VersionedSerialization.Generator/AnalyzerReleases.Shipped.md new file mode 100644 index 00000000..60b59dd9 --- /dev/null +++ b/VersionedSerialization.Generator/AnalyzerReleases.Shipped.md @@ -0,0 +1,3 @@ +; Shipped analyzer releases +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + diff --git a/VersionedSerialization.Generator/AnalyzerReleases.Unshipped.md b/VersionedSerialization.Generator/AnalyzerReleases.Unshipped.md new file mode 100644 index 00000000..2a7d0cf8 --- /dev/null +++ b/VersionedSerialization.Generator/AnalyzerReleases.Unshipped.md @@ -0,0 +1,8 @@ +; Unshipped analyzer release +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +VS0001 | Usage | Error | InvalidVersionAnalyzer \ No newline at end of file diff --git a/VersionedSerialization.Generator/Models/ObjectSerializationInfo.cs b/VersionedSerialization.Generator/Models/ObjectSerializationInfo.cs new file mode 100644 index 00000000..bb2f002c --- /dev/null +++ b/VersionedSerialization.Generator/Models/ObjectSerializationInfo.cs @@ -0,0 +1,13 @@ +using VersionedSerialization.Generator.Utils; + +namespace VersionedSerialization.Generator.Models; + +public sealed record ObjectSerializationInfo( + string Namespace, + string Name, + bool HasBaseType, + bool IsStruct, + bool ShouldGenerateSizeMethod, + bool CanGenerateSizeMethod, + ImmutableEquatableArray Properties +); \ No newline at end of file diff --git a/VersionedSerialization.Generator/Models/PropertySerializationInfo.cs b/VersionedSerialization.Generator/Models/PropertySerializationInfo.cs new file mode 100644 index 00000000..fb4a961a --- /dev/null +++ b/VersionedSerialization.Generator/Models/PropertySerializationInfo.cs @@ -0,0 +1,11 @@ +using VersionedSerialization.Generator.Utils; + +namespace VersionedSerialization.Generator.Models; + +public sealed record PropertySerializationInfo( + string Name, + string ReadMethod, + string SizeExpression, + int Alignment, + ImmutableEquatableArray VersionConditions +); \ No newline at end of file diff --git a/VersionedSerialization.Generator/Models/PropertyType.cs b/VersionedSerialization.Generator/Models/PropertyType.cs new file mode 100644 index 00000000..3e855d08 --- /dev/null +++ b/VersionedSerialization.Generator/Models/PropertyType.cs @@ -0,0 +1,48 @@ +using System; + +namespace VersionedSerialization.Generator.Models; + +public enum PropertyType +{ + Unsupported = -1, + None, + Boolean, + UInt8, + UInt16, + UInt32, + UInt64, + Int8, + Int16, + Int32, + Int64, + String, +} + +public static class PropertyTypeExtensions +{ + public static string GetTypeName(this PropertyType type) + => type switch + { + PropertyType.Unsupported => nameof(PropertyType.Unsupported), + PropertyType.None => nameof(PropertyType.None), + PropertyType.UInt8 => nameof(Byte), + PropertyType.Int8 => nameof(SByte), + PropertyType.Boolean => nameof(PropertyType.Boolean), + PropertyType.UInt16 => nameof(PropertyType.UInt16), + PropertyType.UInt32 => nameof(PropertyType.UInt32), + PropertyType.UInt64 => nameof(PropertyType.UInt64), + PropertyType.Int16 => nameof(PropertyType.Int16), + PropertyType.Int32 => nameof(PropertyType.Int32), + PropertyType.Int64 => nameof(PropertyType.Int64), + PropertyType.String => nameof(String), + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + }; + + public static bool IsSeperateMethod(this PropertyType type) + => type switch + { + PropertyType.Boolean => true, + PropertyType.String => true, + _ => false + }; +} \ No newline at end of file diff --git a/VersionedSerialization.Generator/Models/VersionCondition.cs b/VersionedSerialization.Generator/Models/VersionCondition.cs new file mode 100644 index 00000000..a7983dfc --- /dev/null +++ b/VersionedSerialization.Generator/Models/VersionCondition.cs @@ -0,0 +1,3 @@ +namespace VersionedSerialization.Generator.Models; + +public sealed record VersionCondition(StructVersion? LessThan, StructVersion? GreaterThan, StructVersion? EqualTo, string? IncludingTag, string? ExcludingTag); \ No newline at end of file diff --git a/VersionedSerialization.Generator/ObjectSerializationGenerator.cs b/VersionedSerialization.Generator/ObjectSerializationGenerator.cs new file mode 100644 index 00000000..5ab1c966 --- /dev/null +++ b/VersionedSerialization.Generator/ObjectSerializationGenerator.cs @@ -0,0 +1,356 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using VersionedSerialization.Generator.Models; +using VersionedSerialization.Generator.Utils; + +namespace VersionedSerialization.Generator +{ + [Generator] + public sealed class ObjectSerializationGenerator : IIncrementalGenerator + { + public void Initialize(IncrementalGeneratorInitializationContext context) + { + //Debugger.Launch(); + + var valueProvider = context.SyntaxProvider + .ForAttributeWithMetadataName(Constants.VersionedStructAttribute, + static (node, _) => node is ClassDeclarationSyntax or StructDeclarationSyntax, + static (context, _) => (ContextClass: (TypeDeclarationSyntax)context.TargetNode, context.SemanticModel)) + .Combine(context.CompilationProvider) + .Select(static (tuple, cancellationToken) => ParseSerializationInfo(tuple.Left.ContextClass, tuple.Left.SemanticModel, tuple.Right, cancellationToken)) + .WithTrackingName(nameof(ObjectSerializationGenerator)); + + context.RegisterSourceOutput(valueProvider, EmitCode); + } + + private static void EmitCode(SourceProductionContext sourceProductionContext, ObjectSerializationInfo info) + { + var generator = new CodeGenerator(); + generator.AppendLine("#nullable restore"); + generator.AppendLine("using VersionedSerialization;"); + generator.AppendLine(); + + generator.AppendLine($"namespace {info.Namespace};"); + + var versions = new HashSet(); + foreach (var condition in info.Properties.SelectMany(static x => x.VersionConditions)) + { + if (condition.LessThan.HasValue) + versions.Add(condition.LessThan.Value); + + if (condition.GreaterThan.HasValue) + versions.Add(condition.GreaterThan.Value); + + if (condition.EqualTo.HasValue) + versions.Add(condition.EqualTo.Value); + } + + if (versions.Count > 0) + { + generator.EnterScope("file static class Versions"); + + foreach (var version in versions) + { + generator.AppendLine($"public static readonly StructVersion {GetVersionIdentifier(version)} = \"{version}\";"); + } + + generator.LeaveScope(); + } + + generator.EnterScope($"public partial {(info.IsStruct ? "struct" : "class")} {info.Name} : IReadable"); + GenerateReadMethod(generator, info); + generator.AppendLine(); + GenerateSizeMethod(generator, info); + generator.LeaveScope(); + + sourceProductionContext.AddSource($"{info.Namespace}.{info.Name}.g.cs", generator.ToString()); + } + + private static void GenerateSizeMethod(CodeGenerator generator, ObjectSerializationInfo info) + { + generator.EnterScope("public static int Size(in StructVersion version = default, bool is32Bit = false)"); + + if (!info.CanGenerateSizeMethod) + { + generator.AppendLine("throw new InvalidOperationException(\"No size can be calculated for this struct.\");"); + } + else + { + generator.AppendLine("var size = 0;"); + if (info.HasBaseType) + generator.AppendLine("size += base.Size(in version, is32Bit);"); + + foreach (var property in info.Properties) + { + if (property.VersionConditions.Length > 0) + GenerateVersionCondition(property.VersionConditions, generator); + + generator.EnterScope(); + + generator.AppendLine($"size += {property.SizeExpression};"); + + if (property.Alignment != 0) + generator.AppendLine($"size += size % {property.Alignment} == 0 ? 0 : {property.Alignment} - (size % {property.Alignment});"); + + generator.LeaveScope(); + } + + generator.AppendLine("return size;"); + } + + generator.LeaveScope(); + } + + private static void GenerateReadMethod(CodeGenerator generator, ObjectSerializationInfo info) + { + generator.EnterScope("public void Read(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct"); + + if (info.HasBaseType) + generator.AppendLine("base.Read(ref reader, in version);"); + + foreach (var property in info.Properties) + { + if (property.VersionConditions.Length > 0) + GenerateVersionCondition(property.VersionConditions, generator); + + generator.EnterScope(); + generator.AppendLine($"this.{property.Name} = {property.ReadMethod}"); + + if (property.Alignment != 0) + generator.AppendLine($"reader.Align({property.Alignment});"); + + generator.LeaveScope(); + } + + generator.LeaveScope(); + } + + private static string GetVersionIdentifier(StructVersion version) + => $"V{version.Major}_{version.Minor}{(version.Tag == null ? "" : $"_{version.Tag}")}"; + + private static void GenerateVersionCondition(ImmutableEquatableArray conditions, + CodeGenerator generator) + { + generator.AppendLine("if ("); + generator.IncreaseIndentation(); + + for (var i = 0; i < conditions.Length; i++) + { + generator.AppendLine("(true"); + + var condition = conditions[i]; + if (condition.LessThan.HasValue) + generator.AppendLine($"&& Versions.{GetVersionIdentifier(condition.LessThan.Value)} >= version"); + + if (condition.GreaterThan.HasValue) + generator.AppendLine($"&& version >= Versions.{GetVersionIdentifier(condition.GreaterThan.Value)}"); + + if (condition.EqualTo.HasValue) + generator.AppendLine($"&& version == Versions.{GetVersionIdentifier(condition.EqualTo.Value)}"); + + if (condition.IncludingTag != null) + generator.AppendLine($"&& version.Tag == \"{condition.IncludingTag}\""); + + if (condition.ExcludingTag != null) + generator.AppendLine($"&& version.Tag != \"{condition.ExcludingTag}\""); + + generator.AppendLine(")"); + + if (i != conditions.Length - 1) + generator.AppendLine("||"); + } + + generator.DecreaseIndentation(); + generator.AppendLine(")"); + } + + private static ObjectSerializationInfo ParseSerializationInfo(TypeDeclarationSyntax contextClass, + SemanticModel model, Compilation compilation, + CancellationToken cancellationToken) + { + var classSymbol = model.GetDeclaredSymbol(contextClass, cancellationToken) ?? throw new InvalidOperationException(); + + var alignedAttribute = compilation.GetTypeByMetadataName(Constants.AlignedAttribute); + var versionConditionAttribute = compilation.GetTypeByMetadataName(Constants.VersionConditionAttribute); + var customSerializationAttribute = compilation.GetTypeByMetadataName(Constants.CustomSerializationAttribute); + + var canGenerateSizeMethod = true; + + var properties = new List(); + foreach (var member in classSymbol.GetMembers()) + { + if (member.IsStatic + || member is IFieldSymbol { AssociatedSymbol: not null } + || member is IPropertySymbol { SetMethod: null }) + continue; + + var alignment = 0; + var versionConditions = new List(); + + ITypeSymbol type; + switch (member) + { + case IFieldSymbol field: + type = field.Type; + break; + case IPropertySymbol property: + type = property.Type; + break; + default: + continue; + } + + var typeInfo = ParseType(type); + + canGenerateSizeMethod &= typeInfo.Type != PropertyType.String; + + string readMethod; + if (typeInfo.Type == PropertyType.None) + { + readMethod = $"reader.ReadObject<{typeInfo.ComplexTypeName}>(in version);"; + } + else + { + readMethod = typeInfo.Type.IsSeperateMethod() + ? $"reader.Read{typeInfo.Type.GetTypeName()}();" + : $"reader.Read<{typeInfo.Type.GetTypeName()}>();"; + + if (typeInfo.ComplexTypeName != "") + readMethod = $"({typeInfo.ComplexTypeName}){readMethod}"; + } + + string sizeExpression; + if (typeInfo.Type == PropertyType.None) + { + sizeExpression = $"{typeInfo.ComplexTypeName}.Size(in version, is32Bit)"; + } + else + { + sizeExpression = $"sizeof({typeInfo.Type.GetTypeName()})"; + } + + foreach (var attribute in member.GetAttributes()) + { + if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, alignedAttribute)) + { + alignment = (int)attribute.ConstructorArguments[0].Value!; + } + else if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, versionConditionAttribute)) + { + StructVersion? lessThan = null, + moreThan = null, + equalTo = null; + + string? includingTag = null, + excludingTag = null; + + foreach (var argument in attribute.NamedArguments) + { + switch (argument.Key) + { + case Constants.LessThan: + lessThan = (StructVersion)(string)argument.Value.Value!; + break; + case Constants.GreaterThan: + moreThan = (StructVersion)(string)argument.Value.Value!; + break; + case Constants.EqualTo: + equalTo = (StructVersion)(string)argument.Value.Value!; + break; + case Constants.IncludingTag: + includingTag = (string)argument.Value.Value!; + break; + case Constants.ExcludingTag: + excludingTag = (string)argument.Value.Value!; + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + versionConditions.Add(new VersionCondition(lessThan, moreThan, equalTo, includingTag, excludingTag)); + } + else if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, customSerializationAttribute)) + { + readMethod = (string)attribute.ConstructorArguments[0].Value!; + sizeExpression = (string)attribute.ConstructorArguments[1].Value!; + } + } + + properties.Add(new PropertySerializationInfo( + member.Name, + readMethod, + sizeExpression, + alignment, + versionConditions.ToImmutableEquatableArray() + )); + } + + var hasBaseType = false; + if (classSymbol.BaseType != null) + { + var objectSymbol = compilation.GetSpecialType(SpecialType.System_Object); + var valueTypeSymbol = compilation.GetSpecialType(SpecialType.System_ValueType); + + if (!SymbolEqualityComparer.Default.Equals(objectSymbol, classSymbol.BaseType) + && !SymbolEqualityComparer.Default.Equals(valueTypeSymbol, classSymbol.BaseType)) + hasBaseType = true; + } + + return new ObjectSerializationInfo( + classSymbol.ContainingNamespace.ToDisplayString(), + classSymbol.Name, + hasBaseType, + contextClass.Kind() == SyntaxKind.StructDeclaration, + true, + canGenerateSizeMethod, + properties.ToImmutableEquatableArray() + ); + } + + private static (PropertyType Type, string ComplexTypeName, bool IsArray) ParseType(ITypeSymbol typeSymbol) + { + switch (typeSymbol) + { + case IArrayTypeSymbol arrayTypeSymbol: + { + var elementType = ParseType(arrayTypeSymbol.ElementType); + return (elementType.Type, elementType.ComplexTypeName, true); + } + case INamedTypeSymbol { EnumUnderlyingType: not null } namedTypeSymbol: + var res = ParseType(namedTypeSymbol.EnumUnderlyingType); + return (res.Type, typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), false); + } + + if (typeSymbol.SpecialType != SpecialType.None) + { + var type = typeSymbol.SpecialType switch + { + SpecialType.System_Boolean => PropertyType.Boolean, + SpecialType.System_Byte => PropertyType.UInt8, + SpecialType.System_UInt16 => PropertyType.UInt16, + SpecialType.System_UInt32 => PropertyType.UInt32, + SpecialType.System_UInt64 => PropertyType.UInt64, + SpecialType.System_SByte => PropertyType.Int8, + SpecialType.System_Int16 => PropertyType.Int16, + SpecialType.System_Int32 => PropertyType.Int32, + SpecialType.System_Int64 => PropertyType.Int64, + SpecialType.System_String => PropertyType.String, + _ => PropertyType.Unsupported + }; + + return (type, "", false); + } + + var complexType = typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + + return (PropertyType.None, complexType, false); + } + } +} diff --git a/VersionedSerialization.Generator/StructVersion.cs b/VersionedSerialization.Generator/StructVersion.cs new file mode 100644 index 00000000..c69018b4 --- /dev/null +++ b/VersionedSerialization.Generator/StructVersion.cs @@ -0,0 +1,71 @@ +using System; + +namespace VersionedSerialization; + +public readonly struct StructVersion(int major = 0, int minor = 0, string? tag = null) +{ + public readonly int Major = major; + public readonly int Minor = minor; + public readonly string? Tag = tag; + + #region Equality operators + + public static bool operator ==(StructVersion left, StructVersion right) + => left.Major == right.Major && left.Minor == right.Minor; + + public static bool operator !=(StructVersion left, StructVersion right) + => !(left == right); + + public static bool operator >(StructVersion left, StructVersion right) + => left.Major > right.Major || (left.Major == right.Major && left.Minor > right.Minor); + + public static bool operator <(StructVersion left, StructVersion right) + => left.Major < right.Major || (left.Major == right.Major && left.Minor < right.Minor); + + public static bool operator >=(StructVersion left, StructVersion right) + => left.Major > right.Major || (left.Major == right.Major && left.Minor >= right.Minor); + + public static bool operator <=(StructVersion left, StructVersion right) + => left.Major < right.Major || (left.Major == right.Major && left.Minor <= right.Minor); + + public override bool Equals(object? obj) + => obj is StructVersion other && Equals(other); + + public bool Equals(StructVersion other) + => Major == other.Major && Minor == other.Minor; + + public override int GetHashCode() + => HashCode.Combine(Major, Minor, Tag); + + #endregion + + public override string ToString() => $"{Major}.{Minor}{(Tag != null ? $"-{Tag}" : "")}"; + + public static bool TryParse(string version, out StructVersion parsed) + { + parsed = default; + + var versionParts = version.Split('.'); + if (versionParts.Length is 1 or > 2) + return false; + + var tagParts = versionParts[1].Split('-'); + if (tagParts.Length > 2) + return false; + + var major = int.Parse(versionParts[0]); + var minor = int.Parse(tagParts[0]); + var tag = tagParts.Length == 1 ? null : tagParts[1]; + + parsed = new StructVersion(major, minor, tag); + return true; + } + + public static implicit operator StructVersion(string value) + { + if (!TryParse(value, out var ver)) + throw new InvalidOperationException("Invalid version string"); + + return ver; + } +} \ No newline at end of file diff --git a/VersionedSerialization.Generator/Utils/CodeGenerator.cs b/VersionedSerialization.Generator/Utils/CodeGenerator.cs new file mode 100644 index 00000000..5c9ac9bf --- /dev/null +++ b/VersionedSerialization.Generator/Utils/CodeGenerator.cs @@ -0,0 +1,54 @@ +using System.Text; + +namespace VersionedSerialization.Generator.Utils +{ + public class CodeGenerator + { + private const string Indent = " "; + + private readonly StringBuilder _sb = new(); + private string _currentIndent = ""; + + public void EnterScope(string? header = null) + { + if (header != null) + { + AppendLine(header); + } + + AppendLine("{"); + IncreaseIndentation(); + } + + public void LeaveScope(string suffix = "") + { + DecreaseIndentation(); + AppendLine($"}}{suffix}"); + } + + public void IncreaseIndentation() + { + _currentIndent += Indent; + } + + public void DecreaseIndentation() + { + _currentIndent = _currentIndent.Substring(0, _currentIndent.Length - Indent.Length); + } + + public void AppendLine() + { + _sb.AppendLine(); + } + + public void AppendLine(string text) + { + _sb.AppendLine(_currentIndent + text); + } + + public override string ToString() + { + return _sb.ToString(); + } + } +} \ No newline at end of file diff --git a/VersionedSerialization.Generator/Utils/Constants.cs b/VersionedSerialization.Generator/Utils/Constants.cs new file mode 100644 index 00000000..db705d07 --- /dev/null +++ b/VersionedSerialization.Generator/Utils/Constants.cs @@ -0,0 +1,17 @@ +namespace VersionedSerialization.Generator.Utils; + +public static class Constants +{ + private const string AttributeNamespace = "VersionedSerialization.Attributes"; + + public const string VersionedStructAttribute = $"{AttributeNamespace}.{nameof(VersionedStructAttribute)}"; + public const string AlignedAttribute = $"{AttributeNamespace}.{nameof(AlignedAttribute)}"; + public const string VersionConditionAttribute = $"{AttributeNamespace}.{nameof(VersionConditionAttribute)}"; + public const string CustomSerializationAttribute = $"{AttributeNamespace}.{nameof(CustomSerializationAttribute)}"; + + public const string LessThan = nameof(LessThan); + public const string GreaterThan = nameof(GreaterThan); + public const string EqualTo = nameof(EqualTo); + public const string IncludingTag = nameof(IncludingTag); + public const string ExcludingTag = nameof(ExcludingTag); +} \ No newline at end of file diff --git a/VersionedSerialization.Generator/Utils/HashCode.cs b/VersionedSerialization.Generator/Utils/HashCode.cs new file mode 100644 index 00000000..ff67559e --- /dev/null +++ b/VersionedSerialization.Generator/Utils/HashCode.cs @@ -0,0 +1,454 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// NOTE: This code is derived from an implementation originally in dotnet/roslyn-analyzers +// https://github.com/dotnet/roslyn-analyzers/blob/1dc431ec828e9cc816fc69f89ad9b8286e74d707/src/Utilities/Compiler/RoslynHashCode.cs + +/* + +The xxHash32 implementation is based on the code published by Yann Collet: +https://raw.githubusercontent.com/Cyan4973/xxHash/5c174cfa4e45a42f94082dc0d4539b39696afea1/xxhash.c + + xxHash - Fast Hash algorithm + Copyright (C) 2012-2016, Yann Collet + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - xxHash homepage: http://www.xxhash.com + - xxHash source repository : https://github.com/Cyan4973/xxHash + +*/ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; + +[SuppressMessage("Design", "CA1066:Implement IEquatable when overriding Object.Equals", Justification = "This type is not equatable.")] +[SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "This type is not equatable.")] +[SuppressMessage("Usage", "CA2231:Overload operator equals on overriding value type Equals", Justification = "This type is not equatable.")] +internal struct HashCode +{ + private static readonly uint s_seed = GenerateGlobalSeed(); + + private const uint Prime1 = 2654435761U; + private const uint Prime2 = 2246822519U; + private const uint Prime3 = 3266489917U; + private const uint Prime4 = 668265263U; + private const uint Prime5 = 374761393U; + + private uint _v1, _v2, _v3, _v4; + private uint _queue1, _queue2, _queue3; + private uint _length; + + private static uint GenerateGlobalSeed() + { + using var randomNumberGenerator = RandomNumberGenerator.Create(); + var array = new byte[sizeof(uint)]; + randomNumberGenerator.GetBytes(array); + return BitConverter.ToUInt32(array, 0); + } + + public static int Combine(T1 value1) + { + // Provide a way of diffusing bits from something with a limited + // input hash space. For example, many enums only have a few + // possible hashes, only using the bottom few bits of the code. Some + // collections are built on the assumption that hashes are spread + // over a larger space, so diffusing the bits may help the + // collection work more efficiently. + + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + + var hash = MixEmptyState(); + hash += 4; + + hash = QueueRound(hash, hc1); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + + var hash = MixEmptyState(); + hash += 8; + + hash = QueueRound(hash, hc1); + hash = QueueRound(hash, hc2); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + + var hash = MixEmptyState(); + hash += 12; + + hash = QueueRound(hash, hc1); + hash = QueueRound(hash, hc2); + hash = QueueRound(hash, hc3); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + + Initialize(out var v1, out var v2, out var v3, out var v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + var hash = MixState(v1, v2, v3, v4); + hash += 16; + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + var hc5 = (uint)(value5?.GetHashCode() ?? 0); + + Initialize(out var v1, out var v2, out var v3, out var v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + var hash = MixState(v1, v2, v3, v4); + hash += 20; + + hash = QueueRound(hash, hc5); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + var hc5 = (uint)(value5?.GetHashCode() ?? 0); + var hc6 = (uint)(value6?.GetHashCode() ?? 0); + + Initialize(out var v1, out var v2, out var v3, out var v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + var hash = MixState(v1, v2, v3, v4); + hash += 24; + + hash = QueueRound(hash, hc5); + hash = QueueRound(hash, hc6); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + var hc5 = (uint)(value5?.GetHashCode() ?? 0); + var hc6 = (uint)(value6?.GetHashCode() ?? 0); + var hc7 = (uint)(value7?.GetHashCode() ?? 0); + + Initialize(out var v1, out var v2, out var v3, out var v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + var hash = MixState(v1, v2, v3, v4); + hash += 28; + + hash = QueueRound(hash, hc5); + hash = QueueRound(hash, hc6); + hash = QueueRound(hash, hc7); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + var hc5 = (uint)(value5?.GetHashCode() ?? 0); + var hc6 = (uint)(value6?.GetHashCode() ?? 0); + var hc7 = (uint)(value7?.GetHashCode() ?? 0); + var hc8 = (uint)(value8?.GetHashCode() ?? 0); + + Initialize(out var v1, out var v2, out var v3, out var v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + v1 = Round(v1, hc5); + v2 = Round(v2, hc6); + v3 = Round(v3, hc7); + v4 = Round(v4, hc8); + + var hash = MixState(v1, v2, v3, v4); + hash += 32; + + hash = MixFinal(hash); + return (int)hash; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Initialize(out uint v1, out uint v2, out uint v3, out uint v4) + { + v1 = s_seed + Prime1 + Prime2; + v2 = s_seed + Prime2; + v3 = s_seed; + v4 = s_seed - Prime1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Round(uint hash, uint input) + { + return BitOperations.RotateLeft(hash + (input * Prime2), 13) * Prime1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint QueueRound(uint hash, uint queuedValue) + { + return BitOperations.RotateLeft(hash + (queuedValue * Prime3), 17) * Prime4; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint MixState(uint v1, uint v2, uint v3, uint v4) + { + return BitOperations.RotateLeft(v1, 1) + BitOperations.RotateLeft(v2, 7) + BitOperations.RotateLeft(v3, 12) + BitOperations.RotateLeft(v4, 18); + } + + private static uint MixEmptyState() + { + return s_seed + Prime5; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint MixFinal(uint hash) + { + hash ^= hash >> 15; + hash *= Prime2; + hash ^= hash >> 13; + hash *= Prime3; + hash ^= hash >> 16; + return hash; + } + + public void Add(T value) + { + Add(value?.GetHashCode() ?? 0); + } + + public void Add(T value, IEqualityComparer? comparer) + { + Add(value is null ? 0 : (comparer?.GetHashCode(value) ?? value.GetHashCode())); + } + + private void Add(int value) + { + // The original xxHash works as follows: + // 0. Initialize immediately. We can't do this in a struct (no + // default ctor). + // 1. Accumulate blocks of length 16 (4 uints) into 4 accumulators. + // 2. Accumulate remaining blocks of length 4 (1 uint) into the + // hash. + // 3. Accumulate remaining blocks of length 1 into the hash. + + // There is no need for #3 as this type only accepts ints. _queue1, + // _queue2 and _queue3 are basically a buffer so that when + // ToHashCode is called we can execute #2 correctly. + + // We need to initialize the xxHash32 state (_v1 to _v4) lazily (see + // #0) nd the last place that can be done if you look at the + // original code is just before the first block of 16 bytes is mixed + // in. The xxHash32 state is never used for streams containing fewer + // than 16 bytes. + + // To see what's really going on here, have a look at the Combine + // methods. + + var val = (uint)value; + + // Storing the value of _length locally shaves of quite a few bytes + // in the resulting machine code. + var previousLength = _length++; + var position = previousLength % 4; + + // Switch can't be inlined. + + if (position == 0) + { + _queue1 = val; + } + else if (position == 1) + { + _queue2 = val; + } + else if (position == 2) + { + _queue3 = val; + } + else // position == 3 + { + if (previousLength == 3) + { + Initialize(out _v1, out _v2, out _v3, out _v4); + } + + _v1 = Round(_v1, _queue1); + _v2 = Round(_v2, _queue2); + _v3 = Round(_v3, _queue3); + _v4 = Round(_v4, val); + } + } + + public int ToHashCode() + { + // Storing the value of _length locally shaves of quite a few bytes + // in the resulting machine code. + var length = _length; + + // position refers to the *next* queue position in this method, so + // position == 1 means that _queue1 is populated; _queue2 would have + // been populated on the next call to Add. + var position = length % 4; + + // If the length is less than 4, _v1 to _v4 don't contain anything + // yet. xxHash32 treats this differently. + + var hash = length < 4 ? MixEmptyState() : MixState(_v1, _v2, _v3, _v4); + + // _length is incremented once per Add(Int32) and is therefore 4 + // times too small (xxHash length is in bytes, not ints). + + hash += length * 4; + + // Mix what remains in the queue + + // Switch can't be inlined right now, so use as few branches as + // possible by manually excluding impossible scenarios (position > 1 + // is always false if position is not > 0). + if (position > 0) + { + hash = QueueRound(hash, _queue1); + if (position > 1) + { + hash = QueueRound(hash, _queue2); + if (position > 2) + { + hash = QueueRound(hash, _queue3); + } + } + } + + hash = MixFinal(hash); + return (int)hash; + } + +#pragma warning disable CS0809 // Obsolete member overrides non-obsolete member +#pragma warning disable CA1065 // Do not raise exceptions in unexpected locations + // Obsolete member 'memberA' overrides non-obsolete member 'memberB'. + // Disallowing GetHashCode and Equals is by design + + // * We decided to not override GetHashCode() to produce the hash code + // as this would be weird, both naming-wise as well as from a + // behavioral standpoint (GetHashCode() should return the object's + // hash code, not the one being computed). + + // * Even though ToHashCode() can be called safely multiple times on + // this implementation, it is not part of the contract. If the + // implementation has to change in the future we don't want to worry + // about people who might have incorrectly used this type. + + [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.", error: true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => throw new NotSupportedException(SR.HashCode_HashCodeNotSupported); + + [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes.", error: true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object? obj) => throw new NotSupportedException(SR.HashCode_EqualityNotSupported); +#pragma warning restore CA1065 // Do not raise exceptions in unexpected locations +#pragma warning restore CS0809 // Obsolete member overrides non-obsolete member + + private static class SR + { + public static string HashCode_HashCodeNotSupported = "HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code."; + public static string HashCode_EqualityNotSupported = "HashCode is a mutable struct and should not be compared with other HashCodes."; + } + + private static class BitOperations + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint RotateLeft(uint value, int offset) + => (value << offset) | (value >> (32 - offset)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong RotateLeft(ulong value, int offset) + => (value << offset) | (value >> (64 - offset)); + } +} \ No newline at end of file diff --git a/VersionedSerialization.Generator/Utils/ImmutableEquatableArray.cs b/VersionedSerialization.Generator/Utils/ImmutableEquatableArray.cs new file mode 100644 index 00000000..94d81829 --- /dev/null +++ b/VersionedSerialization.Generator/Utils/ImmutableEquatableArray.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; + +namespace VersionedSerialization.Generator.Utils; + +[DebuggerDisplay("Length = {Length}")] +[DebuggerTypeProxy(typeof(ImmutableEquatableArray<>.DebugView))] +public sealed class ImmutableEquatableArray : + IEquatable>, + IReadOnlyList, + IList, + IList + + where T : IEquatable +{ + public static ImmutableEquatableArray Empty { get; } = new([]); + + private readonly T[] _values; + public ref readonly T this[int index] => ref _values[index]; + public int Length => _values.Length; + + private ImmutableEquatableArray(T[] values) + => _values = values; + + public bool Equals(ImmutableEquatableArray? other) + => ReferenceEquals(this, other) || ((ReadOnlySpan)_values).SequenceEqual(other?._values); + + public override bool Equals(object? obj) + => obj is ImmutableEquatableArray other && Equals(other); + + public override int GetHashCode() + { + var hash = 0; + foreach (var value in _values) + { + hash = HashCode.Combine(hash, value.GetHashCode()); + } + + return hash; + } + + public Enumerator GetEnumerator() => new(_values); + + public struct Enumerator + { + private readonly T[] _values; + private int _index; + + internal Enumerator(T[] values) + { + _values = values; + _index = -1; + } + + public bool MoveNext() => ++_index < _values.Length; + public readonly ref T Current => ref _values[_index]; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + internal static ImmutableEquatableArray UnsafeCreateFromArray(T[] values) + => new(values); + + #region Explicit interface implementations + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_values).GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_values).GetEnumerator(); + bool ICollection.IsReadOnly => true; + bool IList.IsFixedSize => true; + bool IList.IsReadOnly => true; + T IReadOnlyList.this[int index] => _values[index]; + T IList.this[int index] { get => _values[index]; set => throw new InvalidOperationException(); } + object? IList.this[int index] { get => _values[index]; set => throw new InvalidOperationException(); } + void ICollection.CopyTo(T[] array, int arrayIndex) => _values.CopyTo(array, arrayIndex); + void ICollection.CopyTo(Array array, int index) => _values.CopyTo(array, index); + int IList.IndexOf(T item) => _values.AsSpan().IndexOf(item); + int IList.IndexOf(object? value) => ((IList)_values).IndexOf(value); + bool ICollection.Contains(T item) => _values.AsSpan().IndexOf(item) >= 0; + bool IList.Contains(object? value) => ((IList)_values).Contains(value); + bool ICollection.IsSynchronized => false; + object ICollection.SyncRoot => this; + + int IReadOnlyCollection.Count => Length; + int ICollection.Count => Length; + int ICollection.Count => Length; + + void ICollection.Add(T item) => throw new InvalidOperationException(); + bool ICollection.Remove(T item) => throw new InvalidOperationException(); + void ICollection.Clear() => throw new InvalidOperationException(); + void IList.Insert(int index, T item) => throw new InvalidOperationException(); + void IList.RemoveAt(int index) => throw new InvalidOperationException(); + int IList.Add(object? value) => throw new InvalidOperationException(); + void IList.Clear() => throw new InvalidOperationException(); + void IList.Insert(int index, object? value) => throw new InvalidOperationException(); + void IList.Remove(object? value) => throw new InvalidOperationException(); + void IList.RemoveAt(int index) => throw new InvalidOperationException(); + #endregion + + private sealed class DebugView(ImmutableEquatableArray array) + { + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Items => array.ToArray(); + } +} + +public static class ImmutableEquatableArray +{ + public static ImmutableEquatableArray ToImmutableEquatableArray(this IEnumerable values) where T : IEquatable + => values is ICollection { Count: 0 } ? ImmutableEquatableArray.Empty : ImmutableEquatableArray.UnsafeCreateFromArray(values.ToArray()); + + public static ImmutableEquatableArray Create(ReadOnlySpan values) where T : IEquatable + => values.IsEmpty ? ImmutableEquatableArray.Empty : ImmutableEquatableArray.UnsafeCreateFromArray(values.ToArray()); +} \ No newline at end of file diff --git a/VersionedSerialization.Generator/VersionedSerialization.Generator.csproj b/VersionedSerialization.Generator/VersionedSerialization.Generator.csproj new file mode 100644 index 00000000..ba0dd087 --- /dev/null +++ b/VersionedSerialization.Generator/VersionedSerialization.Generator.csproj @@ -0,0 +1,20 @@ + + + + netstandard2.0 + latest + 4.10 + 4.10.0 + enable + true + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/VersionedSerialization/Attributes/AlignedAttribute.cs b/VersionedSerialization/Attributes/AlignedAttribute.cs new file mode 100644 index 00000000..bfb6c04e --- /dev/null +++ b/VersionedSerialization/Attributes/AlignedAttribute.cs @@ -0,0 +1,6 @@ +namespace VersionedSerialization.Attributes; + +#pragma warning disable CS9113 // Parameter is unread. +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] +public class AlignedAttribute(int alignment) : Attribute; +#pragma warning restore CS9113 // Parameter is unread. diff --git a/VersionedSerialization/Attributes/CustomSerializationAttribute.cs b/VersionedSerialization/Attributes/CustomSerializationAttribute.cs new file mode 100644 index 00000000..8e444303 --- /dev/null +++ b/VersionedSerialization/Attributes/CustomSerializationAttribute.cs @@ -0,0 +1,6 @@ +namespace VersionedSerialization.Attributes; + +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] +#pragma warning disable CS9113 // Parameter is unread. +public class CustomSerializationAttribute(string methodName, string sizeExpression) : Attribute; +#pragma warning restore CS9113 // Parameter is unread. diff --git a/VersionedSerialization/Attributes/VersionConditionAttribute.cs b/VersionedSerialization/Attributes/VersionConditionAttribute.cs new file mode 100644 index 00000000..d1058009 --- /dev/null +++ b/VersionedSerialization/Attributes/VersionConditionAttribute.cs @@ -0,0 +1,11 @@ +namespace VersionedSerialization.Attributes; + +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] +public class VersionConditionAttribute : Attribute +{ + public string LessThan { get; set; } = ""; + public string GreaterThan { get; set; } = ""; + public string EqualTo { get; set; } = ""; + public string IncludingTag { get; set; } = ""; + public string ExcludingTag { get; set; } = ""; +} \ No newline at end of file diff --git a/VersionedSerialization/Attributes/VersionedStructAttribute.cs b/VersionedSerialization/Attributes/VersionedStructAttribute.cs new file mode 100644 index 00000000..c96776ab --- /dev/null +++ b/VersionedSerialization/Attributes/VersionedStructAttribute.cs @@ -0,0 +1,4 @@ +namespace VersionedSerialization.Attributes; + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] +public class VersionedStructAttribute : Attribute; \ No newline at end of file diff --git a/VersionedSerialization/IReadable.cs b/VersionedSerialization/IReadable.cs new file mode 100644 index 00000000..eb04b41f --- /dev/null +++ b/VersionedSerialization/IReadable.cs @@ -0,0 +1,7 @@ +namespace VersionedSerialization; + +public interface IReadable +{ + public void Read(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct; + public static abstract int Size(in StructVersion version = default, bool is32Bit = false); +} \ No newline at end of file diff --git a/VersionedSerialization/IReader.cs b/VersionedSerialization/IReader.cs new file mode 100644 index 00000000..75b5208e --- /dev/null +++ b/VersionedSerialization/IReader.cs @@ -0,0 +1,22 @@ +using System.Collections.Immutable; + +namespace VersionedSerialization; + +public interface IReader +{ + bool Is32Bit { get; } + + bool ReadBoolean(); + long ReadNInt(); + ulong ReadNUInt(); + string ReadString(); + ReadOnlySpan ReadBytes(int length); + + T Read() where T : unmanaged; + ImmutableArray ReadArray(long count) where T : unmanaged; + + T ReadObject(in StructVersion version = default) where T : IReadable, new(); + ImmutableArray ReadObjectArray(long count, in StructVersion version = default) where T : IReadable, new(); + + public void Align(int alignment = 0); +} \ No newline at end of file diff --git a/VersionedSerialization/ReaderExtensions.cs b/VersionedSerialization/ReaderExtensions.cs new file mode 100644 index 00000000..93912ed9 --- /dev/null +++ b/VersionedSerialization/ReaderExtensions.cs @@ -0,0 +1,62 @@ +using System.Runtime.CompilerServices; + +namespace VersionedSerialization; + +public static class ReaderExtensions +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint ReadCompressedUInt(this ref T reader) where T : struct, IReader, allows ref struct + { + var first = reader.Read(); + + if ((first & 0b10000000) == 0b00000000) + return first; + + if ((first & 0b11000000) == 0b10000000) + return (uint)(((first & ~0b10000000) << 8) | reader.Read()); + + if ((first & 0b11100000) == 0b11000000) + return (uint)(((first & ~0b11000000) << 24) | (reader.Read() << 16) | (reader.Read() << 8) | reader.Read()); + + return first switch + { + 0b11110000 => reader.Read(), + 0b11111110 => uint.MaxValue - 1, + 0b11111111 => uint.MaxValue, + _ => throw new InvalidDataException("Invalid compressed uint") + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ReadCompressedInt(this ref T reader) where T : struct, IReader, allows ref struct + { + var value = reader.ReadCompressedUInt(); + if (value == uint.MaxValue) + return int.MinValue; + + var isNegative = (value & 0b1) == 1; + value >>= 1; + + return (int)(isNegative ? -(value + 1) : value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong ReadSLEB128(this ref T reader) where T : struct, IReader, allows ref struct + { + var value = 0uL; + var shift = 0; + byte current; + + do + { + current = reader.Read(); + value |= (current & 0x7FuL) << shift; + shift += 7; + } while ((current & 0x80) != 0); + + if (64 >= shift && (current & 0x40) != 0) + value |= ulong.MaxValue << shift; + + return value; + } +} \ No newline at end of file diff --git a/VersionedSerialization/SpanReader.cs b/VersionedSerialization/SpanReader.cs new file mode 100644 index 00000000..78dfab5b --- /dev/null +++ b/VersionedSerialization/SpanReader.cs @@ -0,0 +1,146 @@ +using System.Buffers.Binary; +using System.Collections.Immutable; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace VersionedSerialization; + +// ReSharper disable ReplaceSliceWithRangeIndexer | The range indexer gets compiled into .Slice(x, y) and not .Slice(x) which worsens performance +public ref struct SpanReader(ReadOnlySpan data, int offset = 0, bool littleEndian = true, bool is32Bit = false) : IReader +{ + public int Offset = offset; + public readonly byte Peek => _data[Offset]; + public readonly bool IsLittleEndian => _littleEndian; + public readonly bool Is32Bit => _is32Bit; + public readonly int Length => _data.Length; + public readonly int PointerSize => Is32Bit ? sizeof(uint) : sizeof(ulong); + + private readonly ReadOnlySpan _data = data; + private readonly bool _littleEndian = littleEndian; + private readonly bool _is32Bit = is32Bit; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private T ReadInternal() where T : unmanaged + { + var value = MemoryMarshal.Read(_data.Slice(Offset)); + Offset += Unsafe.SizeOf(); + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static TTo Cast(in TFrom from) => Unsafe.As(ref Unsafe.AsRef(in from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan ReadBytes(int length) + { + var val = _data.Slice(Offset, length); + Offset += length; + return val; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Read() where T : unmanaged + { + if (typeof(T) == typeof(byte)) + return Cast(_data[Offset++]); + + var value = ReadInternal(); + if (!_littleEndian) + { + if (value is ulong val) + { + var converted = BinaryPrimitives.ReverseEndianness(val); + value = Cast(converted); + } + else if (typeof(T) == typeof(long)) + { + var converted = BinaryPrimitives.ReverseEndianness(Cast(value)); + value = Cast(converted); + } + else if (typeof(T) == typeof(uint)) + { + var converted = BinaryPrimitives.ReverseEndianness(Cast(value)); + value = Cast(converted); + } + else if (typeof(T) == typeof(int)) + { + var converted = BinaryPrimitives.ReverseEndianness(Cast(value)); + value = Cast(converted); + } + else if (typeof(T) == typeof(ushort)) + { + var converted = BinaryPrimitives.ReverseEndianness(Cast(value)); + value = Cast(converted); + } + else if (typeof(T) == typeof(short)) + { + var converted = BinaryPrimitives.ReverseEndianness(Cast(value)); + value = Cast(converted); + } + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ImmutableArray ReadArray(long count) where T : unmanaged + { + var array = ImmutableArray.CreateBuilder(checked((int)count)); + for (long i = 0; i < count; i++) + array.Add(Read()); + + return array.MoveToImmutable(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T ReadObject(in StructVersion version = default) where T : IReadable, new() + { + var obj = new T(); + obj.Read(ref this, in version); + return obj; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ImmutableArray ReadObjectArray(long count, in StructVersion version = default) where T : IReadable, new() + { + var array = ImmutableArray.CreateBuilder(checked((int)count)); + for (long i = 0; i < count; i++) + array.Add(ReadObject(in version)); + + return array.MoveToImmutable(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public string ReadString() + { + var length = _data.Slice(Offset).IndexOf(byte.MinValue); + + if (length == -1) + throw new InvalidDataException("Failed to find string in span."); + + var val = _data.Slice(Offset, length); + Offset += length + 1; // Skip null terminator + + return Encoding.UTF8.GetString(val); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ReadBoolean() => Read() != 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong ReadNUInt() => _is32Bit ? Read() : Read(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long ReadNInt() => _is32Bit ? Read() : Read(); + + public void Align(int alignment = 0) + { + if (alignment == 0) + alignment = Is32Bit ? 4 : 8; + + var rem = Offset % alignment; + if (rem != 0) + Offset += alignment - rem; + } +} \ No newline at end of file diff --git a/VersionedSerialization/StructVersion.cs b/VersionedSerialization/StructVersion.cs new file mode 100644 index 00000000..23a1c82b --- /dev/null +++ b/VersionedSerialization/StructVersion.cs @@ -0,0 +1,58 @@ +namespace VersionedSerialization; + +public readonly struct StructVersion(int major = 0, int minor = 0, string? tag = null) : IEquatable +{ + public readonly int Major = major; + public readonly int Minor = minor; + public readonly string? Tag = tag; + + #region Equality operators + + public static bool operator ==(StructVersion left, StructVersion right) + => left.Major == right.Major && left.Minor == right.Minor; + + public static bool operator !=(StructVersion left, StructVersion right) + => !(left == right); + + public static bool operator >(StructVersion left, StructVersion right) + => left.Major > right.Major || (left.Major == right.Major && left.Minor > right.Minor); + + public static bool operator <(StructVersion left, StructVersion right) + => left.Major < right.Major || (left.Major == right.Major && left.Minor < right.Minor); + + public static bool operator >=(StructVersion left, StructVersion right) + => left.Major > right.Major || (left.Major == right.Major && left.Minor >= right.Minor); + + public static bool operator <=(StructVersion left, StructVersion right) + => left.Major < right.Major || (left.Major == right.Major && left.Minor <= right.Minor); + + public override bool Equals(object? obj) + => obj is StructVersion other && Equals(other); + + public bool Equals(StructVersion other) + => Major == other.Major && Minor == other.Minor; + + public override int GetHashCode() + => HashCode.Combine(Major, Minor); + + #endregion + + public override string ToString() => $"{Major}.{Minor}{(Tag != null ? $"-{Tag}" : "")}"; + + public static implicit operator StructVersion(string value) + { + var versionParts = value.Split('.'); + if (versionParts.Length is 1 or > 2) + throw new InvalidOperationException("Invalid version string."); + + var tagParts = versionParts[1].Split("-"); + if (tagParts.Length > 2) + throw new InvalidOperationException("Invalid version string."); + + var major = int.Parse(versionParts[0]); + var minor = int.Parse(tagParts[0]); + var tag = tagParts.Length == 1 ? null : tagParts[1]; + + return new StructVersion(major, minor, tag); + } +} \ No newline at end of file diff --git a/VersionedSerialization/VersionedSerialization.csproj b/VersionedSerialization/VersionedSerialization.csproj new file mode 100644 index 00000000..b602acfc --- /dev/null +++ b/VersionedSerialization/VersionedSerialization.csproj @@ -0,0 +1,10 @@ + + + + net9.0 + enable + enable + preview + + + From 23e873280d29768863ad29451b7562f5a9230209 Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Tue, 13 Aug 2024 15:00:20 +0200 Subject: [PATCH 03/27] migrate versioning to StructVersion class, add handling/detection for 29.2/31.2 --- .../Architectures/Il2CppBinaryARM.cs | 5 +- .../Architectures/Il2CppBinaryARM64.cs | 5 +- .../Architectures/Il2CppBinaryX64.cs | 3 +- .../Architectures/Il2CppBinaryX86.cs | 3 +- .../Cpp/UnityHeaders/UnityHeaders.cs | 10 +-- .../FileFormatStreams/FileFormatStream.cs | 3 +- Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs | 37 +++++------ .../IL2CPP/Il2CppBinaryClasses.cs | 10 ++- .../IL2CPP/Il2CppInspector.cs | 26 ++++---- Il2CppInspector.Common/IL2CPP/ImageScan.cs | 15 ++--- Il2CppInspector.Common/IL2CPP/Metadata.cs | 61 +++++++++++++------ .../IL2CPP/MetadataClasses.cs | 3 +- .../IL2CPP/MetadataUsage.cs | 6 +- Il2CppInspector.Common/Il2CppInspector.csproj | 2 + Il2CppInspector.Common/Model/AddressMap.cs | 3 +- Il2CppInspector.Common/Model/AppModel.cs | 5 +- .../Next/MetadataVersions.cs | 33 ++++++++++ .../Outputs/AssemblyShims.cs | 3 +- .../Outputs/CppScaffolding.cs | 2 +- .../Outputs/JSONMetadata.cs | 3 +- .../Reflection/CustomAttributeData.cs | 5 +- .../Reflection/TypeModel.cs | 5 +- Il2CppInspector.Common/Utils/BlobReader.cs | 5 +- VersionedSerialization/StructVersion.cs | 12 +++- 24 files changed, 181 insertions(+), 84 deletions(-) create mode 100644 Il2CppInspector.Common/Next/MetadataVersions.cs diff --git a/Il2CppInspector.Common/Architectures/Il2CppBinaryARM.cs b/Il2CppInspector.Common/Architectures/Il2CppBinaryARM.cs index 508f1acd..415811a6 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 @@ protected override (ulong, ulong) ConsiderCode(IFileFormatStream image, uint loc // 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 7891d45e..692a28f7 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 @@ protected override (ulong, ulong) ConsiderCode(IFileFormatStream image, uint loc // 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 65053140..efe00d37 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 @@ protected override (ulong, ulong) ConsiderCode(IFileFormatStream image, uint loc 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 00fa3dfb..fb4bd76d 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 @@ protected override (ulong, ulong) ConsiderCode(IFileFormatStream image, uint loc 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 9c6f4cd0..f0045bf8 100644 --- a/Il2CppInspector.Common/Cpp/UnityHeaders/UnityHeaders.cs +++ b/Il2CppInspector.Common/Cpp/UnityHeaders/UnityHeaders.cs @@ -11,6 +11,8 @@ All rights reserved. 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 @@ public static List GuessHeadersForBinary(Il2CppBinary binary) { 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 @@ private static IEnumerable GetFunctionNamesFromAPIHeaderText(string text } // 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 f26186c1..74a68ddd 100644 --- a/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs +++ b/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs @@ -11,12 +11,13 @@ All rights reserved. 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 f07220bb..586a3e3a 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 @@ All rights reserved. using System.Linq; using System.Reflection; using System.Text.RegularExpressions; +using VersionedSerialization; namespace Il2CppInspector { @@ -135,7 +137,7 @@ private static Il2CppBinary LoadImpl(IFileFormatStream stream, 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 @@ public void SaveToFile(string pathname) { } // 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 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) // 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 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) || 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 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) // 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 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) 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 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) // 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 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) // >=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 dbfe0326..fee8920c 100644 --- a/Il2CppInspector.Common/IL2CPP/Il2CppBinaryClasses.cs +++ b/Il2CppInspector.Common/IL2CPP/Il2CppBinaryClasses.cs @@ -60,15 +60,19 @@ public class Il2CppCodeRegistration [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 e3323422..97027028 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 @@ All rights reserved. using System.IO.Compression; using System.Linq; using System.Text; +using VersionedSerialization; namespace Il2CppInspector { @@ -31,7 +33,9 @@ public class 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 @@ public class 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 @@ public Il2CppInspector(Il2CppBinary binary, Metadata metadata) { } // 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 @@ public Il2CppInspector(Il2CppBinary binary, Metadata metadata) { // 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 @@ public Il2CppInspector(Il2CppBinary binary, Metadata metadata) { 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 @@ public Il2CppInspector(Il2CppBinary binary, Metadata metadata) { 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 @@ public Il2CppInspector(Il2CppBinary binary, Metadata metadata) { 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 @@ public Il2CppInspector(Il2CppBinary binary, Metadata metadata) { // 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 d4945370..91a05af9 100644 --- a/Il2CppInspector.Common/IL2CPP/ImageScan.cs +++ b/Il2CppInspector.Common/IL2CPP/ImageScan.cs @@ -9,6 +9,7 @@ All rights reserved. using System.Collections.Generic; using System.Linq; using System.Text; +using Il2CppInspector.Next; namespace Il2CppInspector { @@ -120,7 +121,7 @@ private IEnumerable FindAllPointerChains(byte[] blob, ulong va, int indir // 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 @@ ulong FindCodeRegistration() // 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 @@ ulong FindCodeRegistration() // 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 @@ ulong FindCodeRegistration() 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 57841d86..a814585f 100644 --- a/Il2CppInspector.Common/IL2CPP/Metadata.cs +++ b/Il2CppInspector.Common/IL2CPP/Metadata.cs @@ -10,7 +10,9 @@ All rights reserved. using System.IO; using System.Linq; using System.Reflection; +using Il2CppInspector.Next; using NoisyCowStudios.Bin2Object; +using VersionedSerialization; namespace Il2CppInspector { @@ -86,9 +88,9 @@ private void Initialize() } // 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 @@ private void Initialize() 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 @@ private void Initialize() } // 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 @@ private void Initialize() 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 @@ public void SaveToFile(string pathname) { 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 @@ public int Sizeof(Type type, double metadataVersion, int longSizeBytes = 8) { { // 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 2d1ef86a..e2c3cfcb 100644 --- a/Il2CppInspector.Common/IL2CPP/MetadataClasses.cs +++ b/Il2CppInspector.Common/IL2CPP/MetadataClasses.cs @@ -342,7 +342,8 @@ public class Il2CppMethodDefinition 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 f30df667..2c4d7655 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 @@ public MetadataUsage(MetadataUsageType type, int sourceIndex, ulong virtualAddre 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 @@ public static MetadataUsage FromEncodedIndex(Il2CppInspector package, uint encod 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 c7e69caa..bf4f2d78 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 d2122916..2fa7fde4 100644 --- a/Il2CppInspector.Common/Model/AddressMap.cs +++ b/Il2CppInspector.Common/Model/AddressMap.cs @@ -10,6 +10,7 @@ All rights reserved. using System.Linq; using System.Text; using Il2CppInspector.Cpp; +using Il2CppInspector.Next; using Il2CppInspector.Reflection; namespace Il2CppInspector.Model @@ -123,7 +124,7 @@ private void build() { 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 e16c0c72..65adac97 100644 --- a/Il2CppInspector.Common/Model/AppModel.cs +++ b/Il2CppInspector.Common/Model/AppModel.cs @@ -12,6 +12,7 @@ All rights reserved. using Aron.Weiler; using Il2CppInspector.Cpp; using Il2CppInspector.Cpp.UnityHeaders; +using Il2CppInspector.Next; using Il2CppInspector.Reflection; namespace Il2CppInspector.Model @@ -54,7 +55,7 @@ public class AppModel : IEnumerable 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 @@ public AppModel Build(UnityVersion unityVersion = null, CppCompilerType compiler } // 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 00000000..992f6368 --- /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 4dda8f7a..f54fa256 100644 --- a/Il2CppInspector.Common/Outputs/AssemblyShims.cs +++ b/Il2CppInspector.Common/Outputs/AssemblyShims.cs @@ -12,6 +12,7 @@ All rights reserved. using System.Linq; using dnlib.DotNet; using dnlib.DotNet.Emit; +using Il2CppInspector.Next; using Il2CppInspector.Reflection; namespace Il2CppInspector.Outputs @@ -591,7 +592,7 @@ public void Write(string outputPath, EventHandler statusCallback = null) // 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 f7795f09..d19f84e0 100644 --- a/Il2CppInspector.Common/Outputs/CppScaffolding.cs +++ b/Il2CppInspector.Common/Outputs/CppScaffolding.cs @@ -248,7 +248,7 @@ public void Write(string projectPath) { 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 846ab897..42df77c9 100644 --- a/Il2CppInspector.Common/Outputs/JSONMetadata.cs +++ b/Il2CppInspector.Common/Outputs/JSONMetadata.cs @@ -8,6 +8,7 @@ using System.Text.Json; using Il2CppInspector.Reflection; using Il2CppInspector.Model; +using Il2CppInspector.Next; namespace Il2CppInspector.Outputs { @@ -182,7 +183,7 @@ private void writeMetadata() { // 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 60e9c6e1..511544b0 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 @@ private static IEnumerable getCustomAttributes(Assembly asm 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 35bb8dbf..cf0b6eed 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 @@ private TypeInfo resolveTypeReference(Il2CppType typeRef) { // 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 @@ public TypeInfo GetGenericParameterType(int index) { // 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 f4936f25..f2c367ea 100644 --- a/Il2CppInspector.Common/Utils/BlobReader.cs +++ b/Il2CppInspector.Common/Utils/BlobReader.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; using System.IO; +using Il2CppInspector.Next; namespace Il2CppInspector.Utils; @@ -111,7 +112,7 @@ public static object GetConstantValueFromBlob(Il2CppInspector inspector, Il2CppT int ReadInt32() { - if (blob.Version >= 29) + if (blob.Version >= MetadataVersions.V290) { var address = blob.Position; @@ -131,7 +132,7 @@ int ReadInt32() 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 23a1c82b..c7a9f178 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 override int GetHashCode() 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."); From 2d3b186b4d2284dc33e0f5673040e726349c4aac Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Tue, 13 Aug 2024 18:34:22 +0200 Subject: [PATCH 04/27] add new struct definitions --- .../IL2CPP/CustomAttributeDataReader.cs | 4 +- Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs | 68 ++- .../IL2CPP/Il2CppBinaryClasses.cs | 306 ----------- .../IL2CPP/Il2CppInspector.cs | 56 +- Il2CppInspector.Common/IL2CPP/ImageScan.cs | 11 +- Il2CppInspector.Common/IL2CPP/Metadata.cs | 81 +-- .../IL2CPP/MetadataClasses.cs | 483 ------------------ Il2CppInspector.Common/Il2CppInspector.csproj | 4 +- Il2CppInspector.Common/Model/AddressMap.cs | 2 +- Il2CppInspector.Common/Model/AppModel.cs | 4 +- .../Next/BinaryMetadata/Il2CppArrayType.cs | 18 + .../BinaryMetadata/Il2CppCodeGenModule.cs | 61 +++ .../BinaryMetadata/Il2CppCodeRegistration.cs | 101 ++++ .../Next/BinaryMetadata/Il2CppGenericClass.cs | 18 + .../BinaryMetadata/Il2CppGenericContext.cs | 10 + .../Next/BinaryMetadata/Il2CppGenericInst.cs | 14 + ...Il2CppGenericMethodFunctionsDefinitions.cs | 12 + .../Il2CppGenericMethodIndices.cs | 16 + .../Next/BinaryMetadata/Il2CppGuid.cs | 22 + .../Next/BinaryMetadata/Il2CppInteropData.cs | 20 + .../Il2CppMetadataRegistration.cs | 60 +++ .../BinaryMetadata/Il2CppMethodPointer.cs | 19 + .../Next/BinaryMetadata/Il2CppMethodSpec.cs | 14 + .../Next/BinaryMetadata/Il2CppRange.cs | 10 + .../Il2CppRgctxConstrainedData.cs | 13 + .../BinaryMetadata/Il2CppRgctxDataType.cs | 12 + .../BinaryMetadata/Il2CppRgctxDefinition.cs | 16 + .../Il2CppRgctxDefinitionData.cs | 15 + .../Il2CppTokenAdjustorThunkPair.cs | 12 + .../Il2CppTokenIndexMethodTuple.cs | 15 + .../BinaryMetadata/Il2CppTokenRangePair.cs | 10 + .../Next/BinaryMetadata/Il2CppType.cs | 111 ++++ .../Il2CppTypeDefinitionSizes.cs | 12 + .../Next/BinaryMetadata/Il2CppTypeEnum.cs | 45 ++ .../Il2CppWindowsRuntimeFactoryTableEntry.cs | 10 + .../Next/Metadata/Il2CppAssemblyDefinition.cs | 37 ++ .../Metadata/Il2CppAssemblyNameDefinition.cs | 64 +++ .../Il2CppCustomAttributeDataRange.cs | 10 + .../Il2CppCustomAttributeTypeRange.cs | 13 + .../Next/Metadata/Il2CppEventDefinition.cs | 24 + .../Next/Metadata/Il2CppFieldDefaultValue.cs | 15 + .../Next/Metadata/Il2CppFieldDefinition.cs | 20 + .../Next/Metadata/Il2CppFieldMarshaledSize.cs | 13 + .../Next/Metadata/Il2CppFieldRef.cs | 13 + .../Next/Metadata/Il2CppGenericContainer.cs | 13 + .../Next/Metadata/Il2CppGenericParameter.cs | 21 + .../Metadata/Il2CppGlobalMetadataHeader.cs | 182 +++++++ .../Next/Metadata/Il2CppImageDefinition.cs | 37 ++ .../Metadata/Il2CppInterfaceOffsetPair.cs | 12 + .../Next/Metadata/Il2CppMetadataRange.cs | 10 + .../Next/Metadata/Il2CppMetadataUsage.cs | 64 +++ .../Next/Metadata/Il2CppMetadataUsageList.cs | 10 + .../Next/Metadata/Il2CppMetadataUsagePair.cs | 10 + .../Next/Metadata/Il2CppMetadataUsageType.cs | 13 + .../Next/Metadata/Il2CppMethodDefinition.cs | 62 +++ .../Metadata/Il2CppParameterDefaultValue.cs | 14 + .../Metadata/Il2CppParameterDefinition.cs | 19 + .../Next/Metadata/Il2CppPropertyDefinition.cs | 24 + .../Next/Metadata/Il2CppStringLiteral.cs | 11 + .../Next/Metadata/Il2CppTypeDefinition.cs | 83 +++ .../Metadata/Il2CppTypeDefinitionBitfield.cs | 21 + .../Il2CppWindowsRuntimeTypeNamePair.cs | 12 + .../Next/Metadata/PackingSize.cs | 14 + Il2CppInspector.Common/Next/Pointer.cs | 60 +++ .../Outputs/AssemblyShims.cs | 14 +- .../Outputs/CSharpCodeStubs.cs | 4 +- .../Outputs/JSONMetadata.cs | 2 +- Il2CppInspector.Common/Reflection/Assembly.cs | 28 +- .../Reflection/CustomAttributeData.cs | 24 +- .../Reflection/EventInfo.cs | 23 +- .../Reflection/FieldInfo.cs | 13 +- .../Reflection/MethodBase.cs | 19 +- .../Reflection/ParameterInfo.cs | 17 +- .../Reflection/PropertyInfo.cs | 16 +- Il2CppInspector.Common/Reflection/TypeInfo.cs | 76 +-- .../Reflection/TypeModel.cs | 59 +-- Il2CppInspector.Common/Utils/BlobReader.cs | 10 +- .../VersionedSerialization.csproj | 8 + 78 files changed, 1852 insertions(+), 1047 deletions(-) delete mode 100644 Il2CppInspector.Common/IL2CPP/Il2CppBinaryClasses.cs delete mode 100644 Il2CppInspector.Common/IL2CPP/MetadataClasses.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppArrayType.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeGenModule.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeRegistration.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericClass.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericContext.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericInst.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericMethodFunctionsDefinitions.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericMethodIndices.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGuid.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppInteropData.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMetadataRegistration.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMethodPointer.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMethodSpec.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRange.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxConstrainedData.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDataType.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDefinition.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDefinitionData.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenAdjustorThunkPair.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenIndexMethodTuple.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenRangePair.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppType.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTypeDefinitionSizes.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTypeEnum.cs create mode 100644 Il2CppInspector.Common/Next/BinaryMetadata/Il2CppWindowsRuntimeFactoryTableEntry.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyDefinition.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyNameDefinition.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppCustomAttributeDataRange.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppCustomAttributeTypeRange.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppEventDefinition.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppFieldDefaultValue.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppFieldDefinition.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppFieldMarshaledSize.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppFieldRef.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppGenericContainer.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppGenericParameter.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppGlobalMetadataHeader.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppImageDefinition.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppInterfaceOffsetPair.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppMetadataRange.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsage.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsageList.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsagePair.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsageType.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppMethodDefinition.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppParameterDefaultValue.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppParameterDefinition.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppPropertyDefinition.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppStringLiteral.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppTypeDefinition.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppTypeDefinitionBitfield.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/Il2CppWindowsRuntimeTypeNamePair.cs create mode 100644 Il2CppInspector.Common/Next/Metadata/PackingSize.cs create mode 100644 Il2CppInspector.Common/Next/Pointer.cs diff --git a/Il2CppInspector.Common/IL2CPP/CustomAttributeDataReader.cs b/Il2CppInspector.Common/IL2CPP/CustomAttributeDataReader.cs index 8901ba86..423646a5 100644 --- a/Il2CppInspector.Common/IL2CPP/CustomAttributeDataReader.cs +++ b/Il2CppInspector.Common/IL2CPP/CustomAttributeDataReader.cs @@ -4,6 +4,8 @@ using System.IO; using System.Linq; using dnlib.DotNet; +using Il2CppInspector.Next.BinaryMetadata; +using Il2CppInspector.Next.Metadata; using Il2CppInspector.Reflection; using Il2CppInspector.Utils; using NoisyCowStudios.Bin2Object; @@ -143,7 +145,7 @@ private object ConvertAttributeValue(object value) } private TypeInfo ConvertTypeDef(Il2CppTypeDefinition typeDef, Il2CppTypeEnum type) - => typeDef == null + => typeDef.IsValid ? _assembly.Model.GetTypeDefinitionFromTypeEnum(type) : _assembly.Model.TypesByDefinitionIndex[Array.IndexOf(_inspector.TypeDefinitions, typeDef)]; diff --git a/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs b/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs index 586a3e3a..16fd6204 100644 --- a/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs +++ b/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs @@ -13,6 +13,7 @@ All rights reserved. using System.Linq; using System.Reflection; using System.Text.RegularExpressions; +using Il2CppInspector.Next.BinaryMetadata; using VersionedSerialization; namespace Il2CppInspector @@ -284,20 +285,20 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) // genericAdjustorThunks was inserted before invokerPointersCount in 24.5 and 27.1 // pointer expected if we need to bump version - if (Image.Version == MetadataVersions.V244 && CodeRegistration.invokerPointersCount > 0x50000) + if (Image.Version == MetadataVersions.V244 && CodeRegistration.InvokerPointersCount > 0x50000) { Image.Version = MetadataVersions.V245; CodeRegistration = Image.ReadMappedObject(codeRegistration); } - if (Image.Version == MetadataVersions.V244 && CodeRegistration.reversePInvokeWrapperCount > 0x50000) { + if (Image.Version == MetadataVersions.V244 && CodeRegistration.ReversePInvokeWrapperCount > 0x50000) { Image.Version = MetadataVersions.V245; codeRegistration -= 1 * pointerSize; CodeRegistration = Image.ReadMappedObject(codeRegistration); } if ((Image.Version == MetadataVersions.V290 || Image.Version == MetadataVersions.V310) && - (long)CodeRegistration.genericMethodPointersCount - MetadataRegistration.genericMethodTableCount > 0x10000) + (long)CodeRegistration.GenericMethodPointersCount - MetadataRegistration.GenericMethodTableCount > 0x10000) { Image.Version = new StructVersion(Image.Version.Major, 1, Image.Version.Tag); codeRegistration -= 2 * pointerSize; @@ -316,16 +317,16 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) * typeRefPointers must be a series of pointers in __const * MethodInvokePointers must be a series of pointers in __text or .text, and in sequential order */ - if ((Metadata != null && Metadata.Types.Length != MetadataRegistration.typeDefinitionsSizesCount) - || CodeRegistration.reversePInvokeWrapperCount > 0x10000 - || CodeRegistration.unresolvedVirtualCallCount > 0x4000 // >= 22 - || CodeRegistration.interopDataCount > 0x1000 // >= 23 - || (Image.Version <= MetadataVersions.V241 && CodeRegistration.invokerPointersCount > CodeRegistration.methodPointersCount)) + if ((Metadata != null && Metadata.Types.Length != MetadataRegistration.TypeDefinitionsSizesCount) + || CodeRegistration.ReversePInvokeWrapperCount > 0x10000 + || CodeRegistration.UnresolvedVirtualCallCount > 0x4000 // >= 22 + || CodeRegistration.InteropDataCount > 0x1000 // >= 23 + || (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 <= MetadataVersions.V241) - GlobalMethodPointers = Image.ReadMappedArray(CodeRegistration.pmethodPointers, (int) CodeRegistration.methodPointersCount); + GlobalMethodPointers = Image.ReadMappedArray(CodeRegistration.MethodPointers, (int) CodeRegistration.MethodPointersCount); // After v24 method pointers and RGCTX data were stored in Il2CppCodeGenModules if (Image.Version >= MetadataVersions.V242) { @@ -333,19 +334,19 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) // 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) { + if (CodeRegistration.CodeGenModulesCount == 0) { Image.Version = MetadataVersions.V243; CodeRegistration = Image.ReadMappedObject(codeRegistration); } // Array of pointers to Il2CppCodeGenModule - var codeGenModulePointers = Image.ReadMappedArray(CodeRegistration.pcodeGenModules, (int) CodeRegistration.codeGenModulesCount); - var modules = Image.ReadMappedObjectPointerArray(CodeRegistration.pcodeGenModules, (int) CodeRegistration.codeGenModulesCount); + var codeGenModulePointers = Image.ReadMappedArray(CodeRegistration.CodeGenModules, (int) CodeRegistration.CodeGenModulesCount); + var modules = Image.ReadMappedObjectPointerArray(CodeRegistration.CodeGenModules, (int) CodeRegistration.CodeGenModulesCount); foreach (var mp in modules.Zip(codeGenModulePointers, (m, p) => new { Module = m, Pointer = p })) { var module = mp.Module; - var name = Image.ReadMappedNullTerminatedString(module.moduleName); + var name = Image.ReadMappedNullTerminatedString(module.ModuleName); Modules.Add(name, module); CodeGenModulePointers.Add(name, mp.Pointer); @@ -354,13 +355,13 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) // the entire method pointer array will be NULL values, causing the methodPointer to be mapped to .bss // and therefore out of scope of the binary image try { - ModuleMethodPointers.Add(module, Image.ReadMappedArray(module.methodPointers, (int) module.methodPointerCount)); + ModuleMethodPointers.Add(module, Image.ReadMappedArray(module.MethodPointers, (int) module.MethodPointerCount)); } catch (InvalidOperationException) { - ModuleMethodPointers.Add(module, new ulong[module.methodPointerCount]); + ModuleMethodPointers.Add(module, new ulong[module.MethodPointerCount]); } // Read method invoker pointer indices - one per method - MethodInvokerIndices.Add(module, Image.ReadMappedArray(module.invokerIndices, (int) module.methodPointerCount)); + MethodInvokerIndices.Add(module, Image.ReadMappedArray(module.InvokerIndices, (int) module.MethodPointerCount)); } } @@ -371,7 +372,7 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) // Some variants of 21 also use an array of pointers if (Image.Version == MetadataVersions.V210) { - var fieldTest = Image.ReadMappedWordArray(MetadataRegistration.pfieldOffsets, 6); + var fieldTest = Image.ReadMappedWordArray(MetadataRegistration.FieldOffsets, 6); // We detect this by relying on the fact Module, Object, ValueType, Attribute, _Attribute and Int32 // are always the first six defined types, and that all but Int32 have no fields @@ -380,29 +381,24 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) // All older versions use values directly in the array if (!fieldOffsetsArePointers) - FieldOffsets = Image.ReadMappedArray(MetadataRegistration.pfieldOffsets, (int)MetadataRegistration.fieldOffsetsCount); + FieldOffsets = Image.ReadMappedArray(MetadataRegistration.FieldOffsets, (int)MetadataRegistration.FieldOffsetsCount); else - FieldOffsetPointers = Image.ReadMappedWordArray(MetadataRegistration.pfieldOffsets, (int)MetadataRegistration.fieldOffsetsCount); + FieldOffsetPointers = Image.ReadMappedWordArray(MetadataRegistration.FieldOffsets, (int)MetadataRegistration.FieldOffsetsCount); // Type references (pointer array) - var typeRefPointers = Image.ReadMappedArray(MetadataRegistration.ptypes, (int) MetadataRegistration.typesCount); + var typeRefPointers = Image.ReadMappedArray(MetadataRegistration.Types, (int) MetadataRegistration.TypesCount); TypeReferenceIndicesByAddress = typeRefPointers.Zip(Enumerable.Range(0, typeRefPointers.Length), (a, i) => new { a, i }).ToDictionary(x => x.a, x => x.i); - TypeReferences = - Image.Version >= MetadataVersions.V272 - ? Image.ReadMappedObjectPointerArray(MetadataRegistration.ptypes, (int) MetadataRegistration.typesCount) - .Cast() - .ToList() - : Image.ReadMappedObjectPointerArray(MetadataRegistration.ptypes, (int)MetadataRegistration.typesCount); + TypeReferences = Image.ReadMappedObjectPointerArray(MetadataRegistration.Types, (int)MetadataRegistration.TypesCount); // Custom attribute constructors (function pointers) // This is managed in Il2CppInspector for metadata >= 27 if (Image.Version < MetadataVersions.V270) { - CustomAttributeGenerators = Image.ReadMappedArray(CodeRegistration.customAttributeGenerators, (int) CodeRegistration.customAttributeCount); + CustomAttributeGenerators = Image.ReadMappedArray(CodeRegistration.CustomAttributeGenerators, (int) CodeRegistration.CustomAttributeCount); } // Method.Invoke function pointers - MethodInvokePointers = Image.ReadMappedArray(CodeRegistration.invokerPointers, (int) CodeRegistration.invokerPointersCount); + MethodInvokePointers = Image.ReadMappedArray(CodeRegistration.InvokerPointers, (int) CodeRegistration.InvokerPointersCount); // TODO: Function pointers as shown below // reversePInvokeWrappers @@ -412,25 +408,25 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) // >=23: interopData if (Image.Version < MetadataVersions.V190) { - VTableMethodReferences = Image.ReadMappedArray(MetadataRegistration.methodReferences, (int)MetadataRegistration.methodReferencesCount); + VTableMethodReferences = Image.ReadMappedArray(MetadataRegistration.MethodReferences, (int)MetadataRegistration.MethodReferencesCount); } // Generic type and method specs (open and closed constructed types) - MethodSpecs = Image.ReadMappedArray(MetadataRegistration.methodSpecs, (int) MetadataRegistration.methodSpecsCount); + MethodSpecs = Image.ReadMappedArray(MetadataRegistration.MethodSpecs, (int) MetadataRegistration.MethodSpecsCount); // Concrete generic class and method signatures - GenericInstances = Image.ReadMappedObjectPointerArray(MetadataRegistration.genericInsts, (int) MetadataRegistration.genericInstsCount); + GenericInstances = Image.ReadMappedObjectPointerArray(MetadataRegistration.GenericInsts, (int) MetadataRegistration.GenericInstsCount); // Concrete generic method pointers - var genericMethodPointers = Image.ReadMappedArray(CodeRegistration.genericMethodPointers, (int) CodeRegistration.genericMethodPointersCount); - var genericMethodTable = Image.ReadMappedArray(MetadataRegistration.genericMethodTable, (int) MetadataRegistration.genericMethodTableCount); + var genericMethodPointers = Image.ReadMappedArray(CodeRegistration.GenericMethodPointers, (int) CodeRegistration.GenericMethodPointersCount); + var genericMethodTable = Image.ReadMappedArray(MetadataRegistration.GenericMethodTable, (int) MetadataRegistration.GenericMethodTableCount); foreach (var tableEntry in genericMethodTable) { - GenericMethodPointers.Add(MethodSpecs[tableEntry.genericMethodIndex], genericMethodPointers[tableEntry.indices.methodIndex]); - GenericMethodInvokerIndices.Add(MethodSpecs[tableEntry.genericMethodIndex], tableEntry.indices.invokerIndex); + GenericMethodPointers.Add(MethodSpecs[tableEntry.GenericMethodIndex], genericMethodPointers[tableEntry.Indices.MethodIndex]); + GenericMethodInvokerIndices.Add(MethodSpecs[tableEntry.GenericMethodIndex], tableEntry.Indices.InvokerIndex); } TypeDefinitionSizes = Image.ReadMappedObjectPointerArray( - MetadataRegistration.typeDefinitionsSizes, (int) MetadataRegistration.typeDefinitionsSizesCount); + MetadataRegistration.TypeDefinitionsSizes, (int) MetadataRegistration.TypeDefinitionsSizesCount); // Plugin hook to pre-process binary isModified |= PluginHooks.PostProcessBinary(this).IsStreamModified; diff --git a/Il2CppInspector.Common/IL2CPP/Il2CppBinaryClasses.cs b/Il2CppInspector.Common/IL2CPP/Il2CppBinaryClasses.cs deleted file mode 100644 index fee8920c..00000000 --- a/Il2CppInspector.Common/IL2CPP/Il2CppBinaryClasses.cs +++ /dev/null @@ -1,306 +0,0 @@ -/* - Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper - Copyright 2017-2021 Katy Coe - http://www.djkaty.com - https://github.com/djkaty - - All rights reserved. -*/ - -using NoisyCowStudios.Bin2Object; - -namespace Il2CppInspector -{ - // From class-internals.h / il2cpp-class-internals.h - public class Il2CppCodeRegistration - { - // Moved to Il2CppCodeGenModule in v24.2 - [Version(Max = 24.1)] - public ulong methodPointersCount; - [Version(Max = 24.1)] - public ulong pmethodPointers; - - public ulong reversePInvokeWrapperCount; // (was renamed from delegateWrappersFromNativeToManagedCount in v22) - public ulong reversePInvokeWrappers; // (was renamed from delegateWrappersFromNativeToManaged in v22) - - // Removed in metadata v23 - [Version(Max = 22)] - public ulong delegateWrappersFromManagedToNativeCount; - [Version(Max = 22)] - public ulong delegateWrappersFromManagedToNative; - [Version(Max = 22)] - public ulong marshalingFunctionsCount; - [Version(Max = 22)] - public ulong marshalingFunctions; - [Version(Min = 21, Max = 22)] - public ulong ccwMarshalingFunctionsCount; - [Version(Min = 21, Max = 22)] - public ulong ccwMarshalingFunctions; - - public ulong genericMethodPointersCount; - public ulong genericMethodPointers; - [Version(Min = 24.5, Max = 24.5)] - [Version(Min = 27.1)] - public ulong genericAdjustorThunks; - - public ulong invokerPointersCount; - public ulong invokerPointers; - - // Removed in metadata v27 - [Version(Max = 24.5)] - public long customAttributeCount; - [Version(Max = 24.5)] - public ulong customAttributeGenerators; - - // Removed in metadata v23 - [Version(Min = 21, Max = 22)] - public long guidCount; - [Version(Min = 21, Max = 22)] - public ulong guids; // Il2CppGuid - - // Added in metadata v22 - [Version(Min = 22, Max = 29)] - public ulong unresolvedVirtualCallCount; - - [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, Max = 29.2)] - [Version(Min = 31.1, Max = 31.2)] - public ulong unresolvedInstanceCallPointers; - - [Version(Min = 29.1, Max = 29.2)] - [Version(Min = 31.1, Max = 31.2)] - public ulong unresolvedStaticCallPointers; - - // Added in metadata v23 - [Version(Min = 23)] - public ulong interopDataCount; - [Version(Min = 23)] - public ulong interopData; - - [Version(Min = 24.3)] - public ulong windowsRuntimeFactoryCount; - [Version(Min = 24.3)] - public ulong windowsRuntimeFactoryTable; - - // Added in metadata v24.2 to replace methodPointers and methodPointersCount - [Version(Min = 24.2)] - public ulong codeGenModulesCount; - [Version(Min = 24.2)] - public ulong pcodeGenModules; - } - - // Introduced in metadata v24.2 (replaces method pointers in Il2CppCodeRegistration) - public class Il2CppCodeGenModule - { - public ulong moduleName; - public ulong methodPointerCount; - public ulong methodPointers; - [Version(Min = 24.5, Max = 24.5)] - [Version(Min = 27.1)] - public long adjustorThunkCount; - [Version(Min = 24.5, Max = 24.5)] - [Version(Min = 27.1)] - public ulong adjustorThunks; //Pointer - public ulong invokerIndices; - public ulong reversePInvokeWrapperCount; - public ulong reversePInvokeWrapperIndices; - public ulong rgctxRangesCount; - public ulong rgctxRanges; - public ulong rgctxsCount; - public ulong rgctxs; - public ulong debuggerMetadata; - - // Added in metadata v27 - [Version(Min = 27, Max = 27.2)] - public ulong customAttributeCacheGenerator; // CustomAttributesCacheGenerator* - [Version(Min = 27)] - public ulong moduleInitializer; // Il2CppMethodPointer - [Version(Min = 27)] - public ulong staticConstructorTypeIndices; // TypeDefinitionIndex* - [Version(Min = 27)] - public ulong metadataRegistration; // Il2CppMetadataRegistration* // Per-assembly mode only - [Version(Min = 27)] - public ulong codeRegistration; // Il2CppCodeRegistration* // Per-assembly mode only - } - -#pragma warning disable CS0649 - public class Il2CppMetadataRegistration - { - public long genericClassesCount; - public ulong genericClasses; - public long genericInstsCount; - public ulong genericInsts; - public long genericMethodTableCount; - public ulong genericMethodTable; // Il2CppGenericMethodFunctionsDefinitions - public long typesCount; - public ulong ptypes; - public long methodSpecsCount; - public ulong methodSpecs; - [Version(Max = 16)] - public long methodReferencesCount; - [Version(Max = 16)] - public ulong methodReferences; - - public long fieldOffsetsCount; - public ulong pfieldOffsets; // Changed from int32_t* to int32_t** after 5.4.0f3, before 5.5.0f3 - - public long typeDefinitionsSizesCount; - public ulong typeDefinitionsSizes; - [Version(Min = 19)] - public ulong metadataUsagesCount; - [Version(Min = 19)] - public ulong metadataUsages; - } -#pragma warning restore CS0649 - - // From blob.h / il2cpp-blob.h - public enum Il2CppTypeEnum - { - IL2CPP_TYPE_END = 0x00, /* End of List */ - IL2CPP_TYPE_VOID = 0x01, - IL2CPP_TYPE_BOOLEAN = 0x02, - IL2CPP_TYPE_CHAR = 0x03, - IL2CPP_TYPE_I1 = 0x04, - IL2CPP_TYPE_U1 = 0x05, - IL2CPP_TYPE_I2 = 0x06, - IL2CPP_TYPE_U2 = 0x07, - IL2CPP_TYPE_I4 = 0x08, - IL2CPP_TYPE_U4 = 0x09, - IL2CPP_TYPE_I8 = 0x0a, - IL2CPP_TYPE_U8 = 0x0b, - IL2CPP_TYPE_R4 = 0x0c, - IL2CPP_TYPE_R8 = 0x0d, - IL2CPP_TYPE_STRING = 0x0e, - IL2CPP_TYPE_PTR = 0x0f, /* arg: token */ - IL2CPP_TYPE_BYREF = 0x10, /* arg: token */ - IL2CPP_TYPE_VALUETYPE = 0x11, /* arg: token */ - IL2CPP_TYPE_CLASS = 0x12, /* arg: token */ - IL2CPP_TYPE_VAR = 0x13, /* Generic parameter in a generic type definition, represented as number (compressed unsigned integer) number */ - IL2CPP_TYPE_ARRAY = 0x14, /* type, rank, boundsCount, bound1, loCount, lo1 */ - IL2CPP_TYPE_GENERICINST = 0x15, /* \x{2026} */ - IL2CPP_TYPE_TYPEDBYREF = 0x16, - IL2CPP_TYPE_I = 0x18, - IL2CPP_TYPE_U = 0x19, - IL2CPP_TYPE_FNPTR = 0x1b, /* arg: full method signature */ - IL2CPP_TYPE_OBJECT = 0x1c, - IL2CPP_TYPE_SZARRAY = 0x1d, /* 0-based one-dim-array */ - IL2CPP_TYPE_MVAR = 0x1e, /* Generic parameter in a generic method definition, represented as number (compressed unsigned integer) */ - IL2CPP_TYPE_CMOD_REQD = 0x1f, /* arg: typedef or typeref token */ - IL2CPP_TYPE_CMOD_OPT = 0x20, /* optional arg: typedef or typref token */ - IL2CPP_TYPE_INTERNAL = 0x21, /* CLR internal type */ - - IL2CPP_TYPE_MODIFIER = 0x40, /* Or with the following types */ - IL2CPP_TYPE_SENTINEL = 0x41, /* Sentinel for varargs method signature */ - IL2CPP_TYPE_PINNED = 0x45, /* Local var that points to pinned object */ - - IL2CPP_TYPE_ENUM = 0x55, /* an enumeration */ - IL2CPP_TYPE_IL2CPP_TYPE_INDEX = 0xff /* Type index metadata table */ - } - - // From metadata.h / il2cpp-runtime-metadata.h - public class Il2CppType - { - public ulong datapoint; - public ulong bits; // this should be private but we need it to be public for BinaryObjectReader to work - //public Union data { get; set; } - - public virtual uint attrs => (uint) bits & 0xffff; - public virtual Il2CppTypeEnum type => (Il2CppTypeEnum)((bits >> 16) & 0xff); - - public virtual uint num_mods => (uint) (bits >> 24) & 0x3f; - public virtual bool byref => ((bits >> 30) & 1) == 1; - public virtual bool pinned => ((bits >> 31) & 1) == 1; - public virtual bool valuetype => false; - - /* - union - { - TypeDefinitionIndex klassIndex; // for VALUETYPE and CLASS ( (uint) (bits >> 24) & 0x1f; - public override bool byref => ((bits >> 29) & 1) == 1; - public override bool pinned => ((bits >> 30) & 1) == 1; - public override bool valuetype => ((bits >> 31) & 1) == 1; - } - - public class Il2CppGenericClass - { - [Version(Max = 24.5)] - public long typeDefinitionIndex; /* the generic type definition */ - [Version(Min = 27)] - public ulong type; // Il2CppType* /* the generic type definition */ - - public Il2CppGenericContext context; /* a context that contains the type instantiation doesn't contain any method instantiation */ - public ulong cached_class; /* if present, the Il2CppClass corresponding to the instantiation. */ - } - - public class Il2CppGenericContext - { - /* The instantiation corresponding to the class generic parameters */ - public ulong class_inst; - /* The instantiation corresponding to the method generic parameters */ - public ulong method_inst; - } - - public class Il2CppGenericInst - { - public ulong type_argc; - public ulong type_argv; - } - - public class Il2CppArrayType - { - public ulong etype; - public byte rank; - public byte numsizes; - public byte numlobounds; - public ulong sizes; - public ulong lobounds; - } - - public class Il2CppMethodSpec - { - public int methodDefinitionIndex; - public int classIndexIndex; - public int methodIndexIndex; - } - - public class Il2CppGenericMethodFunctionsDefinitions - { - public int genericMethodIndex; - public Il2CppGenericMethodIndices indices; - } - - public class Il2CppGenericMethodIndices - { - public int methodIndex; - public int invokerIndex; - [Version(Min = 24.5, Max = 24.5)] - [Version(Min = 27.1)] - public int adjustorThunk; - } - - public class Il2CppTypeDefinitionSizes - { - public uint instanceSize; - public int nativeSize; - public uint staticFieldsSize; - public uint threadStaticFieldsSize; - } -} diff --git a/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs b/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs index 97027028..8eb55707 100644 --- a/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs +++ b/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs @@ -13,6 +13,8 @@ All rights reserved. using System.IO.Compression; using System.Linq; using System.Text; +using Il2CppInspector.Next.BinaryMetadata; +using Il2CppInspector.Next.Metadata; using VersionedSerialization; namespace Il2CppInspector @@ -81,7 +83,7 @@ public class Il2CppInspector return (0ul, null); // Get pointer in binary to default value - var pValue = Metadata.Header.fieldAndParameterDefaultValueDataOffset + dataIndex; + var pValue = Metadata.Header.FieldAndParameterDefaultValueDataOffset + dataIndex; var typeRef = TypeReferences[typeIndex]; // Default value is null @@ -89,7 +91,7 @@ public class Il2CppInspector return (0ul, null); Metadata.Position = pValue; - var value = BlobReader.GetConstantValueFromBlob(this, typeRef.type, Metadata); + var value = BlobReader.GetConstantValueFromBlob(this, typeRef.Type, Metadata); return ((ulong) pValue, value); } @@ -108,10 +110,10 @@ private List buildMetadataUsages() var usages = new Dictionary(); foreach (var metadataUsageList in Metadata.MetadataUsageLists) { - for (var i = 0; i < metadataUsageList.count; i++) + for (var i = 0; i < metadataUsageList.Count; i++) { - var metadataUsagePair = Metadata.MetadataUsagePairs[metadataUsageList.start + i]; - usages.TryAdd(metadataUsagePair.destinationindex, MetadataUsage.FromEncodedIndex(this, metadataUsagePair.encodedSourceIndex)); + var metadataUsagePair = Metadata.MetadataUsagePairs[metadataUsageList.Start + i]; + usages.TryAdd(metadataUsagePair.DestinationIndex, MetadataUsage.FromEncodedIndex(this, metadataUsagePair.EncodedSourceIndex)); } } @@ -119,7 +121,7 @@ private List buildMetadataUsages() // Unfortunately the value supplied in MetadataRegistration.matadataUsagesCount seems to be incorrect, // so we have to calculate the correct number of usages above before reading the usage address list from the binary var count = usages.Keys.Max() + 1; - var addresses = Binary.Image.ReadMappedArray(Binary.MetadataRegistration.metadataUsages, (int) count); + var addresses = Binary.Image.ReadMappedArray(Binary.MetadataRegistration.MetadataUsages, (int) count); foreach (var usage in usages) usage.Value.SetAddress(addresses[usage.Key]); @@ -184,11 +186,11 @@ public Il2CppInspector(Il2CppBinary binary, Metadata metadata) { // Get all field default values foreach (var fdv in Metadata.FieldDefaultValues) - FieldDefaultValue.Add(fdv.fieldIndex, ((ulong,object)) getDefaultValue(fdv.typeIndex, fdv.dataIndex)); + FieldDefaultValue.Add(fdv.FieldIndex, ((ulong,object)) getDefaultValue(fdv.TypeIndex, fdv.DataIndex)); // Get all parameter default values foreach (var pdv in Metadata.ParameterDefaultValues) - ParameterDefaultValue.Add(pdv.parameterIndex, ((ulong,object)) getDefaultValue(pdv.typeIndex, pdv.dataIndex)); + ParameterDefaultValue.Add(pdv.ParameterIndex, ((ulong,object)) getDefaultValue(pdv.TypeIndex, pdv.DataIndex)); // Get all field offsets if (Binary.FieldOffsets != null) { @@ -212,8 +214,8 @@ public Il2CppInspector(Il2CppBinary binary, Metadata metadata) { available = false; } - for (var f = 0; f < def.field_count; f++) - offsets.Add(def.fieldStart + f, available? BinaryImage.ReadUInt32() : 0); + for (var f = 0; f < def.FieldCount; f++) + offsets.Add(def.FieldIndex + f, available? BinaryImage.ReadUInt32() : 0); } } @@ -225,16 +227,16 @@ public Il2CppInspector(Il2CppBinary binary, Metadata metadata) { CustomAttributeGenerators = Binary.CustomAttributeGenerators; else if (Version < MetadataVersions.V290) { - var cagCount = Images.Sum(i => i.customAttributeCount); + var cagCount = Images.Sum(i => i.CustomAttributeCount); CustomAttributeGenerators = new ulong[cagCount]; foreach (var image in Images) { // Get CodeGenModule for this image - var codeGenModule = Binary.Modules[Strings[image.nameIndex]]; - var cags = BinaryImage.ReadMappedWordArray(codeGenModule.customAttributeCacheGenerator, - (int) image.customAttributeCount); - cags.CopyTo(CustomAttributeGenerators, image.customAttributeStart); + var codeGenModule = Binary.Modules[Strings[image.NameIndex]]; + var cags = BinaryImage.ReadMappedWordArray(codeGenModule.CustomAttributeCacheGenerator, + (int) image.CustomAttributeCount); + cags.CopyTo(CustomAttributeGenerators, image.CustomAttributeStart); } } else @@ -270,15 +272,15 @@ public Il2CppInspector(Il2CppBinary binary, Metadata metadata) { foreach (var image in Images) { var attsByToken = new Dictionary(); - for (int i = 0; i < image.customAttributeCount; i++) + for (int i = 0; i < image.CustomAttributeCount; i++) { - var index = image.customAttributeStart + i; - var token = Version >= MetadataVersions.V290 ? AttributeDataRanges[index].token : AttributeTypeRanges[index].token; + var index = image.CustomAttributeStart + i; + var token = Version >= MetadataVersions.V290 ? AttributeDataRanges[index].Token : AttributeTypeRanges[index].Token; attsByToken.Add(token, index); } if (attsByToken.Count > 0) - AttributeIndicesByToken.Add(image.customAttributeStart, attsByToken); + AttributeIndicesByToken.Add(image.CustomAttributeStart, attsByToken); } } @@ -292,20 +294,20 @@ public Il2CppInspector(Il2CppBinary binary, Metadata metadata) { // Get a method pointer if available public (ulong Start, ulong End)? GetMethodPointer(Il2CppCodeGenModule module, Il2CppMethodDefinition methodDef) { // Find method pointer - if (methodDef.methodIndex < 0) + if (methodDef.MethodIndex < 0) return null; ulong start = 0; // Global method pointer array if (Version <= MetadataVersions.V241) { - start = Binary.GlobalMethodPointers[methodDef.methodIndex]; + 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 >= MetadataVersions.V242) { - var method = (methodDef.token & 0xffffff); + var method = (methodDef.Token & 0xffffff); if (method == 0) return null; @@ -340,18 +342,18 @@ public Il2CppInspector(Il2CppBinary binary, Metadata metadata) { // Get a method invoker index from a method definition public int GetInvokerIndex(Il2CppCodeGenModule module, Il2CppMethodDefinition methodDef) { if (Version <= MetadataVersions.V241) { - return methodDef.invokerIndex; + return methodDef.InvokerIndex; } // Version >= 24.2 - var methodInModule = (methodDef.token & 0xffffff); + var methodInModule = (methodDef.Token & 0xffffff); return Binary.MethodInvokerIndices[module][methodInModule - 1]; } public MetadataUsage[] GetVTable(Il2CppTypeDefinition definition) { - MetadataUsage[] res = new MetadataUsage[definition.vtable_count]; - for (int i = 0; i < definition.vtable_count; i++) { - var encodedIndex = VTableMethodIndices[definition.vtableStart + i]; + MetadataUsage[] res = new MetadataUsage[definition.VTableCount]; + for (int i = 0; i < definition.VTableCount; i++) { + var encodedIndex = VTableMethodIndices[definition.VTableIndex + i]; MetadataUsage usage = MetadataUsage.FromEncodedIndex(this, encodedIndex); if (usage.SourceIndex != 0) res[i] = usage; diff --git a/Il2CppInspector.Common/IL2CPP/ImageScan.cs b/Il2CppInspector.Common/IL2CPP/ImageScan.cs index 91a05af9..869abf32 100644 --- a/Il2CppInspector.Common/IL2CPP/ImageScan.cs +++ b/Il2CppInspector.Common/IL2CPP/ImageScan.cs @@ -10,6 +10,7 @@ All rights reserved. using System.Linq; using System.Text; using Il2CppInspector.Next; +using Il2CppInspector.Next.BinaryMetadata; namespace Il2CppInspector { @@ -211,12 +212,12 @@ ulong FindCodeRegistration() // if this changes we'll have to get smarter about disambiguating these two. var cr = Image.ReadMappedObject(codeRegistration); - if (Image.Version == MetadataVersions.V242 && cr.interopDataCount == 0) { + if (Image.Version == MetadataVersions.V242 && cr.InteropDataCount == 0) { Image.Version = MetadataVersions.V243; codeRegistration -= ptrSize * 2; // two extra words for WindowsRuntimeFactory } - if (Image.Version == MetadataVersions.V270 && 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. @@ -229,7 +230,7 @@ ulong FindCodeRegistration() // <= 24.1 else { // The first item in CodeRegistration is the total number of method pointers - vas = FindAllMappedWords(imageBytes, (ulong) metadata.Methods.Count(m => (uint) m.methodIndex != 0xffff_ffff)); + vas = FindAllMappedWords(imageBytes, (ulong) metadata.Methods.Count(m => (uint) m.MethodIndex != 0xffff_ffff)); if (!vas.Any()) return (0, 0); @@ -240,7 +241,7 @@ ulong FindCodeRegistration() foreach (var va in vas) { var cr = Image.ReadMappedObject(va); - if (cr.customAttributeCount == metadata.AttributeTypeRanges.Length) + if (cr.CustomAttributeCount == metadata.AttributeTypeRanges.Length) codeRegistration = va; } @@ -263,7 +264,7 @@ ulong FindCodeRegistration() if (Image.Version < MetadataVersions.V270) foreach (var va in vas) { var mr = Image.ReadMappedObject(va); - if (mr.metadataUsagesCount == (ulong) metadata.MetadataUsageLists.Length) + if (mr.MetadataUsagesCount == (ulong) metadata.MetadataUsageLists.Length) metadataRegistration = va; } diff --git a/Il2CppInspector.Common/IL2CPP/Metadata.cs b/Il2CppInspector.Common/IL2CPP/Metadata.cs index a814585f..63116d16 100644 --- a/Il2CppInspector.Common/IL2CPP/Metadata.cs +++ b/Il2CppInspector.Common/IL2CPP/Metadata.cs @@ -11,6 +11,7 @@ All rights reserved. using System.Linq; using System.Reflection; using Il2CppInspector.Next; +using Il2CppInspector.Next.Metadata; using NoisyCowStudios.Bin2Object; using VersionedSerialization; @@ -83,15 +84,15 @@ private void Initialize() Header = ReadObject(0); // Check for correct magic bytes - if (Header.signature != Il2CppConstants.MetadataSignature) { + if (!Header.SanityValid) { throw new InvalidOperationException("The supplied metadata file is not valid."); } // Set object versioning for Bin2Object from metadata version - Version = new StructVersion(Header.version); + Version = new StructVersion(Header.Version); if (Version < MetadataVersions.V160 || Version > MetadataVersions.V310) { - throw new InvalidOperationException($"The supplied metadata file is not of a supported version ({Header.version})."); + throw new InvalidOperationException($"The supplied metadata file is not of a supported version ({Header.Version})."); } // Rewind and read metadata header with the correct version settings @@ -107,7 +108,7 @@ private void Initialize() // we can use this value to determine the actual header length and therefore narrow down the metadata version to 24.0/24.1 or 24.2. if (!pluginResult.SkipValidation) { - var realHeaderLength = Header.stringLiteralOffset; + var realHeaderLength = Header.StringLiteralOffset; if (realHeaderLength != Sizeof(typeof(Il2CppGlobalMetadataHeader))) { if (Version == MetadataVersions.V240) { @@ -123,43 +124,43 @@ private void Initialize() // Load all the relevant metadata using offsets provided in the header if (Version >= MetadataVersions.V160) - Images = ReadArray(Header.imagesOffset, Header.imagesCount / Sizeof(typeof(Il2CppImageDefinition))); + Images = ReadArray(Header.ImagesOffset, Header.ImagesSize / 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 >= MetadataVersions.V190 && Images.Any(x => x.token != 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))); + Images = ReadArray(Header.ImagesOffset, Header.ImagesSize / Sizeof(typeof(Il2CppImageDefinition))); - if (Images.Any(x => x.token != 1)) + if (Images.Any(x => x.Token != 1)) throw new InvalidOperationException("Could not verify the integrity of the metadata file image list"); } - Types = ReadArray(Header.typeDefinitionsOffset, Header.typeDefinitionsCount / Sizeof(typeof(Il2CppTypeDefinition))); - Methods = ReadArray(Header.methodsOffset, Header.methodsCount / Sizeof(typeof(Il2CppMethodDefinition))); - Params = ReadArray(Header.parametersOffset, Header.parametersCount / Sizeof(typeof(Il2CppParameterDefinition))); - Fields = ReadArray(Header.fieldsOffset, Header.fieldsCount / Sizeof(typeof(Il2CppFieldDefinition))); - FieldDefaultValues = ReadArray(Header.fieldDefaultValuesOffset, Header.fieldDefaultValuesCount / Sizeof(typeof(Il2CppFieldDefaultValue))); - Properties = ReadArray(Header.propertiesOffset, Header.propertiesCount / Sizeof(typeof(Il2CppPropertyDefinition))); - Events = ReadArray(Header.eventsOffset, Header.eventsCount / Sizeof(typeof(Il2CppEventDefinition))); - InterfaceUsageIndices = ReadArray(Header.interfacesOffset, Header.interfacesCount / sizeof(int)); - NestedTypeIndices = ReadArray(Header.nestedTypesOffset, Header.nestedTypesCount / sizeof(int)); - GenericContainers = ReadArray(Header.genericContainersOffset, Header.genericContainersCount / Sizeof(typeof(Il2CppGenericContainer))); - GenericParameters = ReadArray(Header.genericParametersOffset, Header.genericParametersCount / Sizeof(typeof(Il2CppGenericParameter))); - GenericConstraintIndices = ReadArray(Header.genericParameterConstraintsOffset, Header.genericParameterConstraintsCount / sizeof(int)); - InterfaceOffsets = ReadArray(Header.interfaceOffsetsOffset, Header.interfaceOffsetsCount / Sizeof(typeof(Il2CppInterfaceOffsetPair))); - VTableMethodIndices = ReadArray(Header.vtableMethodsOffset, Header.vtableMethodsCount / sizeof(uint)); + Types = ReadArray(Header.TypeDefinitionsOffset, Header.TypeDefinitionsSize / Sizeof(typeof(Il2CppTypeDefinition))); + Methods = ReadArray(Header.MethodsOffset, Header.MethodsSize / Sizeof(typeof(Il2CppMethodDefinition))); + Params = ReadArray(Header.ParametersOffset, Header.ParametersSize / Sizeof(typeof(Il2CppParameterDefinition))); + Fields = ReadArray(Header.FieldsOffset, Header.FieldsSize / Sizeof(typeof(Il2CppFieldDefinition))); + FieldDefaultValues = ReadArray(Header.FieldDefaultValuesOffset, Header.FieldDefaultValuesSize / Sizeof(typeof(Il2CppFieldDefaultValue))); + Properties = ReadArray(Header.PropertiesOffset, Header.PropertiesSize / Sizeof(typeof(Il2CppPropertyDefinition))); + Events = ReadArray(Header.EventsOffset, Header.EventsSize / Sizeof(typeof(Il2CppEventDefinition))); + InterfaceUsageIndices = ReadArray(Header.InterfacesOffset, Header.InterfacesSize / sizeof(int)); + NestedTypeIndices = ReadArray(Header.NestedTypesOffset, Header.NestedTypesSize / sizeof(int)); + GenericContainers = ReadArray(Header.GenericContainersOffset, Header.GenericContainersSize / Sizeof(typeof(Il2CppGenericContainer))); + GenericParameters = ReadArray(Header.GenericParametersOffset, Header.GenericParametersSize / Sizeof(typeof(Il2CppGenericParameter))); + GenericConstraintIndices = ReadArray(Header.GenericParameterConstraintsOffset, Header.GenericParameterConstraintsSize / sizeof(int)); + InterfaceOffsets = ReadArray(Header.InterfaceOffsetsOffset, Header.InterfaceOffsetsSize / Sizeof(typeof(Il2CppInterfaceOffsetPair))); + VTableMethodIndices = ReadArray(Header.VTableMethodsOffset, Header.VTableMethodsSize / sizeof(uint)); 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 assemblyCount = Header.AssembliesSize / Sizeof(typeof(Il2CppAssemblyDefinition)); var changedAssemblyDefStruct = false; if ((Version == MetadataVersions.V241 || Version == MetadataVersions.V242 || Version == MetadataVersions.V243) && assemblyCount < Images.Length) { @@ -168,29 +169,29 @@ private void Initialize() Version = MetadataVersions.V244; } - Assemblies = ReadArray(Header.assembliesOffset, Images.Length); + Assemblies = ReadArray(Header.AssembliesOffset, Images.Length); if (changedAssemblyDefStruct) Version = MetadataVersions.V241; - ParameterDefaultValues = ReadArray(Header.parameterDefaultValuesOffset, Header.parameterDefaultValuesCount / Sizeof(typeof(Il2CppParameterDefaultValue))); + ParameterDefaultValues = ReadArray(Header.ParameterDefaultValuesOffset, Header.ParameterDefaultValuesSize / Sizeof(typeof(Il2CppParameterDefaultValue))); } 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))); + MetadataUsageLists = ReadArray(Header.MetadataUsageListsOffset, Header.MetadataUsageListsCount / Sizeof(typeof(Il2CppMetadataUsageList))); + MetadataUsagePairs = ReadArray(Header.MetadataUsagePairsOffset, Header.MetadataUsagePairsCount / Sizeof(typeof(Il2CppMetadataUsagePair))); } if (Version >= MetadataVersions.V190) { - FieldRefs = ReadArray(Header.fieldRefsOffset, Header.fieldRefsCount / Sizeof(typeof(Il2CppFieldRef))); + FieldRefs = ReadArray(Header.FieldRefsOffset, Header.FieldRefsSize / Sizeof(typeof(Il2CppFieldRef))); } if (Version >= MetadataVersions.V210 && Version < MetadataVersions.V290) { - AttributeTypeIndices = ReadArray(Header.attributeTypesOffset, Header.attributeTypesCount / sizeof(int)); - AttributeTypeRanges = ReadArray(Header.attributesInfoOffset, Header.attributesInfoCount / Sizeof(typeof(Il2CppCustomAttributeTypeRange))); + AttributeTypeIndices = ReadArray(Header.AttributesTypesOffset, Header.AttributesTypesCount / sizeof(int)); + AttributeTypeRanges = ReadArray(Header.AttributesInfoOffset, Header.AttributesInfoCount / Sizeof(typeof(Il2CppCustomAttributeTypeRange))); } if (Version >= MetadataVersions.V290) { - AttributeDataRanges = ReadArray(Header.attributeDataRangeOffset, - Header.attributeDataRangeSize / Sizeof(typeof(Il2CppCustomAttributeDataRange))); + AttributeDataRanges = ReadArray(Header.AttributeDataRangeOffset, + Header.AttributeDataRangeSize / Sizeof(typeof(Il2CppCustomAttributeDataRange))); } if (Version == MetadataVersions.V290 || Version == MetadataVersions.V310) @@ -201,13 +202,13 @@ private void Initialize() if (Methods.Length >= 2) { - var secondToken = Methods[1].token; + 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))); + Methods = ReadArray(Header.MethodsOffset, + Header.MethodsSize / Sizeof(typeof(Il2CppMethodDefinition))); } } } @@ -218,10 +219,10 @@ private void Initialize() Strings = pluginGetStringsResult.Strings; else { - Position = Header.stringOffset; + Position = Header.StringOffset; - while (Position < Header.stringOffset + Header.stringCount) - Strings.Add((int) Position - Header.stringOffset, ReadNullTerminatedString()); + while (Position < Header.StringOffset + Header.StringSize) + Strings.Add((int) Position - Header.StringOffset, ReadNullTerminatedString()); } // Get all string literals @@ -230,11 +231,11 @@ private void Initialize() StringLiterals = pluginGetStringLiteralsResult.StringLiterals.ToArray(); else { - var stringLiteralList = ReadArray(Header.stringLiteralOffset, Header.stringLiteralCount / Sizeof(typeof(Il2CppStringLiteral))); + var stringLiteralList = ReadArray(Header.StringLiteralOffset, Header.StringLiteralSize / Sizeof(typeof(Il2CppStringLiteral))); StringLiterals = new string[stringLiteralList.Length]; for (var i = 0; i < stringLiteralList.Length; i++) - StringLiterals[i] = ReadFixedLengthString(Header.stringLiteralDataOffset + stringLiteralList[i].dataIndex, stringLiteralList[i].length); + StringLiterals[i] = ReadFixedLengthString(Header.StringLiteralDataOffset + stringLiteralList[i].DataIndex, (int)stringLiteralList[i].Length); } // Post-processing hook diff --git a/Il2CppInspector.Common/IL2CPP/MetadataClasses.cs b/Il2CppInspector.Common/IL2CPP/MetadataClasses.cs deleted file mode 100644 index e2c3cfcb..00000000 --- a/Il2CppInspector.Common/IL2CPP/MetadataClasses.cs +++ /dev/null @@ -1,483 +0,0 @@ -/* - Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper - Copyright 2017-2021 Katy Coe - http://www.djkaty.com - https://github.com/djkaty - - All rights reserved. -*/ - -using NoisyCowStudios.Bin2Object; - -namespace Il2CppInspector -{ - // Unity 4.6.1p5 - first release, no global-metadata.dat - // Unity 5.2.0f3 -> v15 - // Unity 5.3.0f4 -> v16 - // Unity 5.3.2f1 -> v19 - // Unity 5.3.3f1 -> v20 - // Unity 5.3.5f1 -> v21 - // Unity 5.5.0f3 -> v22 - // Unity 5.6.0f3 -> v23 - // Unity 2017.1.0f3 -> v24 - // Unity 2018.3.0f2 -> v24.1 - // Unity 2019.1.0f2 -> v24.2 - // Unity 2019.3.7f1 -> v24.3 - // Unity 2019.4.15f1 -> v24.4 - // Unity 2019.4.21f1 -> v24.5 - // Unity 2020.1.0f1 -> v24.3 - // Unity 2020.1.11f1 -> v24.4 - // Unity 2020.2.0f1 -> v27 - // Unity 2020.2.4f1 -> v27.1 - // Unity 2021.1.0f1 -> v27.2 - // https://unity3d.com/get-unity/download/archive - // Metadata version is written at the end of Unity.IL2CPP.MetadataCacheWriter.WriteLibIl2CppMetadata or WriteMetadata (Unity.IL2CPP.dll) - - // From il2cpp-metadata.h -#pragma warning disable CS0649 - public class Il2CppGlobalMetadataHeader - { - public uint signature; - public int version; - public int stringLiteralOffset; // string data for managed code - public int stringLiteralCount; - public int stringLiteralDataOffset; - public int stringLiteralDataCount; - public int stringOffset; // string data for metadata - public int stringCount; - public int eventsOffset; // Il2CppEventDefinition - public int eventsCount; - public int propertiesOffset; // Il2CppPropertyDefinition - public int propertiesCount; - public int methodsOffset; // Il2CppMethodDefinition - public int methodsCount; - - [Version(Min = 16)] - public int parameterDefaultValuesOffset; // Il2CppParameterDefaultValue - [Version(Min = 16)] - public int parameterDefaultValuesCount; - - public int fieldDefaultValuesOffset; // Il2CppFieldDefaultValue - public int fieldDefaultValuesCount; - public int fieldAndParameterDefaultValueDataOffset; // uint8_t - public int fieldAndParameterDefaultValueDataCount; - - [Version(Min = 16)] - public int fieldMarshaledSizesOffset; // Il2CppFieldMarshaledSize - [Version(Min = 16)] - public int fieldMarshaledSizesCount; - - public int parametersOffset; // Il2CppParameterDefinition - public int parametersCount; - public int fieldsOffset; // Il2CppFieldDefinition - public int fieldsCount; - public int genericParametersOffset; // Il2CppGenericParameter - public int genericParametersCount; - public int genericParameterConstraintsOffset; // TypeIndex - public int genericParameterConstraintsCount; - public int genericContainersOffset; // Il2CppGenericContainer - public int genericContainersCount; - public int nestedTypesOffset; // TypeDefinitionIndex - public int nestedTypesCount; - public int interfacesOffset; // TypeIndex - public int interfacesCount; - public int vtableMethodsOffset; // EncodedMethodIndex - public int vtableMethodsCount; - public int interfaceOffsetsOffset; // Il2CppInterfaceOffsetPair - public int interfaceOffsetsCount; - public int typeDefinitionsOffset; // Il2CppTypeDefinition - public int typeDefinitionsCount; - - [Version(Max = 24.1)] - public int rgctxEntriesOffset; // Il2CppRGCTXDefinition - [Version(Max = 24.1)] - public int rgctxEntriesCount; - - [Version(Min = 16)] - public int imagesOffset; // Il2CppImageDefinition - [Version(Min = 16)] - public int imagesCount; - [Version(Min = 16)] - public int assembliesOffset; // Il2CppAssemblyDefinition - [Version(Min = 16)] - public int assembliesCount; - - [Version(Min = 19, Max = 24.5)] - public int metadataUsageListsOffset; // Il2CppMetadataUsageList - [Version(Min = 19, Max = 24.5)] - public int metadataUsageListsCount; - [Version(Min = 19, Max = 24.5)] - public int metadataUsagePairsOffset; // Il2CppMetadataUsagePair - [Version(Min = 19, Max = 24.5)] - public int metadataUsagePairsCount; - [Version(Min = 19)] - public int fieldRefsOffset; // Il2CppFieldRef - [Version(Min = 19)] - public int fieldRefsCount; - [Version(Min = 20)] - public int referencedAssembliesOffset; // int32_t - [Version(Min = 20)] - public int referencedAssembliesCount; - - [Version(Min = 21, Max = 27.2)] - public int attributesInfoOffset; // Il2CppCustomAttributeTypeRange - [Version(Min = 21, Max = 27.2)] - public int attributesInfoCount; - [Version(Min = 21, Max = 27.2)] - public int attributeTypesOffset; // TypeIndex - [Version(Min = 21, Max = 27.2)] - public int attributeTypesCount; - [Version(Min = 29)] - public uint attributeDataOffset; - [Version(Min = 29)] - public int attributeDataSize; - [Version(Min = 29)] - public uint attributeDataRangeOffset; - [Version(Min = 29)] - public int attributeDataRangeSize; - - // Added in metadata v22 - [Version(Min = 22)] - public int unresolvedVirtualCallParameterTypesOffset; // TypeIndex - [Version(Min = 22)] - public int unresolvedVirtualCallParameterTypesCount; - [Version(Min = 22)] - public int unresolvedVirtualCallParameterRangesOffset; // Il2CppRange - [Version(Min = 22)] - public int unresolvedVirtualCallParameterRangesCount; - - // Added in metadata v23 - [Version(Min = 23)] - public int windowsRuntimeTypeNamesOffset; // Il2CppWindowsRuntimeTypeNamePair - [Version(Min = 23)] - public int windowsRuntimeTypeNamesSize; - - // Added in metadata v27 - [Version(Min = 27)] - public int windowsRuntimeStringsOffset; // const char* - [Version(Min = 27)] - public int windowsRuntimeStringsSize; - - // Added in metadata v24 - [Version(Min = 24)] - public int exportedTypeDefinitionsOffset; // TypeDefinitionIndex - [Version(Min = 24)] - public int exportedTypeDefinitionsCount; - } - - public class Il2CppImageDefinition - { - public int nameIndex; - public int assemblyIndex; - - public int typeStart; - public uint typeCount; - - [Version(Min = 24)] - public int exportedTypeStart; - [Version(Min = 24)] - public uint exportedTypeCount; - - public int entryPointIndex; - - [Version(Min = 19)] - public uint token; - - [Version(Min = 24.1)] - public int customAttributeStart; - [Version(Min = 24.1)] - public uint customAttributeCount; - } -#pragma warning restore CS0649 - - // Renamed from Il2CppAssembly somewhere after Unity 2017.2f3 up to Unity 2018.2.0f2 - public class Il2CppAssemblyDefinition - { - // They moved the position of aname in v16 from the top to the bottom of the struct - public Il2CppAssemblyNameDefinition aname => aname_pre16 ?? aname_post16; - - [Version(Max = 15)] - public Il2CppAssemblyNameDefinition aname_pre16; - - public int imageIndex; - - [Version(Min = 24.1)] - public uint token; - - [Version(Max = 24.0)] - public int customAttributeIndex; - - [Version(Min = 20)] - public int referencedAssemblyStart; - [Version(Min = 20)] - public int referencedAssemblyCount; - - [Version(Min = 16)] - public Il2CppAssemblyNameDefinition aname_post16; - } - - // Renamed from Il2CppAssemblyName somewhere after Unity 2017.2f3 up to Unity 2018.2.0f2 - public class Il2CppAssemblyNameDefinition - { - // They moved the position of publicKeyToken in v16 from the middle to the bottom of the struct - public byte[] publicKeyToken => publicKeyToken_post16; - - public int nameIndex; - public int cultureIndex; - [Version(Max = 24.3)] - public int hashValueIndex; - public int publicKeyIndex; - [Version(Max = 15), ArrayLength(FixedSize = 8)] - public byte[] publicKeyToken_pre16; - public uint hash_alg; - public int hash_len; - public uint flags; - public int major; - public int minor; - public int build; - public int revision; - [Version(Min = 16), ArrayLength(FixedSize = 8)] - public byte[] publicKeyToken_post16; - } - - public class Il2CppTypeDefinition - { - public int nameIndex; - public int namespaceIndex; - - // Removed in metadata v24.1 - [Version(Max = 24.0)] - public int customAttributeIndex; - - public int byvalTypeIndex; - [Version(Max = 24.5)] - public int byrefTypeIndex; - - public int declaringTypeIndex; - public int parentIndex; - public int elementTypeIndex; // we can probably remove this one. Only used for enums - - [Version(Max = 24.1)] - public int rgctxStartIndex; - [Version(Max = 24.1)] - public int rgctxCount; - - public int genericContainerIndex; - - // Removed in metadata v23 - [Version(Max = 22)] - public int delegateWrapperFromManagedToNativeIndex; // (was renamed to reversePInvokeWrapperIndex in v22) - [Version(Max = 22)] - public int marshalingFunctionsIndex; - [Version(Min = 21, Max = 22)] - public int ccwFunctionIndex; - [Version(Min = 21, Max = 22)] - public int guidIndex; - - public uint flags; - - public int fieldStart; - public int methodStart; - public int eventStart; - public int propertyStart; - public int nestedTypesStart; - public int interfacesStart; - public int vtableStart; - public int interfaceOffsetsStart; - - public ushort method_count; - public ushort property_count; - public ushort field_count; - public ushort event_count; - public ushort nested_type_count; - public ushort vtable_count; - public ushort interfaces_count; - public ushort interface_offsets_count; - - // bitfield to portably encode boolean values as single bits - // 01 - valuetype; - // 02 - enumtype; - // 03 - has_finalize; - // 04 - has_cctor; - // 05 - is_blittable; - // 06 - is_import; (from v22: is_import_or_windows_runtime) - // 07-10 - One of nine possible PackingSize values (0, 1, 2, 4, 8, 16, 32, 64, or 128) - public uint bitfield; - - [Version(Min = 19)] - public uint token; - } - - public class Il2CppMethodDefinition - { - public int nameIndex; - - [Version(Min = 16)] - public int declaringType; - - public int returnType; - - [Version(Min = 31)] - public int returnParameterToken; - - public int parameterStart; - - [Version(Max = 24.0)] - public int customAttributeIndex; - - public int genericContainerIndex; - - [Version(Max = 24.1)] - public int methodIndex; - [Version(Max = 24.1)] - public int invokerIndex; - [Version(Max = 24.1)] - public int reversePInvokeWrapperIndex; // (was renamed from delegateWrapperIndex in v22) - [Version(Max = 24.1)] - public int rgctxStartIndex; - [Version(Max = 24.1)] - public int rgctxCount; - - public uint token; - public ushort flags; - public ushort iflags; - public ushort slot; - public ushort parameterCount; - - [Version(Min = 29.2, Max = 29.2)] - [Version(Min = 31.2, Max = 31.2)] - public bool isUnmanagedCallersOnly; - } - - public class Il2CppParameterDefinition - { - public int nameIndex; - public uint token; - - [Version(Max = 24.0)] - public int customAttributeIndex; - - public int typeIndex; - } - - public class Il2CppParameterDefaultValue - { - public int parameterIndex; - public int typeIndex; - public int dataIndex; - } - - public class Il2CppFieldDefinition - { - public int nameIndex; - public int typeIndex; - - [Version(Max = 24.0)] - public int customAttributeIndex; - - [Version(Min = 19)] - public uint token; - } - - public class Il2CppFieldDefaultValue - { - public int fieldIndex; - public int typeIndex; - public int dataIndex; - } - - public class Il2CppPropertyDefinition - { - public int nameIndex; - public int get; - public int set; - public uint attrs; - - [Version(Max = 24.0)] - public int customAttributeIndex; - - [Version(Min = 19)] - public uint token; - } - - public class Il2CppEventDefinition - { - public int nameIndex; - public int typeIndex; - public int add; - public int remove; - public int raise; - - [Version(Max = 24.0)] - public int customAttributeIndex; - - [Version(Min = 19)] - public uint token; - } - - public class Il2CppGenericContainer - { - /* index of the generic type definition or the generic method definition corresponding to this container */ - public int ownerIndex; // either index into Il2CppClass metadata array or Il2CppMethodDefinition array - public int type_argc; - /* If true, we're a generic method, otherwise a generic type definition. */ - public int is_method; - /* Our type parameters. */ - public uint genericParameterStart; // GenericParameterIndex - } - - public class Il2CppGenericParameter - { - public int ownerIndex; /* Type or method this parameter was defined in. */ // GenericContainerIndex - public int nameIndex; // StringIndex - public short constraintsStart; // GenericParameterConstraintIndex - public short constraintsCount; - public ushort num; // Generic parameter position - public ushort flags; // GenericParameterAttributes - } - - public class Il2CppCustomAttributeTypeRange - { - [Version(Min = 24.1)] - public uint token; - - public int start; - public int count; - } - - public class Il2CppInterfaceOffsetPair - { - public int interfaceTypeIndex; - public int offset; - } - - // Removed in metadata v27 - public class Il2CppMetadataUsageList - { - public uint start; - public uint count; - } - - // Removed in metadata v27 - public class Il2CppMetadataUsagePair - { - public uint destinationindex; - public uint encodedSourceIndex; - } - - public class Il2CppStringLiteral - { - public int length; - public int dataIndex; - } - - public class Il2CppFieldRef - { - public int typeIndex; - public int fieldIndex; // local offset into type fields - } - - public class Il2CppCustomAttributeDataRange - { - public uint token; - public uint startOffset; - } -} diff --git a/Il2CppInspector.Common/Il2CppInspector.csproj b/Il2CppInspector.Common/Il2CppInspector.csproj index bf4f2d78..fa982aa9 100644 --- a/Il2CppInspector.Common/Il2CppInspector.csproj +++ b/Il2CppInspector.Common/Il2CppInspector.csproj @@ -2,6 +2,8 @@ net9.0 + enable + preview Il2CppInspector.Common Katy Coe 2023.1 @@ -47,7 +49,7 @@ - + diff --git a/Il2CppInspector.Common/Model/AddressMap.cs b/Il2CppInspector.Common/Model/AddressMap.cs index 2fa7fde4..44aa0c12 100644 --- a/Il2CppInspector.Common/Model/AddressMap.cs +++ b/Il2CppInspector.Common/Model/AddressMap.cs @@ -126,7 +126,7 @@ private void build() { 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); + Add(binary.CodeRegistration.CodeGenModules, binary.CodeGenModulePointers); foreach (var ptr in binary.CodeGenModulePointers) Add(ptr.Value, binary.Modules[ptr.Key]); diff --git a/Il2CppInspector.Common/Model/AppModel.cs b/Il2CppInspector.Common/Model/AppModel.cs index 65adac97..1f3b3e36 100644 --- a/Il2CppInspector.Common/Model/AppModel.cs +++ b/Il2CppInspector.Common/Model/AppModel.cs @@ -253,11 +253,11 @@ public AppModel Build(UnityVersion unityVersion = null, CppCompilerType compiler case MetadataUsageType.FieldInfo or MetadataUsageType.FieldRva: var fieldRef = TypeModel.Package.FieldRefs[usage.SourceIndex]; var fieldType = TypeModel.GetMetadataUsageType(usage); - var field = fieldType.DeclaredFields.First(f => f.Index == fieldType.Definition.fieldStart + fieldRef.fieldIndex); + var field = fieldType.DeclaredFields.First(f => f.Index == fieldType.Definition.FieldIndex + fieldRef.FieldIndex); var value = field.HasFieldRVA ? Convert.ToHexString(Package.Metadata.ReadBytes( - (long) field.DefaultValueMetadataAddress, field.FieldType.Sizes.nativeSize)) + (long) field.DefaultValueMetadataAddress, field.FieldType.Sizes.NativeSize)) : ""; diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppArrayType.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppArrayType.cs new file mode 100644 index 00000000..1296aaa9 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppArrayType.cs @@ -0,0 +1,18 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +[VersionedStruct] +public partial struct Il2CppArrayType +{ + public Pointer ElementType; + public byte Rank; + public byte NumSizes; + public byte NumLowerBound; + + [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] + public ulong Sizes; // int* + + [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] + public ulong LoBounds; // int* +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeGenModule.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeGenModule.cs new file mode 100644 index 00000000..f9f1808a --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeGenModule.cs @@ -0,0 +1,61 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +[VersionedStruct] +public partial struct Il2CppCodeGenModule +{ + [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] + public ulong ModuleName; // const char* + + [Aligned(0)] + public uint MethodPointerCount; + + public Pointer MethodPointers; + + [Aligned(0)] + [VersionCondition(EqualTo = "24.5")] + [VersionCondition(GreaterThan = "27.1")] + public uint AdjustorThunksCount; + + [VersionCondition(EqualTo = "24.5")] + [VersionCondition(GreaterThan = "27.1")] + public Pointer AdjustorThunks; + + [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] + public ulong InvokerIndices; // int* + + [Aligned(0)] + public uint ReversePInvokeWrapperCount; + + public Pointer ReversePInvokeWrapperIndices; + + [Aligned(0)] + public uint RgctxRangesCount; + public Pointer RgctxRanges; + + [Aligned(0)] + public uint RgctxsCount; + public Pointer Rgctxs; + + [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] + public ulong DebuggerMetadata; // Pointer DebuggerMetadata; + + [VersionCondition(GreaterThan = "27.0", LessThan = "27.2")] + public Pointer CustomAttributeCacheGenerator; + + [VersionCondition(GreaterThan = "27.0")] + public Il2CppMethodPointer ModuleInitializer; + + [VersionCondition(GreaterThan = "27.0")] + [Aligned(0)] + public ulong StaticConstructorTypeIndices; // TypeDefinitionIndex* + + [VersionCondition(GreaterThan = "27.0")] + [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] + public ulong MetadataRegistration; // Pointer + + [VersionCondition(GreaterThan = "27.0")] + [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] + public ulong CodeRegistration; // Pointer +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeRegistration.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeRegistration.cs new file mode 100644 index 00000000..428f0449 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeRegistration.cs @@ -0,0 +1,101 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +using InvokerMethod = Il2CppMethodPointer; + +[VersionedStruct] +public partial struct Il2CppCodeRegistration +{ + [VersionCondition(LessThan = "24.1"), Aligned(0)] + public uint MethodPointersCount; + + [VersionCondition(LessThan = "24.1")] + public Pointer MethodPointers; + + [Aligned(0)] + public uint ReversePInvokeWrapperCount; + + public Pointer ReversePInvokeWrappers; + + [VersionCondition(LessThan = "22.0"), Aligned(0)] + public uint DelegateWrappersFromManagedToNativeCount; + + [VersionCondition(LessThan = "22.0")] + public Pointer DelegateWrappersFromManagedToNative; + + [VersionCondition(LessThan = "22.0"), Aligned(0)] + public uint MarshalingFunctionsCount; + + [VersionCondition(LessThan = "22.0")] + public Pointer MarshalingFunctions; + + [VersionCondition(GreaterThan = "21.0", LessThan = "22.0"), Aligned(0)] + public uint CcwMarshalingFunctionsCount; + + [VersionCondition(GreaterThan = "21.0", LessThan = "22.0")] + public Pointer CcwMarshalingFunctions; + + [Aligned(0)] + public uint GenericMethodPointersCount; + + public Pointer GenericMethodPointers; + + [VersionCondition(EqualTo = "24.5")] + [VersionCondition(GreaterThan = "27.1")] + public Pointer GenericAdjustorThunks; + + [Aligned(0)] + public uint InvokerPointersCount; + + public Pointer InvokerPointers; + + [VersionCondition(LessThan = "24.5"), Aligned(0)] + public int CustomAttributeCount; + + [VersionCondition(LessThan = "24.5")] + public Pointer CustomAttributeGenerators; + + [VersionCondition(GreaterThan = "21.0", LessThan = "22.0"), Aligned(0)] + public int GuidCount; + + [VersionCondition(GreaterThan = "21.0", LessThan = "22.0")] + public Pointer Guids; + + [VersionCondition(GreaterThan = "22.0", LessThan = "29.0")] + public int UnresolvedVirtualCallCount; + + [VersionCondition(EqualTo = "29.1"), VersionCondition(EqualTo = "31.1")] + [VersionCondition(EqualTo = "29.2"), VersionCondition(EqualTo = "31.2")] + [Aligned(0)] + public uint UnresolvedIndirectCallCount; // UnresolvedVirtualCallCount pre 29.1 + + [VersionCondition(GreaterThan = "22.0")] + public Pointer UnresolvedVirtualCallPointers; + + [VersionCondition(EqualTo = "29.1"), VersionCondition(EqualTo = "31.1")] + [VersionCondition(EqualTo = "29.2"), VersionCondition(EqualTo = "31.2")] + public Pointer UnresolvedInstanceCallWrappers; + + [VersionCondition(EqualTo = "29.1"), VersionCondition(EqualTo = "31.1")] + [VersionCondition(EqualTo = "29.2"), VersionCondition(EqualTo = "31.2")] + public Pointer UnresolvedStaticCallPointers; + + [VersionCondition(GreaterThan = "23.0"), Aligned(0)] + public uint InteropDataCount; + + [VersionCondition(GreaterThan = "23.0")] + public Pointer InteropData; + + [VersionCondition(GreaterThan = "24.3"), Aligned(0)] + public uint WindowsRuntimeFactoryCount; + + [VersionCondition(GreaterThan = "24.3")] + public Pointer WindowsRuntimeFactoryTable; + + [VersionCondition(GreaterThan = "24.2"), Aligned(0)] + public uint CodeGenModulesCount; + + [VersionCondition(GreaterThan = "24.2")] + public Pointer> CodeGenModules; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericClass.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericClass.cs new file mode 100644 index 00000000..9259a0cb --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericClass.cs @@ -0,0 +1,18 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +[VersionedStruct] +public partial struct Il2CppGenericClass +{ + [VersionCondition(LessThan = "24.5"), Aligned(0)] + public int TypeDefinitionIndex; + + [VersionCondition(GreaterThan = "27.0")] + public Pointer Type; + + public Il2CppGenericContext Context; + + [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] + public ulong CachedClass; // Il2CppClass*, optional +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericContext.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericContext.cs new file mode 100644 index 00000000..1737bcf3 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericContext.cs @@ -0,0 +1,10 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +[VersionedStruct] +public partial struct Il2CppGenericContext +{ + public Pointer ClassInst; + public Pointer MethodInst; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericInst.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericInst.cs new file mode 100644 index 00000000..0a55c914 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericInst.cs @@ -0,0 +1,14 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +[VersionedStruct] +public partial struct Il2CppGenericInst +{ + public readonly bool Valid => TypeArgc > 0; + + [Aligned(0)] + public uint TypeArgc; + + public Pointer> TypeArgv; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericMethodFunctionsDefinitions.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericMethodFunctionsDefinitions.cs new file mode 100644 index 00000000..97478ef7 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericMethodFunctionsDefinitions.cs @@ -0,0 +1,12 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +using GenericMethodIndex = int; + +[VersionedStruct] +public partial struct Il2CppGenericMethodFunctionsDefinitions +{ + public GenericMethodIndex GenericMethodIndex; + public Il2CppGenericMethodIndices Indices; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericMethodIndices.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericMethodIndices.cs new file mode 100644 index 00000000..e18bf103 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericMethodIndices.cs @@ -0,0 +1,16 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +using MethodIndex = int; + +[VersionedStruct] +public partial struct Il2CppGenericMethodIndices +{ + public MethodIndex MethodIndex; + public MethodIndex InvokerIndex; + + [VersionCondition(EqualTo = "24.5")] + [VersionCondition(GreaterThan = "27.1")] + public MethodIndex AdjustorThunkIndex; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGuid.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGuid.cs new file mode 100644 index 00000000..341ab1ec --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGuid.cs @@ -0,0 +1,22 @@ +using VersionedSerialization; + +namespace Il2CppInspector.Next.BinaryMetadata; + + +public struct Il2CppGuid : IReadable +{ + public Guid Value; + + public void Read(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct + { + var guid = reader.ReadBytes(16); + Value = new Guid(guid, false); + } + + public static int Size(in StructVersion version = default, bool is32Bit = false) + { + return 16; + } + + public static implicit operator Guid(Il2CppGuid value) => value.Value; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppInteropData.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppInteropData.cs new file mode 100644 index 00000000..93055698 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppInteropData.cs @@ -0,0 +1,20 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +using PInvokeMarshalToNativeFunc = Il2CppMethodPointer; +using PInvokeMarshalFromNativeFunc = Il2CppMethodPointer; +using PInvokeMarshalCleanupFunc = Il2CppMethodPointer; +using CreateCCWFunc = Il2CppMethodPointer; + +[VersionedStruct] +public partial struct Il2CppInteropData +{ + public Il2CppMethodPointer DelegatePInvokeWrapperFunction; + public PInvokeMarshalToNativeFunc PInvokeMarshalToNativeFunction; + public PInvokeMarshalFromNativeFunc PInvokeMarshalFromNativeFunction; + public PInvokeMarshalCleanupFunc PInvokeMarshalCleanupFunction; + public CreateCCWFunc CreateCCWFunction; + public Pointer Guid; + public Pointer Type; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMetadataRegistration.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMetadataRegistration.cs new file mode 100644 index 00000000..2df65c91 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMetadataRegistration.cs @@ -0,0 +1,60 @@ +using Il2CppInspector.Next.Metadata; +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +using FieldIndex = int; +using TypeDefinitionIndex = int; + +[VersionedStruct] +public partial struct Il2CppMetadataRegistration +{ + [Aligned(0)] + public int GenericClassesCount; + + public Pointer> GenericClasses; + + [Aligned(0)] + public int GenericInstsCount; + + public Pointer> GenericInsts; + + [Aligned(0)] + public int GenericMethodTableCount; + + public Pointer GenericMethodTable; + + [Aligned(0)] + public int TypesCount; + + public Pointer> Types; + + [Aligned(0)] + public int MethodSpecsCount; + + public Pointer MethodSpecs; + + [VersionCondition(LessThan = "16.0")] + public int MethodReferencesCount; + + [VersionCondition(LessThan = "16.0")] + [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] + public ulong MethodReferences; // uint** + + [Aligned(0)] + public FieldIndex FieldOffsetsCount; + + [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] + public ulong FieldOffsets; // int** + + [Aligned(0)] + public TypeDefinitionIndex TypeDefinitionsSizesCount; + public Pointer> TypeDefinitionsSizes; + + [Aligned(0)] + [VersionCondition(GreaterThan = "19.0")] + public ulong MetadataUsagesCount; + + [VersionCondition(GreaterThan = "19.0")] + public Pointer> MetadataUsages; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMethodPointer.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMethodPointer.cs new file mode 100644 index 00000000..a6d10053 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMethodPointer.cs @@ -0,0 +1,19 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +[VersionedStruct] +public partial struct Il2CppMethodPointer(ulong addr = 0) +{ + public static readonly Il2CppMethodPointer Null = new(); + + [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] + public ulong Value { get; set; } = addr; + + public readonly bool IsNull => Value == 0; + + public readonly override string ToString() => $"0x{Value:X}"; + + public static implicit operator ulong(Il2CppMethodPointer ptr) => ptr.Value; + public static implicit operator Il2CppMethodPointer(ulong ptr) => new(ptr); +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMethodSpec.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMethodSpec.cs new file mode 100644 index 00000000..0fa7d047 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMethodSpec.cs @@ -0,0 +1,14 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +using MethodIndex = int; +using GenericInstIndex = int; + +[VersionedStruct] +public partial struct Il2CppMethodSpec +{ + public MethodIndex MethodDefinitionIndex; + public GenericInstIndex ClassIndexIndex; + public GenericInstIndex MethodIndexIndex; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRange.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRange.cs new file mode 100644 index 00000000..557861e9 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRange.cs @@ -0,0 +1,10 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +[VersionedStruct] +public partial struct Il2CppRange +{ + public int Start; + public int Length; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxConstrainedData.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxConstrainedData.cs new file mode 100644 index 00000000..f51ddd3f --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxConstrainedData.cs @@ -0,0 +1,13 @@ +using Il2CppInspector.Next.Metadata; +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +using TypeIndex = int; + +[VersionedStruct] +public partial struct Il2CppRgctxConstrainedData +{ + public TypeIndex TypeIndex; + public Il2CppMetadataUsage EncodedMethodIndex; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDataType.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDataType.cs new file mode 100644 index 00000000..fecde530 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDataType.cs @@ -0,0 +1,12 @@ +// ReSharper disable InconsistentNaming +namespace Il2CppInspector.Next.BinaryMetadata; + +public enum Il2CppRgctxDataType +{ + IL2CPP_RGCTX_DATA_INVALID, + IL2CPP_RGCTX_DATA_TYPE, + IL2CPP_RGCTX_DATA_CLASS, + IL2CPP_RGCTX_DATA_METHOD, + IL2CPP_RGCTX_DATA_ARRAY, + IL2CPP_RGCTX_DATA_CONSTRAINED, +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDefinition.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDefinition.cs new file mode 100644 index 00000000..0fb2efea --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDefinition.cs @@ -0,0 +1,16 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +[VersionedStruct] +public partial struct Il2CppRgctxDefinition +{ + [Aligned(0)] + public Il2CppRgctxDataType Type; + + [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] + public ulong Data; // void* + + public readonly Pointer Definition => Data; + public readonly Pointer Constrained => Data; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDefinitionData.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDefinitionData.cs new file mode 100644 index 00000000..a6b19a21 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDefinitionData.cs @@ -0,0 +1,15 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +using MethodIndex = int; +using TypeIndex = int; + +[VersionedStruct] +public partial struct Il2CppRgctxDefinitionData +{ + public int Value; + + public readonly MethodIndex MethodIndex => Value; + public readonly TypeIndex TypeIndex => Value; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenAdjustorThunkPair.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenAdjustorThunkPair.cs new file mode 100644 index 00000000..e6ac2c50 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenAdjustorThunkPair.cs @@ -0,0 +1,12 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +[VersionedStruct] +public partial struct Il2CppTokenAdjustorThunkPair +{ + [Aligned(0)] + public uint Token; + + public Il2CppMethodPointer AdjustorThunk; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenIndexMethodTuple.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenIndexMethodTuple.cs new file mode 100644 index 00000000..61099019 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenIndexMethodTuple.cs @@ -0,0 +1,15 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +[VersionedStruct] +public partial struct Il2CppTokenIndexMethodTuple +{ + public uint Token; + public int Index; + + [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] + public ulong Method; // void** + + public uint GenericMethodIndex; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenRangePair.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenRangePair.cs new file mode 100644 index 00000000..dfc7836c --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenRangePair.cs @@ -0,0 +1,10 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +[VersionedStruct] +public partial struct Il2CppTokenRangePair +{ + public uint Token; + public Il2CppRange Range; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppType.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppType.cs new file mode 100644 index 00000000..7b518ac9 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppType.cs @@ -0,0 +1,111 @@ +using Il2CppInspector.Next.Metadata; +using System.Reflection; +using VersionedSerialization; + +namespace Il2CppInspector.Next.BinaryMetadata; + +using TypeDefinitionIndex = int; +using GenericParameterIndex = int; +using Il2CppMetadataTypeHandle = Pointer; +using Il2CppMetadataGenericParameterHandle = Pointer; + +public record struct Il2CppType : IReadable +{ + public record struct DataUnion : IReadable + { + public ulong Value; + + public readonly TypeDefinitionIndex KlassIndex => (int)Value; + public readonly Il2CppMetadataTypeHandle TypeHandle => Value; + public readonly Pointer Type => Value; + public readonly Pointer ArrayType => Value; + public readonly GenericParameterIndex GenericParameterIndex => (int)Value; + public readonly Il2CppMetadataGenericParameterHandle GenericParameterHandle => Value; + public readonly Pointer GenericClass => Value; + + public void Read(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct + { + Value = reader.ReadNUInt(); + } + + public static int Size(in StructVersion version = default, bool is32Bit = false) + { + return is32Bit ? 4 : 8; + } + } + + public DataUnion Data; + public uint Value; + + public TypeAttributes Attrs + { + readonly get => (TypeAttributes)(Value & 0xFFFF); + set => Value = (Value & 0xFFFF0000) | (uint)value; + } + + public Il2CppTypeEnum Type + { + readonly get => (Il2CppTypeEnum)((Value >> 16) & 0b11111111); + set => Value = (Value & 0xFF00FFFF) | ((uint)value) << 16; + } + + public uint NumModifiers + { + readonly get => (Value >> 24) & 0b11111; + set => Value = (Value & 0xE0FFFFFF) | value << 24; + } + + public bool ByRef + { + readonly get => ((Value >> 29) & 1) == 1; + set => Value = (Value & 0xDFFFFFFF) | (value ? 1u : 0u) << 29; + } + + public bool Pinned + { + readonly get => ((Value >> 30) & 1) == 1; + set => Value = (Value & 0xBFFFFFFF) | (value ? 1u : 0u) << 30; + } + + public bool ValueType + { + readonly get => ((Value >> 31) & 1) == 1; + set => Value = (Value & 0x7FFFFFFF) | (value ? 1u : 0u) << 31; + } + + public void Read(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct + { + Data.Read(ref reader, version); + Value = reader.Read(); + + if (MetadataVersions.V272 > version) + { + // Versions pre-27.2 had NumModifiers at 6 bits and no ValueType bit + var numModifiers = (Value >> 24) & 0b111111; + + // If NumModifiers > 31, we throw here (as the old behavior isn't implemented + if (numModifiers > 31) + throw new InvalidOperationException( + "Versions pre-27.2 with a type having more than 31 modifiers are not supported yet"); + + // Else, we do some bit-juggling to convert the old value into the new format: + Value = + (Value & 0xFFFFFF) | // Attributes + Type + (((Value >> 24) & 0b111111) << 24) | // 5 Bits for the modifiers + (((Value >> 30) & 1) << 29) | // Shifted ByRef + (((Value >> 31) & 1) << 30) | // Shifted Pinned + 0; // 0 ValueType + } + } + + public static int Size(in StructVersion version = default, bool is32Bit = false) + { + return DataUnion.Size(version, is32Bit) + sizeof(uint); + } + + public static Il2CppType FromTypeEnum(Il2CppTypeEnum type) + => new() + { + Value = (uint)type << 16 + }; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTypeDefinitionSizes.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTypeDefinitionSizes.cs new file mode 100644 index 00000000..dc46ae4a --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTypeDefinitionSizes.cs @@ -0,0 +1,12 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +[VersionedStruct] +public partial struct Il2CppTypeDefinitionSizes +{ + public uint InstanceSize; + public int NativeSize; + public uint StaticFieldsSize; + public uint ThreadStaticFieldsSize; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTypeEnum.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTypeEnum.cs new file mode 100644 index 00000000..0c441424 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTypeEnum.cs @@ -0,0 +1,45 @@ +// ReSharper disable InconsistentNaming +namespace Il2CppInspector.Next.BinaryMetadata; + +public enum Il2CppTypeEnum : byte +{ + Il2CPP_TYPE_END, + IL2CPP_TYPE_VOID, + IL2CPP_TYPE_BOOLEAN, + IL2CPP_TYPE_CHAR, + IL2CPP_TYPE_I1, + IL2CPP_TYPE_U1, + IL2CPP_TYPE_I2, + IL2CPP_TYPE_U2, + IL2CPP_TYPE_I4, + IL2CPP_TYPE_U4, + IL2CPP_TYPE_I8, + IL2CPP_TYPE_U8, + IL2CPP_TYPE_R4, + IL2CPP_TYPE_R8, + IL2CPP_TYPE_STRING, + IL2CPP_TYPE_PTR, + IL2CPP_TYPE_BYREF, + IL2CPP_TYPE_VALUETYPE, + IL2CPP_TYPE_CLASS, + IL2CPP_TYPE_VAR, + IL2CPP_TYPE_ARRAY, + IL2CPP_TYPE_GENERICINST, + IL2CPP_TYPE_TYPEDBYREF, + IL2CPP_TYPE_I = 0x18, + IL2CPP_TYPE_U, + IL2CPP_TYPE_FNPTR = 0x1b, + IL2CPP_TYPE_OBJECT, + IL2CPP_TYPE_SZARRAY, + IL2CPP_TYPE_MVAR, + IL2CPP_TYPE_CMOD_REQD, + IL2CPP_TYPE_CMOD_OPT, + IL2CPP_TYPE_INTERNAL, + + IL2CPP_TYPE_MODIFIER = 0x40, + IL2CPP_TYPE_SENTINEL = 0x41, + IL2CPP_TYPE_PINNED = 0x45, + + IL2CPP_TYPE_ENUM = 0x55, + IL2CPP_TYPE_IL2CPP_TYPE_INDEX = 0xff +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppWindowsRuntimeFactoryTableEntry.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppWindowsRuntimeFactoryTableEntry.cs new file mode 100644 index 00000000..1c42edc9 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppWindowsRuntimeFactoryTableEntry.cs @@ -0,0 +1,10 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.BinaryMetadata; + +[VersionedStruct] +public partial struct Il2CppWindowsRuntimeFactoryTableEntry +{ + public Pointer Type; + public Il2CppMethodPointer CreateFactoryFunction; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyDefinition.cs new file mode 100644 index 00000000..ca62195a --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyDefinition.cs @@ -0,0 +1,37 @@ +using System.Runtime.InteropServices; +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.Metadata; + +using ImageIndex = int; + +[VersionedStruct] +[StructLayout(LayoutKind.Explicit)] +public partial struct Il2CppAssemblyDefinition +{ + [FieldOffset(20)] + [VersionCondition(LessThan = "15.0")] + public Il2CppAssemblyNameDefinition LegacyAname; + + [FieldOffset(0)] + public ImageIndex ImageIndex; + + [FieldOffset(4)] + [VersionCondition(GreaterThan = "24.1")] + public uint Token; + + [FieldOffset(8)] + [VersionCondition(LessThan = "24.0")] + public int CustomAttributeIndex; + + [FieldOffset(12)] + [VersionCondition(GreaterThan = "20.0")] + public int ReferencedAssemblyStart; + + [FieldOffset(16)] + [VersionCondition(GreaterThan = "20.0")] + public int ReferencedAssemblyCount; + + [FieldOffset(20)] + public Il2CppAssemblyNameDefinition Aname; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyNameDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyNameDefinition.cs new file mode 100644 index 00000000..f93ab692 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyNameDefinition.cs @@ -0,0 +1,64 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.Metadata; + +using StringIndex = int; + +[InlineArray(PublicKeyLength)] +public struct PublicKeyToken +{ + private const int PublicKeyLength = 8; + + private byte _value; +} + +[VersionedStruct] +[StructLayout(LayoutKind.Explicit)] +public partial struct Il2CppAssemblyNameDefinition +{ + [FieldOffset(0)] + public StringIndex NameIndex; + + [FieldOffset(4)] + public StringIndex CultureIndex; + + [FieldOffset(8)] + [VersionCondition(LessThan = "24.3")] + public int HashValueIndex; + + [FieldOffset(12)] + public StringIndex PublicKeyIndex; + + [FieldOffset(44)] + [VersionCondition(LessThan = "15.0")] + [CustomSerialization("reader.Read();", "8")] + private PublicKeyToken _legacyPublicKeyToken; + + [FieldOffset(16)] + public AssemblyHashAlgorithm HashAlg; + + [FieldOffset(20)] + public int HashLen; + + [FieldOffset(24)] + public AssemblyNameFlags Flags; + + [FieldOffset(28)] + public int Major; + + [FieldOffset(32)] + public int Minor; + + [FieldOffset(36)] + public int Build; + + [FieldOffset(40)] + public int Revision; + + [FieldOffset(44)] + [CustomSerialization("reader.Read();", "8")] + public PublicKeyToken PublicKeyToken; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppCustomAttributeDataRange.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppCustomAttributeDataRange.cs new file mode 100644 index 00000000..a38db962 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppCustomAttributeDataRange.cs @@ -0,0 +1,10 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.Metadata; + +[VersionedStruct] +public partial struct Il2CppCustomAttributeDataRange +{ + public uint Token { get; private set; } + public uint StartOffset { get; private set; } +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppCustomAttributeTypeRange.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppCustomAttributeTypeRange.cs new file mode 100644 index 00000000..4be20de3 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppCustomAttributeTypeRange.cs @@ -0,0 +1,13 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.Metadata; + +[VersionedStruct] +public partial struct Il2CppCustomAttributeTypeRange +{ + [VersionCondition(GreaterThan = "24.1")] + public uint Token { get; private set; } + + public int Start { get; private set; } + public int Count { get; private set; } +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppEventDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppEventDefinition.cs new file mode 100644 index 00000000..7658f5bf --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppEventDefinition.cs @@ -0,0 +1,24 @@ +namespace Il2CppInspector.Next.Metadata; + +using StringIndex = int; +using TypeIndex = int; +using MethodIndex = int; +using VersionedSerialization.Attributes; + +[VersionedStruct] +public partial struct Il2CppEventDefinition +{ + public StringIndex NameIndex { get; private set; } + public TypeIndex TypeIndex { get; private set; } + public MethodIndex Add { get; private set; } + public MethodIndex Remove { get; private set; } + public MethodIndex Raise { get; private set; } + + [VersionCondition(LessThan = "24.0")] + public int CustomAttributeIndex { get; private set; } + + [VersionCondition(GreaterThan = "19.0")] + public uint Token { get; private set; } + + public readonly bool IsValid => NameIndex != 0; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppFieldDefaultValue.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppFieldDefaultValue.cs new file mode 100644 index 00000000..ed984e29 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppFieldDefaultValue.cs @@ -0,0 +1,15 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.Metadata; + +using FieldIndex = int; +using TypeIndex = int; +using DefaultValueDataIndex = int; + +[VersionedStruct] +public partial struct Il2CppFieldDefaultValue +{ + public FieldIndex FieldIndex { get; private set; } + public TypeIndex TypeIndex { get; private set; } + public DefaultValueDataIndex DataIndex { get; private set; } +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppFieldDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppFieldDefinition.cs new file mode 100644 index 00000000..84d0ef48 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppFieldDefinition.cs @@ -0,0 +1,20 @@ +namespace Il2CppInspector.Next.Metadata; + +using VersionedSerialization.Attributes; +using StringIndex = int; +using TypeIndex = int; + +[VersionedStruct] +public partial struct Il2CppFieldDefinition +{ + public StringIndex NameIndex { get; private set; } + public TypeIndex TypeIndex { get; private set; } + + [VersionCondition(LessThan = "24.0")] + public int CustomAttributeIndex { get; private set; } + + [VersionCondition(GreaterThan = "19.0")] + public uint Token { get; private set; } + + public readonly bool IsValid => NameIndex != 0; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppFieldMarshaledSize.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppFieldMarshaledSize.cs new file mode 100644 index 00000000..bc71ad7c --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppFieldMarshaledSize.cs @@ -0,0 +1,13 @@ +namespace Il2CppInspector.Next.Metadata; + +using VersionedSerialization.Attributes; +using FieldIndex = int; +using TypeIndex = int; + +[VersionedStruct] +public partial struct Il2CppFieldMarshaledSize +{ + public FieldIndex FieldIndex { get; private set; } + public TypeIndex TypeIndex { get; private set; } + public int MarshaledSize { get; private set; } +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppFieldRef.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppFieldRef.cs new file mode 100644 index 00000000..d02500d3 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppFieldRef.cs @@ -0,0 +1,13 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.Metadata; + +using FieldIndex = int; +using TypeIndex = int; + +[VersionedStruct] +public partial struct Il2CppFieldRef +{ + public TypeIndex TypeIndex { get; private set; } + public FieldIndex FieldIndex { get; private set; } +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppGenericContainer.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppGenericContainer.cs new file mode 100644 index 00000000..43421c36 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppGenericContainer.cs @@ -0,0 +1,13 @@ +namespace Il2CppInspector.Next.Metadata; + +using VersionedSerialization.Attributes; +using GenericParameterIndex = int; + +[VersionedStruct] +public partial struct Il2CppGenericContainer +{ + public int OwnerIndex { get; private set; } + public int TypeArgc { get; private set; } + public int IsMethod { get; private set; } + public GenericParameterIndex GenericParameterStart { get; private set; } +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppGenericParameter.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppGenericParameter.cs new file mode 100644 index 00000000..beaa94a8 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppGenericParameter.cs @@ -0,0 +1,21 @@ +using System.Reflection; +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.Metadata; + +using GenericContainerIndex = int; +using StringIndex = int; +using GenericParameterConstraintIndex = short; + +[VersionedStruct] +public partial struct Il2CppGenericParameter +{ + public GenericContainerIndex OwnerIndex { get; private set; } + public StringIndex NameIndex { get; private set; } + public GenericParameterConstraintIndex ConstraintsStart { get; private set; } + public short ConstraintsCount { get; private set; } + public ushort Num { get; private set; } + public ushort Flags { get; private set; } + + public readonly GenericParameterAttributes Attributes => (GenericParameterAttributes)Flags; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppGlobalMetadataHeader.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppGlobalMetadataHeader.cs new file mode 100644 index 00000000..d137be35 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppGlobalMetadataHeader.cs @@ -0,0 +1,182 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.Metadata; + +// Unity 4.6.1p5 - first release, no global-metadata.dat +// Unity 5.2.0f3 -> v15 +// Unity 5.3.0f4 -> v16 +// Unity 5.3.2f1 -> v19 +// Unity 5.3.3f1 -> v20 +// Unity 5.3.5f1 -> v21 +// Unity 5.5.0f3 -> v22 +// Unity 5.6.0f3 -> v23 +// Unity 2017.1.0f3 -> v24 +// Unity 2018.3.0f2 -> v24.1 +// Unity 2019.1.0f2 -> v24.2 +// Unity 2019.3.7f1 -> v24.3 +// Unity 2019.4.15f1 -> v24.4 +// Unity 2019.4.21f1 -> v24.5 +// Unity 2020.1.0f1 -> v24.3 +// Unity 2020.1.11f1 -> v24.4 +// Unity 2020.2.0f1 -> v27 +// Unity 2020.2.4f1 -> v27.1 +// Unity 2021.1.0f1 -> v27.2 +// https://unity3d.com/get-unity/download/archive +// Metadata version is written at the end of Unity.IL2CPP.MetadataCacheWriter.WriteLibIl2CppMetadata or WriteMetadata (Unity.IL2CPP.dll) + +[VersionedStruct] +public partial struct Il2CppGlobalMetadataHeader +{ + public int Sanity { get; private set; } + public int Version { get; private set; } + public int StringLiteralOffset { get; private set; } + public int StringLiteralSize { get; private set; } + public int StringLiteralDataOffset { get; private set; } + public int StringLiteralDataSize { get; private set; } + public int StringOffset { get; private set; } + public int StringSize { get; private set; } + public int EventsOffset { get; private set; } + public int EventsSize { get; private set; } + public int PropertiesOffset { get; private set; } + public int PropertiesSize { get; private set; } + public int MethodsOffset { get; private set; } + public int MethodsSize { get; private set; } + + [VersionCondition(GreaterThan = "16.0")] + [VersionCondition(EqualTo = "16.0")] + public int ParameterDefaultValuesOffset { get; private set; } + + [VersionCondition(GreaterThan = "16.0")] + [VersionCondition(EqualTo = "16.0")] + public int ParameterDefaultValuesSize { get; private set; } + public int FieldDefaultValuesOffset { get; private set; } + public int FieldDefaultValuesSize { get; private set; } + public int FieldAndParameterDefaultValueDataOffset { get; private set; } + public int FieldAndParameterDefaultValueDataSize { get; private set; } + + [VersionCondition(GreaterThan = "16.0")] + public int FieldMarshaledSizesOffset { get; private set; } + + [VersionCondition(GreaterThan = "16.0")] + public int FieldMarshaledSizesSize { get; private set; } + public int ParametersOffset { get; private set; } + public int ParametersSize { get; private set; } + public int FieldsOffset { get; private set; } + public int FieldsSize { get; private set; } + public int GenericParametersOffset { get; private set; } + public int GenericParametersSize { get; private set; } + public int GenericParameterConstraintsOffset { get; private set; } + public int GenericParameterConstraintsSize { get; private set; } + public int GenericContainersOffset { get; private set; } + public int GenericContainersSize { get; private set; } + public int NestedTypesOffset { get; private set; } + public int NestedTypesSize { get; private set; } + public int InterfacesOffset { get; private set; } + public int InterfacesSize { get; private set; } + public int VTableMethodsOffset { get; private set; } + public int VTableMethodsSize { get; private set; } + public int InterfaceOffsetsOffset { get; private set; } + public int InterfaceOffsetsSize { get; private set; } + public int TypeDefinitionsOffset { get; private set; } + public int TypeDefinitionsSize { get; private set; } + + [VersionCondition(LessThan = "24.1")] + public int RgctxEntriesOffset { get; private set; } + + [VersionCondition(LessThan = "24.1")] + public int RgctxEntriesCount { get; private set; } + + [VersionCondition(GreaterThan = "16.0")] + public int ImagesOffset { get; private set; } + + [VersionCondition(GreaterThan = "16.0")] + public int ImagesSize { get; private set; } + + [VersionCondition(GreaterThan = "16.0")] + public int AssembliesOffset { get; private set; } + + [VersionCondition(GreaterThan = "16.0")] + public int AssembliesSize { get; private set; } + + [VersionCondition(GreaterThan = "19.0", LessThan = "24.5")] + public int MetadataUsageListsOffset { get; private set; } + + [VersionCondition(GreaterThan = "19.0", LessThan = "24.5")] + public int MetadataUsageListsCount { get; private set; } + + [VersionCondition(GreaterThan = "19.0", LessThan = "24.5")] + public int MetadataUsagePairsOffset { get; private set; } + + [VersionCondition(GreaterThan = "19.0", LessThan = "24.5")] + public int MetadataUsagePairsCount { get; private set; } + + [VersionCondition(GreaterThan = "19.0")] + public int FieldRefsOffset { get; private set; } + + [VersionCondition(GreaterThan = "19.0")] + public int FieldRefsSize { get; private set; } + + [VersionCondition(GreaterThan = "20.0")] + public int ReferencedAssembliesOffset { get; private set; } + + [VersionCondition(GreaterThan = "20.0")] + public int ReferencedAssembliesSize { get; private set; } + + [VersionCondition(GreaterThan = "21.0", LessThan = "27.2")] + public int AttributesInfoOffset { get; private set; } + + [VersionCondition(GreaterThan = "21.0", LessThan = "27.2")] + public int AttributesInfoCount { get; private set; } + + [VersionCondition(GreaterThan = "21.0", LessThan = "27.2")] + public int AttributesTypesOffset { get; private set; } + + [VersionCondition(GreaterThan = "21.0", LessThan = "27.2")] + public int AttributesTypesCount { get; private set; } + + [VersionCondition(GreaterThan = "29.0")] + public int AttributeDataOffset { get; private set; } + + [VersionCondition(GreaterThan = "29.0")] + public int AttributeDataSize { get; private set; } + + [VersionCondition(GreaterThan = "29.0")] + public int AttributeDataRangeOffset { get; private set; } + + [VersionCondition(GreaterThan = "29.0")] + public int AttributeDataRangeSize { get; private set; } + + [VersionCondition(GreaterThan = "22.0")] + public int UnresolvedIndirectCallParameterTypesOffset { get; private set; } + + [VersionCondition(GreaterThan = "22.0")] + public int UnresolvedIndirectCallParameterTypesSize { get; private set; } + + [VersionCondition(GreaterThan = "22.0")] + public int UnresolvedIndirectCallParameterRangesOffset { get; private set; } + + [VersionCondition(GreaterThan = "22.0")] + public int UnresolvedIndirectCallParameterRangesSize { get; private set; } + + [VersionCondition(GreaterThan = "23.0")] + public int WindowsRuntimeTypeNamesOffset { get; private set; } + + [VersionCondition(GreaterThan = "23.0")] + public int WindowsRuntimeTypeNamesSize { get; private set; } + + [VersionCondition(GreaterThan = "27.0")] + public int WindowsRuntimeStringsOffset { get; private set; } + + [VersionCondition(GreaterThan = "27.0")] + public int WindowsRuntimeStringsSize { get; private set; } + + [VersionCondition(GreaterThan = "24.0")] + public int ExportedTypeDefinitionsOffset { get; private set; } + + [VersionCondition(GreaterThan = "24.0")] + public int ExportedTypeDefinitionsSize { get; private set; } + + + public const int ExpectedSanity = unchecked((int)0xFAB11BAF); + public readonly bool SanityValid => Sanity == ExpectedSanity; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppImageDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppImageDefinition.cs new file mode 100644 index 00000000..56bc753f --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppImageDefinition.cs @@ -0,0 +1,37 @@ +namespace Il2CppInspector.Next.Metadata; + +using StringIndex = int; +using AssemblyIndex = int; +using TypeDefinitionIndex = int; +using MethodIndex = int; +using CustomAttributeIndex = int; +using VersionedSerialization.Attributes; + +[VersionedStruct] +public partial struct Il2CppImageDefinition +{ + public StringIndex NameIndex { get; private set; } + public AssemblyIndex AssemblyIndex { get; private set; } + + public TypeDefinitionIndex TypeStart { get; private set; } + public uint TypeCount { get; private set; } + + [VersionCondition(GreaterThan = "24.0")] + public TypeDefinitionIndex ExportedTypeStart { get; private set; } + + [VersionCondition(GreaterThan = "24.0")] + public uint ExportedTypeCount { get; private set; } + + public MethodIndex EntryPointIndex { get; private set; } + + [VersionCondition(GreaterThan = "19.0")] + public uint Token { get; private set; } + + [VersionCondition(GreaterThan = "24.1")] + public CustomAttributeIndex CustomAttributeStart { get; private set; } + + [VersionCondition(GreaterThan = "24.1")] + public uint CustomAttributeCount { get; private set; } + + public readonly bool IsValid => NameIndex != 0; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppInterfaceOffsetPair.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppInterfaceOffsetPair.cs new file mode 100644 index 00000000..93337b2a --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppInterfaceOffsetPair.cs @@ -0,0 +1,12 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.Metadata; + +using TypeIndex = int; + +[VersionedStruct] +public partial struct Il2CppInterfaceOffsetPair +{ + public TypeIndex InterfaceTypeIndex { get; private set; } + public int Offset { get; private set; } +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataRange.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataRange.cs new file mode 100644 index 00000000..4ab7a243 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataRange.cs @@ -0,0 +1,10 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.Metadata; + +[VersionedStruct] +public partial struct Il2CppMetadataRange +{ + public int Start { get; private set; } + public int Length { get; private set; } +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsage.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsage.cs new file mode 100644 index 00000000..70855f1b --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsage.cs @@ -0,0 +1,64 @@ +using System.Diagnostics; +using VersionedSerialization; + +namespace Il2CppInspector.Next.Metadata; + +using VersionedSerialization.Attributes; +using EncodedMethodIndex = uint; + +[VersionedStruct] +public partial struct Il2CppMetadataUsage +{ + private const uint TypeMask = 0b111u << 29; + private const uint InflatedMask = 0b1; + private const uint IndexMask = ~(TypeMask | InflatedMask); + + public readonly Il2CppMetadataUsageType Type => (Il2CppMetadataUsageType)((EncodedValue & TypeMask) >> 29); + public readonly uint Index => (EncodedValue & IndexMask) >> 1; + public readonly bool Inflated => (EncodedValue & InflatedMask) == 1; + + public EncodedMethodIndex EncodedValue; + + public static Il2CppMetadataUsage FromValue(in StructVersion version, uint encodedValue) + { + if (version >= MetadataVersions.V270) + { + return new Il2CppMetadataUsage + { + EncodedValue = encodedValue + }; + } + + if (version >= MetadataVersions.V190) + { + // Below v27 we need to fake the 'inflated' flag, so shift the value by one + + var type = (encodedValue & TypeMask) >> 29; + var value = encodedValue & (IndexMask | 1); + Debug.Assert((value & 0x10000000) == 0); + + return new Il2CppMetadataUsage + { + EncodedValue = (type << 29) | (value << 1) + }; + } + + /* These encoded indices appear only in vtables, and are decoded by IsGenericMethodIndex/GetDecodedMethodIndex */ + var methodType = (encodedValue >> 31) != 0 + ? Il2CppMetadataUsageType.MethodRef + : Il2CppMetadataUsageType.MethodDef; + + var index = encodedValue & 0x7FFFFFFF; + Debug.Assert((index & 0x60000000) == 0); + + return new Il2CppMetadataUsage + { + EncodedValue = ((uint)methodType << 29) | (index << 1) + }; + } + + public readonly override string ToString() + { + return $"{Type} @ 0x{Index:X}"; + } +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsageList.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsageList.cs new file mode 100644 index 00000000..517a05a6 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsageList.cs @@ -0,0 +1,10 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.Metadata; + +[VersionedStruct] +public partial struct Il2CppMetadataUsageList +{ + public int Start { get; private set; } + public int Count { get; private set; } +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsagePair.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsagePair.cs new file mode 100644 index 00000000..b5b2359a --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsagePair.cs @@ -0,0 +1,10 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.Metadata; + +[VersionedStruct] +public partial struct Il2CppMetadataUsagePair +{ + public uint DestinationIndex { get; private set; } + public uint EncodedSourceIndex { get; private set; } +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsageType.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsageType.cs new file mode 100644 index 00000000..485036e3 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsageType.cs @@ -0,0 +1,13 @@ +namespace Il2CppInspector.Next.Metadata; + +public enum Il2CppMetadataUsageType +{ + Invalid = 0b000, + TypeInfo = 0b001, + Il2CppType = 0b010, + MethodDef = 0b011, + FieldInfo = 0b100, + StringLiteral = 0b101, + MethodRef = 0b110, + FieldRva = 0b111, +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppMethodDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppMethodDefinition.cs new file mode 100644 index 00000000..360f0c59 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppMethodDefinition.cs @@ -0,0 +1,62 @@ +using System.Reflection; +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.Metadata; + +using StringIndex = int; +using TypeDefinitionIndex = int; +using TypeIndex = int; +using ParameterIndex = int; +using GenericContainerIndex = int; + +[VersionedStruct] +public partial struct Il2CppMethodDefinition +{ + public StringIndex NameIndex { get; private set; } + + [VersionCondition(GreaterThan = "16.0")] + public TypeDefinitionIndex DeclaringType { get; private set; } + public TypeIndex ReturnType { get; private set; } + + [VersionCondition(EqualTo = "31.0")] + [VersionCondition(EqualTo = "31.1")] + [VersionCondition(EqualTo = "31.2")] + public uint ReturnParameterToken { get; private set; } + + public ParameterIndex ParameterStart { get; private set; } + + [VersionCondition(LessThan = "24.0")] + public int CustomAttributeIndex { get; private set; } + + public GenericContainerIndex GenericContainerIndex { get; private set; } + + [VersionCondition(LessThan = "24.1")] + public int MethodIndex { get; private set; } + + [VersionCondition(LessThan = "24.1")] + public int InvokerIndex { get; private set; } + + [VersionCondition(LessThan = "24.1")] + public int ReversePInvokeWrapperIndex { get; private set; } + + [VersionCondition(LessThan = "24.1")] + public int RgctxStartIndex { get; private set; } + + [VersionCondition(LessThan = "24.1")] + public int RgctxCount { get; private set; } + + public uint Token { get; private set; } + public ushort Flags { get; private set; } + public ushort ImplFlags { get; private set; } + public ushort Slot { get; private set; } + public ushort ParameterCount { get; private set; } + + [VersionCondition(EqualTo = "29.2")] + [VersionCondition(EqualTo = "31.2")] + public bool IsUnmanagedCallersOnly { get; private set; } + + public readonly MethodAttributes Attributes => (MethodAttributes)Flags; + public readonly MethodImplAttributes ImplAttributes => (MethodImplAttributes)ImplFlags; + + public readonly bool IsValid => NameIndex != 0; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppParameterDefaultValue.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppParameterDefaultValue.cs new file mode 100644 index 00000000..1a3e4737 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppParameterDefaultValue.cs @@ -0,0 +1,14 @@ +namespace Il2CppInspector.Next.Metadata; + +using ParameterIndex = int; +using TypeIndex = int; +using DefaultValueDataIndex = int; +using VersionedSerialization.Attributes; + +[VersionedStruct] +public partial struct Il2CppParameterDefaultValue +{ + public ParameterIndex ParameterIndex { get; private set; } + public TypeIndex TypeIndex { get; private set; } + public DefaultValueDataIndex DataIndex { get; private set; } +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppParameterDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppParameterDefinition.cs new file mode 100644 index 00000000..a66af4d9 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppParameterDefinition.cs @@ -0,0 +1,19 @@ +namespace Il2CppInspector.Next.Metadata; + +using VersionedSerialization.Attributes; +using StringIndex = int; +using TypeIndex = int; + +[VersionedStruct] +public partial struct Il2CppParameterDefinition +{ + public StringIndex NameIndex { get; private set; } + public uint Token { get; private set; } + + [VersionCondition(LessThan = "24.0")] + public int CustomAttributeIndex { get; private set; } + + public TypeIndex TypeIndex { get; private set; } + + public readonly bool IsValid => NameIndex != 0; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppPropertyDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppPropertyDefinition.cs new file mode 100644 index 00000000..6ba2ad85 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppPropertyDefinition.cs @@ -0,0 +1,24 @@ +using System.Reflection; +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.Metadata; + +using StringIndex = int; +using MethodIndex = int; + +[VersionedStruct] +public partial struct Il2CppPropertyDefinition +{ + public StringIndex NameIndex { get; private set; } + public MethodIndex Get { get; private set; } + public MethodIndex Set { get; private set; } + public PropertyAttributes Attrs { get; private set; } + + [VersionCondition(LessThan = "24.0")] + public int CustomAttributeIndex { get; private set; } + + [VersionCondition(GreaterThan = "19.0")] + public uint Token { get; private set; } + + public readonly bool IsValid => NameIndex != 0; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppStringLiteral.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppStringLiteral.cs new file mode 100644 index 00000000..93f4ac69 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppStringLiteral.cs @@ -0,0 +1,11 @@ +namespace Il2CppInspector.Next.Metadata; + +using VersionedSerialization.Attributes; +using StringLiteralIndex = int; + +[VersionedStruct] +public partial struct Il2CppStringLiteral +{ + public uint Length { get; private set; } + public StringLiteralIndex DataIndex { get; private set; } +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppTypeDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppTypeDefinition.cs new file mode 100644 index 00000000..cb89a97b --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppTypeDefinition.cs @@ -0,0 +1,83 @@ +using System.Reflection; +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.Metadata; + +using StringIndex = int; +using TypeIndex = int; +using GenericContainerIndex = int; +using FieldIndex = int; +using MethodIndex = int; +using EventIndex = int; +using PropertyIndex = int; +using NestedTypeIndex = int; +using InterfacesIndex = int; +using VTableIndex = int; + +[VersionedStruct] +public partial struct Il2CppTypeDefinition +{ + public const TypeIndex InvalidTypeIndex = -1; + + public StringIndex NameIndex { get; private set; } + public StringIndex NamespaceIndex { get; private set; } + + [VersionCondition(LessThan = "24.0")] + public int CustomAttributeIndex { get; private set; } + + public TypeIndex ByValTypeIndex { get; private set; } + + [VersionCondition(LessThan = "24.5")] + public TypeIndex ByRefTypeIndex { get; private set; } + + public TypeIndex DeclaringTypeIndex { get; private set; } + public TypeIndex ParentIndex { get; private set; } + public TypeIndex ElementTypeIndex { get; private set; } + + [VersionCondition(LessThan = "24.1")] + public int RgctxStartIndex { get; private set; } + + [VersionCondition(LessThan = "24.1")] + public int RgctxCount { get; private set; } + + public GenericContainerIndex GenericContainerIndex { get; private set; } + + [VersionCondition(LessThan = "22.0")] + public int ReversePInvokeWrapperIndex { get; private set; } + + [VersionCondition(LessThan = "22.0")] + public int MarshalingFunctionsIndex { get; private set; } + + [VersionCondition(GreaterThan = "21.0", LessThan = "22.0")] + public int CcwFunctionIndex { get; private set; } + + [VersionCondition(GreaterThan = "21.0", LessThan = "22.0")] + public int GuidIndex { get; private set; } + + public TypeAttributes Flags { get; private set; } + + public FieldIndex FieldIndex { get; private set; } + public MethodIndex MethodIndex { get; private set; } + public EventIndex EventIndex { get; private set; } + public PropertyIndex PropertyIndex { get; private set; } + public NestedTypeIndex NestedTypeIndex { get; private set; } + public InterfacesIndex InterfacesIndex { get; private set; } + public VTableIndex VTableIndex { get; private set; } + public InterfacesIndex InterfaceOffsetsStart { get; private set; } + + public ushort MethodCount { get; private set; } + public ushort PropertyCount { get; private set; } + public ushort FieldCount { get; private set; } + public ushort EventCount { get; private set; } + public ushort NestedTypeCount { get; private set; } + public ushort VTableCount { get; private set; } + public ushort InterfacesCount { get; private set; } + public ushort InterfaceOffsetsCount { get; private set; } + + public Il2CppTypeDefinitionBitfield Bitfield { get; private set; } + + [VersionCondition(GreaterThan = "19.0")] + public uint Token { get; private set; } + + public readonly bool IsValid => NameIndex != 0; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppTypeDefinitionBitfield.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppTypeDefinitionBitfield.cs new file mode 100644 index 00000000..28875b89 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppTypeDefinitionBitfield.cs @@ -0,0 +1,21 @@ +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next.Metadata; + +[VersionedStruct] +public partial struct Il2CppTypeDefinitionBitfield +{ + private uint _value; + + public bool ValueType => ((_value >> 0) & 1) == 1; + public bool EnumType => ((_value >> 1) & 1) == 1; + public bool HasFinalize => ((_value >> 2) & 1) == 1; + public bool HasCctor => ((_value >> 3) & 1) == 1; + public bool IsBlittable => ((_value >> 4) & 1) == 1; + public bool IsImportOrWindowsRuntime => ((_value >> 5) & 1) == 1; + public PackingSize PackingSize => (PackingSize)((_value >> 6) & 0b1111); + public bool DefaultPackingSize => ((_value >> 10) & 1) == 1; + public bool DefaultClassSize => ((_value >> 11) & 1) == 1; + public PackingSize ClassSize => (PackingSize)((_value >> 12) & 0b1111); + public bool IsByRefLike => ((_value >> 13) & 1) == 1; +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppWindowsRuntimeTypeNamePair.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppWindowsRuntimeTypeNamePair.cs new file mode 100644 index 00000000..217a2129 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppWindowsRuntimeTypeNamePair.cs @@ -0,0 +1,12 @@ +namespace Il2CppInspector.Next.Metadata; + +using VersionedSerialization.Attributes; +using StringIndex = int; +using TypeIndex = int; + +[VersionedStruct] +public partial struct Il2CppWindowsRuntimeTypeNamePair +{ + public StringIndex NameIndex { get; private set; } + public TypeIndex TypeIndex { get; private set; } +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/PackingSize.cs b/Il2CppInspector.Common/Next/Metadata/PackingSize.cs new file mode 100644 index 00000000..59380562 --- /dev/null +++ b/Il2CppInspector.Common/Next/Metadata/PackingSize.cs @@ -0,0 +1,14 @@ +namespace Il2CppInspector.Next.Metadata; + +public enum PackingSize +{ + Zero, + One, + Two, + Four, + Eight, + Sixteen, + ThirtyTwo, + SixtyFour, + OneHundredTwentyEight +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Pointer.cs b/Il2CppInspector.Common/Next/Pointer.cs new file mode 100644 index 00000000..a3a11130 --- /dev/null +++ b/Il2CppInspector.Common/Next/Pointer.cs @@ -0,0 +1,60 @@ +using System.Collections.Immutable; +using VersionedSerialization; +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next; + +public struct Pointer(ulong value = 0) : IReadable, IEquatable> where T : IReadable, new() +{ + [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] + private ulong _value = value; + + public readonly ulong PointerValue => _value; + public readonly bool Null => _value == 0; + + public void Read(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct + { + _value = reader.ReadNUInt(); + } + + public static int Size(in StructVersion version = default, bool is32Bit = false) + { + return is32Bit ? 4 : 8; + } + + public readonly T Read(ref SpanReader reader, in StructVersion version) + { + reader.Offset = (int)PointerValue; + return reader.ReadObject(version); + } + + public readonly ImmutableArray ReadArray(ref SpanReader reader, long count, in StructVersion version) + { + reader.Offset = (int)PointerValue; + return reader.ReadObjectArray(count, version); + } + + public static implicit operator Pointer(ulong value) => new(value); + public static implicit operator ulong(Pointer ptr) => ptr.PointerValue; + + #region Equality operators + ToString + + public static bool operator ==(Pointer left, Pointer right) + => left._value == right._value; + + public static bool operator !=(Pointer left, Pointer right) + => !(left == right); + + public readonly override bool Equals(object? obj) + => obj is Pointer other && Equals(other); + + public readonly bool Equals(Pointer other) + => this == other; + + public readonly override int GetHashCode() + => HashCode.Combine(_value); + + public readonly override string ToString() => $"0x{_value:X} <{typeof(T).Name}>"; + + #endregion +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Outputs/AssemblyShims.cs b/Il2CppInspector.Common/Outputs/AssemblyShims.cs index f54fa256..c111691d 100644 --- a/Il2CppInspector.Common/Outputs/AssemblyShims.cs +++ b/Il2CppInspector.Common/Outputs/AssemblyShims.cs @@ -187,7 +187,7 @@ private TypeDefUser CreateTypeShallow(ModuleDef module, TypeInfo type) }; if (mType.IsExplicitLayout || mType.IsSequentialLayout) - mType.ClassLayout = new ClassLayoutUser(1, (uint)type.Sizes.nativeSize); + mType.ClassLayout = new ClassLayoutUser(1, (uint)type.Sizes.NativeSize); // Add nested types foreach (var nestedType in type.DeclaredNestedTypes) @@ -242,7 +242,7 @@ private TypeDefUser PopulateType(ModuleDef module, TypeDefUser mType, TypeInfo t AddMethod(module, mType, method); // Add token attribute - if (type.Definition != null) + if (type.Definition.IsValid) mType.AddAttribute(module, tokenAttribute, ("Token", $"0x{type.MetadataToken:X8}")); // Add custom attribute attributes @@ -270,7 +270,7 @@ private FieldDef AddField(ModuleDef module, TypeDef mType, FieldInfo field) { if (field.HasFieldRVA) { // Attempt to get field size - var fieldSize = field.FieldType.Sizes.nativeSize; + var fieldSize = field.FieldType.Sizes.NativeSize; var preview = model.Package.Metadata.ReadBytes((long) field.DefaultValueMetadataAddress, fieldSize); mField.InitialValue = preview; @@ -311,7 +311,7 @@ private PropertyDef AddProperty(ModuleDef module, TypeDef mType, PropertyInfo pr // Add token attribute // Generic properties and constructed properties (from disperate get/set methods) have no definition - if (prop.Definition != null) + if (prop.Definition.IsValid) mProp.AddAttribute(module, tokenAttribute, ("Token", $"0x{prop.MetadataToken:X8}")); // Add custom attribute attributes @@ -433,8 +433,8 @@ private MethodDef AddMethod(ModuleDef module, TypeDef mType, MethodBase method) ("Offset", string.Format("0x{0:X}", model.Package.BinaryImage.MapVATR(method.VirtualAddress.Value.Start))), ("VA", method.VirtualAddress.Value.Start.ToAddressString()) }; - if (method.Definition.slot != ushort.MaxValue) - args.Add(("Slot", method.Definition.slot.ToString())); + if (method.Definition.Slot != ushort.MaxValue) + args.Add(("Slot", method.Definition.Slot.ToString())); mMethod.AddAttribute(module, addressAttribute, args.ToArray()); } @@ -649,7 +649,7 @@ public void Write(string outputPath, EventHandler statusCallback = null) AddCustomAttribute(module, module.Assembly, ca); // Add token attributes - module.AddAttribute(module, tokenAttribute, ("Token", $"0x{asm.ImageDefinition.token:X8}")); + module.AddAttribute(module, tokenAttribute, ("Token", $"0x{asm.ImageDefinition.Token:X8}")); module.Assembly.AddAttribute(module, tokenAttribute, ("Token", $"0x{asm.MetadataToken:X8}")); if (types.TryGetValue(module, out var shallowTypes)) diff --git a/Il2CppInspector.Common/Outputs/CSharpCodeStubs.cs b/Il2CppInspector.Common/Outputs/CSharpCodeStubs.cs index 1d6b2045..52768a7f 100644 --- a/Il2CppInspector.Common/Outputs/CSharpCodeStubs.cs +++ b/Il2CppInspector.Common/Outputs/CSharpCodeStubs.cs @@ -353,7 +353,7 @@ private string generateAssemblyInfo(IEnumerable assemblies, foreach (var asm in assemblies) { text.Append($"// Image {asm.Index}: {asm.ShortName} - Assembly: {asm.FullName}"); if (!SuppressMetadata) - text.Append($" - Types {asm.ImageDefinition.typeStart}-{asm.ImageDefinition.typeStart + asm.ImageDefinition.typeCount - 1}"); + text.Append($" - Types {asm.ImageDefinition.TypeStart}-{asm.ImageDefinition.TypeStart + asm.ImageDefinition.TypeCount - 1}"); text.AppendLine(); // Assembly-level attributes @@ -426,7 +426,7 @@ private StringBuilder generateType(TypeInfo type, IEnumerable namespaces sb.Append($" // Metadata: {field.DefaultValueMetadataAddress.ToAddressString()}"); // For static array initializers, output metadata address and preview if (field.HasFieldRVA && !SuppressMetadata) { - var preview = model.Package.Metadata.ReadBytes((long) field.DefaultValueMetadataAddress, field.FieldType.Sizes.nativeSize); + var preview = model.Package.Metadata.ReadBytes((long) field.DefaultValueMetadataAddress, field.FieldType.Sizes.NativeSize); sb.Append($" // Static value (base64): {Convert.ToBase64String(preview)} - Metadata: {field.DefaultValueMetadataAddress.ToAddressString()}"); } sb.Append("\n"); diff --git a/Il2CppInspector.Common/Outputs/JSONMetadata.cs b/Il2CppInspector.Common/Outputs/JSONMetadata.cs index 42df77c9..57a27c0d 100644 --- a/Il2CppInspector.Common/Outputs/JSONMetadata.cs +++ b/Il2CppInspector.Common/Outputs/JSONMetadata.cs @@ -184,7 +184,7 @@ private void writeMetadata() { // TODO: In the future, add data ranges for the entire IL2CPP metadata tree writeArray("arrayMetadata", () => { if (model.Package.Version >= MetadataVersions.V242) { - writeObject(() => writeTypedArray(binary.CodeRegistration.pcodeGenModules, binary.Modules.Count, "struct Il2CppCodeGenModule *", "g_CodeGenModules")); + writeObject(() => writeTypedArray(binary.CodeRegistration.CodeGenModules, binary.Modules.Count, "struct Il2CppCodeGenModule *", "g_CodeGenModules")); } }, "IL2CPP Array Metadata"); } diff --git a/Il2CppInspector.Common/Reflection/Assembly.cs b/Il2CppInspector.Common/Reflection/Assembly.cs index a84a6e43..30b0b101 100644 --- a/Il2CppInspector.Common/Reflection/Assembly.cs +++ b/Il2CppInspector.Common/Reflection/Assembly.cs @@ -7,6 +7,8 @@ All rights reserved. using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +using Il2CppInspector.Next.BinaryMetadata; +using Il2CppInspector.Next.Metadata; namespace Il2CppInspector.Reflection { public class Assembly @@ -43,37 +45,37 @@ public class Assembly public Assembly(TypeModel model, int imageIndex) { Model = model; ImageDefinition = Model.Package.Images[imageIndex]; - AssemblyDefinition = Model.Package.Assemblies[ImageDefinition.assemblyIndex]; + AssemblyDefinition = Model.Package.Assemblies[ImageDefinition.AssemblyIndex]; - if (AssemblyDefinition.imageIndex != imageIndex) + if (AssemblyDefinition.ImageIndex != imageIndex) throw new InvalidOperationException("Assembly/image index mismatch"); - MetadataToken = (int) AssemblyDefinition.token; - Index = ImageDefinition.assemblyIndex; - ShortName = Model.Package.Strings[ImageDefinition.nameIndex]; + MetadataToken = (int) AssemblyDefinition.Token; + Index = ImageDefinition.AssemblyIndex; + ShortName = Model.Package.Strings[ImageDefinition.NameIndex]; // Get full assembly name - var nameDef = AssemblyDefinition.aname; - var name = Regex.Replace(Model.Package.Strings[nameDef.nameIndex], @"[^A-Za-z0-9_\-\.()]", ""); - var culture = Model.Package.Strings[nameDef.cultureIndex]; + var nameDef = AssemblyDefinition.Aname; + var name = Regex.Replace(Model.Package.Strings[nameDef.NameIndex], @"[^A-Za-z0-9_\-\.()]", ""); + var culture = Model.Package.Strings[nameDef.CultureIndex]; if (string.IsNullOrEmpty(culture)) culture = "neutral"; - var pkt = BitConverter.ToString(nameDef.publicKeyToken).Replace("-", ""); + var pkt = Convert.ToHexString(nameDef.PublicKeyToken); if (pkt == "0000000000000000") pkt = "null"; - var version = string.Format($"{nameDef.major}.{nameDef.minor}.{nameDef.build}.{nameDef.revision}"); + var version = string.Format($"{nameDef.Major}.{nameDef.Minor}.{nameDef.Build}.{nameDef.Revision}"); FullName = string.Format($"{name}, Version={version}, Culture={culture}, PublicKeyToken={pkt.ToLower()}"); - if (ImageDefinition.entryPointIndex != -1) { + if (ImageDefinition.EntryPointIndex != -1) { // TODO: Generate EntryPoint method from entryPointIndex } // Find corresponding module (we'll need this for method pointers) - ModuleDefinition = Model.Package.Modules?[ShortName]; + ModuleDefinition = Model.Package.Modules[ShortName]; // Generate types in DefinedTypes from typeStart to typeStart+typeCount-1 - for (var t = ImageDefinition.typeStart; t < ImageDefinition.typeStart + ImageDefinition.typeCount; t++) { + for (var t = ImageDefinition.TypeStart; t < ImageDefinition.TypeStart + ImageDefinition.TypeCount; t++) { var type = new TypeInfo(t, this); // Don't add empty module definitions diff --git a/Il2CppInspector.Common/Reflection/CustomAttributeData.cs b/Il2CppInspector.Common/Reflection/CustomAttributeData.cs index 511544b0..12204555 100644 --- a/Il2CppInspector.Common/Reflection/CustomAttributeData.cs +++ b/Il2CppInspector.Common/Reflection/CustomAttributeData.cs @@ -95,7 +95,7 @@ private static IEnumerable getCustomAttributes(Assembly asm if (pkg.Version < MetadataVersions.V290) { var range = pkg.AttributeTypeRanges[customAttributeIndex]; - for (var i = range.start; i < range.start + range.count; i++) + for (var i = range.Start; i < range.Start + range.Count; i++) { var typeIndex = pkg.AttributeTypeIndices[i]; @@ -118,8 +118,8 @@ private static IEnumerable getCustomAttributes(Assembly asm var range = pkg.Metadata.AttributeDataRanges[customAttributeIndex]; var next = pkg.Metadata.AttributeDataRanges[customAttributeIndex + 1]; - var startOffset = pkg.Metadata.Header.attributeDataOffset + range.startOffset; - var endOffset = pkg.Metadata.Header.attributeDataOffset + next.startOffset; + var startOffset = (uint)pkg.Metadata.Header.AttributeDataOffset + range.StartOffset; + var endOffset = (uint)pkg.Metadata.Header.AttributeDataOffset + next.StartOffset; var reader = new CustomAttributeDataReader(pkg, asm, pkg.Metadata, startOffset, endOffset); if (reader.Count == 0) @@ -143,13 +143,17 @@ private static IEnumerable getCustomAttributes(Assembly asm public static IList GetCustomAttributes(Assembly asm, int token, int customAttributeIndex) => getCustomAttributes(asm, asm.Model.GetCustomAttributeIndex(asm, token, customAttributeIndex)).ToList(); - public static IList GetCustomAttributes(Assembly asm) => GetCustomAttributes(asm, asm.MetadataToken, asm.AssemblyDefinition.customAttributeIndex); - public static IList GetCustomAttributes(EventInfo evt) => GetCustomAttributes(evt.Assembly, evt.MetadataToken, evt.Definition.customAttributeIndex); - public static IList GetCustomAttributes(FieldInfo field) => GetCustomAttributes(field.Assembly, field.MetadataToken, field.Definition.customAttributeIndex); - public static IList GetCustomAttributes(MethodBase method) => GetCustomAttributes(method.Assembly, method.MetadataToken, method.Definition.customAttributeIndex); - public static IList GetCustomAttributes(ParameterInfo param) => GetCustomAttributes(param.DeclaringMethod.Assembly, param.MetadataToken, param.Definition.customAttributeIndex); + public static IList GetCustomAttributes(Assembly asm) => GetCustomAttributes(asm, asm.MetadataToken, asm.AssemblyDefinition.CustomAttributeIndex); + public static IList GetCustomAttributes(EventInfo evt) => GetCustomAttributes(evt.Assembly, evt.MetadataToken, evt.Definition.CustomAttributeIndex); + public static IList GetCustomAttributes(FieldInfo field) => GetCustomAttributes(field.Assembly, field.MetadataToken, field.Definition.CustomAttributeIndex); + public static IList GetCustomAttributes(MethodBase method) => GetCustomAttributes(method.Assembly, method.MetadataToken, method.Definition.CustomAttributeIndex); + public static IList GetCustomAttributes(ParameterInfo param) => GetCustomAttributes(param.DeclaringMethod.Assembly, param.MetadataToken, param.Definition.CustomAttributeIndex); public static IList GetCustomAttributes(PropertyInfo prop) - => prop.Definition != null ? GetCustomAttributes(prop.Assembly, prop.MetadataToken, prop.Definition.customAttributeIndex) : new List(); - public static IList GetCustomAttributes(TypeInfo type) => type.Definition != null? GetCustomAttributes(type.Assembly, type.MetadataToken, type.Definition.customAttributeIndex) : new List(); + => prop.Definition.IsValid + ? GetCustomAttributes(prop.Assembly, prop.MetadataToken, prop.Definition.CustomAttributeIndex) + : new List(); + public static IList GetCustomAttributes(TypeInfo type) => type.Definition.IsValid + ? GetCustomAttributes(type.Assembly, type.MetadataToken, type.Definition.CustomAttributeIndex) + : new List(); } } diff --git a/Il2CppInspector.Common/Reflection/EventInfo.cs b/Il2CppInspector.Common/Reflection/EventInfo.cs index 9f5a998b..2495495b 100644 --- a/Il2CppInspector.Common/Reflection/EventInfo.cs +++ b/Il2CppInspector.Common/Reflection/EventInfo.cs @@ -7,6 +7,7 @@ All rights reserved. using System.Collections.Generic; using System.Linq; using System.Reflection; +using Il2CppInspector.Next.Metadata; namespace Il2CppInspector.Reflection { @@ -41,25 +42,25 @@ public class EventInfo : MemberInfo public EventInfo(Il2CppInspector pkg, int eventIndex, TypeInfo declaringType) : base(declaringType) { Definition = pkg.Events[eventIndex]; - MetadataToken = (int) Definition.token; + MetadataToken = (int) Definition.Token; Index = eventIndex; - Name = pkg.Strings[Definition.nameIndex]; + Name = pkg.Strings[Definition.NameIndex]; rootDefinition = this; - eventTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.typeIndex); - var eventType = pkg.TypeReferences[Definition.typeIndex]; + eventTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.TypeIndex); + var eventType = pkg.TypeReferences[Definition.TypeIndex]; // Copy attributes - Attributes = (EventAttributes) eventType.attrs; + Attributes = (EventAttributes) eventType.Attrs; // NOTE: This relies on methods being added to TypeInfo.DeclaredMethods in the same order they are defined in the Il2Cpp metadata // add, remove and raise are method indices from the first method of the declaring type - if (Definition.add >= 0) - AddMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.methodStart + Definition.add); - if (Definition.remove >= 0) - RemoveMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.methodStart + Definition.remove); - if (Definition.raise >= 0) - RaiseMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.methodStart + Definition.raise); + if (Definition.Add >= 0) + AddMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.MethodIndex + Definition.Add); + if (Definition.Remove >= 0) + RemoveMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.MethodIndex + Definition.Remove); + if (Definition.Raise >= 0) + RaiseMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.MethodIndex + Definition.Raise); } public EventInfo(EventInfo eventDef, TypeInfo declaringType) : base(declaringType) { diff --git a/Il2CppInspector.Common/Reflection/FieldInfo.cs b/Il2CppInspector.Common/Reflection/FieldInfo.cs index 0b76299d..f207693a 100644 --- a/Il2CppInspector.Common/Reflection/FieldInfo.cs +++ b/Il2CppInspector.Common/Reflection/FieldInfo.cs @@ -10,6 +10,7 @@ All rights reserved. using System.Linq; using System.Reflection; using System.Text; +using Il2CppInspector.Next.Metadata; namespace Il2CppInspector.Reflection { public class FieldInfo : MemberInfo // L-TODO: Add support for [ThreadLocal] fields @@ -96,9 +97,9 @@ namespace Il2CppInspector.Reflection { public FieldInfo(Il2CppInspector pkg, int fieldIndex, TypeInfo declaringType) : base(declaringType) { Definition = pkg.Fields[fieldIndex]; - MetadataToken = (int) Definition.token; + MetadataToken = (int) Definition.Token; Index = fieldIndex; - Name = pkg.Strings[Definition.nameIndex]; + Name = pkg.Strings[Definition.NameIndex]; rawOffset = pkg.FieldOffsets[fieldIndex]; if (0 > rawOffset) @@ -109,11 +110,11 @@ public FieldInfo(Il2CppInspector pkg, int fieldIndex, TypeInfo declaringType) : rootDefinition = this; - fieldTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.typeIndex); - var fieldType = pkg.TypeReferences[Definition.typeIndex]; + fieldTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.TypeIndex); + var fieldType = pkg.TypeReferences[Definition.TypeIndex]; // Copy attributes - Attributes = (FieldAttributes) fieldType.attrs; + Attributes = (FieldAttributes) fieldType.Attrs; // Default initialization value if present if (pkg.FieldDefaultValue.TryGetValue(fieldIndex, out (ulong address, object variant) value)) { @@ -123,7 +124,7 @@ public FieldInfo(Il2CppInspector pkg, int fieldIndex, TypeInfo declaringType) : } public FieldInfo(FieldInfo fieldDef, TypeInfo declaringType) : base(declaringType) { - if (fieldDef.Definition == null) + if (!fieldDef.Definition.IsValid) throw new ArgumentException("Argument must be a bare field definition"); rootDefinition = fieldDef; diff --git a/Il2CppInspector.Common/Reflection/MethodBase.cs b/Il2CppInspector.Common/Reflection/MethodBase.cs index 44b9ab7f..79e82227 100644 --- a/Il2CppInspector.Common/Reflection/MethodBase.cs +++ b/Il2CppInspector.Common/Reflection/MethodBase.cs @@ -10,6 +10,7 @@ All rights reserved. using System.Linq; using System.Reflection; using System.Text; +using Il2CppInspector.Next.Metadata; namespace Il2CppInspector.Reflection { @@ -117,9 +118,9 @@ public byte[] GetMethodBody() { // Initialize a method from a method definition (MethodDef) protected MethodBase(Il2CppInspector pkg, int methodIndex, TypeInfo declaringType) : base(declaringType) { Definition = pkg.Methods[methodIndex]; - MetadataToken = (int) Definition.token; + MetadataToken = (int) Definition.Token; Index = methodIndex; - Name = pkg.Strings[Definition.nameIndex]; + Name = pkg.Strings[Definition.NameIndex]; // Find method pointer VirtualAddress = pkg.GetMethodPointer(Assembly.ModuleDefinition, Definition); @@ -130,28 +131,28 @@ protected MethodBase(Il2CppInspector pkg, int methodIndex, TypeInfo declaringTyp rootDefinition = this; // Generic method definition? - if (Definition.genericContainerIndex >= 0) { + if (Definition.GenericContainerIndex >= 0) { IsGenericMethod = true; // Store the generic type parameters for later instantiation - var container = pkg.GenericContainers[Definition.genericContainerIndex]; - genericArguments = Enumerable.Range((int)container.genericParameterStart, container.type_argc) + var container = pkg.GenericContainers[Definition.GenericContainerIndex]; + genericArguments = Enumerable.Range(container.GenericParameterStart, container.TypeArgc) .Select(index => Assembly.Model.GetGenericParameterType(index)).ToArray(); genericMethodInstances = new Dictionary(new TypeInfo.TypeArgumentsComparer()); genericMethodInstances[genericArguments] = this; } // Copy attributes - Attributes = (MethodAttributes) Definition.flags; - MethodImplementationFlags = (MethodImplAttributes) Definition.iflags; + Attributes = (MethodAttributes) Definition.Flags; + MethodImplementationFlags = (MethodImplAttributes) Definition.ImplFlags; // Add arguments - for (var p = Definition.parameterStart; p < Definition.parameterStart + Definition.parameterCount; p++) + for (var p = Definition.ParameterStart; p < Definition.ParameterStart + Definition.ParameterCount; p++) DeclaredParameters.Add(new ParameterInfo(pkg, p, this)); } protected MethodBase(MethodBase methodDef, TypeInfo declaringType) : base(declaringType) { - if (methodDef.Definition == null) + if (!methodDef.Definition.IsValid) throw new ArgumentException("Argument must be a bare method definition"); rootDefinition = methodDef; diff --git a/Il2CppInspector.Common/Reflection/ParameterInfo.cs b/Il2CppInspector.Common/Reflection/ParameterInfo.cs index cf6d0a64..f31fe5dc 100644 --- a/Il2CppInspector.Common/Reflection/ParameterInfo.cs +++ b/Il2CppInspector.Common/Reflection/ParameterInfo.cs @@ -8,6 +8,7 @@ All rights reserved. using System.Collections.Generic; using System.Linq; using System.Reflection; +using Il2CppInspector.Next.Metadata; namespace Il2CppInspector.Reflection { @@ -64,28 +65,28 @@ public ParameterInfo(Il2CppInspector pkg, int paramIndex, MethodBase declaringMe if (paramIndex == -1) { Position = -1; - paramTypeReference = TypeRef.FromReferenceIndex(declaringMethod.Assembly.Model, declaringMethod.Definition.returnType); - MetadataToken = declaringMethod.Definition.returnParameterToken; + paramTypeReference = TypeRef.FromReferenceIndex(declaringMethod.Assembly.Model, declaringMethod.Definition.ReturnType); + MetadataToken = (int)declaringMethod.Definition.ReturnParameterToken; Attributes |= ParameterAttributes.Retval; return; } Definition = pkg.Params[Index]; - MetadataToken = (int) Definition.token; - Name = pkg.Strings[Definition.nameIndex]; + MetadataToken = (int) Definition.Token; + Name = pkg.Strings[Definition.NameIndex]; rootDefinition = this; // Handle unnamed/obfuscated parameter names if (string.IsNullOrEmpty(Name)) Name = string.Format($"param_{Index:x8}"); - Position = paramIndex - declaringMethod.Definition.parameterStart; - paramTypeReference = TypeRef.FromReferenceIndex(declaringMethod.Assembly.Model, Definition.typeIndex); + Position = paramIndex - declaringMethod.Definition.ParameterStart; + paramTypeReference = TypeRef.FromReferenceIndex(declaringMethod.Assembly.Model, Definition.TypeIndex); - var paramType = pkg.TypeReferences[Definition.typeIndex]; + var paramType = pkg.TypeReferences[Definition.TypeIndex]; // Copy attributes - Attributes = (ParameterAttributes) paramType.attrs; + Attributes = (ParameterAttributes) paramType.Attrs; // Default initialization value if present if (pkg.ParameterDefaultValue.TryGetValue(paramIndex, out (ulong address, object variant) value)) { diff --git a/Il2CppInspector.Common/Reflection/PropertyInfo.cs b/Il2CppInspector.Common/Reflection/PropertyInfo.cs index 440a5627..d44e627a 100644 --- a/Il2CppInspector.Common/Reflection/PropertyInfo.cs +++ b/Il2CppInspector.Common/Reflection/PropertyInfo.cs @@ -8,6 +8,7 @@ All rights reserved. using System.Collections.Generic; using System.Linq; using System.Reflection; +using Il2CppInspector.Next.Metadata; namespace Il2CppInspector.Reflection { public class PropertyInfo : MemberInfo @@ -52,25 +53,24 @@ public PropertyInfo(Il2CppInspector pkg, int propIndex, TypeInfo declaringType) base(declaringType) { Index = propIndex; Definition = pkg.Properties[propIndex]; - MetadataToken = (int) Definition.token; - Name = pkg.Strings[Definition.nameIndex]; + MetadataToken = (int) Definition.Token; + Name = pkg.Strings[Definition.NameIndex]; rootDefinition = this; // Copy attributes - Attributes = (PropertyAttributes) Definition.attrs; + Attributes = (PropertyAttributes) Definition.Attrs; // prop.get and prop.set are method indices from the first method of the declaring type - if (Definition.get >= 0) - GetMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.methodStart + Definition.get); - if (Definition.set >= 0) - SetMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.methodStart + Definition.set); + if (Definition.Get >= 0) + GetMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.MethodIndex + Definition.Get); + if (Definition.Set >= 0) + SetMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.MethodIndex + Definition.Set); } // Create a property based on a get and set method public PropertyInfo(MethodInfo getter, MethodInfo setter, TypeInfo declaringType) : base(declaringType) { Index = -1; - Definition = null; rootDefinition = this; Name = (getter ?? setter).Name.Replace(".get_", ".").Replace(".set_", "."); diff --git a/Il2CppInspector.Common/Reflection/TypeInfo.cs b/Il2CppInspector.Common/Reflection/TypeInfo.cs index 42088a22..305ba43c 100644 --- a/Il2CppInspector.Common/Reflection/TypeInfo.cs +++ b/Il2CppInspector.Common/Reflection/TypeInfo.cs @@ -13,6 +13,8 @@ All rights reserved. using System.Reflection; using System.Text; using System.Text.RegularExpressions; +using Il2CppInspector.Next.BinaryMetadata; +using Il2CppInspector.Next.Metadata; namespace Il2CppInspector.Reflection { @@ -53,9 +55,9 @@ public TypeInfo BaseType { return null; if (IsArray) return Assembly.Model.TypesByFullName["System.Array"]; - if (Definition != null) { - if (Definition.parentIndex >= 0) - return Assembly.Model.TypesByReferenceIndex[Definition.parentIndex]; + if (Definition.IsValid) { + if (Definition.ParentIndex >= 0) + return Assembly.Model.TypesByReferenceIndex[Definition.ParentIndex]; } if (genericTypeDefinition != null) { return genericTypeDefinition.BaseType.SubstituteGenericArguments(genericArguments); @@ -73,15 +75,15 @@ public TypeInfo BaseType { public override TypeInfo DeclaringType { get { - if (Definition != null) { + if (Definition.IsValid) { /* Type definition */ - if (Definition.declaringTypeIndex == -1) + if (Definition.DeclaringTypeIndex == -1) return null; - var type = Assembly.Model.TypesByReferenceIndex[Definition.declaringTypeIndex]; + var type = Assembly.Model.TypesByReferenceIndex[Definition.DeclaringTypeIndex]; if (type == null) { /* This might happen while initially setting up the types */ - var typeRef = Assembly.Model.Package.TypeReferences[Definition.declaringTypeIndex]; - type = Assembly.Model.TypesByDefinitionIndex[(int)typeRef.datapoint]; + var typeRef = Assembly.Model.Package.TypeReferences[Definition.DeclaringTypeIndex]; + type = Assembly.Model.TypesByDefinitionIndex[typeRef.Data.KlassIndex]; } return type; } @@ -253,7 +255,7 @@ public MethodInfo[] GetAllMethods() { public PropertyInfo GetProperty(string name) => DeclaredProperties.FirstOrDefault(p => p.Name == name); public MethodBase[] GetVTable() { - if (Definition != null) { + if (!Definition.IsValid) { MetadataUsage[] vt = Assembly.Model.Package.GetVTable(Definition); MethodBase[] res = new MethodBase[vt.Length]; for (int i = 0; i < vt.Length; i++) { @@ -667,7 +669,7 @@ public int GenericParameterPosition { private readonly TypeRef[] implementedInterfaceReferences; public IEnumerable ImplementedInterfaces { get { - if (Definition != null) + if (Definition.IsValid) return implementedInterfaceReferences.Select(x => x.Value); if (genericTypeDefinition != null) return genericTypeDefinition.ImplementedInterfaces.Select(t => t.SubstituteGenericArguments(genericArguments)); @@ -687,7 +689,7 @@ public IEnumerable ImplementedInterfaces { public bool IsEnum { get; } public bool IsGenericParameter { get; } public bool IsGenericType { get; } - public bool IsGenericTypeDefinition => (Definition != null) && genericArguments.Any(); + public bool IsGenericTypeDefinition => (Definition.IsValid) && genericArguments.Any(); public bool IsImport => (Attributes & TypeAttributes.Import) == TypeAttributes.Import; public bool IsInterface => (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface; public bool IsNested => (MemberType & MemberTypes.NestedType) == MemberTypes.NestedType; @@ -746,13 +748,13 @@ public TypeInfo(int typeIndex, Assembly owner) : base(owner) { Definition = pkg.TypeDefinitions[typeIndex]; Sizes = pkg.TypeDefinitionSizes[typeIndex]; - MetadataToken = (int) Definition.token; + MetadataToken = (int) Definition.Token; Index = typeIndex; - Namespace = Regex.Replace(pkg.Strings[Definition.namespaceIndex], @"[^A-Za-z0-9_\-\.<>{}]", ""); - Name = pkg.Strings[Definition.nameIndex]; + Namespace = Regex.Replace(pkg.Strings[Definition.NamespaceIndex], @"[^A-Za-z0-9_\-\.<>{}]", ""); + Name = pkg.Strings[Definition.NameIndex]; // Nested type? - if (Definition.declaringTypeIndex >= 0) { + if (Definition.DeclaringTypeIndex >= 0) { MemberType |= MemberTypes.NestedType; } @@ -760,14 +762,14 @@ public TypeInfo(int typeIndex, Assembly owner) : base(owner) { Assembly.Model.TypesByDefinitionIndex[Index] = this; // Generic type definition? - if (Definition.genericContainerIndex >= 0) { + if (Definition.GenericContainerIndex >= 0) { IsGenericType = true; IsGenericParameter = false; // Store the generic type parameters for later instantiation - var container = pkg.GenericContainers[Definition.genericContainerIndex]; + var container = pkg.GenericContainers[Definition.GenericContainerIndex]; - genericArguments = Enumerable.Range((int)container.genericParameterStart, container.type_argc) + genericArguments = Enumerable.Range((int)container.GenericParameterStart, container.TypeArgc) .Select(index => Assembly.Model.GetGenericParameterType(index)).ToArray(); genericTypeInstances = new Dictionary(new TypeArgumentsComparer()); genericTypeInstances[genericArguments] = this; @@ -777,12 +779,12 @@ public TypeInfo(int typeIndex, Assembly owner) : base(owner) { Assembly.Model.TypesByFullName[FullName] = this; // Copy attributes - Attributes = (TypeAttributes) Definition.flags; + Attributes = (TypeAttributes) Definition.Flags; // Enumerations - bit 1 of bitfield indicates this (also the baseTypeReference will be System.Enum) - if (((Definition.bitfield >> 1) & 1) == 1) { + if (Definition.Bitfield.EnumType) { IsEnum = true; - enumUnderlyingTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.elementTypeIndex); + enumUnderlyingTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.ElementTypeIndex); } // Pass-by-reference type @@ -792,24 +794,24 @@ public TypeInfo(int typeIndex, Assembly owner) : base(owner) { IsByRef = false; // Add all implemented interfaces - implementedInterfaceReferences = new TypeRef[Definition.interfaces_count]; - for (var i = 0; i < Definition.interfaces_count; i++) - implementedInterfaceReferences[i] = TypeRef.FromReferenceIndex(Assembly.Model, pkg.InterfaceUsageIndices[Definition.interfacesStart + i]); + implementedInterfaceReferences = new TypeRef[Definition.InterfacesCount]; + for (var i = 0; i < Definition.InterfacesCount; i++) + implementedInterfaceReferences[i] = TypeRef.FromReferenceIndex(Assembly.Model, pkg.InterfaceUsageIndices[Definition.InterfacesIndex + i]); // Add all nested types - declaredNestedTypes = new TypeRef[Definition.nested_type_count]; - for (var n = 0; n < Definition.nested_type_count; n++) - declaredNestedTypes[n] = TypeRef.FromDefinitionIndex(Assembly.Model, pkg.NestedTypeIndices[Definition.nestedTypesStart + n]); + declaredNestedTypes = new TypeRef[Definition.NestedTypeCount]; + for (var n = 0; n < Definition.NestedTypeCount; n++) + declaredNestedTypes[n] = TypeRef.FromDefinitionIndex(Assembly.Model, pkg.NestedTypeIndices[Definition.NestedTypeIndex + n]); // Add all fields declaredFields = new List(); - for (var f = Definition.fieldStart; f < Definition.fieldStart + Definition.field_count; f++) + for (var f = Definition.FieldIndex; f < Definition.FieldIndex + Definition.FieldCount; f++) declaredFields.Add(new FieldInfo(pkg, f, this)); // Add all methods declaredConstructors = new List(); declaredMethods = new List(); - for (var m = Definition.methodStart; m < Definition.methodStart + Definition.method_count; m++) { + for (var m = Definition.MethodIndex; m < Definition.MethodIndex + Definition.MethodCount; m++) { var method = new MethodInfo(pkg, m, this); if (method.Name == ConstructorInfo.ConstructorName || method.Name == ConstructorInfo.TypeConstructorName) declaredConstructors.Add(new ConstructorInfo(pkg, m, this)); @@ -819,7 +821,7 @@ public TypeInfo(int typeIndex, Assembly owner) : base(owner) { // Add all properties declaredProperties = new List(); - for (var p = Definition.propertyStart; p < Definition.propertyStart + Definition.property_count; p++) + for (var p = Definition.PropertyIndex; p < Definition.PropertyIndex + Definition.PropertyCount; p++) declaredProperties.Add(new PropertyInfo(pkg, p, this)); // There are rare cases when explicitly implemented interface properties @@ -856,7 +858,7 @@ public TypeInfo(int typeIndex, Assembly owner) : base(owner) { // Add all events declaredEvents = new List(); - for (var e = Definition.eventStart; e < Definition.eventStart + Definition.event_count; e++) + for (var e = Definition.EventIndex; e < Definition.EventIndex + Definition.EventCount; e++) declaredEvents.Add(new EventInfo(pkg, e, this)); // TODO: Events have the same edge case issue as properties above, eg. PoGo 0.35.0 @@ -937,21 +939,21 @@ public TypeInfo(TypeInfo declaringType, Il2CppGenericParameter param) : base(dec Namespace = declaringType.Namespace; // Special constraints - GenericParameterAttributes = (GenericParameterAttributes)param.flags; + GenericParameterAttributes = (GenericParameterAttributes)param.Flags; // Type constraints - genericParameterConstraints = new TypeRef[param.constraintsCount]; - for (int c = 0; c < param.constraintsCount; c++) - genericParameterConstraints[c] = TypeRef.FromReferenceIndex(Assembly.Model, Assembly.Model.Package.GenericConstraintIndices[param.constraintsStart + c]); + genericParameterConstraints = new TypeRef[param.ConstraintsCount]; + for (int c = 0; c < param.ConstraintsCount; c++) + genericParameterConstraints[c] = TypeRef.FromReferenceIndex(Assembly.Model, Assembly.Model.Package.GenericConstraintIndices[param.ConstraintsStart + c]); // Base type of object (set by default) // TODO: ImplementedInterfaces should be set to interface types constraints // Name of parameter - Name = Assembly.Model.Package.Strings[param.nameIndex]; + Name = Assembly.Model.Package.Strings[param.NameIndex]; // Position - GenericParameterPosition = param.num; + GenericParameterPosition = param.Num; IsGenericParameter = true; IsGenericType = false; diff --git a/Il2CppInspector.Common/Reflection/TypeModel.cs b/Il2CppInspector.Common/Reflection/TypeModel.cs index cf0b6eed..e052d321 100644 --- a/Il2CppInspector.Common/Reflection/TypeModel.cs +++ b/Il2CppInspector.Common/Reflection/TypeModel.cs @@ -10,6 +10,7 @@ All rights reserved. using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using Il2CppInspector.Next.BinaryMetadata; namespace Il2CppInspector.Reflection { @@ -112,13 +113,13 @@ public TypeModel(Il2CppInspector package) { // Create types and methods from MethodSpec (which incorporates TypeSpec in IL2CPP) foreach (var spec in Package.MethodSpecs) { - var methodDefinition = MethodsByDefinitionIndex[spec.methodDefinitionIndex]; + var methodDefinition = MethodsByDefinitionIndex[spec.MethodDefinitionIndex]; var declaringType = methodDefinition.DeclaringType; // Concrete instance of a generic class // If the class index is not specified, we will later create a generic method in a non-generic class - if (spec.classIndexIndex != -1) { - var genericInstance = Package.GenericInstances[spec.classIndexIndex]; + if (spec.ClassIndexIndex != -1) { + var genericInstance = Package.GenericInstances[spec.ClassIndexIndex]; var genericArguments = ResolveGenericArguments(genericInstance); declaringType = declaringType.MakeGenericType(genericArguments); } @@ -129,8 +130,8 @@ public TypeModel(Il2CppInspector package) { else method = declaringType.GetMethodByDefinition((MethodInfo)methodDefinition); - if (spec.methodIndexIndex != -1) { - var genericInstance = Package.GenericInstances[spec.methodIndexIndex]; + if (spec.MethodIndexIndex != -1) { + var genericInstance = Package.GenericInstances[spec.MethodIndexIndex]; var genericArguments = ResolveGenericArguments(genericInstance); method = method.MakeGenericMethod(genericArguments); } @@ -190,7 +191,7 @@ public TypeModel(Il2CppInspector package) { public TypeInfo[] ResolveGenericArguments(Il2CppGenericInst inst) { // Get list of pointers to type parameters (both unresolved and concrete) - var genericTypeArguments = Package.BinaryImage.ReadMappedArray(inst.type_argv, (int)inst.type_argc); + var genericTypeArguments = Package.BinaryImage.ReadMappedArray(inst.TypeArgv, (int)inst.TypeArgc); return genericTypeArguments.Select(a => GetTypeFromVirtualAddress(a)).ToArray(); } @@ -201,17 +202,17 @@ private TypeInfo resolveTypeReference(Il2CppType typeRef) { var image = Package.BinaryImage; TypeInfo underlyingType; - switch (typeRef.type) { + switch (typeRef.Type) { // Classes defined in the metadata (reference to a TypeDef) case Il2CppTypeEnum.IL2CPP_TYPE_CLASS: case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE: - underlyingType = TypesByDefinitionIndex[typeRef.datapoint]; // klassIndex + underlyingType = TypesByDefinitionIndex[typeRef.Data.KlassIndex]; // klassIndex break; // Constructed types case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: // TODO: Replace with array load from Il2CppMetadataRegistration.genericClasses - var generic = image.ReadMappedObject(typeRef.datapoint); // Il2CppGenericClass * + var generic = image.ReadMappedObject(typeRef.Data.GenericClass); // Il2CppGenericClass * // Get generic type definition TypeInfo genericTypeDef; @@ -219,49 +220,49 @@ private TypeInfo resolveTypeReference(Il2CppType typeRef) { // 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. - if ((generic.typeDefinitionIndex & 0xffff_ffff) == 0x0000_0000_ffff_ffff) + if ((generic.TypeDefinitionIndex & 0xffff_ffff) == 0x0000_0000_ffff_ffff) return null; - genericTypeDef = TypesByDefinitionIndex[generic.typeDefinitionIndex]; + genericTypeDef = TypesByDefinitionIndex[generic.TypeDefinitionIndex]; } else { - genericTypeDef = GetTypeFromVirtualAddress(generic.type); + genericTypeDef = GetTypeFromVirtualAddress(generic.Type); } // Get the instantiation // TODO: Replace with array load from Il2CppMetadataRegistration.genericInsts - var genericInstance = image.ReadMappedObject(generic.context.class_inst); + var genericInstance = image.ReadMappedObject(generic.Context.ClassInst); var genericArguments = ResolveGenericArguments(genericInstance); underlyingType = genericTypeDef.MakeGenericType(genericArguments); break; case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: - var descriptor = image.ReadMappedObject(typeRef.datapoint); - var elementType = GetTypeFromVirtualAddress(descriptor.etype); - underlyingType = elementType.MakeArrayType(descriptor.rank); + var descriptor = image.ReadMappedObject(typeRef.Data.ArrayType); + var elementType = GetTypeFromVirtualAddress(descriptor.ElementType); + underlyingType = elementType.MakeArrayType(descriptor.Rank); break; case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY: - elementType = GetTypeFromVirtualAddress(typeRef.datapoint); + elementType = GetTypeFromVirtualAddress(typeRef.Data.Type); underlyingType = elementType.MakeArrayType(1); break; case Il2CppTypeEnum.IL2CPP_TYPE_PTR: - elementType = GetTypeFromVirtualAddress(typeRef.datapoint); + elementType = GetTypeFromVirtualAddress(typeRef.Data.Type); underlyingType = elementType.MakePointerType(); break; // Generic type and generic method parameters case Il2CppTypeEnum.IL2CPP_TYPE_VAR: case Il2CppTypeEnum.IL2CPP_TYPE_MVAR: - underlyingType = GetGenericParameterType((int)typeRef.datapoint); + underlyingType = GetGenericParameterType(typeRef.Data.GenericParameterIndex); break; // Primitive types default: - underlyingType = GetTypeDefinitionFromTypeEnum(typeRef.type); + underlyingType = GetTypeDefinitionFromTypeEnum(typeRef.Type); break; } // Create a reference type if necessary - return typeRef.byref ? underlyingType.MakeByRefType() : underlyingType; + return typeRef.ByRef ? underlyingType.MakeByRefType() : underlyingType; } // Basic primitive types are specified via a flag value @@ -302,14 +303,14 @@ public TypeInfo GetGenericParameterType(int index) { return GenericParameterTypes[index]; var paramType = Package.GenericParameters[index]; // genericParameterIndex - var container = Package.GenericContainers[paramType.ownerIndex]; + var container = Package.GenericContainers[paramType.OwnerIndex]; TypeInfo result; - if (container.is_method == 1) { - var owner = MethodsByDefinitionIndex[container.ownerIndex]; + if (container.IsMethod == 1) { + var owner = MethodsByDefinitionIndex[container.OwnerIndex]; result = new TypeInfo(owner, paramType); } else { - var owner = TypesByDefinitionIndex[container.ownerIndex]; + var owner = TypesByDefinitionIndex[container.OwnerIndex]; result = new TypeInfo(owner, paramType); } GenericParameterTypes[index] = result; @@ -324,7 +325,7 @@ public int GetCustomAttributeIndex(Assembly asm, int token, int customAttributeI // From v24.1 onwards, token was added to Il2CppCustomAttributeTypeRange and each Il2CppImageDefinition noted the CustomAttributeTypeRanges for the image // v29 uses this same system but with CustomAttributeDataRanges instead - if (!Package.AttributeIndicesByToken.TryGetValue(asm.ImageDefinition.customAttributeStart, out var indices) + if (!Package.AttributeIndicesByToken.TryGetValue(asm.ImageDefinition.CustomAttributeStart, out var indices) || !indices.TryGetValue((uint)token, out var index)) return -1; @@ -345,7 +346,7 @@ public string GetMetadataUsageName(MetadataUsage usage) { case MetadataUsageType.FieldInfo: var fieldRef = Package.FieldRefs[usage.SourceIndex]; var type = GetMetadataUsageType(usage); - var field = type.DeclaredFields.First(f => f.Index == type.Definition.fieldStart + fieldRef.fieldIndex); + var field = type.DeclaredFields.First(f => f.Index == type.Definition.FieldIndex + fieldRef.FieldIndex); return $"{type.Name}.{field.Name}"; case MetadataUsageType.StringLiteral: @@ -359,7 +360,7 @@ public string GetMetadataUsageName(MetadataUsage usage) { case MetadataUsageType.FieldRva: fieldRef = Package.FieldRefs[usage.SourceIndex]; type = GetMetadataUsageType(usage); - field = type.DeclaredFields.First(f => f.Index == type.Definition.fieldStart + fieldRef.fieldIndex); + field = type.DeclaredFields.First(f => f.Index == type.Definition.FieldIndex + fieldRef.FieldIndex); return $"{type.Name}.{field.Name}_Default"; // TODO: Find out if this is really needed for anything } throw new NotImplementedException("Unknown metadata usage type: " + usage.Type); @@ -369,7 +370,7 @@ public string GetMetadataUsageName(MetadataUsage usage) { public TypeInfo GetMetadataUsageType(MetadataUsage usage) => usage.Type switch { MetadataUsageType.Type or MetadataUsageType.TypeInfo => TypesByReferenceIndex[usage.SourceIndex], MetadataUsageType.MethodDef or MetadataUsageType.MethodRef => GetMetadataUsageMethod(usage).DeclaringType, - MetadataUsageType.FieldInfo or MetadataUsageType.FieldRva => TypesByReferenceIndex[Package.FieldRefs[usage.SourceIndex].typeIndex], + MetadataUsageType.FieldInfo or MetadataUsageType.FieldRva => TypesByReferenceIndex[Package.FieldRefs[usage.SourceIndex].TypeIndex], _ => throw new InvalidOperationException("Incorrect metadata usage type to retrieve referenced type") }; diff --git a/Il2CppInspector.Common/Utils/BlobReader.cs b/Il2CppInspector.Common/Utils/BlobReader.cs index f2c367ea..c031950b 100644 --- a/Il2CppInspector.Common/Utils/BlobReader.cs +++ b/Il2CppInspector.Common/Utils/BlobReader.cs @@ -4,6 +4,8 @@ using System.Diagnostics; using System.IO; using Il2CppInspector.Next; +using Il2CppInspector.Next.BinaryMetadata; +using Il2CppInspector.Next.Metadata; namespace Il2CppInspector.Utils; @@ -154,18 +156,18 @@ uint ReadUInt32() public static Il2CppTypeEnum ReadEncodedTypeEnum(Il2CppInspector inspector, BinaryObjectStream blob, out Il2CppTypeDefinition enumType) { - enumType = null; + enumType = default; var typeEnum = (Il2CppTypeEnum)blob.ReadByte(); if (typeEnum == Il2CppTypeEnum.IL2CPP_TYPE_ENUM) { var typeIndex = blob.ReadCompressedInt32(); - var typeHandle = (uint)inspector.TypeReferences[typeIndex].datapoint; + var typeHandle = (uint)inspector.TypeReferences[typeIndex].Data.KlassIndex; enumType = inspector.TypeDefinitions[typeHandle]; - var elementTypeHandle = inspector.TypeReferences[enumType.elementTypeIndex].datapoint; + var elementTypeHandle = inspector.TypeReferences[enumType.ElementTypeIndex].Data.KlassIndex; var elementType = inspector.TypeDefinitions[elementTypeHandle]; - typeEnum = inspector.TypeReferences[elementType.byvalTypeIndex].type; + typeEnum = inspector.TypeReferences[elementType.ByValTypeIndex].Type; } // This technically also handles SZARRAY (System.Array) and all others by just returning their system type diff --git a/VersionedSerialization/VersionedSerialization.csproj b/VersionedSerialization/VersionedSerialization.csproj index b602acfc..bb44983d 100644 --- a/VersionedSerialization/VersionedSerialization.csproj +++ b/VersionedSerialization/VersionedSerialization.csproj @@ -7,4 +7,12 @@ preview + + True + + + + True + + From 6c594349847958238e93b1705704cbf85303711a Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Wed, 14 Aug 2024 01:00:32 +0200 Subject: [PATCH 05/27] rename serialization methods and add BinaryObjectStreamReader for interop --- .../FileFormatStreams/FileFormatStream.cs | 35 ++++- .../FileFormatStreams/UBReader.cs | 2 +- Il2CppInspector.Common/IL2CPP/Metadata.cs | 2 +- .../Next/BinaryMetadata/Il2CppType.cs | 2 +- .../Next/BinaryObjectStreamReader.cs | 130 ++++++++++++++++++ .../Metadata/Il2CppAssemblyNameDefinition.cs | 4 +- Il2CppInspector.Common/Next/Pointer.cs | 4 +- .../ObjectSerializationGenerator.cs | 4 +- VersionedSerialization/IReader.cs | 8 +- VersionedSerialization/ReaderExtensions.cs | 10 +- VersionedSerialization/SpanReader.cs | 18 +-- 11 files changed, 191 insertions(+), 28 deletions(-) create mode 100644 Il2CppInspector.Common/Next/BinaryObjectStreamReader.cs diff --git a/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs b/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs index 74a68ddd..dab3cfb8 100644 --- a/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs +++ b/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs @@ -6,10 +6,12 @@ All rights reserved. using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Reflection; using System.Text; +using Il2CppInspector.Next; using NoisyCowStudios.Bin2Object; using VersionedSerialization; @@ -121,6 +123,27 @@ public interface IFileFormatStream public void AddPrimitiveMapping(Type objType, Type streamType); public void CopyTo(Stream stream); + + public TType ReadMappedPrimitive(ulong addr) where TType : unmanaged; + public TType ReadPrimitive(long addr) where TType : unmanaged; + public TType ReadPrimitive() where TType : unmanaged; + + public ImmutableArray ReadMappedPrimitiveArray(ulong addr, long count) where TType : unmanaged; + public ImmutableArray ReadPrimitiveArray(long addr, long count) where TType : unmanaged; + public ImmutableArray ReadPrimitiveArray(long count) where TType : unmanaged; + + public TType ReadMappedVersionedObject(ulong addr) where TType : IReadable, new(); + public TType ReadVersionedObject(long addr) where TType : IReadable, new(); + public TType ReadVersionedObject() where TType : IReadable, new(); + + public ImmutableArray ReadMappedVersionedObjectArray(ulong addr, long count) + where TType : IReadable, new(); + + public ImmutableArray ReadVersionedObjectArray(long addr, long count) + where TType : IReadable, new(); + + public ImmutableArray ReadVersionedObjectArray(long count) + where TType : IReadable, new(); } public class FileFormatStream @@ -161,7 +184,7 @@ public static IFileFormatStream Load(Stream stream, LoadOptions loadOptions = nu } } - public abstract class FileFormatStream : BinaryObjectStream, IFileFormatStream where T : FileFormatStream + public abstract class FileFormatStream : BinaryObjectStreamReader, IFileFormatStream where T : FileFormatStream { public abstract string DefaultFilename { get; } @@ -323,5 +346,15 @@ public bool TryMapFileOffsetToVA(uint offset, out ulong va) { array.Add(ReadMappedObject(pointers[i])); return array; } + + public TType ReadMappedPrimitive(ulong addr) where TType : unmanaged => ReadPrimitive(MapVATR(addr)); + + public ImmutableArray ReadMappedPrimitiveArray(ulong addr, long count) where TType : unmanaged + => ReadPrimitiveArray(MapVATR(addr), count); + + public TType ReadMappedVersionedObject(ulong addr) where TType : IReadable, new() => ReadVersionedObject(MapVATR(addr)); + + public ImmutableArray ReadMappedVersionedObjectArray(ulong addr, long count) where TType : IReadable, new() + => ReadVersionedObjectArray(MapVATR(addr), count); } } \ No newline at end of file diff --git a/Il2CppInspector.Common/FileFormatStreams/UBReader.cs b/Il2CppInspector.Common/FileFormatStreams/UBReader.cs index c4ff2550..38d27f57 100644 --- a/Il2CppInspector.Common/FileFormatStreams/UBReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/UBReader.cs @@ -38,7 +38,7 @@ public override IFileFormatStream this[uint index] { Position = arch.Offset; Endianness = Endianness.Little; - using var s = new BinaryObjectStream(ReadBytes((int) arch.Size)); + using var s = new BinaryObjectStream(ReadBytes((int) arch.Size).ToArray()); return (IFileFormatStream) MachOReader32.Load(s, LoadOptions, OnStatusUpdate) ?? MachOReader64.Load(s, LoadOptions, OnStatusUpdate); } } diff --git a/Il2CppInspector.Common/IL2CPP/Metadata.cs b/Il2CppInspector.Common/IL2CPP/Metadata.cs index 63116d16..7040ff7e 100644 --- a/Il2CppInspector.Common/IL2CPP/Metadata.cs +++ b/Il2CppInspector.Common/IL2CPP/Metadata.cs @@ -17,7 +17,7 @@ All rights reserved. namespace Il2CppInspector { - public class Metadata : BinaryObjectStream + public class Metadata : BinaryObjectStreamReader { public Il2CppGlobalMetadataHeader Header { get; set; } diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppType.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppType.cs index 7b518ac9..508576f7 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppType.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppType.cs @@ -76,7 +76,7 @@ public bool ValueType public void Read(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct { Data.Read(ref reader, version); - Value = reader.Read(); + Value = reader.ReadPrimitive(); if (MetadataVersions.V272 > version) { diff --git a/Il2CppInspector.Common/Next/BinaryObjectStreamReader.cs b/Il2CppInspector.Common/Next/BinaryObjectStreamReader.cs new file mode 100644 index 00000000..a5fae3e9 --- /dev/null +++ b/Il2CppInspector.Common/Next/BinaryObjectStreamReader.cs @@ -0,0 +1,130 @@ +using System.Collections.Immutable; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using NoisyCowStudios.Bin2Object; +using VersionedSerialization; + +namespace Il2CppInspector.Next; + +public class BinaryObjectStreamReader : BinaryObjectStream, IReader +{ + public virtual int Bits { get; set; } + public bool Is32Bit => Bits == 32; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static TTo Cast(in TFrom from) => Unsafe.As(ref Unsafe.AsRef(in from)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private T ReadInternal() where T : unmanaged + { + var size = Unsafe.SizeOf(); + var value = MemoryMarshal.Read(ReadBytes(size)); + return value; + } + + public T ReadPrimitive() where T : unmanaged + { + if (typeof(T) == typeof(sbyte)) + return Cast(ReadByte()); + + if (typeof(T) == typeof(short)) + return Cast(ReadInt16()); + + if (typeof(T) == typeof(int)) + return Cast(ReadInt32()); + + if (typeof(T) == typeof(long)) + return Cast(ReadInt64()); + + if (typeof(T) == typeof(byte)) + return Cast(ReadByte()); + + if (typeof(T) == typeof(ushort)) + return Cast(ReadUInt16()); + + if (typeof(T) == typeof(uint)) + return Cast(ReadUInt32()); + + if (typeof(T) == typeof(ulong)) + return Cast(ReadUInt64()); + + return ReadInternal(); + } + + public ImmutableArray ReadPrimitiveArray(long count) where T : unmanaged + { + var array = ImmutableArray.CreateBuilder(checked((int)count)); + for (long i = 0; i < count; i++) + array.Add(ReadPrimitive()); + + return array.MoveToImmutable(); + } + + public T ReadVersionedObject() where T : IReadable, new() => ReadVersionedObject(Version); + + public T ReadVersionedObject(in StructVersion version = default) where T : IReadable, new() + { + var obj = new T(); + var a = this; + obj.Read(ref a, in version); + return obj; + } + + public ImmutableArray ReadVersionedObjectArray(long count) where T : IReadable, new() => ReadVersionedObjectArray(count, Version); + + public ImmutableArray ReadVersionedObjectArray(long count, in StructVersion version = default) where T : IReadable, new() + { + var array = ImmutableArray.CreateBuilder(checked((int)count)); + for (long i = 0; i < count; i++) + array.Add(ReadVersionedObject(in version)); + + return array.MoveToImmutable(); + } + + public long ReadNInt() + => Is32Bit ? ReadPrimitive() : ReadPrimitive(); + + public ulong ReadNUInt() + => Is32Bit ? ReadPrimitive() : ReadPrimitive(); + + public string ReadString() => ReadNullTerminatedString(); + + public new ReadOnlySpan ReadBytes(int length) + { + return base.ReadBytes(length); + } + + public void Align(int alignment = 0) + { + if (alignment == 0) + alignment = Is32Bit ? 4 : 8; + + var rem = Position % alignment; + if (rem != 0) + Position += alignment - rem; + } + + public TType ReadPrimitive(long addr) where TType : unmanaged + { + Position = addr; + return ReadPrimitive(); + } + + public ImmutableArray ReadPrimitiveArray(long addr, long count) where TType : unmanaged + { + Position = addr; + return ReadPrimitiveArray(count); + } + + public TType ReadVersionedObject(long addr) where TType : IReadable, new() + { + Position = addr; + return ReadVersionedObject(Version); + } + + public ImmutableArray ReadVersionedObjectArray(long addr, long count) where TType : IReadable, new() + { + Position = addr; + return ReadVersionedObjectArray(count, Version); + } +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyNameDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyNameDefinition.cs index f93ab692..83068e72 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyNameDefinition.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyNameDefinition.cs @@ -34,7 +34,7 @@ public partial struct Il2CppAssemblyNameDefinition [FieldOffset(44)] [VersionCondition(LessThan = "15.0")] - [CustomSerialization("reader.Read();", "8")] + [CustomSerialization("reader.ReadPrimitive();", "8")] private PublicKeyToken _legacyPublicKeyToken; [FieldOffset(16)] @@ -59,6 +59,6 @@ public partial struct Il2CppAssemblyNameDefinition public int Revision; [FieldOffset(44)] - [CustomSerialization("reader.Read();", "8")] + [CustomSerialization("reader.ReadPrimitive();", "8")] public PublicKeyToken PublicKeyToken; } \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Pointer.cs b/Il2CppInspector.Common/Next/Pointer.cs index a3a11130..db0de19b 100644 --- a/Il2CppInspector.Common/Next/Pointer.cs +++ b/Il2CppInspector.Common/Next/Pointer.cs @@ -25,13 +25,13 @@ public static int Size(in StructVersion version = default, bool is32Bit = false) public readonly T Read(ref SpanReader reader, in StructVersion version) { reader.Offset = (int)PointerValue; - return reader.ReadObject(version); + return reader.ReadVersionedObject(version); } public readonly ImmutableArray ReadArray(ref SpanReader reader, long count, in StructVersion version) { reader.Offset = (int)PointerValue; - return reader.ReadObjectArray(count, version); + return reader.ReadVersionedObjectArray(count, version); } public static implicit operator Pointer(ulong value) => new(value); diff --git a/VersionedSerialization.Generator/ObjectSerializationGenerator.cs b/VersionedSerialization.Generator/ObjectSerializationGenerator.cs index 5ab1c966..ca372802 100644 --- a/VersionedSerialization.Generator/ObjectSerializationGenerator.cs +++ b/VersionedSerialization.Generator/ObjectSerializationGenerator.cs @@ -213,13 +213,13 @@ private static ObjectSerializationInfo ParseSerializationInfo(TypeDeclarationSyn string readMethod; if (typeInfo.Type == PropertyType.None) { - readMethod = $"reader.ReadObject<{typeInfo.ComplexTypeName}>(in version);"; + readMethod = $"reader.ReadVersionedObject<{typeInfo.ComplexTypeName}>(in version);"; } else { readMethod = typeInfo.Type.IsSeperateMethod() ? $"reader.Read{typeInfo.Type.GetTypeName()}();" - : $"reader.Read<{typeInfo.Type.GetTypeName()}>();"; + : $"reader.ReadPrimitive<{typeInfo.Type.GetTypeName()}>();"; if (typeInfo.ComplexTypeName != "") readMethod = $"({typeInfo.ComplexTypeName}){readMethod}"; diff --git a/VersionedSerialization/IReader.cs b/VersionedSerialization/IReader.cs index 75b5208e..c216fc27 100644 --- a/VersionedSerialization/IReader.cs +++ b/VersionedSerialization/IReader.cs @@ -12,11 +12,11 @@ public interface IReader string ReadString(); ReadOnlySpan ReadBytes(int length); - T Read() where T : unmanaged; - ImmutableArray ReadArray(long count) where T : unmanaged; + T ReadPrimitive() where T : unmanaged; + ImmutableArray ReadPrimitiveArray(long count) where T : unmanaged; - T ReadObject(in StructVersion version = default) where T : IReadable, new(); - ImmutableArray ReadObjectArray(long count, in StructVersion version = default) where T : IReadable, new(); + T ReadVersionedObject(in StructVersion version = default) where T : IReadable, new(); + ImmutableArray ReadVersionedObjectArray(long count, in StructVersion version = default) where T : IReadable, new(); public void Align(int alignment = 0); } \ No newline at end of file diff --git a/VersionedSerialization/ReaderExtensions.cs b/VersionedSerialization/ReaderExtensions.cs index 93912ed9..58b00a39 100644 --- a/VersionedSerialization/ReaderExtensions.cs +++ b/VersionedSerialization/ReaderExtensions.cs @@ -7,20 +7,20 @@ public static class ReaderExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint ReadCompressedUInt(this ref T reader) where T : struct, IReader, allows ref struct { - var first = reader.Read(); + var first = reader.ReadPrimitive(); if ((first & 0b10000000) == 0b00000000) return first; if ((first & 0b11000000) == 0b10000000) - return (uint)(((first & ~0b10000000) << 8) | reader.Read()); + return (uint)(((first & ~0b10000000) << 8) | reader.ReadPrimitive()); if ((first & 0b11100000) == 0b11000000) - return (uint)(((first & ~0b11000000) << 24) | (reader.Read() << 16) | (reader.Read() << 8) | reader.Read()); + return (uint)(((first & ~0b11000000) << 24) | (reader.ReadPrimitive() << 16) | (reader.ReadPrimitive() << 8) | reader.ReadPrimitive()); return first switch { - 0b11110000 => reader.Read(), + 0b11110000 => reader.ReadPrimitive(), 0b11111110 => uint.MaxValue - 1, 0b11111111 => uint.MaxValue, _ => throw new InvalidDataException("Invalid compressed uint") @@ -49,7 +49,7 @@ public static ulong ReadSLEB128(this ref T reader) where T : struct, IReader, do { - current = reader.Read(); + current = reader.ReadPrimitive(); value |= (current & 0x7FuL) << shift; shift += 7; } while ((current & 0x80) != 0); diff --git a/VersionedSerialization/SpanReader.cs b/VersionedSerialization/SpanReader.cs index 78dfab5b..de26c030 100644 --- a/VersionedSerialization/SpanReader.cs +++ b/VersionedSerialization/SpanReader.cs @@ -40,7 +40,7 @@ public ReadOnlySpan ReadBytes(int length) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T Read() where T : unmanaged + public T ReadPrimitive() where T : unmanaged { if (typeof(T) == typeof(byte)) return Cast(_data[Offset++]); @@ -84,17 +84,17 @@ public T Read() where T : unmanaged } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ImmutableArray ReadArray(long count) where T : unmanaged + public ImmutableArray ReadPrimitiveArray(long count) where T : unmanaged { var array = ImmutableArray.CreateBuilder(checked((int)count)); for (long i = 0; i < count; i++) - array.Add(Read()); + array.Add(ReadPrimitive()); return array.MoveToImmutable(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T ReadObject(in StructVersion version = default) where T : IReadable, new() + public T ReadVersionedObject(in StructVersion version = default) where T : IReadable, new() { var obj = new T(); obj.Read(ref this, in version); @@ -102,11 +102,11 @@ public ImmutableArray ReadArray(long count) where T : unmanaged } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ImmutableArray ReadObjectArray(long count, in StructVersion version = default) where T : IReadable, new() + public ImmutableArray ReadVersionedObjectArray(long count, in StructVersion version = default) where T : IReadable, new() { var array = ImmutableArray.CreateBuilder(checked((int)count)); for (long i = 0; i < count; i++) - array.Add(ReadObject(in version)); + array.Add(ReadVersionedObject(in version)); return array.MoveToImmutable(); } @@ -126,13 +126,13 @@ public string ReadString() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool ReadBoolean() => Read() != 0; + public bool ReadBoolean() => ReadPrimitive() != 0; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ulong ReadNUInt() => _is32Bit ? Read() : Read(); + public ulong ReadNUInt() => _is32Bit ? ReadPrimitive() : ReadPrimitive(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public long ReadNInt() => _is32Bit ? Read() : Read(); + public long ReadNInt() => _is32Bit ? ReadPrimitive() : ReadPrimitive(); public void Align(int alignment = 0) { From 43d7433e12982cbdfe53967097e3d39729535b7b Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Sat, 17 Aug 2024 13:52:09 +0200 Subject: [PATCH 06/27] Rework metadata struct loading to use new struct versioning --- .../FileFormatStreams/FileFormatStream.cs | 33 ++++ .../IL2CPP/CustomAttributeDataReader.cs | 10 +- Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs | 79 ++++----- .../IL2CPP/Il2CppInspector.cs | 53 +++--- Il2CppInspector.Common/IL2CPP/ImageScan.cs | 58 ++++--- Il2CppInspector.Common/IL2CPP/Metadata.cs | 155 +++++++----------- .../IL2CPP/MetadataUsage.cs | 4 +- .../Next/BinaryMetadata/Il2CppArrayType.cs | 9 +- .../BinaryMetadata/Il2CppCodeGenModule.cs | 30 ++-- .../BinaryMetadata/Il2CppCodeRegistration.cs | 38 +++-- .../Next/BinaryMetadata/Il2CppGenericClass.cs | 8 +- .../BinaryMetadata/Il2CppGenericContext.cs | 2 +- .../Next/BinaryMetadata/Il2CppGenericInst.cs | 4 +- ...Il2CppGenericMethodFunctionsDefinitions.cs | 2 +- .../Il2CppGenericMethodIndices.cs | 2 +- .../Next/BinaryMetadata/Il2CppGuid.cs | 3 +- .../Next/BinaryMetadata/Il2CppInteropData.cs | 2 +- .../Il2CppMetadataRegistration.cs | 25 ++- .../BinaryMetadata/Il2CppMethodPointer.cs | 4 +- .../Next/BinaryMetadata/Il2CppMethodSpec.cs | 2 +- .../Next/BinaryMetadata/Il2CppRange.cs | 2 +- .../Il2CppRgctxConstrainedData.cs | 2 +- .../BinaryMetadata/Il2CppRgctxDefinition.cs | 11 +- .../Il2CppRgctxDefinitionData.cs | 2 +- .../Il2CppTokenAdjustorThunkPair.cs | 4 +- .../Il2CppTokenIndexMethodTuple.cs | 5 +- .../BinaryMetadata/Il2CppTokenRangePair.cs | 2 +- .../Il2CppTypeDefinitionSizes.cs | 2 +- .../Il2CppWindowsRuntimeFactoryTableEntry.cs | 2 +- .../Next/BinaryObjectStreamReader.cs | 17 ++ .../Next/Metadata/Il2CppAssemblyDefinition.cs | 2 +- .../Metadata/Il2CppAssemblyNameDefinition.cs | 2 +- .../Il2CppCustomAttributeDataRange.cs | 2 +- .../Il2CppCustomAttributeTypeRange.cs | 2 +- .../Next/Metadata/Il2CppEventDefinition.cs | 2 +- .../Next/Metadata/Il2CppFieldDefaultValue.cs | 2 +- .../Next/Metadata/Il2CppFieldDefinition.cs | 2 +- .../Next/Metadata/Il2CppFieldMarshaledSize.cs | 2 +- .../Next/Metadata/Il2CppFieldRef.cs | 2 +- .../Next/Metadata/Il2CppGenericContainer.cs | 2 +- .../Next/Metadata/Il2CppGenericParameter.cs | 2 +- .../Metadata/Il2CppGlobalMetadataHeader.cs | 2 +- .../Next/Metadata/Il2CppImageDefinition.cs | 2 +- .../Metadata/Il2CppInterfaceOffsetPair.cs | 2 +- .../Next/Metadata/Il2CppMetadataRange.cs | 2 +- .../Next/Metadata/Il2CppMetadataUsage.cs | 2 +- .../Next/Metadata/Il2CppMetadataUsageList.cs | 2 +- .../Next/Metadata/Il2CppMetadataUsagePair.cs | 2 +- .../Next/Metadata/Il2CppMethodDefinition.cs | 2 +- .../Metadata/Il2CppParameterDefaultValue.cs | 2 +- .../Metadata/Il2CppParameterDefinition.cs | 2 +- .../Next/Metadata/Il2CppPropertyDefinition.cs | 2 +- .../Next/Metadata/Il2CppStringLiteral.cs | 2 +- .../Next/Metadata/Il2CppTypeDefinition.cs | 2 +- .../Metadata/Il2CppTypeDefinitionBitfield.cs | 2 +- .../Il2CppWindowsRuntimeTypeNamePair.cs | 2 +- Il2CppInspector.Common/Next/Pointer.cs | 4 +- .../Next/PrimitivePointer.cs | 60 +++++++ .../Reflection/TypeModel.cs | 12 +- Il2CppInspector.Common/Utils/BlobReader.cs | 8 +- .../Models/ObjectSerializationInfo.cs | 6 +- .../Models/PropertySerializationInfo.cs | 2 +- .../Models/PropertyType.cs | 40 +++++ .../ObjectSerializationGenerator.cs | 105 ++++++------ .../Utils/Constants.cs | 2 +- .../Attributes/AlignedAttribute.cs | 6 - .../Attributes/NativeIntegerAttribute.cs | 3 + VersionedSerialization/IReader.cs | 3 +- VersionedSerialization/SpanReader.cs | 5 + 69 files changed, 497 insertions(+), 383 deletions(-) create mode 100644 Il2CppInspector.Common/Next/PrimitivePointer.cs delete mode 100644 VersionedSerialization/Attributes/AlignedAttribute.cs create mode 100644 VersionedSerialization/Attributes/NativeIntegerAttribute.cs diff --git a/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs b/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs index dab3cfb8..f7db93fa 100644 --- a/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs +++ b/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs @@ -94,6 +94,9 @@ public interface IFileFormatStream long[] ReadMappedWordArray(ulong uiAddr, int count); List ReadMappedObjectPointerArray(ulong uiAddr, int count) where U : new(); + ulong ReadMappedUWord(ulong uiAddr); + ulong[] ReadMappedUWordArray(ulong uiAddr, int count); + void WriteEndianBytes(byte[] bytes); void Write(long int64); void Write(ulong uint64); @@ -144,6 +147,9 @@ public ImmutableArray ReadVersionedObjectArray(long addr, long cou public ImmutableArray ReadVersionedObjectArray(long count) where TType : IReadable, new(); + + public ImmutableArray ReadMappedVersionedObjectPointerArray(ulong addr, int count) + where TType : IReadable, new(); } public class FileFormatStream @@ -356,5 +362,32 @@ public ImmutableArray ReadMappedPrimitiveArray(ulong addr, long co public ImmutableArray ReadMappedVersionedObjectArray(ulong addr, long count) where TType : IReadable, new() => ReadVersionedObjectArray(MapVATR(addr), count); + + public ImmutableArray ReadMappedVersionedObjectPointerArray(ulong addr, int count) + where TType : IReadable, new() + { + var pointers = ReadMappedUWordArray(addr, count); + var builder = ImmutableArray.CreateBuilder((int)count); + for (long i = 0; i < count; i++) + builder.Add(ReadMappedVersionedObject(pointers[i])); + + return builder.MoveToImmutable(); + } + + public ulong ReadMappedUWord(ulong uiAddr) + { + Position = MapVATR(uiAddr); + return ReadNUInt(); + } + + public ulong[] ReadMappedUWordArray(ulong uiAddr, int count) + { + Position = MapVATR(uiAddr); + var arr = new ulong[count]; + for (int i = 0; i < count; i++) + arr[i] = ReadNUInt(); + + return arr; + } } } \ No newline at end of file diff --git a/Il2CppInspector.Common/IL2CPP/CustomAttributeDataReader.cs b/Il2CppInspector.Common/IL2CPP/CustomAttributeDataReader.cs index 423646a5..136f0aa8 100644 --- a/Il2CppInspector.Common/IL2CPP/CustomAttributeDataReader.cs +++ b/Il2CppInspector.Common/IL2CPP/CustomAttributeDataReader.cs @@ -4,11 +4,11 @@ using System.IO; using System.Linq; using dnlib.DotNet; +using Il2CppInspector.Next; using Il2CppInspector.Next.BinaryMetadata; using Il2CppInspector.Next.Metadata; using Il2CppInspector.Reflection; using Il2CppInspector.Utils; -using NoisyCowStudios.Bin2Object; namespace Il2CppInspector { @@ -16,7 +16,7 @@ public class CustomAttributeDataReader { private readonly Il2CppInspector _inspector; private readonly Assembly _assembly; - private readonly BinaryObjectStream _data; + private readonly BinaryObjectStreamReader _data; private readonly uint _start; private readonly uint _end; @@ -26,7 +26,7 @@ public class CustomAttributeDataReader public uint Count { get; } - public CustomAttributeDataReader(Il2CppInspector inspector, Assembly assembly, BinaryObjectStream data, uint startOffset, uint endOffset) + public CustomAttributeDataReader(Il2CppInspector inspector, Assembly assembly, BinaryObjectStreamReader data, uint startOffset, uint endOffset) { _inspector = inspector; _assembly = assembly; @@ -146,8 +146,8 @@ private object ConvertAttributeValue(object value) private TypeInfo ConvertTypeDef(Il2CppTypeDefinition typeDef, Il2CppTypeEnum type) => typeDef.IsValid - ? _assembly.Model.GetTypeDefinitionFromTypeEnum(type) - : _assembly.Model.TypesByDefinitionIndex[Array.IndexOf(_inspector.TypeDefinitions, typeDef)]; + ? _assembly.Model.TypesByDefinitionIndex[_inspector.TypeDefinitions.IndexOf(typeDef)] + : _assembly.Model.GetTypeDefinitionFromTypeEnum(type); private (TypeInfo, int) ReadCustomAttributeNamedArgumentClassAndIndex(TypeInfo attrInfo) { diff --git a/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs b/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs index 16fd6204..ab7c7aa0 100644 --- a/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs +++ b/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs @@ -8,6 +8,7 @@ All rights reserved. using Il2CppInspector.Next; using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Linq; @@ -37,16 +38,16 @@ public abstract partial class Il2CppBinary public ulong CodeRegistrationPointer { get; private set; } public ulong MetadataRegistrationPointer { get; private set; } public ulong RegistrationFunctionPointer { get; private set; } - public Dictionary CodeGenModulePointers { get; } = new Dictionary(); + public Dictionary CodeGenModulePointers { get; } = new(); // Only for <=v24.1 public ulong[] GlobalMethodPointers { get; set; } // Only for >=v24.2 - public Dictionary ModuleMethodPointers { get; set; } = new Dictionary(); + public Dictionary ModuleMethodPointers { get; set; } = new(); // Only for >=v24.2. In earlier versions, invoker indices are stored in Il2CppMethodDefinition in the metadata file - public Dictionary MethodInvokerIndices { get; set; } = new Dictionary(); + public Dictionary> MethodInvokerIndices { get; set; } = new(); // NOTE: In versions <21 and earlier releases of v21, use FieldOffsets: // global field index => field offset @@ -54,7 +55,7 @@ public abstract partial class Il2CppBinary // type index => RVA in image where the list of field offsets for the type start (4 bytes per field) // Negative field offsets from start of each function - public uint[] FieldOffsets { get; private set; } + public ImmutableArray FieldOffsets { get; private set; } // Pointers to field offsets public long[] FieldOffsetPointers { get; private set; } @@ -68,13 +69,13 @@ public abstract partial class Il2CppBinary public ulong[] MethodInvokePointers { get; private set; } // Version 16 and below: method references for vtable - public uint[] VTableMethodReferences { get; private set; } + public ImmutableArray VTableMethodReferences { get; private set; } // Generic method specs for vtables - public Il2CppMethodSpec[] MethodSpecs { get; private set; } + public ImmutableArray MethodSpecs { get; private set; } // List of run-time concrete generic class and method signatures - public List GenericInstances { get; private set; } + public ImmutableArray GenericInstances { get; private set; } // List of constructed generic method function pointers corresponding to each possible method instantiation public Dictionary GenericMethodPointers { get; } = new Dictionary(); @@ -83,7 +84,7 @@ public abstract partial class Il2CppBinary public Dictionary GenericMethodInvokerIndices { get; } = new Dictionary(); // Every type reference (TypeRef) sorted by index - public List TypeReferences { get; private set; } + public ImmutableArray TypeReferences { get; private set; } // Every type reference index sorted by virtual address public Dictionary TypeReferenceIndicesByAddress { get; private set; } @@ -92,7 +93,7 @@ public abstract partial class Il2CppBinary // One assembly may contain multiple modules public Dictionary Modules { get; private set; } - public List TypeDefinitionSizes { get; private set; } + public ImmutableArray TypeDefinitionSizes { get; private set; } // Status update callback private EventHandler OnStatusUpdate { get; set; } @@ -280,30 +281,8 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) Console.WriteLine("MetadataRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? metadataRegistration & 0xffff_ffff : metadataRegistration, Image.MapVATR(metadataRegistration)); // Root structures from which we find everything else - CodeRegistration = Image.ReadMappedObject(codeRegistration); - MetadataRegistration = Image.ReadMappedObject(metadataRegistration); - - // genericAdjustorThunks was inserted before invokerPointersCount in 24.5 and 27.1 - // pointer expected if we need to bump version - if (Image.Version == MetadataVersions.V244 && CodeRegistration.InvokerPointersCount > 0x50000) - { - Image.Version = MetadataVersions.V245; - CodeRegistration = Image.ReadMappedObject(codeRegistration); - } - - if (Image.Version == MetadataVersions.V244 && CodeRegistration.ReversePInvokeWrapperCount > 0x50000) { - Image.Version = MetadataVersions.V245; - codeRegistration -= 1 * pointerSize; - CodeRegistration = Image.ReadMappedObject(codeRegistration); - } - - if ((Image.Version == MetadataVersions.V290 || Image.Version == MetadataVersions.V310) && - (long)CodeRegistration.GenericMethodPointersCount - MetadataRegistration.GenericMethodTableCount > 0x10000) - { - Image.Version = new StructVersion(Image.Version.Major, 1, Image.Version.Tag); - codeRegistration -= 2 * pointerSize; - CodeRegistration = Image.ReadMappedObject(codeRegistration); - } + CodeRegistration = Image.ReadMappedVersionedObject(codeRegistration); + MetadataRegistration = Image.ReadMappedVersionedObject(metadataRegistration); // Plugin hook to pre-process binary isModified |= PluginHooks.PreProcessBinary(this).IsStreamModified; @@ -326,7 +305,7 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) // The global method pointer list was deprecated in v24.2 in favour of Il2CppCodeGenModule if (Image.Version <= MetadataVersions.V241) - GlobalMethodPointers = Image.ReadMappedArray(CodeRegistration.MethodPointers, (int) CodeRegistration.MethodPointersCount); + GlobalMethodPointers = Image.ReadMappedUWordArray(CodeRegistration.MethodPointers, (int) CodeRegistration.MethodPointersCount); // After v24 method pointers and RGCTX data were stored in Il2CppCodeGenModules if (Image.Version >= MetadataVersions.V242) { @@ -336,12 +315,12 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) // if this changes we'll have to get smarter about disambiguating these two. if (CodeRegistration.CodeGenModulesCount == 0) { Image.Version = MetadataVersions.V243; - CodeRegistration = Image.ReadMappedObject(codeRegistration); + CodeRegistration = Image.ReadMappedVersionedObject(codeRegistration); } // Array of pointers to Il2CppCodeGenModule - var codeGenModulePointers = Image.ReadMappedArray(CodeRegistration.CodeGenModules, (int) CodeRegistration.CodeGenModulesCount); - var modules = Image.ReadMappedObjectPointerArray(CodeRegistration.CodeGenModules, (int) CodeRegistration.CodeGenModulesCount); + var codeGenModulePointers = Image.ReadMappedUWordArray(CodeRegistration.CodeGenModules, (int) CodeRegistration.CodeGenModulesCount); + var modules = Image.ReadMappedVersionedObjectPointerArray(CodeRegistration.CodeGenModules, (int) CodeRegistration.CodeGenModulesCount); foreach (var mp in modules.Zip(codeGenModulePointers, (m, p) => new { Module = m, Pointer = p })) { var module = mp.Module; @@ -355,13 +334,13 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) // the entire method pointer array will be NULL values, causing the methodPointer to be mapped to .bss // and therefore out of scope of the binary image try { - ModuleMethodPointers.Add(module, Image.ReadMappedArray(module.MethodPointers, (int) module.MethodPointerCount)); + ModuleMethodPointers.Add(module, Image.ReadMappedUWordArray(module.MethodPointers, (int) module.MethodPointerCount)); } catch (InvalidOperationException) { ModuleMethodPointers.Add(module, new ulong[module.MethodPointerCount]); } // Read method invoker pointer indices - one per method - MethodInvokerIndices.Add(module, Image.ReadMappedArray(module.InvokerIndices, (int) module.MethodPointerCount)); + MethodInvokerIndices.Add(module, Image.ReadMappedPrimitiveArray(module.InvokerIndices, (int) module.MethodPointerCount)); } } @@ -381,24 +360,24 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) // All older versions use values directly in the array if (!fieldOffsetsArePointers) - FieldOffsets = Image.ReadMappedArray(MetadataRegistration.FieldOffsets, (int)MetadataRegistration.FieldOffsetsCount); + FieldOffsets = Image.ReadMappedPrimitiveArray(MetadataRegistration.FieldOffsets, (int)MetadataRegistration.FieldOffsetsCount); else FieldOffsetPointers = Image.ReadMappedWordArray(MetadataRegistration.FieldOffsets, (int)MetadataRegistration.FieldOffsetsCount); // Type references (pointer array) - var typeRefPointers = Image.ReadMappedArray(MetadataRegistration.Types, (int) MetadataRegistration.TypesCount); + var typeRefPointers = Image.ReadMappedUWordArray(MetadataRegistration.Types, (int) MetadataRegistration.TypesCount); TypeReferenceIndicesByAddress = typeRefPointers.Zip(Enumerable.Range(0, typeRefPointers.Length), (a, i) => new { a, i }).ToDictionary(x => x.a, x => x.i); - TypeReferences = Image.ReadMappedObjectPointerArray(MetadataRegistration.Types, (int)MetadataRegistration.TypesCount); + TypeReferences = Image.ReadMappedVersionedObjectPointerArray(MetadataRegistration.Types, (int)MetadataRegistration.TypesCount); // Custom attribute constructors (function pointers) // This is managed in Il2CppInspector for metadata >= 27 if (Image.Version < MetadataVersions.V270) { - CustomAttributeGenerators = Image.ReadMappedArray(CodeRegistration.CustomAttributeGenerators, (int) CodeRegistration.CustomAttributeCount); + CustomAttributeGenerators = Image.ReadMappedUWordArray(CodeRegistration.CustomAttributeGenerators, (int) CodeRegistration.CustomAttributeCount); } // Method.Invoke function pointers - MethodInvokePointers = Image.ReadMappedArray(CodeRegistration.InvokerPointers, (int) CodeRegistration.InvokerPointersCount); + MethodInvokePointers = Image.ReadMappedUWordArray(CodeRegistration.InvokerPointers, (int) CodeRegistration.InvokerPointersCount); // TODO: Function pointers as shown below // reversePInvokeWrappers @@ -408,24 +387,24 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) // >=23: interopData if (Image.Version < MetadataVersions.V190) { - VTableMethodReferences = Image.ReadMappedArray(MetadataRegistration.MethodReferences, (int)MetadataRegistration.MethodReferencesCount); + VTableMethodReferences = Image.ReadMappedPrimitiveArray(MetadataRegistration.MethodReferences, (int)MetadataRegistration.MethodReferencesCount); } // Generic type and method specs (open and closed constructed types) - MethodSpecs = Image.ReadMappedArray(MetadataRegistration.MethodSpecs, (int) MetadataRegistration.MethodSpecsCount); + MethodSpecs = Image.ReadMappedVersionedObjectArray(MetadataRegistration.MethodSpecs, (int) MetadataRegistration.MethodSpecsCount); // Concrete generic class and method signatures - GenericInstances = Image.ReadMappedObjectPointerArray(MetadataRegistration.GenericInsts, (int) MetadataRegistration.GenericInstsCount); + GenericInstances = Image.ReadMappedVersionedObjectPointerArray(MetadataRegistration.GenericInsts, (int) MetadataRegistration.GenericInstsCount); // Concrete generic method pointers - var genericMethodPointers = Image.ReadMappedArray(CodeRegistration.GenericMethodPointers, (int) CodeRegistration.GenericMethodPointersCount); - var genericMethodTable = Image.ReadMappedArray(MetadataRegistration.GenericMethodTable, (int) MetadataRegistration.GenericMethodTableCount); + var genericMethodPointers = Image.ReadMappedUWordArray(CodeRegistration.GenericMethodPointers, (int) CodeRegistration.GenericMethodPointersCount); + var genericMethodTable = Image.ReadMappedVersionedObjectArray(MetadataRegistration.GenericMethodTable, (int) MetadataRegistration.GenericMethodTableCount); foreach (var tableEntry in genericMethodTable) { GenericMethodPointers.Add(MethodSpecs[tableEntry.GenericMethodIndex], genericMethodPointers[tableEntry.Indices.MethodIndex]); GenericMethodInvokerIndices.Add(MethodSpecs[tableEntry.GenericMethodIndex], tableEntry.Indices.InvokerIndex); } - TypeDefinitionSizes = Image.ReadMappedObjectPointerArray( + TypeDefinitionSizes = Image.ReadMappedVersionedObjectPointerArray( MetadataRegistration.TypeDefinitionsSizes, (int) MetadataRegistration.TypeDefinitionsSizesCount); // Plugin hook to pre-process binary diff --git a/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs b/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs index 8eb55707..b0ed4014 100644 --- a/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs +++ b/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs @@ -9,6 +9,7 @@ All rights reserved. using NoisyCowStudios.Bin2Object; using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.IO.Compression; using System.Linq; @@ -41,38 +42,38 @@ public class Il2CppInspector public Dictionary Strings => Metadata.Strings; public string[] StringLiterals => Metadata.StringLiterals; - public Il2CppTypeDefinition[] TypeDefinitions => Metadata.Types; - public Il2CppAssemblyDefinition[] Assemblies => Metadata.Assemblies; - public Il2CppImageDefinition[] Images => Metadata.Images; - public Il2CppMethodDefinition[] Methods => Metadata.Methods; - public Il2CppParameterDefinition[] Params => Metadata.Params; - public Il2CppFieldDefinition[] Fields => Metadata.Fields; - public Il2CppPropertyDefinition[] Properties => Metadata.Properties; - public Il2CppEventDefinition[] Events => Metadata.Events; - public Il2CppGenericContainer[] GenericContainers => Metadata.GenericContainers; - public Il2CppGenericParameter[] GenericParameters => Metadata.GenericParameters; - public int[] GenericConstraintIndices => Metadata.GenericConstraintIndices; - public Il2CppCustomAttributeTypeRange[] AttributeTypeRanges => Metadata.AttributeTypeRanges; - public Il2CppCustomAttributeDataRange[] AttributeDataRanges => Metadata.AttributeDataRanges; - public Il2CppInterfaceOffsetPair[] InterfaceOffsets => Metadata.InterfaceOffsets; - public int[] InterfaceUsageIndices => Metadata.InterfaceUsageIndices; - public int[] NestedTypeIndices => Metadata.NestedTypeIndices; - public int[] AttributeTypeIndices => Metadata.AttributeTypeIndices; - public uint[] VTableMethodIndices => Metadata.VTableMethodIndices; - public Il2CppFieldRef[] FieldRefs => Metadata.FieldRefs; + public ImmutableArray TypeDefinitions => Metadata.Types; + public ImmutableArray Assemblies => Metadata.Assemblies; + public ImmutableArray Images => Metadata.Images; + public ImmutableArray Methods => Metadata.Methods; + public ImmutableArray Params => Metadata.Params; + public ImmutableArray Fields => Metadata.Fields; + public ImmutableArray Properties => Metadata.Properties; + public ImmutableArray Events => Metadata.Events; + public ImmutableArray GenericContainers => Metadata.GenericContainers; + public ImmutableArray GenericParameters => Metadata.GenericParameters; + public ImmutableArray GenericConstraintIndices => Metadata.GenericConstraintIndices; + public ImmutableArray AttributeTypeRanges => Metadata.AttributeTypeRanges; + public ImmutableArray AttributeDataRanges => Metadata.AttributeDataRanges; + public ImmutableArray InterfaceOffsets => Metadata.InterfaceOffsets; + public ImmutableArray InterfaceUsageIndices => Metadata.InterfaceUsageIndices; + public ImmutableArray NestedTypeIndices => Metadata.NestedTypeIndices; + public ImmutableArray AttributeTypeIndices => Metadata.AttributeTypeIndices; + public ImmutableArray VTableMethodIndices => Metadata.VTableMethodIndices; + public ImmutableArray FieldRefs => Metadata.FieldRefs; public Dictionary FieldDefaultValue { get; } = new Dictionary(); public Dictionary ParameterDefaultValue { get; } = new Dictionary(); public List FieldOffsets { get; } - public List TypeReferences => Binary.TypeReferences; + public ImmutableArray TypeReferences => Binary.TypeReferences; public Dictionary TypeReferenceIndicesByAddress => Binary.TypeReferenceIndicesByAddress; - public List GenericInstances => Binary.GenericInstances; + public ImmutableArray GenericInstances => Binary.GenericInstances; public Dictionary Modules => Binary.Modules; public ulong[] CustomAttributeGenerators { get; } public ulong[] MethodInvokePointers { get; } - public Il2CppMethodSpec[] MethodSpecs => Binary.MethodSpecs; + public ImmutableArray MethodSpecs => Binary.MethodSpecs; public Dictionary GenericMethodPointers { get; } public Dictionary GenericMethodInvokerIndices => Binary.GenericMethodInvokerIndices; - public List TypeDefinitionSizes => Binary.TypeDefinitionSizes; + public ImmutableArray TypeDefinitionSizes => Binary.TypeDefinitionSizes; // TODO: Finish all file access in the constructor and eliminate the need for this public IFileFormatStream BinaryImage => Binary.Image; @@ -121,7 +122,7 @@ private List buildMetadataUsages() // Unfortunately the value supplied in MetadataRegistration.matadataUsagesCount seems to be incorrect, // so we have to calculate the correct number of usages above before reading the usage address list from the binary var count = usages.Keys.Max() + 1; - var addresses = Binary.Image.ReadMappedArray(Binary.MetadataRegistration.MetadataUsages, (int) count); + var addresses = Binary.Image.ReadMappedUWordArray(Binary.MetadataRegistration.MetadataUsages, (int) count); foreach (var usage in usages) usage.Value.SetAddress(addresses[usage.Key]); @@ -161,7 +162,7 @@ bool CheckMetadataUsageSanity(MetadataUsage usage) { return usage.Type switch { - MetadataUsageType.TypeInfo or MetadataUsageType.Type => TypeReferences.Count > usage.SourceIndex, + MetadataUsageType.TypeInfo or MetadataUsageType.Type => TypeReferences.Length > usage.SourceIndex, MetadataUsageType.MethodDef => Methods.Length > usage.SourceIndex, MetadataUsageType.FieldInfo or MetadataUsageType.FieldRva => FieldRefs.Length > usage.SourceIndex, MetadataUsageType.StringLiteral => StringLiterals.Length > usage.SourceIndex, @@ -347,7 +348,7 @@ public int GetInvokerIndex(Il2CppCodeGenModule module, Il2CppMethodDefinition me // Version >= 24.2 var methodInModule = (methodDef.Token & 0xffffff); - return Binary.MethodInvokerIndices[module][methodInModule - 1]; + return Binary.MethodInvokerIndices[module][(int)methodInModule - 1]; } public MetadataUsage[] GetVTable(Il2CppTypeDefinition definition) { diff --git a/Il2CppInspector.Common/IL2CPP/ImageScan.cs b/Il2CppInspector.Common/IL2CPP/ImageScan.cs index 869abf32..637ae7be 100644 --- a/Il2CppInspector.Common/IL2CPP/ImageScan.cs +++ b/Il2CppInspector.Common/IL2CPP/ImageScan.cs @@ -11,6 +11,7 @@ All rights reserved. using System.Text; using Il2CppInspector.Next; using Il2CppInspector.Next.BinaryMetadata; +using VersionedSerialization; namespace Il2CppInspector { @@ -147,7 +148,7 @@ ulong FindCodeRegistration() potentialCodeGenModules - (ulong) i * ptrSize, 1)) { var expectedImageCountPtr = potentialCodeRegistrationPtr - ptrSize; - var expectedImageCount = ptrSize == 4 ? Image.ReadMappedInt32(expectedImageCountPtr) : Image.ReadMappedInt64(expectedImageCountPtr); + var expectedImageCount = Image.ReadMappedWord(expectedImageCountPtr); if (expectedImageCount == imagesCount) return potentialCodeRegistrationPtr; } @@ -206,11 +207,12 @@ ulong FindCodeRegistration() // pCodeGenModules is the last field in CodeRegistration so we subtract the size of one pointer from the struct size - codeRegistration = codeRegVa - ((ulong) metadata.Sizeof(typeof(Il2CppCodeRegistration), Image.Version, Image.Bits / 8) - ptrSize); + var codeRegSize = (ulong)Il2CppCodeRegistration.Size(Image.Version, Image.Bits == 32); + codeRegistration = codeRegVa - codeRegSize - ptrSize; // 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. - var cr = Image.ReadMappedObject(codeRegistration); + var cr = Image.ReadMappedVersionedObject(codeRegistration); if (Image.Version == MetadataVersions.V242 && cr.InteropDataCount == 0) { Image.Version = MetadataVersions.V243; @@ -224,6 +226,22 @@ ulong FindCodeRegistration() Image.Version = MetadataVersions.V271; codeRegistration -= ptrSize; } + + // genericAdjustorThunks was inserted before invokerPointersCount in 24.5 and 27.1 + // pointer expected if we need to bump version + if (Image.Version == MetadataVersions.V244 && cr.InvokerPointersCount > 0x50000) + { + Image.Version = MetadataVersions.V245; + codeRegistration += 1 * ptrSize; + cr = Image.ReadMappedVersionedObject(codeRegistration); + } + + if ((Image.Version == MetadataVersions.V290 || Image.Version == MetadataVersions.V310) && + cr.InteropData + cr.InteropDataCount >= (ulong)Image.Length) + { + Image.Version = new StructVersion(Image.Version.Major, 1, Image.Version.Tag); + cr = Image.ReadMappedVersionedObject(codeRegistration); + } } // Find CodeRegistration @@ -239,7 +257,7 @@ ulong FindCodeRegistration() // the count of custom attribute generators; the distance between them // depends on the il2cpp version so we just use ReadMappedObject to simplify the math foreach (var va in vas) { - var cr = Image.ReadMappedObject(va); + var cr = Image.ReadMappedVersionedObject(va); if (cr.CustomAttributeCount == metadata.AttributeTypeRanges.Length) codeRegistration = va; @@ -255,15 +273,16 @@ ulong FindCodeRegistration() // Find TypeDefinitionsSizesCount (4th last field) then work back to the start of the struct // This saves us from guessing where metadataUsagesCount is later - var mrSize = (ulong) metadata.Sizeof(typeof(Il2CppMetadataRegistration), Image.Version, Image.Bits / 8); + var mrSize = (ulong)Il2CppMetadataRegistration.Size(Image.Version, Image.Bits == 32); var typesLength = (ulong) metadata.Types.Length; vas = FindAllMappedWords(imageBytes, typesLength).Select(a => a - mrSize + ptrSize * 4); // >= 19 && < 27 if (Image.Version < MetadataVersions.V270) - foreach (var va in vas) { - var mr = Image.ReadMappedObject(va); + foreach (var va in vas) + { + var mr = Image.ReadMappedVersionedObject(va); if (mr.MetadataUsagesCount == (ulong) metadata.MetadataUsageLists.Length) metadataRegistration = va; } @@ -273,22 +292,17 @@ ulong FindCodeRegistration() // Synonyms: copying, piracy, theft, strealing, infringement of copyright // >= 27 - else { - // We're going to just sanity check all of the fields - // All counts should be under a certain threshold - // All pointers should be mappable to the binary - - var mrFieldCount = mrSize / (ulong) (Image.Bits / 8); - foreach (var va in vas) { - var mrWords = Image.ReadMappedWordArray(va, (int) mrFieldCount); - - // Even field indices are counts, odd field indices are pointers - bool ok = true; - for (var i = 0; i < mrWords.Length && ok; i++) { - ok = i % 2 == 0 || Image.TryMapVATR((ulong) mrWords[i], out _); - } - if (ok) + else + { + foreach (var va in vas) + { + var mr = Image.ReadMappedVersionedObject(va); + if (mr.TypeDefinitionsSizesCount == metadata.Types.Length + && mr.FieldOffsetsCount == metadata.Types.Length) + { metadataRegistration = va; + break; + } } } if (metadataRegistration == 0) diff --git a/Il2CppInspector.Common/IL2CPP/Metadata.cs b/Il2CppInspector.Common/IL2CPP/Metadata.cs index 7040ff7e..17c9ee6f 100644 --- a/Il2CppInspector.Common/IL2CPP/Metadata.cs +++ b/Il2CppInspector.Common/IL2CPP/Metadata.cs @@ -7,6 +7,7 @@ All rights reserved. using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Reflection; @@ -21,30 +22,30 @@ public class Metadata : BinaryObjectStreamReader { public Il2CppGlobalMetadataHeader Header { get; set; } - public Il2CppAssemblyDefinition[] Assemblies { get; set; } - public Il2CppImageDefinition[] Images { get; set; } - public Il2CppTypeDefinition[] Types { get; set; } - public Il2CppMethodDefinition[] Methods { get; set; } - public Il2CppParameterDefinition[] Params { get; set; } - public Il2CppFieldDefinition[] Fields { get; set; } - public Il2CppFieldDefaultValue[] FieldDefaultValues { get; set; } - public Il2CppParameterDefaultValue[] ParameterDefaultValues { get; set; } - public Il2CppPropertyDefinition[] Properties { get; set; } - public Il2CppEventDefinition[] Events { get; set; } - public Il2CppGenericContainer[] GenericContainers { get; set; } - public Il2CppGenericParameter[] GenericParameters { get; set; } - public Il2CppCustomAttributeTypeRange[] AttributeTypeRanges { get; set; } - public Il2CppCustomAttributeDataRange[] AttributeDataRanges { get; set; } - public Il2CppInterfaceOffsetPair[] InterfaceOffsets { get; set; } - public Il2CppMetadataUsageList[] MetadataUsageLists { get; set; } - public Il2CppMetadataUsagePair[] MetadataUsagePairs { get; set; } - public Il2CppFieldRef[] FieldRefs { get; set; } - - public int[] InterfaceUsageIndices { get; set; } - public int[] NestedTypeIndices { get; set; } - public int[] AttributeTypeIndices { get; set; } - public int[] GenericConstraintIndices { get; set; } - public uint[] VTableMethodIndices { get; set; } + public ImmutableArray Assemblies { get; set; } + public ImmutableArray Images { get; set; } + public ImmutableArray Types { get; set; } + public ImmutableArray Methods { get; set; } + public ImmutableArray Params { get; set; } + public ImmutableArray Fields { get; set; } + public ImmutableArray FieldDefaultValues { get; set; } + public ImmutableArray ParameterDefaultValues { get; set; } + public ImmutableArray Properties { get; set; } + public ImmutableArray Events { get; set; } + public ImmutableArray GenericContainers { get; set; } + public ImmutableArray GenericParameters { get; set; } + public ImmutableArray AttributeTypeRanges { get; set; } + public ImmutableArray AttributeDataRanges { get; set; } + public ImmutableArray InterfaceOffsets { get; set; } + public ImmutableArray MetadataUsageLists { get; set; } + public ImmutableArray MetadataUsagePairs { get; set; } + public ImmutableArray FieldRefs { get; set; } + + public ImmutableArray InterfaceUsageIndices { get; set; } + public ImmutableArray NestedTypeIndices { get; set; } + public ImmutableArray AttributeTypeIndices { get; set; } + public ImmutableArray GenericConstraintIndices { get; set; } + public ImmutableArray VTableMethodIndices { get; set; } public string[] StringLiterals { get; set; } public Dictionary Strings { get; private set; } = new Dictionary(); @@ -81,7 +82,7 @@ private void Initialize() StatusUpdate("Processing metadata"); // Read metadata header - Header = ReadObject(0); + Header = ReadVersionedObject(0); // Check for correct magic bytes if (!Header.SanityValid) { @@ -96,7 +97,7 @@ private void Initialize() } // Rewind and read metadata header with the correct version settings - Header = ReadObject(0); + Header = ReadVersionedObject(0); // Sanity checking // Unity.IL2CPP.MetadataCacheWriter.WriteLibIl2CppMetadata always writes the metadata information in the same order it appears in the header, @@ -110,21 +111,21 @@ private void Initialize() if (!pluginResult.SkipValidation) { var realHeaderLength = Header.StringLiteralOffset; - if (realHeaderLength != Sizeof(typeof(Il2CppGlobalMetadataHeader))) { + if (realHeaderLength != Sizeof()) { if (Version == MetadataVersions.V240) { Version = MetadataVersions.V242; - Header = ReadObject(0); + Header = ReadVersionedObject(0); } } - if (realHeaderLength != Sizeof(typeof(Il2CppGlobalMetadataHeader))) { + if (realHeaderLength != Sizeof()) { throw new InvalidOperationException("Could not verify the integrity of the metadata file or accurately identify the metadata sub-version"); } } // Load all the relevant metadata using offsets provided in the header if (Version >= MetadataVersions.V160) - Images = ReadArray(Header.ImagesOffset, Header.ImagesSize / Sizeof(typeof(Il2CppImageDefinition))); + Images = ReadVersionedObjectArray(Header.ImagesOffset, Header.ImagesSize / Sizeof()); // 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. @@ -135,32 +136,32 @@ private void Initialize() Version = MetadataVersions.V241; // No need to re-read the header, it's the same for both sub-versions - Images = ReadArray(Header.ImagesOffset, Header.ImagesSize / Sizeof(typeof(Il2CppImageDefinition))); + Images = ReadVersionedObjectArray(Header.ImagesOffset, Header.ImagesSize / Sizeof()); if (Images.Any(x => x.Token != 1)) throw new InvalidOperationException("Could not verify the integrity of the metadata file image list"); } - Types = ReadArray(Header.TypeDefinitionsOffset, Header.TypeDefinitionsSize / Sizeof(typeof(Il2CppTypeDefinition))); - Methods = ReadArray(Header.MethodsOffset, Header.MethodsSize / Sizeof(typeof(Il2CppMethodDefinition))); - Params = ReadArray(Header.ParametersOffset, Header.ParametersSize / Sizeof(typeof(Il2CppParameterDefinition))); - Fields = ReadArray(Header.FieldsOffset, Header.FieldsSize / Sizeof(typeof(Il2CppFieldDefinition))); - FieldDefaultValues = ReadArray(Header.FieldDefaultValuesOffset, Header.FieldDefaultValuesSize / Sizeof(typeof(Il2CppFieldDefaultValue))); - Properties = ReadArray(Header.PropertiesOffset, Header.PropertiesSize / Sizeof(typeof(Il2CppPropertyDefinition))); - Events = ReadArray(Header.EventsOffset, Header.EventsSize / Sizeof(typeof(Il2CppEventDefinition))); - InterfaceUsageIndices = ReadArray(Header.InterfacesOffset, Header.InterfacesSize / sizeof(int)); - NestedTypeIndices = ReadArray(Header.NestedTypesOffset, Header.NestedTypesSize / sizeof(int)); - GenericContainers = ReadArray(Header.GenericContainersOffset, Header.GenericContainersSize / Sizeof(typeof(Il2CppGenericContainer))); - GenericParameters = ReadArray(Header.GenericParametersOffset, Header.GenericParametersSize / Sizeof(typeof(Il2CppGenericParameter))); - GenericConstraintIndices = ReadArray(Header.GenericParameterConstraintsOffset, Header.GenericParameterConstraintsSize / sizeof(int)); - InterfaceOffsets = ReadArray(Header.InterfaceOffsetsOffset, Header.InterfaceOffsetsSize / Sizeof(typeof(Il2CppInterfaceOffsetPair))); - VTableMethodIndices = ReadArray(Header.VTableMethodsOffset, Header.VTableMethodsSize / sizeof(uint)); + Types = ReadVersionedObjectArray(Header.TypeDefinitionsOffset, Header.TypeDefinitionsSize / Sizeof()); + Methods = ReadVersionedObjectArray(Header.MethodsOffset, Header.MethodsSize / Sizeof()); + Params = ReadVersionedObjectArray(Header.ParametersOffset, Header.ParametersSize / Sizeof()); + Fields = ReadVersionedObjectArray(Header.FieldsOffset, Header.FieldsSize / Sizeof()); + FieldDefaultValues = ReadVersionedObjectArray(Header.FieldDefaultValuesOffset, Header.FieldDefaultValuesSize / Sizeof()); + Properties = ReadVersionedObjectArray(Header.PropertiesOffset, Header.PropertiesSize / Sizeof()); + Events = ReadVersionedObjectArray(Header.EventsOffset, Header.EventsSize / Sizeof()); + InterfaceUsageIndices = ReadPrimitiveArray(Header.InterfacesOffset, Header.InterfacesSize / sizeof(int)); + NestedTypeIndices = ReadPrimitiveArray(Header.NestedTypesOffset, Header.NestedTypesSize / sizeof(int)); + GenericContainers = ReadVersionedObjectArray(Header.GenericContainersOffset, Header.GenericContainersSize / Sizeof()); + GenericParameters = ReadVersionedObjectArray(Header.GenericParametersOffset, Header.GenericParametersSize / Sizeof()); + GenericConstraintIndices = ReadPrimitiveArray(Header.GenericParameterConstraintsOffset, Header.GenericParameterConstraintsSize / sizeof(int)); + InterfaceOffsets = ReadVersionedObjectArray(Header.InterfaceOffsetsOffset, Header.InterfaceOffsetsSize / Sizeof()); + VTableMethodIndices = ReadPrimitiveArray(Header.VTableMethodsOffset, Header.VTableMethodsSize / sizeof(uint)); 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.AssembliesSize / Sizeof(typeof(Il2CppAssemblyDefinition)); + var assemblyCount = Header.AssembliesSize / Sizeof(); var changedAssemblyDefStruct = false; if ((Version == MetadataVersions.V241 || Version == MetadataVersions.V242 || Version == MetadataVersions.V243) && assemblyCount < Images.Length) { @@ -169,29 +170,29 @@ private void Initialize() Version = MetadataVersions.V244; } - Assemblies = ReadArray(Header.AssembliesOffset, Images.Length); + Assemblies = ReadVersionedObjectArray(Header.AssembliesOffset, Images.Length); if (changedAssemblyDefStruct) Version = MetadataVersions.V241; - ParameterDefaultValues = ReadArray(Header.ParameterDefaultValuesOffset, Header.ParameterDefaultValuesSize / Sizeof(typeof(Il2CppParameterDefaultValue))); + ParameterDefaultValues = ReadVersionedObjectArray(Header.ParameterDefaultValuesOffset, Header.ParameterDefaultValuesSize / Sizeof()); } 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))); + MetadataUsageLists = ReadVersionedObjectArray(Header.MetadataUsageListsOffset, Header.MetadataUsageListsCount / Sizeof()); + MetadataUsagePairs = ReadVersionedObjectArray(Header.MetadataUsagePairsOffset, Header.MetadataUsagePairsCount / Sizeof()); } if (Version >= MetadataVersions.V190) { - FieldRefs = ReadArray(Header.FieldRefsOffset, Header.FieldRefsSize / Sizeof(typeof(Il2CppFieldRef))); + FieldRefs = ReadVersionedObjectArray(Header.FieldRefsOffset, Header.FieldRefsSize / Sizeof()); } if (Version >= MetadataVersions.V210 && Version < MetadataVersions.V290) { - AttributeTypeIndices = ReadArray(Header.AttributesTypesOffset, Header.AttributesTypesCount / sizeof(int)); - AttributeTypeRanges = ReadArray(Header.AttributesInfoOffset, Header.AttributesInfoCount / Sizeof(typeof(Il2CppCustomAttributeTypeRange))); + AttributeTypeIndices = ReadPrimitiveArray(Header.AttributesTypesOffset, Header.AttributesTypesCount / sizeof(int)); + AttributeTypeRanges = ReadVersionedObjectArray(Header.AttributesInfoOffset, Header.AttributesInfoCount / Sizeof()); } if (Version >= MetadataVersions.V290) { - AttributeDataRanges = ReadArray(Header.AttributeDataRangeOffset, - Header.AttributeDataRangeSize / Sizeof(typeof(Il2CppCustomAttributeDataRange))); + AttributeDataRanges = ReadVersionedObjectArray(Header.AttributeDataRangeOffset, + Header.AttributeDataRangeSize / Sizeof()); } if (Version == MetadataVersions.V290 || Version == MetadataVersions.V310) @@ -207,8 +208,8 @@ private void Initialize() { Version = new StructVersion(Version.Major, 1, Version.Tag); - Methods = ReadArray(Header.MethodsOffset, - Header.MethodsSize / Sizeof(typeof(Il2CppMethodDefinition))); + Methods = ReadVersionedObjectArray(Header.MethodsOffset, + Header.MethodsSize / Sizeof()); } } } @@ -231,7 +232,7 @@ private void Initialize() StringLiterals = pluginGetStringLiteralsResult.StringLiterals.ToArray(); else { - var stringLiteralList = ReadArray(Header.StringLiteralOffset, Header.StringLiteralSize / Sizeof(typeof(Il2CppStringLiteral))); + var stringLiteralList = ReadVersionedObjectArray(Header.StringLiteralOffset, Header.StringLiteralSize / Sizeof()); StringLiterals = new string[stringLiteralList.Length]; for (var i = 0; i < stringLiteralList.Length; i++) @@ -249,42 +250,6 @@ public void SaveToFile(string pathname) { CopyTo(outFile); } - public int Sizeof(Type type) => Sizeof(type, Version); - - public int Sizeof(Type type, StructVersion metadataVersion, int longSizeBytes = 8) - { - var doubleRepresentation = metadataVersion.AsDouble; - - if (Reader.ObjectMappings.TryGetValue(type, out var streamType)) - type = streamType; - - int size = 0; - foreach (var i in type.GetTypeInfo().GetFields()) - { - // 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 <= doubleRepresentation || v.Min == -1) && (v.Max >= doubleRepresentation || v.Max == -1))) - continue; - - if (i.FieldType == typeof(long) || i.FieldType == typeof(ulong)) - size += longSizeBytes; - else if (i.FieldType == typeof(int) || i.FieldType == typeof(uint)) - size += 4; - else if (i.FieldType == typeof(short) || i.FieldType == typeof(ushort)) - size += 2; - - // Fixed-length array - else if (i.FieldType.IsArray) { - var attr = i.GetCustomAttribute(false) ?? - throw new InvalidOperationException("Array field " + i.Name + " must have ArrayLength attribute"); - size += attr.FixedSize; - } - - // Embedded object - else - size += Sizeof(i.FieldType, metadataVersion); - } - return size; - } + public int Sizeof() where T : IReadable => T.Size(Version, Is32Bit); } } diff --git a/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs b/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs index 2c4d7655..d05d1b82 100644 --- a/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs +++ b/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs @@ -21,7 +21,7 @@ public enum MetadataUsageType FieldRva = 7 } - public class MetadataUsage + public record struct MetadataUsage { public MetadataUsageType Type { get; } public int SourceIndex { get; } @@ -39,7 +39,7 @@ public static MetadataUsage FromEncodedIndex(Il2CppInspector package, uint encod 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]; + index = package.Binary.VTableMethodReferences[(int)(encodedIndex & 0x7FFFFFFF)]; usageType = (isGeneric != 0) ? MetadataUsageType.MethodRef : MetadataUsageType.MethodDef; } else { /* These encoded indices appear in metadata usages, and are decoded by GetEncodedIndexType/GetDecodedMethodIndex */ diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppArrayType.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppArrayType.cs index 1296aaa9..8ff56bea 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppArrayType.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppArrayType.cs @@ -3,16 +3,13 @@ namespace Il2CppInspector.Next.BinaryMetadata; [VersionedStruct] -public partial struct Il2CppArrayType +public partial record struct Il2CppArrayType { public Pointer ElementType; public byte Rank; public byte NumSizes; public byte NumLowerBound; - [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] - public ulong Sizes; // int* - - [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] - public ulong LoBounds; // int* + public PrimitivePointer Sizes; + public PrimitivePointer LoBounds; } \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeGenModule.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeGenModule.cs index f9f1808a..55bd08fe 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeGenModule.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeGenModule.cs @@ -3,17 +3,16 @@ namespace Il2CppInspector.Next.BinaryMetadata; [VersionedStruct] -public partial struct Il2CppCodeGenModule +public partial record struct Il2CppCodeGenModule { - [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] - public ulong ModuleName; // const char* + public PrimitivePointer ModuleName; // const char* - [Aligned(0)] + [NativeInteger] public uint MethodPointerCount; public Pointer MethodPointers; - [Aligned(0)] + [NativeInteger] [VersionCondition(EqualTo = "24.5")] [VersionCondition(GreaterThan = "27.1")] public uint AdjustorThunksCount; @@ -22,24 +21,22 @@ public partial struct Il2CppCodeGenModule [VersionCondition(GreaterThan = "27.1")] public Pointer AdjustorThunks; - [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] - public ulong InvokerIndices; // int* + public PrimitivePointer InvokerIndices; // int* - [Aligned(0)] + [NativeInteger] public uint ReversePInvokeWrapperCount; public Pointer ReversePInvokeWrapperIndices; - [Aligned(0)] + [NativeInteger] public uint RgctxRangesCount; public Pointer RgctxRanges; - [Aligned(0)] + [NativeInteger] public uint RgctxsCount; public Pointer Rgctxs; - [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] - public ulong DebuggerMetadata; // Pointer DebuggerMetadata; + public PrimitivePointer DebuggerMetadata; // Pointer DebuggerMetadata; [VersionCondition(GreaterThan = "27.0", LessThan = "27.2")] public Pointer CustomAttributeCacheGenerator; @@ -48,14 +45,11 @@ public partial struct Il2CppCodeGenModule public Il2CppMethodPointer ModuleInitializer; [VersionCondition(GreaterThan = "27.0")] - [Aligned(0)] - public ulong StaticConstructorTypeIndices; // TypeDefinitionIndex* + public PrimitivePointer StaticConstructorTypeIndices; // TypeDefinitionIndex* [VersionCondition(GreaterThan = "27.0")] - [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] - public ulong MetadataRegistration; // Pointer + public PrimitivePointer MetadataRegistration; // Pointer [VersionCondition(GreaterThan = "27.0")] - [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] - public ulong CodeRegistration; // Pointer + public PrimitivePointer CodeRegistration; // Pointer } \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeRegistration.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeRegistration.cs index 428f0449..8350d290 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeRegistration.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeRegistration.cs @@ -5,38 +5,42 @@ namespace Il2CppInspector.Next.BinaryMetadata; using InvokerMethod = Il2CppMethodPointer; [VersionedStruct] -public partial struct Il2CppCodeRegistration +public partial record struct Il2CppCodeRegistration { - [VersionCondition(LessThan = "24.1"), Aligned(0)] + [NativeInteger] + [VersionCondition(LessThan = "24.1")] public uint MethodPointersCount; [VersionCondition(LessThan = "24.1")] public Pointer MethodPointers; - [Aligned(0)] + [NativeInteger] public uint ReversePInvokeWrapperCount; public Pointer ReversePInvokeWrappers; - [VersionCondition(LessThan = "22.0"), Aligned(0)] + [NativeInteger] + [VersionCondition(LessThan = "22.0")] public uint DelegateWrappersFromManagedToNativeCount; [VersionCondition(LessThan = "22.0")] public Pointer DelegateWrappersFromManagedToNative; - [VersionCondition(LessThan = "22.0"), Aligned(0)] + [NativeInteger] + [VersionCondition(LessThan = "22.0")] public uint MarshalingFunctionsCount; [VersionCondition(LessThan = "22.0")] public Pointer MarshalingFunctions; - [VersionCondition(GreaterThan = "21.0", LessThan = "22.0"), Aligned(0)] + [NativeInteger] + [VersionCondition(GreaterThan = "21.0", LessThan = "22.0")] public uint CcwMarshalingFunctionsCount; [VersionCondition(GreaterThan = "21.0", LessThan = "22.0")] public Pointer CcwMarshalingFunctions; - [Aligned(0)] + [NativeInteger] public uint GenericMethodPointersCount; public Pointer GenericMethodPointers; @@ -45,29 +49,32 @@ public partial struct Il2CppCodeRegistration [VersionCondition(GreaterThan = "27.1")] public Pointer GenericAdjustorThunks; - [Aligned(0)] + [NativeInteger] public uint InvokerPointersCount; public Pointer InvokerPointers; - [VersionCondition(LessThan = "24.5"), Aligned(0)] + [NativeInteger] + [VersionCondition(LessThan = "24.5")] public int CustomAttributeCount; [VersionCondition(LessThan = "24.5")] public Pointer CustomAttributeGenerators; - [VersionCondition(GreaterThan = "21.0", LessThan = "22.0"), Aligned(0)] + [NativeInteger] + [VersionCondition(GreaterThan = "21.0", LessThan = "22.0")] public int GuidCount; [VersionCondition(GreaterThan = "21.0", LessThan = "22.0")] public Pointer Guids; + [NativeInteger] [VersionCondition(GreaterThan = "22.0", LessThan = "29.0")] public int UnresolvedVirtualCallCount; + [NativeInteger] [VersionCondition(EqualTo = "29.1"), VersionCondition(EqualTo = "31.1")] [VersionCondition(EqualTo = "29.2"), VersionCondition(EqualTo = "31.2")] - [Aligned(0)] public uint UnresolvedIndirectCallCount; // UnresolvedVirtualCallCount pre 29.1 [VersionCondition(GreaterThan = "22.0")] @@ -81,19 +88,22 @@ public partial struct Il2CppCodeRegistration [VersionCondition(EqualTo = "29.2"), VersionCondition(EqualTo = "31.2")] public Pointer UnresolvedStaticCallPointers; - [VersionCondition(GreaterThan = "23.0"), Aligned(0)] + [NativeInteger] + [VersionCondition(GreaterThan = "23.0")] public uint InteropDataCount; [VersionCondition(GreaterThan = "23.0")] public Pointer InteropData; - [VersionCondition(GreaterThan = "24.3"), Aligned(0)] + [NativeInteger] + [VersionCondition(GreaterThan = "24.3")] public uint WindowsRuntimeFactoryCount; [VersionCondition(GreaterThan = "24.3")] public Pointer WindowsRuntimeFactoryTable; - [VersionCondition(GreaterThan = "24.2"), Aligned(0)] + [NativeInteger] + [VersionCondition(GreaterThan = "24.2")] public uint CodeGenModulesCount; [VersionCondition(GreaterThan = "24.2")] diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericClass.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericClass.cs index 9259a0cb..e147a8ad 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericClass.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericClass.cs @@ -3,9 +3,10 @@ namespace Il2CppInspector.Next.BinaryMetadata; [VersionedStruct] -public partial struct Il2CppGenericClass +public partial record struct Il2CppGenericClass { - [VersionCondition(LessThan = "24.5"), Aligned(0)] + [NativeInteger] + [VersionCondition(LessThan = "24.5")] public int TypeDefinitionIndex; [VersionCondition(GreaterThan = "27.0")] @@ -13,6 +14,5 @@ public partial struct Il2CppGenericClass public Il2CppGenericContext Context; - [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] - public ulong CachedClass; // Il2CppClass*, optional + public PrimitivePointer CachedClass; // Il2CppClass*, optional } \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericContext.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericContext.cs index 1737bcf3..b5f31016 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericContext.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericContext.cs @@ -3,7 +3,7 @@ namespace Il2CppInspector.Next.BinaryMetadata; [VersionedStruct] -public partial struct Il2CppGenericContext +public partial record struct Il2CppGenericContext { public Pointer ClassInst; public Pointer MethodInst; diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericInst.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericInst.cs index 0a55c914..e77edc54 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericInst.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericInst.cs @@ -3,11 +3,11 @@ namespace Il2CppInspector.Next.BinaryMetadata; [VersionedStruct] -public partial struct Il2CppGenericInst +public partial record struct Il2CppGenericInst { public readonly bool Valid => TypeArgc > 0; - [Aligned(0)] + [NativeInteger] public uint TypeArgc; public Pointer> TypeArgv; diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericMethodFunctionsDefinitions.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericMethodFunctionsDefinitions.cs index 97478ef7..8bdbf961 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericMethodFunctionsDefinitions.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericMethodFunctionsDefinitions.cs @@ -5,7 +5,7 @@ namespace Il2CppInspector.Next.BinaryMetadata; using GenericMethodIndex = int; [VersionedStruct] -public partial struct Il2CppGenericMethodFunctionsDefinitions +public partial record struct Il2CppGenericMethodFunctionsDefinitions { public GenericMethodIndex GenericMethodIndex; public Il2CppGenericMethodIndices Indices; diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericMethodIndices.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericMethodIndices.cs index e18bf103..78afa4bb 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericMethodIndices.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGenericMethodIndices.cs @@ -5,7 +5,7 @@ namespace Il2CppInspector.Next.BinaryMetadata; using MethodIndex = int; [VersionedStruct] -public partial struct Il2CppGenericMethodIndices +public partial record struct Il2CppGenericMethodIndices { public MethodIndex MethodIndex; public MethodIndex InvokerIndex; diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGuid.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGuid.cs index 341ab1ec..b9b304b6 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGuid.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppGuid.cs @@ -2,8 +2,7 @@ namespace Il2CppInspector.Next.BinaryMetadata; - -public struct Il2CppGuid : IReadable +public record struct Il2CppGuid : IReadable { public Guid Value; diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppInteropData.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppInteropData.cs index 93055698..52a0367b 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppInteropData.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppInteropData.cs @@ -8,7 +8,7 @@ namespace Il2CppInspector.Next.BinaryMetadata; using CreateCCWFunc = Il2CppMethodPointer; [VersionedStruct] -public partial struct Il2CppInteropData +public partial record struct Il2CppInteropData { public Il2CppMethodPointer DelegatePInvokeWrapperFunction; public PInvokeMarshalToNativeFunc PInvokeMarshalToNativeFunction; diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMetadataRegistration.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMetadataRegistration.cs index 2df65c91..dbd7b3d0 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMetadataRegistration.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMetadataRegistration.cs @@ -7,51 +7,50 @@ namespace Il2CppInspector.Next.BinaryMetadata; using TypeDefinitionIndex = int; [VersionedStruct] -public partial struct Il2CppMetadataRegistration +public partial record struct Il2CppMetadataRegistration { - [Aligned(0)] + [NativeInteger] public int GenericClassesCount; public Pointer> GenericClasses; - [Aligned(0)] + [NativeInteger] public int GenericInstsCount; public Pointer> GenericInsts; - [Aligned(0)] + [NativeInteger] public int GenericMethodTableCount; public Pointer GenericMethodTable; - [Aligned(0)] + [NativeInteger] public int TypesCount; public Pointer> Types; - [Aligned(0)] + [NativeInteger] public int MethodSpecsCount; public Pointer MethodSpecs; + [NativeInteger] [VersionCondition(LessThan = "16.0")] public int MethodReferencesCount; [VersionCondition(LessThan = "16.0")] - [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] - public ulong MethodReferences; // uint** + public PrimitivePointer> MethodReferences; // uint** - [Aligned(0)] + [NativeInteger] public FieldIndex FieldOffsetsCount; - [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] - public ulong FieldOffsets; // int** + public PrimitivePointer> FieldOffsets; // int** - [Aligned(0)] + [NativeInteger] public TypeDefinitionIndex TypeDefinitionsSizesCount; public Pointer> TypeDefinitionsSizes; - [Aligned(0)] + [NativeInteger] [VersionCondition(GreaterThan = "19.0")] public ulong MetadataUsagesCount; diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMethodPointer.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMethodPointer.cs index a6d10053..14f65aac 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMethodPointer.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMethodPointer.cs @@ -3,11 +3,11 @@ namespace Il2CppInspector.Next.BinaryMetadata; [VersionedStruct] -public partial struct Il2CppMethodPointer(ulong addr = 0) +public partial record struct Il2CppMethodPointer(ulong addr = 0) { public static readonly Il2CppMethodPointer Null = new(); - [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] + [NativeInteger] public ulong Value { get; set; } = addr; public readonly bool IsNull => Value == 0; diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMethodSpec.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMethodSpec.cs index 0fa7d047..25ab1579 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMethodSpec.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppMethodSpec.cs @@ -6,7 +6,7 @@ namespace Il2CppInspector.Next.BinaryMetadata; using GenericInstIndex = int; [VersionedStruct] -public partial struct Il2CppMethodSpec +public partial record struct Il2CppMethodSpec { public MethodIndex MethodDefinitionIndex; public GenericInstIndex ClassIndexIndex; diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRange.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRange.cs index 557861e9..8e8edf64 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRange.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRange.cs @@ -3,7 +3,7 @@ namespace Il2CppInspector.Next.BinaryMetadata; [VersionedStruct] -public partial struct Il2CppRange +public partial record struct Il2CppRange { public int Start; public int Length; diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxConstrainedData.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxConstrainedData.cs index f51ddd3f..b93a9e82 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxConstrainedData.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxConstrainedData.cs @@ -6,7 +6,7 @@ namespace Il2CppInspector.Next.BinaryMetadata; using TypeIndex = int; [VersionedStruct] -public partial struct Il2CppRgctxConstrainedData +public partial record struct Il2CppRgctxConstrainedData { public TypeIndex TypeIndex; public Il2CppMetadataUsage EncodedMethodIndex; diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDefinition.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDefinition.cs index 0fb2efea..4cc7d4d4 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDefinition.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDefinition.cs @@ -3,14 +3,13 @@ namespace Il2CppInspector.Next.BinaryMetadata; [VersionedStruct] -public partial struct Il2CppRgctxDefinition +public partial record struct Il2CppRgctxDefinition { - [Aligned(0)] + [NativeInteger] public Il2CppRgctxDataType Type; - [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] - public ulong Data; // void* + public PrimitivePointer Data; // void* - public readonly Pointer Definition => Data; - public readonly Pointer Constrained => Data; + public readonly Pointer Definition => Data.PointerValue; + public readonly Pointer Constrained => Data.PointerValue; } \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDefinitionData.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDefinitionData.cs index a6b19a21..4343e879 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDefinitionData.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppRgctxDefinitionData.cs @@ -6,7 +6,7 @@ namespace Il2CppInspector.Next.BinaryMetadata; using TypeIndex = int; [VersionedStruct] -public partial struct Il2CppRgctxDefinitionData +public partial record struct Il2CppRgctxDefinitionData { public int Value; diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenAdjustorThunkPair.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenAdjustorThunkPair.cs index e6ac2c50..974024c7 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenAdjustorThunkPair.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenAdjustorThunkPair.cs @@ -3,9 +3,9 @@ namespace Il2CppInspector.Next.BinaryMetadata; [VersionedStruct] -public partial struct Il2CppTokenAdjustorThunkPair +public partial record struct Il2CppTokenAdjustorThunkPair { - [Aligned(0)] + [NativeInteger] public uint Token; public Il2CppMethodPointer AdjustorThunk; diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenIndexMethodTuple.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenIndexMethodTuple.cs index 61099019..45c47ed5 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenIndexMethodTuple.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenIndexMethodTuple.cs @@ -3,13 +3,12 @@ namespace Il2CppInspector.Next.BinaryMetadata; [VersionedStruct] -public partial struct Il2CppTokenIndexMethodTuple +public partial record struct Il2CppTokenIndexMethodTuple { public uint Token; public int Index; - [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] - public ulong Method; // void** + public PrimitivePointer Method; // void** public uint GenericMethodIndex; } \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenRangePair.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenRangePair.cs index dfc7836c..67d1f6a8 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenRangePair.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTokenRangePair.cs @@ -3,7 +3,7 @@ namespace Il2CppInspector.Next.BinaryMetadata; [VersionedStruct] -public partial struct Il2CppTokenRangePair +public partial record struct Il2CppTokenRangePair { public uint Token; public Il2CppRange Range; diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTypeDefinitionSizes.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTypeDefinitionSizes.cs index dc46ae4a..3041d77e 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTypeDefinitionSizes.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTypeDefinitionSizes.cs @@ -3,7 +3,7 @@ namespace Il2CppInspector.Next.BinaryMetadata; [VersionedStruct] -public partial struct Il2CppTypeDefinitionSizes +public partial record struct Il2CppTypeDefinitionSizes { public uint InstanceSize; public int NativeSize; diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppWindowsRuntimeFactoryTableEntry.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppWindowsRuntimeFactoryTableEntry.cs index 1c42edc9..3a6654ff 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppWindowsRuntimeFactoryTableEntry.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppWindowsRuntimeFactoryTableEntry.cs @@ -3,7 +3,7 @@ namespace Il2CppInspector.Next.BinaryMetadata; [VersionedStruct] -public partial struct Il2CppWindowsRuntimeFactoryTableEntry +public partial record struct Il2CppWindowsRuntimeFactoryTableEntry { public Pointer Type; public Il2CppMethodPointer CreateFactoryFunction; diff --git a/Il2CppInspector.Common/Next/BinaryObjectStreamReader.cs b/Il2CppInspector.Common/Next/BinaryObjectStreamReader.cs index a5fae3e9..ac4a21c6 100644 --- a/Il2CppInspector.Common/Next/BinaryObjectStreamReader.cs +++ b/Il2CppInspector.Common/Next/BinaryObjectStreamReader.cs @@ -8,6 +8,18 @@ namespace Il2CppInspector.Next; public class BinaryObjectStreamReader : BinaryObjectStream, IReader { + public new StructVersion Version + { + get => _version; + set + { + _version = value; + base.Version = _version.AsDouble; + } + } + + private StructVersion _version; + public virtual int Bits { get; set; } public bool Is32Bit => Bits == 32; @@ -127,4 +139,9 @@ public ImmutableArray ReadPrimitiveArray(long addr, long count) wh Position = addr; return ReadVersionedObjectArray(count, Version); } + + public void Skip(int count) + { + Position += count; + } } \ No newline at end of file diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyDefinition.cs index ca62195a..40d55ee4 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyDefinition.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyDefinition.cs @@ -7,7 +7,7 @@ namespace Il2CppInspector.Next.Metadata; [VersionedStruct] [StructLayout(LayoutKind.Explicit)] -public partial struct Il2CppAssemblyDefinition +public partial record struct Il2CppAssemblyDefinition { [FieldOffset(20)] [VersionCondition(LessThan = "15.0")] diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyNameDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyNameDefinition.cs index 83068e72..27a0aa02 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyNameDefinition.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppAssemblyNameDefinition.cs @@ -17,7 +17,7 @@ public struct PublicKeyToken [VersionedStruct] [StructLayout(LayoutKind.Explicit)] -public partial struct Il2CppAssemblyNameDefinition +public partial record struct Il2CppAssemblyNameDefinition { [FieldOffset(0)] public StringIndex NameIndex; diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppCustomAttributeDataRange.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppCustomAttributeDataRange.cs index a38db962..b31a87ce 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppCustomAttributeDataRange.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppCustomAttributeDataRange.cs @@ -3,7 +3,7 @@ namespace Il2CppInspector.Next.Metadata; [VersionedStruct] -public partial struct Il2CppCustomAttributeDataRange +public partial record struct Il2CppCustomAttributeDataRange { public uint Token { get; private set; } public uint StartOffset { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppCustomAttributeTypeRange.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppCustomAttributeTypeRange.cs index 4be20de3..6ecd6a5c 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppCustomAttributeTypeRange.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppCustomAttributeTypeRange.cs @@ -3,7 +3,7 @@ namespace Il2CppInspector.Next.Metadata; [VersionedStruct] -public partial struct Il2CppCustomAttributeTypeRange +public partial record struct Il2CppCustomAttributeTypeRange { [VersionCondition(GreaterThan = "24.1")] public uint Token { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppEventDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppEventDefinition.cs index 7658f5bf..c763cbd0 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppEventDefinition.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppEventDefinition.cs @@ -6,7 +6,7 @@ using VersionedSerialization.Attributes; [VersionedStruct] -public partial struct Il2CppEventDefinition +public partial record struct Il2CppEventDefinition { public StringIndex NameIndex { get; private set; } public TypeIndex TypeIndex { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppFieldDefaultValue.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppFieldDefaultValue.cs index ed984e29..47c9fef9 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppFieldDefaultValue.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppFieldDefaultValue.cs @@ -7,7 +7,7 @@ namespace Il2CppInspector.Next.Metadata; using DefaultValueDataIndex = int; [VersionedStruct] -public partial struct Il2CppFieldDefaultValue +public partial record struct Il2CppFieldDefaultValue { public FieldIndex FieldIndex { get; private set; } public TypeIndex TypeIndex { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppFieldDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppFieldDefinition.cs index 84d0ef48..91f81e94 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppFieldDefinition.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppFieldDefinition.cs @@ -5,7 +5,7 @@ using TypeIndex = int; [VersionedStruct] -public partial struct Il2CppFieldDefinition +public partial record struct Il2CppFieldDefinition { public StringIndex NameIndex { get; private set; } public TypeIndex TypeIndex { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppFieldMarshaledSize.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppFieldMarshaledSize.cs index bc71ad7c..0cbb5edf 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppFieldMarshaledSize.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppFieldMarshaledSize.cs @@ -5,7 +5,7 @@ using TypeIndex = int; [VersionedStruct] -public partial struct Il2CppFieldMarshaledSize +public partial record struct Il2CppFieldMarshaledSize { public FieldIndex FieldIndex { get; private set; } public TypeIndex TypeIndex { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppFieldRef.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppFieldRef.cs index d02500d3..aba478e2 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppFieldRef.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppFieldRef.cs @@ -6,7 +6,7 @@ namespace Il2CppInspector.Next.Metadata; using TypeIndex = int; [VersionedStruct] -public partial struct Il2CppFieldRef +public partial record struct Il2CppFieldRef { public TypeIndex TypeIndex { get; private set; } public FieldIndex FieldIndex { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppGenericContainer.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppGenericContainer.cs index 43421c36..54d49696 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppGenericContainer.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppGenericContainer.cs @@ -4,7 +4,7 @@ using GenericParameterIndex = int; [VersionedStruct] -public partial struct Il2CppGenericContainer +public partial record struct Il2CppGenericContainer { public int OwnerIndex { get; private set; } public int TypeArgc { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppGenericParameter.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppGenericParameter.cs index beaa94a8..b9f5a172 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppGenericParameter.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppGenericParameter.cs @@ -8,7 +8,7 @@ namespace Il2CppInspector.Next.Metadata; using GenericParameterConstraintIndex = short; [VersionedStruct] -public partial struct Il2CppGenericParameter +public partial record struct Il2CppGenericParameter { public GenericContainerIndex OwnerIndex { get; private set; } public StringIndex NameIndex { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppGlobalMetadataHeader.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppGlobalMetadataHeader.cs index d137be35..1eefde92 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppGlobalMetadataHeader.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppGlobalMetadataHeader.cs @@ -25,7 +25,7 @@ namespace Il2CppInspector.Next.Metadata; // Metadata version is written at the end of Unity.IL2CPP.MetadataCacheWriter.WriteLibIl2CppMetadata or WriteMetadata (Unity.IL2CPP.dll) [VersionedStruct] -public partial struct Il2CppGlobalMetadataHeader +public partial record struct Il2CppGlobalMetadataHeader { public int Sanity { get; private set; } public int Version { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppImageDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppImageDefinition.cs index 56bc753f..0f50a2ba 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppImageDefinition.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppImageDefinition.cs @@ -8,7 +8,7 @@ using VersionedSerialization.Attributes; [VersionedStruct] -public partial struct Il2CppImageDefinition +public partial record struct Il2CppImageDefinition { public StringIndex NameIndex { get; private set; } public AssemblyIndex AssemblyIndex { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppInterfaceOffsetPair.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppInterfaceOffsetPair.cs index 93337b2a..8ed5d411 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppInterfaceOffsetPair.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppInterfaceOffsetPair.cs @@ -5,7 +5,7 @@ namespace Il2CppInspector.Next.Metadata; using TypeIndex = int; [VersionedStruct] -public partial struct Il2CppInterfaceOffsetPair +public partial record struct Il2CppInterfaceOffsetPair { public TypeIndex InterfaceTypeIndex { get; private set; } public int Offset { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataRange.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataRange.cs index 4ab7a243..22d79f46 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataRange.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataRange.cs @@ -3,7 +3,7 @@ namespace Il2CppInspector.Next.Metadata; [VersionedStruct] -public partial struct Il2CppMetadataRange +public partial record struct Il2CppMetadataRange { public int Start { get; private set; } public int Length { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsage.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsage.cs index 70855f1b..b036e0e9 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsage.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsage.cs @@ -7,7 +7,7 @@ namespace Il2CppInspector.Next.Metadata; using EncodedMethodIndex = uint; [VersionedStruct] -public partial struct Il2CppMetadataUsage +public partial record struct Il2CppMetadataUsage { private const uint TypeMask = 0b111u << 29; private const uint InflatedMask = 0b1; diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsageList.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsageList.cs index 517a05a6..81da582c 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsageList.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsageList.cs @@ -3,7 +3,7 @@ namespace Il2CppInspector.Next.Metadata; [VersionedStruct] -public partial struct Il2CppMetadataUsageList +public partial record struct Il2CppMetadataUsageList { public int Start { get; private set; } public int Count { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsagePair.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsagePair.cs index b5b2359a..7874ac8a 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsagePair.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppMetadataUsagePair.cs @@ -3,7 +3,7 @@ namespace Il2CppInspector.Next.Metadata; [VersionedStruct] -public partial struct Il2CppMetadataUsagePair +public partial record struct Il2CppMetadataUsagePair { public uint DestinationIndex { get; private set; } public uint EncodedSourceIndex { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppMethodDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppMethodDefinition.cs index 360f0c59..376016b8 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppMethodDefinition.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppMethodDefinition.cs @@ -10,7 +10,7 @@ namespace Il2CppInspector.Next.Metadata; using GenericContainerIndex = int; [VersionedStruct] -public partial struct Il2CppMethodDefinition +public partial record struct Il2CppMethodDefinition { public StringIndex NameIndex { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppParameterDefaultValue.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppParameterDefaultValue.cs index 1a3e4737..8451daa4 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppParameterDefaultValue.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppParameterDefaultValue.cs @@ -6,7 +6,7 @@ using VersionedSerialization.Attributes; [VersionedStruct] -public partial struct Il2CppParameterDefaultValue +public partial record struct Il2CppParameterDefaultValue { public ParameterIndex ParameterIndex { get; private set; } public TypeIndex TypeIndex { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppParameterDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppParameterDefinition.cs index a66af4d9..2bca4653 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppParameterDefinition.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppParameterDefinition.cs @@ -5,7 +5,7 @@ using TypeIndex = int; [VersionedStruct] -public partial struct Il2CppParameterDefinition +public partial record struct Il2CppParameterDefinition { public StringIndex NameIndex { get; private set; } public uint Token { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppPropertyDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppPropertyDefinition.cs index 6ba2ad85..f74e2433 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppPropertyDefinition.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppPropertyDefinition.cs @@ -7,7 +7,7 @@ namespace Il2CppInspector.Next.Metadata; using MethodIndex = int; [VersionedStruct] -public partial struct Il2CppPropertyDefinition +public partial record struct Il2CppPropertyDefinition { public StringIndex NameIndex { get; private set; } public MethodIndex Get { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppStringLiteral.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppStringLiteral.cs index 93f4ac69..b1521fe5 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppStringLiteral.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppStringLiteral.cs @@ -4,7 +4,7 @@ using StringLiteralIndex = int; [VersionedStruct] -public partial struct Il2CppStringLiteral +public partial record struct Il2CppStringLiteral { public uint Length { get; private set; } public StringLiteralIndex DataIndex { get; private set; } diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppTypeDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppTypeDefinition.cs index cb89a97b..52efe144 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppTypeDefinition.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppTypeDefinition.cs @@ -15,7 +15,7 @@ namespace Il2CppInspector.Next.Metadata; using VTableIndex = int; [VersionedStruct] -public partial struct Il2CppTypeDefinition +public partial record struct Il2CppTypeDefinition { public const TypeIndex InvalidTypeIndex = -1; diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppTypeDefinitionBitfield.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppTypeDefinitionBitfield.cs index 28875b89..b66dbc9b 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppTypeDefinitionBitfield.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppTypeDefinitionBitfield.cs @@ -3,7 +3,7 @@ namespace Il2CppInspector.Next.Metadata; [VersionedStruct] -public partial struct Il2CppTypeDefinitionBitfield +public partial record struct Il2CppTypeDefinitionBitfield { private uint _value; diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppWindowsRuntimeTypeNamePair.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppWindowsRuntimeTypeNamePair.cs index 217a2129..6ac6357d 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppWindowsRuntimeTypeNamePair.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppWindowsRuntimeTypeNamePair.cs @@ -5,7 +5,7 @@ using TypeIndex = int; [VersionedStruct] -public partial struct Il2CppWindowsRuntimeTypeNamePair +public partial record struct Il2CppWindowsRuntimeTypeNamePair { public StringIndex NameIndex { get; private set; } public TypeIndex TypeIndex { get; private set; } diff --git a/Il2CppInspector.Common/Next/Pointer.cs b/Il2CppInspector.Common/Next/Pointer.cs index db0de19b..f523c491 100644 --- a/Il2CppInspector.Common/Next/Pointer.cs +++ b/Il2CppInspector.Common/Next/Pointer.cs @@ -4,9 +4,9 @@ namespace Il2CppInspector.Next; -public struct Pointer(ulong value = 0) : IReadable, IEquatable> where T : IReadable, new() +public struct Pointer(ulong value = 0) : IReadable, IEquatable> where T : struct, IReadable { - [CustomSerialization("reader.ReadNUInt();", "is32Bit ? 4 : 8")] + [NativeInteger] private ulong _value = value; public readonly ulong PointerValue => _value; diff --git a/Il2CppInspector.Common/Next/PrimitivePointer.cs b/Il2CppInspector.Common/Next/PrimitivePointer.cs new file mode 100644 index 00000000..60d17ff9 --- /dev/null +++ b/Il2CppInspector.Common/Next/PrimitivePointer.cs @@ -0,0 +1,60 @@ +using System.Collections.Immutable; +using VersionedSerialization; +using VersionedSerialization.Attributes; + +namespace Il2CppInspector.Next; + +public struct PrimitivePointer(ulong value = 0) : IReadable, IEquatable> where T : unmanaged +{ + [NativeInteger] + private ulong _value = value; + + public readonly ulong PointerValue => _value; + public readonly bool Null => _value == 0; + + public void Read(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct + { + _value = reader.ReadNUInt(); + } + + public static int Size(in StructVersion version = default, bool is32Bit = false) + { + return is32Bit ? 4 : 8; + } + + public readonly T Read(ref SpanReader reader) + { + reader.Offset = (int)PointerValue; + return reader.ReadPrimitive(); + } + + public readonly ImmutableArray ReadArray(ref SpanReader reader, long count) + { + reader.Offset = (int)PointerValue; + return reader.ReadPrimitiveArray(count); + } + + public static implicit operator PrimitivePointer(ulong value) => new(value); + public static implicit operator ulong(PrimitivePointer ptr) => ptr.PointerValue; + + #region Equality operators + ToString + + public static bool operator ==(PrimitivePointer left, PrimitivePointer right) + => left._value == right._value; + + public static bool operator !=(PrimitivePointer left, PrimitivePointer right) + => !(left == right); + + public readonly override bool Equals(object? obj) + => obj is PrimitivePointer other && Equals(other); + + public readonly bool Equals(PrimitivePointer other) + => this == other; + + public readonly override int GetHashCode() + => HashCode.Combine(_value); + + public readonly override string ToString() => $"0x{_value:X} <{typeof(T).Name}>"; + + #endregion +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Reflection/TypeModel.cs b/Il2CppInspector.Common/Reflection/TypeModel.cs index e052d321..2cdfd7f4 100644 --- a/Il2CppInspector.Common/Reflection/TypeModel.cs +++ b/Il2CppInspector.Common/Reflection/TypeModel.cs @@ -87,7 +87,7 @@ public MethodBase GetGenericMethod(string fullName, params TypeInfo[] typeArgume public TypeModel(Il2CppInspector package) { Package = package; TypesByDefinitionIndex = new TypeInfo[package.TypeDefinitions.Length]; - TypesByReferenceIndex = new TypeInfo[package.TypeReferences.Count]; + TypesByReferenceIndex = new TypeInfo[package.TypeReferences.Length]; GenericParameterTypes = new TypeInfo[package.GenericParameters.Length]; MethodsByDefinitionIndex = new MethodBase[package.Methods.Length]; MethodInvokers = new MethodInvoker[package.MethodInvokePointers.Length]; @@ -99,7 +99,7 @@ public TypeModel(Il2CppInspector package) { // Create and reference types from TypeRefs // Note that you can't resolve any TypeRefs until all the TypeDefs have been processed - for (int typeRefIndex = 0; typeRefIndex < package.TypeReferences.Count; typeRefIndex++) { + for (int typeRefIndex = 0; typeRefIndex < package.TypeReferences.Length; typeRefIndex++) { if(TypesByReferenceIndex[typeRefIndex] != null) { /* type already generated - probably by forward reference through GetTypeFromVirtualAddress */ continue; @@ -191,7 +191,7 @@ public TypeModel(Il2CppInspector package) { public TypeInfo[] ResolveGenericArguments(Il2CppGenericInst inst) { // Get list of pointers to type parameters (both unresolved and concrete) - var genericTypeArguments = Package.BinaryImage.ReadMappedArray(inst.TypeArgv, (int)inst.TypeArgc); + var genericTypeArguments = Package.BinaryImage.ReadMappedUWordArray(inst.TypeArgv, (int)inst.TypeArgc); return genericTypeArguments.Select(a => GetTypeFromVirtualAddress(a)).ToArray(); } @@ -212,7 +212,7 @@ private TypeInfo resolveTypeReference(Il2CppType typeRef) { // Constructed types case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: // TODO: Replace with array load from Il2CppMetadataRegistration.genericClasses - var generic = image.ReadMappedObject(typeRef.Data.GenericClass); // Il2CppGenericClass * + var generic = image.ReadMappedVersionedObject(typeRef.Data.GenericClass); // Il2CppGenericClass * // Get generic type definition TypeInfo genericTypeDef; @@ -230,13 +230,13 @@ private TypeInfo resolveTypeReference(Il2CppType typeRef) { // Get the instantiation // TODO: Replace with array load from Il2CppMetadataRegistration.genericInsts - var genericInstance = image.ReadMappedObject(generic.Context.ClassInst); + var genericInstance = image.ReadMappedVersionedObject(generic.Context.ClassInst); var genericArguments = ResolveGenericArguments(genericInstance); underlyingType = genericTypeDef.MakeGenericType(genericArguments); break; case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: - var descriptor = image.ReadMappedObject(typeRef.Data.ArrayType); + var descriptor = image.ReadMappedVersionedObject(typeRef.Data.ArrayType); var elementType = GetTypeFromVirtualAddress(descriptor.ElementType); underlyingType = elementType.MakeArrayType(descriptor.Rank); break; diff --git a/Il2CppInspector.Common/Utils/BlobReader.cs b/Il2CppInspector.Common/Utils/BlobReader.cs index c031950b..89e4940f 100644 --- a/Il2CppInspector.Common/Utils/BlobReader.cs +++ b/Il2CppInspector.Common/Utils/BlobReader.cs @@ -1,8 +1,6 @@ using NoisyCowStudios.Bin2Object; using System.Text; -using System; using System.Diagnostics; -using System.IO; using Il2CppInspector.Next; using Il2CppInspector.Next.BinaryMetadata; using Il2CppInspector.Next.Metadata; @@ -11,7 +9,7 @@ namespace Il2CppInspector.Utils; public static class BlobReader { - public static object GetConstantValueFromBlob(Il2CppInspector inspector, Il2CppTypeEnum type, BinaryObjectStream blob) + public static object GetConstantValueFromBlob(Il2CppInspector inspector, Il2CppTypeEnum type, BinaryObjectStreamReader blob) { const byte kArrayTypeWithDifferentElements = 1; @@ -28,7 +26,7 @@ public static object GetConstantValueFromBlob(Il2CppInspector inspector, Il2CppT break; case Il2CppTypeEnum.IL2CPP_TYPE_CHAR: // UTF-8 character assumed - value = BitConverter.ToChar(blob.ReadBytes(2), 0); + value = (char)blob.ReadPrimitive(); break; case Il2CppTypeEnum.IL2CPP_TYPE_U2: value = blob.ReadUInt16(); @@ -162,7 +160,7 @@ public static Il2CppTypeEnum ReadEncodedTypeEnum(Il2CppInspector inspector, Bina if (typeEnum == Il2CppTypeEnum.IL2CPP_TYPE_ENUM) { var typeIndex = blob.ReadCompressedInt32(); - var typeHandle = (uint)inspector.TypeReferences[typeIndex].Data.KlassIndex; + var typeHandle = inspector.TypeReferences[typeIndex].Data.KlassIndex; enumType = inspector.TypeDefinitions[typeHandle]; var elementTypeHandle = inspector.TypeReferences[enumType.ElementTypeIndex].Data.KlassIndex; diff --git a/VersionedSerialization.Generator/Models/ObjectSerializationInfo.cs b/VersionedSerialization.Generator/Models/ObjectSerializationInfo.cs index bb2f002c..c2f91e05 100644 --- a/VersionedSerialization.Generator/Models/ObjectSerializationInfo.cs +++ b/VersionedSerialization.Generator/Models/ObjectSerializationInfo.cs @@ -1,4 +1,5 @@ -using VersionedSerialization.Generator.Utils; +using Microsoft.CodeAnalysis.CSharp; +using VersionedSerialization.Generator.Utils; namespace VersionedSerialization.Generator.Models; @@ -6,8 +7,7 @@ public sealed record ObjectSerializationInfo( string Namespace, string Name, bool HasBaseType, - bool IsStruct, - bool ShouldGenerateSizeMethod, + SyntaxKind DefinitionType, bool CanGenerateSizeMethod, ImmutableEquatableArray Properties ); \ No newline at end of file diff --git a/VersionedSerialization.Generator/Models/PropertySerializationInfo.cs b/VersionedSerialization.Generator/Models/PropertySerializationInfo.cs index fb4a961a..2c5eb40a 100644 --- a/VersionedSerialization.Generator/Models/PropertySerializationInfo.cs +++ b/VersionedSerialization.Generator/Models/PropertySerializationInfo.cs @@ -6,6 +6,6 @@ public sealed record PropertySerializationInfo( string Name, string ReadMethod, string SizeExpression, - int Alignment, + PropertyType Type, ImmutableEquatableArray VersionConditions ); \ No newline at end of file diff --git a/VersionedSerialization.Generator/Models/PropertyType.cs b/VersionedSerialization.Generator/Models/PropertyType.cs index 3e855d08..d2a7f486 100644 --- a/VersionedSerialization.Generator/Models/PropertyType.cs +++ b/VersionedSerialization.Generator/Models/PropertyType.cs @@ -16,6 +16,9 @@ public enum PropertyType Int32, Int64, String, + Custom, + NativeInteger, + UNativeInteger, } public static class PropertyTypeExtensions @@ -35,6 +38,9 @@ public static string GetTypeName(this PropertyType type) PropertyType.Int32 => nameof(PropertyType.Int32), PropertyType.Int64 => nameof(PropertyType.Int64), PropertyType.String => nameof(String), + PropertyType.Custom => "", + PropertyType.NativeInteger => "NInt", + PropertyType.UNativeInteger => "NUInt", _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) }; @@ -43,6 +49,40 @@ public static bool IsSeperateMethod(this PropertyType type) { PropertyType.Boolean => true, PropertyType.String => true, + PropertyType.Custom => true, + PropertyType.NativeInteger => true, + PropertyType.UNativeInteger => true, + _ => false + }; + + public static int GetTypeSize(this PropertyType type) + => type switch + { + PropertyType.Unsupported => -1, + PropertyType.None => 0, + PropertyType.UInt8 => 1, + PropertyType.Int8 => 1, + PropertyType.Boolean => 1, + PropertyType.UInt16 => 2, + PropertyType.UInt32 => 4, + PropertyType.UInt64 => 8, + PropertyType.Int16 => 2, + PropertyType.Int32 => 4, + PropertyType.Int64 => 8, + PropertyType.String => -1, + PropertyType.Custom => -1, + PropertyType.NativeInteger => -1, + PropertyType.UNativeInteger => -1, + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + }; + + public static bool IsUnsignedType(this PropertyType type) + => type switch + { + PropertyType.UInt8 + or PropertyType.UInt16 + or PropertyType.UInt32 + or PropertyType.UInt64 => true, _ => false }; } \ No newline at end of file diff --git a/VersionedSerialization.Generator/ObjectSerializationGenerator.cs b/VersionedSerialization.Generator/ObjectSerializationGenerator.cs index ca372802..2c3043a9 100644 --- a/VersionedSerialization.Generator/ObjectSerializationGenerator.cs +++ b/VersionedSerialization.Generator/ObjectSerializationGenerator.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; @@ -20,7 +19,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var valueProvider = context.SyntaxProvider .ForAttributeWithMetadataName(Constants.VersionedStructAttribute, - static (node, _) => node is ClassDeclarationSyntax or StructDeclarationSyntax, + static (node, _) => node is ClassDeclarationSyntax or StructDeclarationSyntax or RecordDeclarationSyntax, static (context, _) => (ContextClass: (TypeDeclarationSyntax)context.TargetNode, context.SemanticModel)) .Combine(context.CompilationProvider) .Select(static (tuple, cancellationToken) => ParseSerializationInfo(tuple.Left.ContextClass, tuple.Left.SemanticModel, tuple.Right, cancellationToken)) @@ -63,7 +62,16 @@ private static void EmitCode(SourceProductionContext sourceProductionContext, Ob generator.LeaveScope(); } - generator.EnterScope($"public partial {(info.IsStruct ? "struct" : "class")} {info.Name} : IReadable"); + var definitionType = info.DefinitionType switch + { + SyntaxKind.ClassDeclaration => "class", + SyntaxKind.StructDeclaration => "struct", + SyntaxKind.RecordDeclaration => "record", + SyntaxKind.RecordStructDeclaration => "record struct", + _ => throw new IndexOutOfRangeException() + }; + + generator.EnterScope($"public partial {definitionType} {info.Name} : IReadable"); GenerateReadMethod(generator, info); generator.AppendLine(); GenerateSizeMethod(generator, info); @@ -92,12 +100,7 @@ private static void GenerateSizeMethod(CodeGenerator generator, ObjectSerializat GenerateVersionCondition(property.VersionConditions, generator); generator.EnterScope(); - generator.AppendLine($"size += {property.SizeExpression};"); - - if (property.Alignment != 0) - generator.AppendLine($"size += size % {property.Alignment} == 0 ? 0 : {property.Alignment} - (size % {property.Alignment});"); - generator.LeaveScope(); } @@ -121,10 +124,6 @@ private static void GenerateReadMethod(CodeGenerator generator, ObjectSerializat generator.EnterScope(); generator.AppendLine($"this.{property.Name} = {property.ReadMethod}"); - - if (property.Alignment != 0) - generator.AppendLine($"reader.Align({property.Alignment});"); - generator.LeaveScope(); } @@ -176,9 +175,10 @@ private static ObjectSerializationInfo ParseSerializationInfo(TypeDeclarationSyn { var classSymbol = model.GetDeclaredSymbol(contextClass, cancellationToken) ?? throw new InvalidOperationException(); - var alignedAttribute = compilation.GetTypeByMetadataName(Constants.AlignedAttribute); + //var versionedStructAttribute = compilation.GetTypeByMetadataName(Constants.VersionedStructAttribute); var versionConditionAttribute = compilation.GetTypeByMetadataName(Constants.VersionConditionAttribute); var customSerializationAttribute = compilation.GetTypeByMetadataName(Constants.CustomSerializationAttribute); + var nativeIntegerAttribute = compilation.GetTypeByMetadataName(Constants.NativeIntegerAttribute); var canGenerateSizeMethod = true; @@ -190,7 +190,6 @@ private static ObjectSerializationInfo ParseSerializationInfo(TypeDeclarationSyn || member is IPropertySymbol { SetMethod: null }) continue; - var alignment = 0; var versionConditions = new List(); ITypeSymbol type; @@ -207,41 +206,12 @@ private static ObjectSerializationInfo ParseSerializationInfo(TypeDeclarationSyn } var typeInfo = ParseType(type); - - canGenerateSizeMethod &= typeInfo.Type != PropertyType.String; - - string readMethod; - if (typeInfo.Type == PropertyType.None) - { - readMethod = $"reader.ReadVersionedObject<{typeInfo.ComplexTypeName}>(in version);"; - } - else - { - readMethod = typeInfo.Type.IsSeperateMethod() - ? $"reader.Read{typeInfo.Type.GetTypeName()}();" - : $"reader.ReadPrimitive<{typeInfo.Type.GetTypeName()}>();"; - - if (typeInfo.ComplexTypeName != "") - readMethod = $"({typeInfo.ComplexTypeName}){readMethod}"; - } - - string sizeExpression; - if (typeInfo.Type == PropertyType.None) - { - sizeExpression = $"{typeInfo.ComplexTypeName}.Size(in version, is32Bit)"; - } - else - { - sizeExpression = $"sizeof({typeInfo.Type.GetTypeName()})"; - } + string? readMethod = null; + string? sizeExpression = null; foreach (var attribute in member.GetAttributes()) { - if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, alignedAttribute)) - { - alignment = (int)attribute.ConstructorArguments[0].Value!; - } - else if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, versionConditionAttribute)) + if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, versionConditionAttribute)) { StructVersion? lessThan = null, moreThan = null, @@ -278,16 +248,54 @@ private static ObjectSerializationInfo ParseSerializationInfo(TypeDeclarationSyn } else if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, customSerializationAttribute)) { + typeInfo = (PropertyType.Custom, "", typeInfo.IsArray); readMethod = (string)attribute.ConstructorArguments[0].Value!; sizeExpression = (string)attribute.ConstructorArguments[1].Value!; } + else if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, nativeIntegerAttribute)) + { + typeInfo = (typeInfo.Type.IsUnsignedType() + ? PropertyType.UNativeInteger + : PropertyType.NativeInteger, + typeInfo.ComplexTypeName == "" + ? typeInfo.Type.GetTypeName() + : typeInfo.ComplexTypeName, + typeInfo.IsArray); + } } + canGenerateSizeMethod &= typeInfo.Type != PropertyType.String; + + if (readMethod == null) + { + if (typeInfo.Type == PropertyType.None) + { + readMethod = $"reader.ReadVersionedObject<{typeInfo.ComplexTypeName}>(in version);"; + } + else + { + readMethod = typeInfo.Type.IsSeperateMethod() + ? $"reader.Read{typeInfo.Type.GetTypeName()}();" + : $"reader.ReadPrimitive<{typeInfo.Type.GetTypeName()}>();"; + + if (typeInfo.ComplexTypeName != "") + readMethod = $"({typeInfo.ComplexTypeName}){readMethod}"; + } + } + + sizeExpression ??= typeInfo.Type switch + { + PropertyType.None => $"{typeInfo.ComplexTypeName}.Size(in version, is32Bit)", + PropertyType.NativeInteger or PropertyType.UNativeInteger => + "is32Bit ? sizeof(uint) : sizeof(ulong)", + _ => $"sizeof({typeInfo.Type.GetTypeName()})" + }; + properties.Add(new PropertySerializationInfo( member.Name, readMethod, sizeExpression, - alignment, + typeInfo.Type, versionConditions.ToImmutableEquatableArray() )); } @@ -307,8 +315,7 @@ private static ObjectSerializationInfo ParseSerializationInfo(TypeDeclarationSyn classSymbol.ContainingNamespace.ToDisplayString(), classSymbol.Name, hasBaseType, - contextClass.Kind() == SyntaxKind.StructDeclaration, - true, + contextClass.Kind(), canGenerateSizeMethod, properties.ToImmutableEquatableArray() ); diff --git a/VersionedSerialization.Generator/Utils/Constants.cs b/VersionedSerialization.Generator/Utils/Constants.cs index db705d07..065fd50b 100644 --- a/VersionedSerialization.Generator/Utils/Constants.cs +++ b/VersionedSerialization.Generator/Utils/Constants.cs @@ -5,9 +5,9 @@ public static class Constants private const string AttributeNamespace = "VersionedSerialization.Attributes"; public const string VersionedStructAttribute = $"{AttributeNamespace}.{nameof(VersionedStructAttribute)}"; - public const string AlignedAttribute = $"{AttributeNamespace}.{nameof(AlignedAttribute)}"; public const string VersionConditionAttribute = $"{AttributeNamespace}.{nameof(VersionConditionAttribute)}"; public const string CustomSerializationAttribute = $"{AttributeNamespace}.{nameof(CustomSerializationAttribute)}"; + public const string NativeIntegerAttribute = $"{AttributeNamespace}.{nameof(NativeIntegerAttribute)}"; public const string LessThan = nameof(LessThan); public const string GreaterThan = nameof(GreaterThan); diff --git a/VersionedSerialization/Attributes/AlignedAttribute.cs b/VersionedSerialization/Attributes/AlignedAttribute.cs deleted file mode 100644 index bfb6c04e..00000000 --- a/VersionedSerialization/Attributes/AlignedAttribute.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace VersionedSerialization.Attributes; - -#pragma warning disable CS9113 // Parameter is unread. -[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] -public class AlignedAttribute(int alignment) : Attribute; -#pragma warning restore CS9113 // Parameter is unread. diff --git a/VersionedSerialization/Attributes/NativeIntegerAttribute.cs b/VersionedSerialization/Attributes/NativeIntegerAttribute.cs new file mode 100644 index 00000000..ae90d98b --- /dev/null +++ b/VersionedSerialization/Attributes/NativeIntegerAttribute.cs @@ -0,0 +1,3 @@ +namespace VersionedSerialization.Attributes; + +public class NativeIntegerAttribute : Attribute; \ No newline at end of file diff --git a/VersionedSerialization/IReader.cs b/VersionedSerialization/IReader.cs index c216fc27..764eac6f 100644 --- a/VersionedSerialization/IReader.cs +++ b/VersionedSerialization/IReader.cs @@ -18,5 +18,6 @@ public interface IReader T ReadVersionedObject(in StructVersion version = default) where T : IReadable, new(); ImmutableArray ReadVersionedObjectArray(long count, in StructVersion version = default) where T : IReadable, new(); - public void Align(int alignment = 0); + void Align(int alignment = 0); + void Skip(int count); } \ No newline at end of file diff --git a/VersionedSerialization/SpanReader.cs b/VersionedSerialization/SpanReader.cs index de26c030..6402cd4c 100644 --- a/VersionedSerialization/SpanReader.cs +++ b/VersionedSerialization/SpanReader.cs @@ -143,4 +143,9 @@ public void Align(int alignment = 0) if (rem != 0) Offset += alignment - rem; } + + public void Skip(int count) + { + Offset += count; + } } \ No newline at end of file From a7081ccfa9441c6c8d3932ae5a5a54cfcbb67322 Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Sun, 18 Aug 2024 22:40:14 +0200 Subject: [PATCH 07/27] move 29/31.1/.2 to use tags (-2022,-2023) instead of minor versions --- Il2CppInspector.Common/IL2CPP/ImageScan.cs | 5 +---- Il2CppInspector.Common/IL2CPP/Metadata.cs | 2 +- .../Next/BinaryMetadata/Il2CppCodeRegistration.cs | 15 ++++++++------- .../Next/Metadata/Il2CppMethodDefinition.cs | 6 ++---- Il2CppInspector.Common/Next/MetadataVersions.cs | 10 ++++------ .../ObjectSerializationGenerator.cs | 8 ++++++-- 6 files changed, 22 insertions(+), 24 deletions(-) diff --git a/Il2CppInspector.Common/IL2CPP/ImageScan.cs b/Il2CppInspector.Common/IL2CPP/ImageScan.cs index 637ae7be..d2e07a30 100644 --- a/Il2CppInspector.Common/IL2CPP/ImageScan.cs +++ b/Il2CppInspector.Common/IL2CPP/ImageScan.cs @@ -239,7 +239,7 @@ ulong FindCodeRegistration() if ((Image.Version == MetadataVersions.V290 || Image.Version == MetadataVersions.V310) && cr.InteropData + cr.InteropDataCount >= (ulong)Image.Length) { - Image.Version = new StructVersion(Image.Version.Major, 1, Image.Version.Tag); + Image.Version = new StructVersion(Image.Version.Major, 0, MetadataVersions.Tag2022); cr = Image.ReadMappedVersionedObject(codeRegistration); } } @@ -250,9 +250,6 @@ ulong FindCodeRegistration() // The first item in CodeRegistration is the total number of method pointers vas = FindAllMappedWords(imageBytes, (ulong) metadata.Methods.Count(m => (uint) m.MethodIndex != 0xffff_ffff)); - if (!vas.Any()) - return (0, 0); - // The count of method pointers will be followed some bytes later by // the count of custom attribute generators; the distance between them // depends on the il2cpp version so we just use ReadMappedObject to simplify the math diff --git a/Il2CppInspector.Common/IL2CPP/Metadata.cs b/Il2CppInspector.Common/IL2CPP/Metadata.cs index 17c9ee6f..28c25658 100644 --- a/Il2CppInspector.Common/IL2CPP/Metadata.cs +++ b/Il2CppInspector.Common/IL2CPP/Metadata.cs @@ -206,7 +206,7 @@ private void Initialize() var secondToken = Methods[1].Token; if (secondToken >> 24 != 0x6) { - Version = new StructVersion(Version.Major, 1, Version.Tag); + Version = new StructVersion(Version.Major, 0, MetadataVersions.Tag2023); Methods = ReadVersionedObjectArray(Header.MethodsOffset, Header.MethodsSize / Sizeof()); diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeRegistration.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeRegistration.cs index 8350d290..c8a07308 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeRegistration.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeRegistration.cs @@ -69,23 +69,24 @@ public partial record struct Il2CppCodeRegistration public Pointer Guids; [NativeInteger] - [VersionCondition(GreaterThan = "22.0", LessThan = "29.0")] + [VersionCondition(GreaterThan = "22.0", LessThan = "27.2")] + [VersionCondition(EqualTo = "29.0", IncludingTag = "")] public int UnresolvedVirtualCallCount; [NativeInteger] - [VersionCondition(EqualTo = "29.1"), VersionCondition(EqualTo = "31.1")] - [VersionCondition(EqualTo = "29.2"), VersionCondition(EqualTo = "31.2")] + [VersionCondition(EqualTo = "29.0", IncludingTag = "2022"), VersionCondition(EqualTo = "31.0", IncludingTag = "2022")] + [VersionCondition(EqualTo = "29.0", IncludingTag = "2023"), VersionCondition(EqualTo = "31.0", IncludingTag = "2023")] public uint UnresolvedIndirectCallCount; // UnresolvedVirtualCallCount pre 29.1 [VersionCondition(GreaterThan = "22.0")] public Pointer UnresolvedVirtualCallPointers; - [VersionCondition(EqualTo = "29.1"), VersionCondition(EqualTo = "31.1")] - [VersionCondition(EqualTo = "29.2"), VersionCondition(EqualTo = "31.2")] + [VersionCondition(EqualTo = "29.0", IncludingTag = "2022"), VersionCondition(EqualTo = "31.0", IncludingTag = "2022")] + [VersionCondition(EqualTo = "29.0", IncludingTag = "2023"), VersionCondition(EqualTo = "31.0", IncludingTag = "2023")] public Pointer UnresolvedInstanceCallWrappers; - [VersionCondition(EqualTo = "29.1"), VersionCondition(EqualTo = "31.1")] - [VersionCondition(EqualTo = "29.2"), VersionCondition(EqualTo = "31.2")] + [VersionCondition(EqualTo = "29.0", IncludingTag = "2022"), VersionCondition(EqualTo = "31.0", IncludingTag = "2022")] + [VersionCondition(EqualTo = "29.0", IncludingTag = "2023"), VersionCondition(EqualTo = "31.0", IncludingTag = "2023")] public Pointer UnresolvedStaticCallPointers; [NativeInteger] diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppMethodDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppMethodDefinition.cs index 376016b8..7e131f00 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppMethodDefinition.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppMethodDefinition.cs @@ -19,8 +19,6 @@ public partial record struct Il2CppMethodDefinition public TypeIndex ReturnType { get; private set; } [VersionCondition(EqualTo = "31.0")] - [VersionCondition(EqualTo = "31.1")] - [VersionCondition(EqualTo = "31.2")] public uint ReturnParameterToken { get; private set; } public ParameterIndex ParameterStart { get; private set; } @@ -51,8 +49,8 @@ public partial record struct Il2CppMethodDefinition public ushort Slot { get; private set; } public ushort ParameterCount { get; private set; } - [VersionCondition(EqualTo = "29.2")] - [VersionCondition(EqualTo = "31.2")] + [VersionCondition(EqualTo = "29.0", IncludingTag = "2023")] + [VersionCondition(EqualTo = "31.0", IncludingTag = "2023")] public bool IsUnmanagedCallersOnly { get; private set; } public readonly MethodAttributes Attributes => (MethodAttributes)Flags; diff --git a/Il2CppInspector.Common/Next/MetadataVersions.cs b/Il2CppInspector.Common/Next/MetadataVersions.cs index 992f6368..383ab0ac 100644 --- a/Il2CppInspector.Common/Next/MetadataVersions.cs +++ b/Il2CppInspector.Common/Next/MetadataVersions.cs @@ -22,12 +22,10 @@ public static class MetadataVersions public static readonly StructVersion V271 = new(27, 1); public static readonly StructVersion V272 = new(27, 2); + // These two versions have three variations: 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 tag - 29.0/31.0 + public static readonly string Tag2022 = "2022"; // 29.1/31.1 + public static readonly string Tag2023 = "2023"; // 29.2/31.2 } \ No newline at end of file diff --git a/VersionedSerialization.Generator/ObjectSerializationGenerator.cs b/VersionedSerialization.Generator/ObjectSerializationGenerator.cs index 2c3043a9..d6d40109 100644 --- a/VersionedSerialization.Generator/ObjectSerializationGenerator.cs +++ b/VersionedSerialization.Generator/ObjectSerializationGenerator.cs @@ -154,10 +154,14 @@ private static void GenerateVersionCondition(ImmutableEquatableArray Date: Sun, 18 Aug 2024 22:40:29 +0200 Subject: [PATCH 08/27] fix metadata usage validity checks --- Il2CppInspector.Common/IL2CPP/MetadataUsage.cs | 2 ++ Il2CppInspector.Common/Reflection/TypeInfo.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs b/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs index d05d1b82..5123ab4c 100644 --- a/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs +++ b/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs @@ -27,6 +27,8 @@ public record struct MetadataUsage public int SourceIndex { get; } public ulong VirtualAddress { get; private set; } + public readonly bool IsValid => Type != 0; + public MetadataUsage(MetadataUsageType type, int sourceIndex, ulong virtualAddress = 0) { Type = type; SourceIndex = sourceIndex; diff --git a/Il2CppInspector.Common/Reflection/TypeInfo.cs b/Il2CppInspector.Common/Reflection/TypeInfo.cs index 305ba43c..3067c40f 100644 --- a/Il2CppInspector.Common/Reflection/TypeInfo.cs +++ b/Il2CppInspector.Common/Reflection/TypeInfo.cs @@ -259,7 +259,7 @@ public MethodBase[] GetVTable() { MetadataUsage[] vt = Assembly.Model.Package.GetVTable(Definition); MethodBase[] res = new MethodBase[vt.Length]; for (int i = 0; i < vt.Length; i++) { - if (vt[i] != null) + if (vt[i].IsValid) res[i] = Assembly.Model.GetMetadataUsageMethod(vt[i]); } return res; From e6c51b47d6327d6902d31d523b4f7b2afc808d71 Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Sat, 24 Aug 2024 22:55:35 +0200 Subject: [PATCH 09/27] rework code registration offsetting a bit and add second 29/31.1 condition --- Il2CppInspector.Common/IL2CPP/ImageScan.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Il2CppInspector.Common/IL2CPP/ImageScan.cs b/Il2CppInspector.Common/IL2CPP/ImageScan.cs index d2e07a30..dba46967 100644 --- a/Il2CppInspector.Common/IL2CPP/ImageScan.cs +++ b/Il2CppInspector.Common/IL2CPP/ImageScan.cs @@ -206,9 +206,9 @@ ulong FindCodeRegistration() return (0, 0); + var codeGenEndPtr = codeRegVa + ptrSize; // pCodeGenModules is the last field in CodeRegistration so we subtract the size of one pointer from the struct size - var codeRegSize = (ulong)Il2CppCodeRegistration.Size(Image.Version, Image.Bits == 32); - codeRegistration = codeRegVa - codeRegSize - ptrSize; + codeRegistration = codeGenEndPtr - (ulong)Il2CppCodeRegistration.Size(Image.Version, Image.Bits == 32); // 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. @@ -216,7 +216,7 @@ ulong FindCodeRegistration() if (Image.Version == MetadataVersions.V242 && cr.InteropDataCount == 0) { Image.Version = MetadataVersions.V243; - codeRegistration -= ptrSize * 2; // two extra words for WindowsRuntimeFactory + codeRegistration = codeGenEndPtr - (ulong)Il2CppCodeRegistration.Size(Image.Version, Image.Bits == 32); } if (Image.Version == MetadataVersions.V270 && cr.ReversePInvokeWrapperCount > 0x30000) @@ -224,7 +224,8 @@ ulong FindCodeRegistration() // 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 = MetadataVersions.V271; - codeRegistration -= ptrSize; + codeRegistration = codeGenEndPtr - (ulong)Il2CppCodeRegistration.Size(Image.Version, Image.Bits == 32); + cr = Image.ReadMappedVersionedObject(codeRegistration); } // genericAdjustorThunks was inserted before invokerPointersCount in 24.5 and 27.1 @@ -232,15 +233,16 @@ ulong FindCodeRegistration() if (Image.Version == MetadataVersions.V244 && cr.InvokerPointersCount > 0x50000) { Image.Version = MetadataVersions.V245; - codeRegistration += 1 * ptrSize; + codeRegistration = codeGenEndPtr - (ulong)Il2CppCodeRegistration.Size(Image.Version, Image.Bits == 32); cr = Image.ReadMappedVersionedObject(codeRegistration); } if ((Image.Version == MetadataVersions.V290 || Image.Version == MetadataVersions.V310) && - cr.InteropData + cr.InteropDataCount >= (ulong)Image.Length) + (!Image.TryMapVATR(cr.InvokerPointers + cr.InvokerPointersCount, out _) || + !Image.TryMapVATR(cr.GenericMethodPointers + cr.GenericMethodPointersCount, out _))) { Image.Version = new StructVersion(Image.Version.Major, 0, MetadataVersions.Tag2022); - cr = Image.ReadMappedVersionedObject(codeRegistration); + codeRegistration = codeGenEndPtr - (ulong)Il2CppCodeRegistration.Size(Image.Version, Image.Bits == 32); } } From 1a12cf5081e58822aff6dde3ebb5103a52f34276 Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Fri, 30 Aug 2024 20:23:17 +0200 Subject: [PATCH 10/27] tweak .1 condition (again) --- Il2CppInspector.Common/IL2CPP/ImageScan.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Il2CppInspector.Common/IL2CPP/ImageScan.cs b/Il2CppInspector.Common/IL2CPP/ImageScan.cs index dba46967..d1cc835e 100644 --- a/Il2CppInspector.Common/IL2CPP/ImageScan.cs +++ b/Il2CppInspector.Common/IL2CPP/ImageScan.cs @@ -238,8 +238,7 @@ ulong FindCodeRegistration() } if ((Image.Version == MetadataVersions.V290 || Image.Version == MetadataVersions.V310) && - (!Image.TryMapVATR(cr.InvokerPointers + cr.InvokerPointersCount, out _) || - !Image.TryMapVATR(cr.GenericMethodPointers + cr.GenericMethodPointersCount, out _))) + cr.GenericMethodPointersCount >= cr.GenericMethodPointers) { Image.Version = new StructVersion(Image.Version.Major, 0, MetadataVersions.Tag2022); codeRegistration = codeGenEndPtr - (ulong)Il2CppCodeRegistration.Size(Image.Version, Image.Bits == 32); From 7632ff228350394211dcb713d9d4ade728f8326e Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Sun, 1 Sep 2024 02:01:34 +0200 Subject: [PATCH 11/27] 29/31.2 was a psyop --- .../Cpp/UnityHeaders/29.2-2023.2.0a22-.h | 2421 ----------------- Il2CppInspector.Common/IL2CPP/Metadata.cs | 19 - .../Next/Metadata/Il2CppMethodDefinition.cs | 4 - .../Next/MetadataVersions.cs | 4 +- 4 files changed, 2 insertions(+), 2446 deletions(-) delete mode 100644 Il2CppInspector.Common/Cpp/UnityHeaders/29.2-2023.2.0a22-.h diff --git a/Il2CppInspector.Common/Cpp/UnityHeaders/29.2-2023.2.0a22-.h b/Il2CppInspector.Common/Cpp/UnityHeaders/29.2-2023.2.0a22-.h deleted file mode 100644 index 9e4b050d..00000000 --- a/Il2CppInspector.Common/Cpp/UnityHeaders/29.2-2023.2.0a22-.h +++ /dev/null @@ -1,2421 +0,0 @@ -typedef struct Il2CppClass Il2CppClass; -typedef struct Il2CppType Il2CppType; -typedef struct EventInfo EventInfo; -typedef struct MethodInfo MethodInfo; -typedef struct FieldInfo FieldInfo; -typedef struct PropertyInfo PropertyInfo; -typedef struct Il2CppAssembly Il2CppAssembly; -typedef struct Il2CppArray Il2CppArray; -typedef struct Il2CppDelegate Il2CppDelegate; -typedef struct Il2CppDomain Il2CppDomain; -typedef struct Il2CppImage Il2CppImage; -typedef struct Il2CppException Il2CppException; -typedef struct Il2CppProfiler Il2CppProfiler; -typedef struct Il2CppObject Il2CppObject; -typedef struct Il2CppReflectionMethod Il2CppReflectionMethod; -typedef struct Il2CppReflectionType Il2CppReflectionType; -typedef struct Il2CppString Il2CppString; -typedef struct Il2CppThread Il2CppThread; -typedef struct Il2CppAsyncResult Il2CppAsyncResult; -typedef struct Il2CppManagedMemorySnapshot Il2CppManagedMemorySnapshot; -typedef struct Il2CppCustomAttrInfo Il2CppCustomAttrInfo; -typedef enum -{ - IL2CPP_PROFILE_NONE = 0, - IL2CPP_PROFILE_APPDOMAIN_EVENTS = 1 << 0, - IL2CPP_PROFILE_ASSEMBLY_EVENTS = 1 << 1, - IL2CPP_PROFILE_MODULE_EVENTS = 1 << 2, - IL2CPP_PROFILE_CLASS_EVENTS = 1 << 3, - IL2CPP_PROFILE_JIT_COMPILATION = 1 << 4, - IL2CPP_PROFILE_INLINING = 1 << 5, - IL2CPP_PROFILE_EXCEPTIONS = 1 << 6, - IL2CPP_PROFILE_ALLOCATIONS = 1 << 7, - IL2CPP_PROFILE_GC = 1 << 8, - IL2CPP_PROFILE_THREADS = 1 << 9, - IL2CPP_PROFILE_REMOTING = 1 << 10, - IL2CPP_PROFILE_TRANSITIONS = 1 << 11, - IL2CPP_PROFILE_ENTER_LEAVE = 1 << 12, - IL2CPP_PROFILE_COVERAGE = 1 << 13, - IL2CPP_PROFILE_INS_COVERAGE = 1 << 14, - IL2CPP_PROFILE_STATISTICAL = 1 << 15, - IL2CPP_PROFILE_METHOD_EVENTS = 1 << 16, - IL2CPP_PROFILE_MONITOR_EVENTS = 1 << 17, - IL2CPP_PROFILE_IOMAP_EVENTS = 1 << 18, - IL2CPP_PROFILE_GC_MOVES = 1 << 19, - IL2CPP_PROFILE_FILEIO = 1 << 20 -} Il2CppProfileFlags; -typedef enum -{ - IL2CPP_PROFILE_FILEIO_WRITE = 0, - IL2CPP_PROFILE_FILEIO_READ -} Il2CppProfileFileIOKind; -typedef enum -{ - IL2CPP_GC_EVENT_START, - IL2CPP_GC_EVENT_MARK_START, - IL2CPP_GC_EVENT_MARK_END, - IL2CPP_GC_EVENT_RECLAIM_START, - IL2CPP_GC_EVENT_RECLAIM_END, - IL2CPP_GC_EVENT_END, - IL2CPP_GC_EVENT_PRE_STOP_WORLD, - IL2CPP_GC_EVENT_POST_STOP_WORLD, - IL2CPP_GC_EVENT_PRE_START_WORLD, - IL2CPP_GC_EVENT_POST_START_WORLD -} Il2CppGCEvent; -typedef enum -{ - IL2CPP_GC_MODE_DISABLED = 0, - IL2CPP_GC_MODE_ENABLED = 1, - IL2CPP_GC_MODE_MANUAL = 2 -} Il2CppGCMode; -typedef enum -{ - IL2CPP_STAT_NEW_OBJECT_COUNT, - IL2CPP_STAT_INITIALIZED_CLASS_COUNT, - IL2CPP_STAT_METHOD_COUNT, - IL2CPP_STAT_CLASS_STATIC_DATA_SIZE, - IL2CPP_STAT_GENERIC_INSTANCE_COUNT, - IL2CPP_STAT_GENERIC_CLASS_COUNT, - IL2CPP_STAT_INFLATED_METHOD_COUNT, - IL2CPP_STAT_INFLATED_TYPE_COUNT, -} Il2CppStat; -typedef enum -{ - IL2CPP_UNHANDLED_POLICY_LEGACY, - IL2CPP_UNHANDLED_POLICY_CURRENT -} Il2CppRuntimeUnhandledExceptionPolicy; -typedef struct Il2CppStackFrameInfo -{ - const MethodInfo *method; - uintptr_t raw_ip; - int sourceCodeLineNumber; - int ilOffset; - const char* filePath; -} Il2CppStackFrameInfo; -typedef void(*Il2CppMethodPointer)(); -typedef struct Il2CppMethodDebugInfo -{ - Il2CppMethodPointer methodPointer; - int32_t code_size; - const char *file; -} Il2CppMethodDebugInfo; -typedef struct -{ - void* (*malloc_func)(size_t size); - void* (*aligned_malloc_func)(size_t size, size_t alignment); - void (*free_func)(void *ptr); - void (*aligned_free_func)(void *ptr); - void* (*calloc_func)(size_t nmemb, size_t size); - void* (*realloc_func)(void *ptr, size_t size); - void* (*aligned_realloc_func)(void *ptr, size_t size, size_t alignment); -} Il2CppMemoryCallbacks; -typedef struct -{ - const char *name; - void(*connect)(const char *address); - int(*wait_for_attach)(void); - void(*close1)(void); - void(*close2)(void); - int(*send)(void *buf, int len); - int(*recv)(void *buf, int len); -} Il2CppDebuggerTransport; -typedef uint16_t Il2CppChar; -typedef char Il2CppNativeChar; -typedef void (*il2cpp_register_object_callback)(Il2CppObject** arr, int size, void* userdata); -typedef void* (*il2cpp_liveness_reallocate_callback)(void* ptr, size_t size, void* userdata); -typedef void (*Il2CppFrameWalkFunc) (const Il2CppStackFrameInfo *info, void *user_data); -typedef void (*Il2CppProfileFunc) (Il2CppProfiler* prof); -typedef void (*Il2CppProfileMethodFunc) (Il2CppProfiler* prof, const MethodInfo *method); -typedef void (*Il2CppProfileAllocFunc) (Il2CppProfiler* prof, Il2CppObject *obj, Il2CppClass *klass); -typedef void (*Il2CppProfileGCFunc) (Il2CppProfiler* prof, Il2CppGCEvent event, int generation); -typedef void (*Il2CppProfileGCResizeFunc) (Il2CppProfiler* prof, int64_t new_size); -typedef void (*Il2CppProfileFileIOFunc) (Il2CppProfiler* prof, Il2CppProfileFileIOKind kind, int count); -typedef void (*Il2CppProfileThreadFunc) (Il2CppProfiler *prof, unsigned long tid); -typedef const Il2CppNativeChar* (*Il2CppSetFindPlugInCallback)(const Il2CppNativeChar*); -typedef void (*Il2CppLogCallback)(const char*); -typedef size_t(*Il2CppBacktraceFunc) (Il2CppMethodPointer* buffer, size_t maxSize); -typedef struct Il2CppManagedMemorySnapshot Il2CppManagedMemorySnapshot; -typedef uintptr_t il2cpp_array_size_t; -typedef void ( *SynchronizationContextCallback)(intptr_t arg); -typedef void ( *CultureInfoChangedCallback)(const Il2CppChar* arg); -typedef uint16_t Il2CppMethodSlot; -static const uint16_t kInvalidIl2CppMethodSlot = 65535; -static const int ipv6AddressSize = 16; -typedef int32_t il2cpp_hresult_t; -typedef enum -{ - IL2CPP_TOKEN_MODULE = 0x00000000, - IL2CPP_TOKEN_TYPE_REF = 0x01000000, - IL2CPP_TOKEN_TYPE_DEF = 0x02000000, - IL2CPP_TOKEN_FIELD_DEF = 0x04000000, - IL2CPP_TOKEN_METHOD_DEF = 0x06000000, - IL2CPP_TOKEN_PARAM_DEF = 0x08000000, - IL2CPP_TOKEN_INTERFACE_IMPL = 0x09000000, - IL2CPP_TOKEN_MEMBER_REF = 0x0a000000, - IL2CPP_TOKEN_CUSTOM_ATTRIBUTE = 0x0c000000, - IL2CPP_TOKEN_PERMISSION = 0x0e000000, - IL2CPP_TOKEN_SIGNATURE = 0x11000000, - IL2CPP_TOKEN_EVENT = 0x14000000, - IL2CPP_TOKEN_PROPERTY = 0x17000000, - IL2CPP_TOKEN_MODULE_REF = 0x1a000000, - IL2CPP_TOKEN_TYPE_SPEC = 0x1b000000, - IL2CPP_TOKEN_ASSEMBLY = 0x20000000, - IL2CPP_TOKEN_ASSEMBLY_REF = 0x23000000, - IL2CPP_TOKEN_FILE = 0x26000000, - IL2CPP_TOKEN_EXPORTED_TYPE = 0x27000000, - IL2CPP_TOKEN_MANIFEST_RESOURCE = 0x28000000, - IL2CPP_TOKEN_GENERIC_PARAM = 0x2a000000, - IL2CPP_TOKEN_METHOD_SPEC = 0x2b000000, -} Il2CppTokenType; -typedef int32_t TypeIndex; -typedef int32_t TypeDefinitionIndex; -typedef int32_t FieldIndex; -typedef int32_t DefaultValueIndex; -typedef int32_t DefaultValueDataIndex; -typedef int32_t CustomAttributeIndex; -typedef int32_t ParameterIndex; -typedef int32_t MethodIndex; -typedef int32_t GenericMethodIndex; -typedef int32_t PropertyIndex; -typedef int32_t EventIndex; -typedef int32_t GenericContainerIndex; -typedef int32_t GenericParameterIndex; -typedef int16_t GenericParameterConstraintIndex; -typedef int32_t NestedTypeIndex; -typedef int32_t InterfacesIndex; -typedef int32_t VTableIndex; -typedef int32_t RGCTXIndex; -typedef int32_t StringIndex; -typedef int32_t StringLiteralIndex; -typedef int32_t GenericInstIndex; -typedef int32_t ImageIndex; -typedef int32_t AssemblyIndex; -typedef int32_t InteropDataIndex; -typedef int32_t TypeFieldIndex; -typedef int32_t TypeMethodIndex; -typedef int32_t MethodParameterIndex; -typedef int32_t TypePropertyIndex; -typedef int32_t TypeEventIndex; -typedef int32_t TypeInterfaceIndex; -typedef int32_t TypeNestedTypeIndex; -typedef int32_t TypeInterfaceOffsetIndex; -typedef int32_t GenericContainerParameterIndex; -typedef int32_t AssemblyTypeIndex; -typedef int32_t AssemblyExportedTypeIndex; -static const TypeIndex kTypeIndexInvalid = -1; -static const TypeDefinitionIndex kTypeDefinitionIndexInvalid = -1; -static const DefaultValueDataIndex kDefaultValueIndexNull = -1; -static const CustomAttributeIndex kCustomAttributeIndexInvalid = -1; -static const EventIndex kEventIndexInvalid = -1; -static const FieldIndex kFieldIndexInvalid = -1; -static const MethodIndex kMethodIndexInvalid = -1; -static const PropertyIndex kPropertyIndexInvalid = -1; -static const GenericContainerIndex kGenericContainerIndexInvalid = -1; -static const GenericParameterIndex kGenericParameterIndexInvalid = -1; -static const RGCTXIndex kRGCTXIndexInvalid = -1; -static const StringLiteralIndex kStringLiteralIndexInvalid = -1; -static const InteropDataIndex kInteropDataIndexInvalid = -1; -static const int kPublicKeyByteLength = 8; -typedef struct Il2CppMethodSpec -{ - MethodIndex methodDefinitionIndex; - GenericInstIndex classIndexIndex; - GenericInstIndex methodIndexIndex; -} Il2CppMethodSpec; -typedef enum Il2CppRGCTXDataType -{ - IL2CPP_RGCTX_DATA_INVALID, - IL2CPP_RGCTX_DATA_TYPE, - IL2CPP_RGCTX_DATA_CLASS, - IL2CPP_RGCTX_DATA_METHOD, - IL2CPP_RGCTX_DATA_ARRAY, - IL2CPP_RGCTX_DATA_CONSTRAINED, -} Il2CppRGCTXDataType; -typedef union Il2CppRGCTXDefinitionData -{ - int32_t rgctxDataDummy; - MethodIndex __methodIndex; - TypeIndex __typeIndex; -} Il2CppRGCTXDefinitionData; -typedef struct Il2CppRGCTXConstrainedData -{ - TypeIndex __typeIndex; - uint32_t __encodedMethodIndex; -} Il2CppRGCTXConstrainedData; -typedef struct Il2CppRGCTXDefinition -{ - Il2CppRGCTXDataType type; - const void* data; -} Il2CppRGCTXDefinition; -typedef struct -{ - MethodIndex methodIndex; - MethodIndex invokerIndex; - MethodIndex adjustorThunkIndex; -} Il2CppGenericMethodIndices; -typedef struct Il2CppGenericMethodFunctionsDefinitions -{ - GenericMethodIndex genericMethodIndex; - Il2CppGenericMethodIndices indices; -} Il2CppGenericMethodFunctionsDefinitions; -static inline uint32_t GetTokenType(uint32_t token) -{ - return token & 0xFF000000; -} -static inline uint32_t GetTokenRowId(uint32_t token) -{ - return token & 0x00FFFFFF; -} -typedef const struct ___Il2CppMetadataImageHandle* Il2CppMetadataImageHandle; -typedef const struct ___Il2CppMetadataCustomAttributeHandle* Il2CppMetadataCustomAttributeHandle; -typedef const struct ___Il2CppMetadataTypeHandle* Il2CppMetadataTypeHandle; -typedef const struct ___Il2CppMetadataMethodHandle* Il2CppMetadataMethodDefinitionHandle; -typedef const struct ___Il2CppMetadataGenericContainerHandle* Il2CppMetadataGenericContainerHandle; -typedef const struct ___Il2CppMetadataGenericParameterHandle* Il2CppMetadataGenericParameterHandle; -typedef uint32_t EncodedMethodIndex; -typedef enum Il2CppMetadataUsage -{ - kIl2CppMetadataUsageInvalid, - kIl2CppMetadataUsageTypeInfo, - kIl2CppMetadataUsageIl2CppType, - kIl2CppMetadataUsageMethodDef, - kIl2CppMetadataUsageFieldInfo, - kIl2CppMetadataUsageStringLiteral, - kIl2CppMetadataUsageMethodRef, -} Il2CppMetadataUsage; -typedef enum Il2CppInvalidMetadataUsageToken -{ - kIl2CppInvalidMetadataUsageNoData = 0, - kIl2CppInvalidMetadataUsageAmbiguousMethod = 1, -} Il2CppInvalidMetadataUsageToken; -typedef struct Il2CppInterfaceOffsetPair -{ - TypeIndex interfaceTypeIndex; - int32_t offset; -} Il2CppInterfaceOffsetPair; -typedef struct Il2CppTypeDefinition -{ - StringIndex nameIndex; - StringIndex namespaceIndex; - TypeIndex byvalTypeIndex; - TypeIndex declaringTypeIndex; - TypeIndex parentIndex; - TypeIndex elementTypeIndex; - GenericContainerIndex genericContainerIndex; - uint32_t flags; - FieldIndex fieldStart; - MethodIndex methodStart; - EventIndex eventStart; - PropertyIndex propertyStart; - NestedTypeIndex nestedTypesStart; - InterfacesIndex interfacesStart; - VTableIndex vtableStart; - InterfacesIndex interfaceOffsetsStart; - uint16_t method_count; - uint16_t property_count; - uint16_t field_count; - uint16_t event_count; - uint16_t nested_type_count; - uint16_t vtable_count; - uint16_t interfaces_count; - uint16_t interface_offsets_count; - uint32_t bitfield; - uint32_t token; -} Il2CppTypeDefinition; -typedef struct Il2CppFieldDefinition -{ - StringIndex nameIndex; - TypeIndex typeIndex; - uint32_t token; -} Il2CppFieldDefinition; -typedef struct Il2CppFieldDefaultValue -{ - FieldIndex fieldIndex; - TypeIndex typeIndex; - DefaultValueDataIndex dataIndex; -} Il2CppFieldDefaultValue; -typedef struct Il2CppFieldMarshaledSize -{ - FieldIndex fieldIndex; - TypeIndex typeIndex; - int32_t size; -} Il2CppFieldMarshaledSize; -typedef struct Il2CppFieldRef -{ - TypeIndex typeIndex; - FieldIndex fieldIndex; -} Il2CppFieldRef; -typedef struct Il2CppParameterDefinition -{ - StringIndex nameIndex; - uint32_t token; - TypeIndex typeIndex; -} Il2CppParameterDefinition; -typedef struct Il2CppParameterDefaultValue -{ - ParameterIndex parameterIndex; - TypeIndex typeIndex; - DefaultValueDataIndex dataIndex; -} Il2CppParameterDefaultValue; -typedef struct Il2CppMethodDefinition -{ - StringIndex nameIndex; - TypeDefinitionIndex declaringType; - TypeIndex returnType; - ParameterIndex parameterStart; - GenericContainerIndex genericContainerIndex; - uint32_t token; - uint16_t flags; - uint16_t iflags; - uint16_t slot; - uint16_t parameterCount; - uint8_t isUnmanagedCallersOnly; -} Il2CppMethodDefinition; -typedef struct Il2CppEventDefinition -{ - StringIndex nameIndex; - TypeIndex typeIndex; - MethodIndex add; - MethodIndex remove; - MethodIndex raise; - uint32_t token; -} Il2CppEventDefinition; -typedef struct Il2CppPropertyDefinition -{ - StringIndex nameIndex; - MethodIndex get; - MethodIndex set; - uint32_t attrs; - uint32_t token; -} Il2CppPropertyDefinition; -typedef struct Il2CppStringLiteral -{ - uint32_t length; - StringLiteralIndex dataIndex; -} Il2CppStringLiteral; -typedef struct Il2CppAssemblyNameDefinition -{ - StringIndex nameIndex; - StringIndex cultureIndex; - StringIndex publicKeyIndex; - uint32_t hash_alg; - int32_t hash_len; - uint32_t flags; - int32_t major; - int32_t minor; - int32_t build; - int32_t revision; - uint8_t public_key_token[8]; -} Il2CppAssemblyNameDefinition; -typedef struct Il2CppImageDefinition -{ - StringIndex nameIndex; - AssemblyIndex assemblyIndex; - TypeDefinitionIndex typeStart; - uint32_t typeCount; - TypeDefinitionIndex exportedTypeStart; - uint32_t exportedTypeCount; - MethodIndex entryPointIndex; - uint32_t token; - CustomAttributeIndex customAttributeStart; - uint32_t customAttributeCount; -} Il2CppImageDefinition; -typedef struct Il2CppAssemblyDefinition -{ - ImageIndex imageIndex; - uint32_t token; - int32_t referencedAssemblyStart; - int32_t referencedAssemblyCount; - Il2CppAssemblyNameDefinition aname; -} Il2CppAssemblyDefinition; -typedef struct Il2CppCustomAttributeDataRange -{ - uint32_t token; - uint32_t startOffset; -} Il2CppCustomAttributeDataRange; -typedef struct Il2CppMetadataRange -{ - int32_t start; - int32_t length; -} Il2CppMetadataRange; -typedef struct Il2CppGenericContainer -{ - int32_t ownerIndex; - int32_t type_argc; - int32_t is_method; - GenericParameterIndex genericParameterStart; -} Il2CppGenericContainer; -typedef struct Il2CppGenericParameter -{ - GenericContainerIndex ownerIndex; - StringIndex nameIndex; - GenericParameterConstraintIndex constraintsStart; - int16_t constraintsCount; - uint16_t num; - uint16_t flags; -} Il2CppGenericParameter; -typedef struct Il2CppWindowsRuntimeTypeNamePair -{ - StringIndex nameIndex; - TypeIndex typeIndex; -} Il2CppWindowsRuntimeTypeNamePair; -#pragma pack(push, p1,4) -typedef struct Il2CppGlobalMetadataHeader -{ - int32_t sanity; - int32_t version; - int32_t stringLiteralOffset; - int32_t stringLiteralCount; - int32_t stringLiteralDataOffset; - int32_t stringLiteralDataCount; - int32_t stringOffset; - int32_t stringCount; - int32_t eventsOffset; - int32_t eventsCount; - int32_t propertiesOffset; - int32_t propertiesCount; - int32_t methodsOffset; - int32_t methodsCount; - int32_t parameterDefaultValuesOffset; - int32_t parameterDefaultValuesCount; - int32_t fieldDefaultValuesOffset; - int32_t fieldDefaultValuesCount; - int32_t fieldAndParameterDefaultValueDataOffset; - int32_t fieldAndParameterDefaultValueDataCount; - int32_t fieldMarshaledSizesOffset; - int32_t fieldMarshaledSizesCount; - int32_t parametersOffset; - int32_t parametersCount; - int32_t fieldsOffset; - int32_t fieldsCount; - int32_t genericParametersOffset; - int32_t genericParametersCount; - int32_t genericParameterConstraintsOffset; - int32_t genericParameterConstraintsCount; - int32_t genericContainersOffset; - int32_t genericContainersCount; - int32_t nestedTypesOffset; - int32_t nestedTypesCount; - int32_t interfacesOffset; - int32_t interfacesCount; - int32_t vtableMethodsOffset; - int32_t vtableMethodsCount; - int32_t interfaceOffsetsOffset; - int32_t interfaceOffsetsCount; - int32_t typeDefinitionsOffset; - int32_t typeDefinitionsCount; - int32_t imagesOffset; - int32_t imagesCount; - int32_t assembliesOffset; - int32_t assembliesCount; - int32_t fieldRefsOffset; - int32_t fieldRefsCount; - int32_t referencedAssembliesOffset; - int32_t referencedAssembliesCount; - int32_t attributeDataOffset; - int32_t attributeDataCount; - int32_t attributeDataRangeOffset; - int32_t attributeDataRangeCount; - int32_t unresolvedVirtualCallParameterTypesOffset; - int32_t unresolvedVirtualCallParameterTypesCount; - int32_t unresolvedVirtualCallParameterRangesOffset; - int32_t unresolvedVirtualCallParameterRangesCount; - int32_t windowsRuntimeTypeNamesOffset; - int32_t windowsRuntimeTypeNamesSize; - int32_t windowsRuntimeStringsOffset; - int32_t windowsRuntimeStringsSize; - int32_t exportedTypeDefinitionsOffset; - int32_t exportedTypeDefinitionsCount; -} Il2CppGlobalMetadataHeader; -#pragma pack(pop, p1) -typedef struct Il2CppMetadataField -{ - uint32_t offset; - uint32_t typeIndex; - const char* name; - uint8_t isStatic; -} Il2CppMetadataField; -typedef enum Il2CppMetadataTypeFlags -{ - kNone = 0, - kValueType = 1 << 0, - kArray = 1 << 1, - kArrayRankMask = 0xFFFF0000 -} Il2CppMetadataTypeFlags; -typedef struct Il2CppMetadataType -{ - Il2CppMetadataTypeFlags flags; - Il2CppMetadataField* fields; - uint32_t fieldCount; - uint32_t staticsSize; - uint8_t* statics; - uint32_t baseOrElementTypeIndex; - char* name; - const char* assemblyName; - uint64_t typeInfoAddress; - uint32_t size; -} Il2CppMetadataType; -typedef struct Il2CppMetadataSnapshot -{ - uint32_t typeCount; - Il2CppMetadataType* types; -} Il2CppMetadataSnapshot; -typedef struct Il2CppManagedMemorySection -{ - uint64_t sectionStartAddress; - uint32_t sectionSize; - uint8_t* sectionBytes; -} Il2CppManagedMemorySection; -typedef struct Il2CppManagedHeap -{ - uint32_t sectionCount; - Il2CppManagedMemorySection* sections; -} Il2CppManagedHeap; -typedef struct Il2CppStacks -{ - uint32_t stackCount; - Il2CppManagedMemorySection* stacks; -} Il2CppStacks; -typedef struct NativeObject -{ - uint32_t gcHandleIndex; - uint32_t size; - uint32_t instanceId; - uint32_t classId; - uint32_t referencedNativeObjectIndicesCount; - uint32_t* referencedNativeObjectIndices; -} NativeObject; -typedef struct Il2CppGCHandles -{ - uint32_t trackedObjectCount; - uint64_t* pointersToObjects; -} Il2CppGCHandles; -typedef struct Il2CppRuntimeInformation -{ - uint32_t pointerSize; - uint32_t objectHeaderSize; - uint32_t arrayHeaderSize; - uint32_t arrayBoundsOffsetInHeader; - uint32_t arraySizeOffsetInHeader; - uint32_t allocationGranularity; -} Il2CppRuntimeInformation; -typedef struct Il2CppManagedMemorySnapshot -{ - Il2CppManagedHeap heap; - Il2CppStacks stacks; - Il2CppMetadataSnapshot metadata; - Il2CppGCHandles gcHandles; - Il2CppRuntimeInformation runtimeInformation; - void* additionalUserInformation; -} Il2CppManagedMemorySnapshot; -typedef enum Il2CppTypeEnum -{ - IL2CPP_TYPE_END = 0x00, - IL2CPP_TYPE_VOID = 0x01, - IL2CPP_TYPE_BOOLEAN = 0x02, - IL2CPP_TYPE_CHAR = 0x03, - IL2CPP_TYPE_I1 = 0x04, - IL2CPP_TYPE_U1 = 0x05, - IL2CPP_TYPE_I2 = 0x06, - IL2CPP_TYPE_U2 = 0x07, - IL2CPP_TYPE_I4 = 0x08, - IL2CPP_TYPE_U4 = 0x09, - IL2CPP_TYPE_I8 = 0x0a, - IL2CPP_TYPE_U8 = 0x0b, - IL2CPP_TYPE_R4 = 0x0c, - IL2CPP_TYPE_R8 = 0x0d, - IL2CPP_TYPE_STRING = 0x0e, - IL2CPP_TYPE_PTR = 0x0f, - IL2CPP_TYPE_BYREF = 0x10, - IL2CPP_TYPE_VALUETYPE = 0x11, - IL2CPP_TYPE_CLASS = 0x12, - IL2CPP_TYPE_VAR = 0x13, - IL2CPP_TYPE_ARRAY = 0x14, - IL2CPP_TYPE_GENERICINST = 0x15, - IL2CPP_TYPE_TYPEDBYREF = 0x16, - IL2CPP_TYPE_I = 0x18, - IL2CPP_TYPE_U = 0x19, - IL2CPP_TYPE_FNPTR = 0x1b, - IL2CPP_TYPE_OBJECT = 0x1c, - IL2CPP_TYPE_SZARRAY = 0x1d, - IL2CPP_TYPE_MVAR = 0x1e, - IL2CPP_TYPE_CMOD_REQD = 0x1f, - IL2CPP_TYPE_CMOD_OPT = 0x20, - IL2CPP_TYPE_INTERNAL = 0x21, - IL2CPP_TYPE_MODIFIER = 0x40, - IL2CPP_TYPE_SENTINEL = 0x41, - IL2CPP_TYPE_PINNED = 0x45, - IL2CPP_TYPE_ENUM = 0x55, - IL2CPP_TYPE_IL2CPP_TYPE_INDEX = 0xff -} Il2CppTypeEnum; -typedef struct Il2CppClass Il2CppClass; -typedef struct MethodInfo MethodInfo; -typedef struct Il2CppType Il2CppType; -typedef struct Il2CppArrayType -{ - const Il2CppType* etype; - uint8_t rank; - uint8_t numsizes; - uint8_t numlobounds; - int *sizes; - int *lobounds; -} Il2CppArrayType; -typedef struct Il2CppGenericInst -{ - uint32_t type_argc; - const Il2CppType **type_argv; -} Il2CppGenericInst; -typedef struct Il2CppGenericContext -{ - const Il2CppGenericInst *class_inst; - const Il2CppGenericInst *method_inst; -} Il2CppGenericContext; -typedef struct Il2CppGenericClass -{ - const Il2CppType* type; - Il2CppGenericContext context; - Il2CppClass *cached_class; -} Il2CppGenericClass; -typedef struct Il2CppGenericMethod -{ - const MethodInfo* methodDefinition; - Il2CppGenericContext context; -} Il2CppGenericMethod; -typedef struct Il2CppType -{ - union - { - void* dummy; - TypeDefinitionIndex __klassIndex; - Il2CppMetadataTypeHandle typeHandle; - const Il2CppType *type; - Il2CppArrayType *array; - GenericParameterIndex __genericParameterIndex; - Il2CppMetadataGenericParameterHandle genericParameterHandle; - Il2CppGenericClass *generic_class; - } data; - unsigned int attrs : 16; - Il2CppTypeEnum type : 8; - unsigned int num_mods : 5; - unsigned int byref : 1; - unsigned int pinned : 1; - unsigned int valuetype : 1; -} Il2CppType; -typedef struct Il2CppMetadataFieldInfo -{ - const Il2CppType* type; - const char* name; - uint32_t token; -} Il2CppMetadataFieldInfo; -typedef struct Il2CppMetadataMethodInfo -{ - Il2CppMetadataMethodDefinitionHandle handle; - const char* name; - const Il2CppType* return_type; - uint32_t token; - uint16_t flags; - uint16_t iflags; - uint16_t slot; - uint16_t parameterCount; -} Il2CppMetadataMethodInfo; -typedef struct Il2CppMetadataParameterInfo -{ - const char* name; - uint32_t token; - const Il2CppType* type; -} Il2CppMetadataParameterInfo; -typedef struct Il2CppMetadataPropertyInfo -{ - const char* name; - const MethodInfo* get; - const MethodInfo* set; - uint32_t attrs; - uint32_t token; -} Il2CppMetadataPropertyInfo; -typedef struct Il2CppMetadataEventInfo -{ - const char* name; - const Il2CppType* type; - const MethodInfo* add; - const MethodInfo* remove; - const MethodInfo* raise; - uint32_t token; -} Il2CppMetadataEventInfo; -typedef struct Il2CppInterfaceOffsetInfo -{ - const Il2CppType* interfaceType; - int32_t offset; -} Il2CppInterfaceOffsetInfo; -typedef struct Il2CppGenericParameterInfo -{ - Il2CppMetadataGenericContainerHandle containerHandle; - const char* name; - uint16_t num; - uint16_t flags; -} Il2CppGenericParameterInfo; -typedef enum Il2CppCallConvention -{ - IL2CPP_CALL_DEFAULT, - IL2CPP_CALL_C, - IL2CPP_CALL_STDCALL, - IL2CPP_CALL_THISCALL, - IL2CPP_CALL_FASTCALL, - IL2CPP_CALL_VARARG -} Il2CppCallConvention; -typedef enum Il2CppCharSet -{ - CHARSET_ANSI, - CHARSET_UNICODE, - CHARSET_NOT_SPECIFIED -} Il2CppCharSet; -typedef struct Il2CppHString__ -{ - int unused; -} Il2CppHString__; -typedef Il2CppHString__* Il2CppHString; -typedef struct Il2CppHStringHeader -{ - union - { - void* Reserved1; - char Reserved2[24]; - } Reserved; -} Il2CppHStringHeader; -typedef struct Il2CppGuid -{ - uint32_t data1; - uint16_t data2; - uint16_t data3; - uint8_t data4[8]; -} Il2CppGuid; -typedef struct Il2CppSafeArrayBound -{ - uint32_t element_count; - int32_t lower_bound; -} Il2CppSafeArrayBound; -typedef struct Il2CppSafeArray -{ - uint16_t dimension_count; - uint16_t features; - uint32_t element_size; - uint32_t lock_count; - void* data; - Il2CppSafeArrayBound bounds[1]; -} Il2CppSafeArray; -typedef struct Il2CppWin32Decimal -{ - uint16_t reserved; - union - { - struct - { - uint8_t scale; - uint8_t sign; - } s; - uint16_t signscale; - } u; - uint32_t hi32; - union - { - struct - { - uint32_t lo32; - uint32_t mid32; - } s2; - uint64_t lo64; - } u2; -} Il2CppWin32Decimal; -typedef int16_t IL2CPP_VARIANT_BOOL; -typedef enum Il2CppVarType -{ - IL2CPP_VT_EMPTY = 0, - IL2CPP_VT_NULL = 1, - IL2CPP_VT_I2 = 2, - IL2CPP_VT_I4 = 3, - IL2CPP_VT_R4 = 4, - IL2CPP_VT_R8 = 5, - IL2CPP_VT_CY = 6, - IL2CPP_VT_DATE = 7, - IL2CPP_VT_BSTR = 8, - IL2CPP_VT_DISPATCH = 9, - IL2CPP_VT_ERROR = 10, - IL2CPP_VT_BOOL = 11, - IL2CPP_VT_VARIANT = 12, - IL2CPP_VT_UNKNOWN = 13, - IL2CPP_VT_DECIMAL = 14, - IL2CPP_VT_I1 = 16, - IL2CPP_VT_UI1 = 17, - IL2CPP_VT_UI2 = 18, - IL2CPP_VT_UI4 = 19, - IL2CPP_VT_I8 = 20, - IL2CPP_VT_UI8 = 21, - IL2CPP_VT_INT = 22, - IL2CPP_VT_UINT = 23, - IL2CPP_VT_VOID = 24, - IL2CPP_VT_HRESULT = 25, - IL2CPP_VT_PTR = 26, - IL2CPP_VT_SAFEARRAY = 27, - IL2CPP_VT_CARRAY = 28, - IL2CPP_VT_USERDEFINED = 29, - IL2CPP_VT_LPSTR = 30, - IL2CPP_VT_LPWSTR = 31, - IL2CPP_VT_RECORD = 36, - IL2CPP_VT_INT_PTR = 37, - IL2CPP_VT_UINT_PTR = 38, - IL2CPP_VT_FILETIME = 64, - IL2CPP_VT_BLOB = 65, - IL2CPP_VT_STREAM = 66, - IL2CPP_VT_STORAGE = 67, - IL2CPP_VT_STREAMED_OBJECT = 68, - IL2CPP_VT_STORED_OBJECT = 69, - IL2CPP_VT_BLOB_OBJECT = 70, - IL2CPP_VT_CF = 71, - IL2CPP_VT_CLSID = 72, - IL2CPP_VT_VERSIONED_STREAM = 73, - IL2CPP_VT_BSTR_BLOB = 0xfff, - IL2CPP_VT_VECTOR = 0x1000, - IL2CPP_VT_ARRAY = 0x2000, - IL2CPP_VT_BYREF = 0x4000, - IL2CPP_VT_RESERVED = 0x8000, - IL2CPP_VT_ILLEGAL = 0xffff, - IL2CPP_VT_ILLEGALMASKED = 0xfff, - IL2CPP_VT_TYPEMASK = 0xfff, -} Il2CppVarType; -typedef struct Il2CppVariant Il2CppVariant; -typedef struct Il2CppIUnknown Il2CppIUnknown; -typedef struct Il2CppVariant -{ - union - { - struct __tagVARIANT - { - uint16_t type; - uint16_t reserved1; - uint16_t reserved2; - uint16_t reserved3; - union - { - int64_t llVal; - int32_t lVal; - uint8_t bVal; - int16_t iVal; - float fltVal; - double dblVal; - IL2CPP_VARIANT_BOOL boolVal; - int32_t scode; - int64_t cyVal; - double date; - Il2CppChar* bstrVal; - Il2CppIUnknown* punkVal; - void* pdispVal; - Il2CppSafeArray* parray; - uint8_t* pbVal; - int16_t* piVal; - int32_t* plVal; - int64_t* pllVal; - float* pfltVal; - double* pdblVal; - IL2CPP_VARIANT_BOOL* pboolVal; - int32_t* pscode; - int64_t* pcyVal; - double* pdate; - Il2CppChar* pbstrVal; - Il2CppIUnknown** ppunkVal; - void** ppdispVal; - Il2CppSafeArray** pparray; - struct Il2CppVariant* pvarVal; - void* byref; - char cVal; - uint16_t uiVal; - uint32_t ulVal; - uint64_t ullVal; - int intVal; - unsigned int uintVal; - Il2CppWin32Decimal* pdecVal; - char* pcVal; - uint16_t* puiVal; - uint32_t* pulVal; - uint64_t* pullVal; - int* pintVal; - unsigned int* puintVal; - struct __tagBRECORD - { - void* pvRecord; - void* pRecInfo; - } n4; - } n3; - } n2; - Il2CppWin32Decimal decVal; - } n1; -} Il2CppVariant; -typedef struct Il2CppFileTime -{ - uint32_t low; - uint32_t high; -} Il2CppFileTime; -typedef struct Il2CppStatStg -{ - Il2CppChar* name; - uint32_t type; - uint64_t size; - Il2CppFileTime mtime; - Il2CppFileTime ctime; - Il2CppFileTime atime; - uint32_t mode; - uint32_t locks; - Il2CppGuid clsid; - uint32_t state; - uint32_t reserved; -} Il2CppStatStg; -typedef enum Il2CppWindowsRuntimeTypeKind -{ - kTypeKindPrimitive = 0, - kTypeKindMetadata, - kTypeKindCustom -} Il2CppWindowsRuntimeTypeKind; -typedef struct Il2CppWindowsRuntimeTypeName -{ - Il2CppHString typeName; - enum Il2CppWindowsRuntimeTypeKind typeKind; -} Il2CppWindowsRuntimeTypeName; -typedef void (*PInvokeMarshalToNativeFunc)(void* managedStructure, void* marshaledStructure); -typedef void (*PInvokeMarshalFromNativeFunc)(void* marshaledStructure, void* managedStructure); -typedef void (*PInvokeMarshalCleanupFunc)(void* marshaledStructure); -typedef struct Il2CppIUnknown* (*CreateCCWFunc)(Il2CppObject* obj); -typedef struct Il2CppInteropData -{ - Il2CppMethodPointer delegatePInvokeWrapperFunction; - PInvokeMarshalToNativeFunc pinvokeMarshalToNativeFunction; - PInvokeMarshalFromNativeFunc pinvokeMarshalFromNativeFunction; - PInvokeMarshalCleanupFunc pinvokeMarshalCleanupFunction; - CreateCCWFunc createCCWFunction; - const Il2CppGuid* guid; - const Il2CppType* type; -} Il2CppInteropData; -typedef struct Il2CppCodeGenModule Il2CppCodeGenModule; -typedef struct Il2CppMetadataRegistration Il2CppMetadataRegistration; -typedef struct Il2CppCodeRegistration Il2CppCodeRegistration; -typedef struct Il2CppClass Il2CppClass; -typedef struct Il2CppGuid Il2CppGuid; -typedef struct Il2CppImage Il2CppImage; -typedef struct Il2CppAppDomain Il2CppAppDomain; -typedef struct Il2CppAppDomainSetup Il2CppAppDomainSetup; -typedef struct Il2CppDelegate Il2CppDelegate; -typedef struct Il2CppAppContext Il2CppAppContext; -typedef struct Il2CppNameToTypeHandleHashTable Il2CppNameToTypeHandleHashTable; -typedef struct Il2CppCodeGenModule Il2CppCodeGenModule; -typedef struct Il2CppMetadataRegistration Il2CppMetadataRegistration; -typedef struct Il2CppCodeRegistration Il2CppCodeRegistration; -typedef struct VirtualInvokeData -{ - Il2CppMethodPointer methodPtr; - const MethodInfo* method; -} VirtualInvokeData; -typedef enum Il2CppTypeNameFormat -{ - IL2CPP_TYPE_NAME_FORMAT_IL, - IL2CPP_TYPE_NAME_FORMAT_REFLECTION, - IL2CPP_TYPE_NAME_FORMAT_FULL_NAME, - IL2CPP_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED -} Il2CppTypeNameFormat; -typedef struct Il2CppDefaults -{ - Il2CppImage *corlib; - Il2CppImage *corlib_gen; - Il2CppClass *object_class; - Il2CppClass *byte_class; - Il2CppClass *void_class; - Il2CppClass *boolean_class; - Il2CppClass *sbyte_class; - Il2CppClass *int16_class; - Il2CppClass *uint16_class; - Il2CppClass *int32_class; - Il2CppClass *uint32_class; - Il2CppClass *int_class; - Il2CppClass *uint_class; - Il2CppClass *int64_class; - Il2CppClass *uint64_class; - Il2CppClass *single_class; - Il2CppClass *double_class; - Il2CppClass *char_class; - Il2CppClass *string_class; - Il2CppClass *enum_class; - Il2CppClass *array_class; - Il2CppClass *delegate_class; - Il2CppClass *multicastdelegate_class; - Il2CppClass *asyncresult_class; - Il2CppClass *manualresetevent_class; - Il2CppClass *typehandle_class; - Il2CppClass *fieldhandle_class; - Il2CppClass *methodhandle_class; - Il2CppClass *systemtype_class; - Il2CppClass *monotype_class; - Il2CppClass *exception_class; - Il2CppClass *threadabortexception_class; - Il2CppClass *thread_class; - Il2CppClass *internal_thread_class; - Il2CppClass *appdomain_class; - Il2CppClass *appdomain_setup_class; - Il2CppClass *member_info_class; - Il2CppClass *field_info_class; - Il2CppClass *method_info_class; - Il2CppClass *property_info_class; - Il2CppClass *event_info_class; - Il2CppClass *stringbuilder_class; - Il2CppClass *stack_frame_class; - Il2CppClass *stack_trace_class; - Il2CppClass *marshal_class; - Il2CppClass *typed_reference_class; - Il2CppClass *marshalbyrefobject_class; - Il2CppClass *generic_ilist_class; - Il2CppClass *generic_icollection_class; - Il2CppClass *generic_ienumerable_class; - Il2CppClass *generic_ireadonlylist_class; - Il2CppClass *generic_ireadonlycollection_class; - Il2CppClass *runtimetype_class; - Il2CppClass *generic_nullable_class; - Il2CppClass *il2cpp_com_object_class; - Il2CppClass *attribute_class; - Il2CppClass *customattribute_data_class; - Il2CppClass *customattribute_typed_argument_class; - Il2CppClass *customattribute_named_argument_class; - Il2CppClass *version; - Il2CppClass *culture_info; - Il2CppClass *async_call_class; - Il2CppClass *assembly_class; - Il2CppClass *assembly_name_class; - Il2CppClass *parameter_info_class; - Il2CppClass *module_class; - Il2CppClass *system_exception_class; - Il2CppClass *argument_exception_class; - Il2CppClass *wait_handle_class; - Il2CppClass *safe_handle_class; - Il2CppClass *sort_key_class; - Il2CppClass *dbnull_class; - Il2CppClass *error_wrapper_class; - Il2CppClass *missing_class; - Il2CppClass *value_type_class; - Il2CppClass *threadpool_wait_callback_class; - MethodInfo *threadpool_perform_wait_callback_method; - Il2CppClass *mono_method_message_class; - Il2CppClass* ireference_class; - Il2CppClass* ireferencearray_class; - Il2CppClass* ikey_value_pair_class; - Il2CppClass* key_value_pair_class; - Il2CppClass* windows_foundation_uri_class; - Il2CppClass* windows_foundation_iuri_runtime_class_class; - Il2CppClass* system_uri_class; - Il2CppClass* system_guid_class; - Il2CppClass* sbyte_shared_enum; - Il2CppClass* int16_shared_enum; - Il2CppClass* int32_shared_enum; - Il2CppClass* int64_shared_enum; - Il2CppClass* byte_shared_enum; - Il2CppClass* uint16_shared_enum; - Il2CppClass* uint32_shared_enum; - Il2CppClass* uint64_shared_enum; - Il2CppClass* il2cpp_fully_shared_type; - Il2CppClass* il2cpp_fully_shared_struct_type; -} Il2CppDefaults; -extern Il2CppDefaults il2cpp_defaults; -typedef struct Il2CppClass Il2CppClass; -typedef struct MethodInfo MethodInfo; -typedef struct FieldInfo FieldInfo; -typedef struct Il2CppObject Il2CppObject; -typedef struct MemberInfo MemberInfo; -typedef struct CustomAttributesCache -{ - int count; - Il2CppObject** attributes; -} CustomAttributesCache; -typedef struct FieldInfo -{ - const char* name; - const Il2CppType* type; - Il2CppClass *parent; - int32_t offset; - uint32_t token; -} FieldInfo; -typedef struct PropertyInfo -{ - Il2CppClass *parent; - const char *name; - const MethodInfo *get; - const MethodInfo *set; - uint32_t attrs; - uint32_t token; -} PropertyInfo; -typedef struct EventInfo -{ - const char* name; - const Il2CppType* eventType; - Il2CppClass* parent; - const MethodInfo* add; - const MethodInfo* remove; - const MethodInfo* raise; - uint32_t token; -} EventInfo; -typedef void (*InvokerMethod)(Il2CppMethodPointer, const MethodInfo*, void*, void**, void*); -typedef enum MethodVariableKind -{ - kMethodVariableKind_This, - kMethodVariableKind_Parameter, - kMethodVariableKind_LocalVariable -} MethodVariableKind; -typedef enum SequencePointKind -{ - kSequencePointKind_Normal, - kSequencePointKind_StepOut -} SequencePointKind; -typedef struct Il2CppMethodExecutionContextInfo -{ - TypeIndex typeIndex; - int32_t nameIndex; - int32_t scopeIndex; -} Il2CppMethodExecutionContextInfo; -typedef struct Il2CppMethodExecutionContextInfoIndex -{ - int32_t startIndex; - int32_t count; -} Il2CppMethodExecutionContextInfoIndex; -typedef struct Il2CppMethodScope -{ - int32_t startOffset; - int32_t endOffset; -} Il2CppMethodScope; -typedef struct Il2CppMethodHeaderInfo -{ - int32_t code_size; - int32_t startScope; - int32_t numScopes; -} Il2CppMethodHeaderInfo; -typedef struct Il2CppSequencePointSourceFile -{ - const char *file; - uint8_t hash[16]; -} Il2CppSequencePointSourceFile; -typedef struct Il2CppTypeSourceFilePair -{ - TypeDefinitionIndex __klassIndex; - int32_t sourceFileIndex; -} Il2CppTypeSourceFilePair; -typedef struct Il2CppSequencePoint -{ - MethodIndex __methodDefinitionIndex; - int32_t sourceFileIndex; - int32_t lineStart, lineEnd; - int32_t columnStart, columnEnd; - int32_t ilOffset; - SequencePointKind kind; - int32_t isActive; - int32_t id; -} Il2CppSequencePoint; -typedef struct Il2CppCatchPoint -{ - MethodIndex __methodDefinitionIndex; - TypeIndex catchTypeIndex; - int32_t ilOffset; - int32_t tryId; - int32_t parentTryId; -} Il2CppCatchPoint; -typedef struct Il2CppDebuggerMetadataRegistration -{ - Il2CppMethodExecutionContextInfo* methodExecutionContextInfos; - Il2CppMethodExecutionContextInfoIndex* methodExecutionContextInfoIndexes; - Il2CppMethodScope* methodScopes; - Il2CppMethodHeaderInfo* methodHeaderInfos; - Il2CppSequencePointSourceFile* sequencePointSourceFiles; - int32_t numSequencePoints; - Il2CppSequencePoint* sequencePoints; - int32_t numCatchPoints; - Il2CppCatchPoint* catchPoints; - int32_t numTypeSourceFileEntries; - Il2CppTypeSourceFilePair* typeSourceFiles; - const char** methodExecutionContextInfoStrings; -} Il2CppDebuggerMetadataRegistration; -typedef union Il2CppRGCTXData -{ - void* rgctxDataDummy; - const MethodInfo* method; - const Il2CppType* type; - Il2CppClass* klass; -} Il2CppRGCTXData; -typedef struct MethodInfo -{ - Il2CppMethodPointer methodPointer; - Il2CppMethodPointer virtualMethodPointer; - InvokerMethod invoker_method; - const char* name; - Il2CppClass *klass; - const Il2CppType *return_type; - const Il2CppType** parameters; - union - { - const Il2CppRGCTXData* rgctx_data; - Il2CppMetadataMethodDefinitionHandle methodMetadataHandle; - }; - union - { - const Il2CppGenericMethod* genericMethod; - Il2CppMetadataGenericContainerHandle genericContainerHandle; - }; - uint32_t token; - uint16_t flags; - uint16_t iflags; - uint16_t slot; - uint8_t parameters_count; - uint8_t is_generic : 1; - uint8_t is_inflated : 1; - uint8_t wrapper_type : 1; - uint8_t has_full_generic_sharing_signature : 1; - uint8_t indirect_call_via_invokers : 1; -} MethodInfo; -typedef struct Il2CppRuntimeInterfaceOffsetPair -{ - Il2CppClass* interfaceType; - int32_t offset; -} Il2CppRuntimeInterfaceOffsetPair; -typedef struct Il2CppClass -{ - const Il2CppImage* image; - void* gc_desc; - const char* name; - const char* namespaze; - Il2CppType byval_arg; - Il2CppType this_arg; - Il2CppClass* element_class; - Il2CppClass* castClass; - Il2CppClass* declaringType; - Il2CppClass* parent; - Il2CppGenericClass *generic_class; - Il2CppMetadataTypeHandle typeMetadataHandle; - const Il2CppInteropData* interopData; - Il2CppClass* klass; - FieldInfo* fields; - const EventInfo* events; - const PropertyInfo* properties; - const MethodInfo** methods; - Il2CppClass** nestedTypes; - Il2CppClass** implementedInterfaces; - Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; - void* static_fields; - const Il2CppRGCTXData* rgctx_data; - struct Il2CppClass** typeHierarchy; - void *unity_user_data; - uint32_t initializationExceptionGCHandle; - uint32_t cctor_started; - uint32_t cctor_finished_or_no_cctor; - __attribute__((aligned(8))) size_t cctor_thread; - Il2CppMetadataGenericContainerHandle genericContainerHandle; - uint32_t instance_size; - uint32_t actualSize; - uint32_t element_size; - int32_t native_size; - uint32_t static_fields_size; - uint32_t thread_static_fields_size; - int32_t thread_static_fields_offset; - uint32_t flags; - uint32_t token; - uint16_t method_count; - uint16_t property_count; - uint16_t field_count; - uint16_t event_count; - uint16_t nested_type_count; - uint16_t vtable_count; - uint16_t interfaces_count; - uint16_t interface_offsets_count; - uint8_t typeHierarchyDepth; - uint8_t genericRecursionDepth; - uint8_t rank; - uint8_t minimumAlignment; - uint8_t naturalAligment; - uint8_t packingSize; - uint8_t initialized_and_no_error : 1; - uint8_t initialized : 1; - uint8_t enumtype : 1; - uint8_t nullabletype : 1; - uint8_t is_generic : 1; - uint8_t has_references : 1; - uint8_t init_pending : 1; - uint8_t size_init_pending : 1; - uint8_t size_inited : 1; - uint8_t has_finalize : 1; - uint8_t has_cctor : 1; - uint8_t is_blittable : 1; - uint8_t is_import_or_windows_runtime : 1; - uint8_t is_vtable_initialized : 1; - uint8_t is_byref_like : 1; - VirtualInvokeData vtable[32]; -} Il2CppClass; - -typedef struct Il2CppClass_0 { - const Il2CppImage* image; - void* gc_desc; - const char* name; - const char* namespaze; - Il2CppType byval_arg; - Il2CppType this_arg; - Il2CppClass* element_class; - Il2CppClass* castClass; - Il2CppClass* declaringType; - Il2CppClass* parent; - Il2CppGenericClass * generic_class; - Il2CppMetadataTypeHandle typeMetadataHandle; - const Il2CppInteropData* interopData; - Il2CppClass* klass; - FieldInfo* fields; - const EventInfo* events; - const PropertyInfo* properties; - const MethodInfo** methods; - Il2CppClass** nestedTypes; - Il2CppClass** implementedInterfaces; -} Il2CppClass_0; - -typedef struct Il2CppClass_1 { - struct Il2CppClass** typeHierarchy; - void * unity_user_data; - uint32_t initializationExceptionGCHandle; - uint32_t cctor_started; - uint32_t cctor_finished_or_no_cctor; -#ifdef IS_32BIT - uint32_t cctor_thread; -#else - __attribute__((aligned(8))) size_t cctor_thread; -#endif - Il2CppMetadataGenericContainerHandle genericContainerHandle; - uint32_t instance_size; - uint32_t actualSize; - uint32_t element_size; - int32_t native_size; - uint32_t static_fields_size; - uint32_t thread_static_fields_size; - int32_t thread_static_fields_offset; - uint32_t flags; - uint32_t token; - uint16_t method_count; - uint16_t property_count; - uint16_t field_count; - uint16_t event_count; - uint16_t nested_type_count; - uint16_t vtable_count; - uint16_t interfaces_count; - uint16_t interface_offsets_count; - uint8_t typeHierarchyDepth; - uint8_t genericRecursionDepth; - uint8_t rank; - uint8_t minimumAlignment; - uint8_t naturalAligment; - uint8_t packingSize; - uint8_t initialized_and_no_error : 1; - uint8_t initialized : 1; - uint8_t enumtype : 1; - uint8_t nullabletype : 1; - uint8_t is_generic : 1; - uint8_t has_references : 1; - uint8_t init_pending : 1; - uint8_t size_init_pending : 1; - uint8_t size_inited : 1; - uint8_t has_finalize : 1; - uint8_t has_cctor : 1; - uint8_t is_blittable : 1; - uint8_t is_import_or_windows_runtime : 1; - uint8_t is_vtable_initialized : 1; - uint8_t is_byref_like : 1; -} Il2CppClass_1; - -typedef struct __attribute__((aligned(8))) Il2CppClass_Merged { - struct Il2CppClass_0 _0; - Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; - void* static_fields; - const Il2CppRGCTXData* rgctx_data; - struct Il2CppClass_1 _1; - VirtualInvokeData vtable[32]; -} Il2CppClass_Merged; - -typedef struct Il2CppTypeDefinitionSizes -{ - uint32_t instance_size; - int32_t native_size; - uint32_t static_fields_size; - uint32_t thread_static_fields_size; -} Il2CppTypeDefinitionSizes; -typedef struct Il2CppDomain -{ - Il2CppAppDomain* domain; - Il2CppAppDomainSetup* setup; - Il2CppAppContext* default_context; - const char* friendly_name; - uint32_t domain_id; - volatile int threadpool_jobs; - void* agent_info; -} Il2CppDomain; -typedef struct Il2CppAssemblyName -{ - const char* name; - const char* culture; - const uint8_t* public_key; - uint32_t hash_alg; - int32_t hash_len; - uint32_t flags; - int32_t major; - int32_t minor; - int32_t build; - int32_t revision; - uint8_t public_key_token[8]; -} Il2CppAssemblyName; -typedef struct Il2CppImage -{ - const char* name; - const char *nameNoExt; - Il2CppAssembly* assembly; - uint32_t typeCount; - uint32_t exportedTypeCount; - uint32_t customAttributeCount; - Il2CppMetadataImageHandle metadataHandle; - Il2CppNameToTypeHandleHashTable * nameToClassHashTable; - const Il2CppCodeGenModule* codeGenModule; - uint32_t token; - uint8_t dynamic; -} Il2CppImage; -typedef struct Il2CppAssembly -{ - Il2CppImage* image; - uint32_t token; - int32_t referencedAssemblyStart; - int32_t referencedAssemblyCount; - Il2CppAssemblyName aname; -} Il2CppAssembly; -typedef struct Il2CppCodeGenOptions -{ - uint8_t enablePrimitiveValueTypeGenericSharing; - int maximumRuntimeGenericDepth; - int recursiveGenericIterations; -} Il2CppCodeGenOptions; -typedef struct Il2CppRange -{ - int32_t start; - int32_t length; -} Il2CppRange; -typedef struct Il2CppTokenRangePair -{ - uint32_t token; - Il2CppRange range; -} Il2CppTokenRangePair; -typedef struct Il2CppTokenIndexMethodTuple -{ - uint32_t token; - int32_t index; - void** method; - uint32_t __genericMethodIndex; -} Il2CppTokenIndexMethodTuple; -typedef struct Il2CppTokenAdjustorThunkPair -{ - uint32_t token; - Il2CppMethodPointer adjustorThunk; -} Il2CppTokenAdjustorThunkPair; -typedef struct Il2CppWindowsRuntimeFactoryTableEntry -{ - const Il2CppType* type; - Il2CppMethodPointer createFactoryFunction; -} Il2CppWindowsRuntimeFactoryTableEntry; -typedef struct Il2CppCodeGenModule -{ - const char* moduleName; - const uint32_t methodPointerCount; - const Il2CppMethodPointer* methodPointers; - const uint32_t adjustorThunkCount; - const Il2CppTokenAdjustorThunkPair* adjustorThunks; - const int32_t* invokerIndices; - const uint32_t reversePInvokeWrapperCount; - const Il2CppTokenIndexMethodTuple* reversePInvokeWrapperIndices; - const uint32_t rgctxRangesCount; - const Il2CppTokenRangePair* rgctxRanges; - const uint32_t rgctxsCount; - const Il2CppRGCTXDefinition* rgctxs; - const Il2CppDebuggerMetadataRegistration *debuggerMetadata; - const Il2CppMethodPointer moduleInitializer; - TypeDefinitionIndex* staticConstructorTypeIndices; - const Il2CppMetadataRegistration* metadataRegistration; - const Il2CppCodeRegistration* codeRegistaration; -} Il2CppCodeGenModule; -typedef struct Il2CppCodeRegistration -{ - uint32_t reversePInvokeWrapperCount; - const Il2CppMethodPointer* reversePInvokeWrappers; - uint32_t genericMethodPointersCount; - const Il2CppMethodPointer* genericMethodPointers; - const Il2CppMethodPointer* genericAdjustorThunks; - uint32_t invokerPointersCount; - const InvokerMethod* invokerPointers; - uint32_t unresolvedVirtualCallCount; - const Il2CppMethodPointer* unresolvedVirtualCallPointers; - uint32_t interopDataCount; - Il2CppInteropData* interopData; - uint32_t windowsRuntimeFactoryCount; - Il2CppWindowsRuntimeFactoryTableEntry* windowsRuntimeFactoryTable; - uint32_t codeGenModulesCount; - const Il2CppCodeGenModule** codeGenModules; -} Il2CppCodeRegistration; -typedef struct Il2CppMetadataRegistration -{ - int32_t genericClassesCount; - Il2CppGenericClass* const * genericClasses; - int32_t genericInstsCount; - const Il2CppGenericInst* const * genericInsts; - int32_t genericMethodTableCount; - const Il2CppGenericMethodFunctionsDefinitions* genericMethodTable; - int32_t typesCount; - const Il2CppType* const * types; - int32_t methodSpecsCount; - const Il2CppMethodSpec* methodSpecs; - FieldIndex fieldOffsetsCount; - const int32_t** fieldOffsets; - TypeDefinitionIndex typeDefinitionsSizesCount; - const Il2CppTypeDefinitionSizes** typeDefinitionsSizes; - const size_t metadataUsagesCount; - void** const* metadataUsages; -} Il2CppMetadataRegistration; -typedef struct Il2CppPerfCounters -{ - uint32_t jit_methods; - uint32_t jit_bytes; - uint32_t jit_time; - uint32_t jit_failures; - uint32_t exceptions_thrown; - uint32_t exceptions_filters; - uint32_t exceptions_finallys; - uint32_t exceptions_depth; - uint32_t aspnet_requests_queued; - uint32_t aspnet_requests; - uint32_t gc_collections0; - uint32_t gc_collections1; - uint32_t gc_collections2; - uint32_t gc_promotions0; - uint32_t gc_promotions1; - uint32_t gc_promotion_finalizers; - uint32_t gc_gen0size; - uint32_t gc_gen1size; - uint32_t gc_gen2size; - uint32_t gc_lossize; - uint32_t gc_fin_survivors; - uint32_t gc_num_handles; - uint32_t gc_allocated; - uint32_t gc_induced; - uint32_t gc_time; - uint32_t gc_total_bytes; - uint32_t gc_committed_bytes; - uint32_t gc_reserved_bytes; - uint32_t gc_num_pinned; - uint32_t gc_sync_blocks; - uint32_t remoting_calls; - uint32_t remoting_channels; - uint32_t remoting_proxies; - uint32_t remoting_classes; - uint32_t remoting_objects; - uint32_t remoting_contexts; - uint32_t loader_classes; - uint32_t loader_total_classes; - uint32_t loader_appdomains; - uint32_t loader_total_appdomains; - uint32_t loader_assemblies; - uint32_t loader_total_assemblies; - uint32_t loader_failures; - uint32_t loader_bytes; - uint32_t loader_appdomains_uloaded; - uint32_t thread_contentions; - uint32_t thread_queue_len; - uint32_t thread_queue_max; - uint32_t thread_num_logical; - uint32_t thread_num_physical; - uint32_t thread_cur_recognized; - uint32_t thread_num_recognized; - uint32_t interop_num_ccw; - uint32_t interop_num_stubs; - uint32_t interop_num_marshals; - uint32_t security_num_checks; - uint32_t security_num_link_checks; - uint32_t security_time; - uint32_t security_depth; - uint32_t unused; - uint64_t threadpool_workitems; - uint64_t threadpool_ioworkitems; - unsigned int threadpool_threads; - unsigned int threadpool_iothreads; -} Il2CppPerfCounters; -typedef struct Il2CppClass Il2CppClass; -typedef struct MethodInfo MethodInfo; -typedef struct PropertyInfo PropertyInfo; -typedef struct FieldInfo FieldInfo; -typedef struct EventInfo EventInfo; -typedef struct Il2CppType Il2CppType; -typedef struct Il2CppAssembly Il2CppAssembly; -typedef struct Il2CppException Il2CppException; -typedef struct Il2CppImage Il2CppImage; -typedef struct Il2CppDomain Il2CppDomain; -typedef struct Il2CppString Il2CppString; -typedef struct Il2CppReflectionMethod Il2CppReflectionMethod; -typedef struct Il2CppAsyncCall Il2CppAsyncCall; -typedef struct Il2CppIUnknown Il2CppIUnknown; -typedef struct Il2CppWaitHandle Il2CppWaitHandle; -typedef struct MonitorData MonitorData; -typedef struct Il2CppReflectionAssembly Il2CppReflectionAssembly; -typedef Il2CppClass Il2CppVTable; -typedef struct Il2CppObject -{ - union - { - Il2CppClass *klass; - Il2CppVTable *vtable; - }; - MonitorData *monitor; -} Il2CppObject; -typedef int32_t il2cpp_array_lower_bound_t; -typedef struct Il2CppArrayBounds -{ - il2cpp_array_size_t length; - il2cpp_array_lower_bound_t lower_bound; -} Il2CppArrayBounds; -typedef struct Il2CppArray -{ - Il2CppObject obj; - Il2CppArrayBounds *bounds; - il2cpp_array_size_t max_length; -} Il2CppArray; -typedef struct Il2CppArraySize -{ - Il2CppObject obj; - Il2CppArrayBounds *bounds; - il2cpp_array_size_t max_length; - __attribute__((aligned(8))) void* vector[32]; -} Il2CppArraySize; -typedef struct Il2CppString -{ - Il2CppObject object; - int32_t length; - Il2CppChar chars[32]; -} Il2CppString; -typedef struct Il2CppReflectionType -{ - Il2CppObject object; - const Il2CppType *type; -} Il2CppReflectionType; -typedef struct Il2CppReflectionRuntimeType -{ - Il2CppReflectionType type; - Il2CppObject* type_info; - Il2CppObject* genericCache; - Il2CppObject* serializationCtor; -} Il2CppReflectionRuntimeType; -typedef struct Il2CppReflectionMonoType -{ - Il2CppReflectionRuntimeType type; -} Il2CppReflectionMonoType; -typedef struct Il2CppReflectionEvent -{ - Il2CppObject object; - Il2CppObject *cached_add_event; -} Il2CppReflectionEvent; -typedef struct Il2CppReflectionMonoEvent -{ - Il2CppReflectionEvent event; - Il2CppReflectionType* reflectedType; - const EventInfo* eventInfo; -} Il2CppReflectionMonoEvent; -typedef struct Il2CppReflectionMonoEventInfo -{ - Il2CppReflectionType* declaringType; - Il2CppReflectionType* reflectedType; - Il2CppString* name; - Il2CppReflectionMethod* addMethod; - Il2CppReflectionMethod* removeMethod; - Il2CppReflectionMethod* raiseMethod; - uint32_t eventAttributes; - Il2CppArray* otherMethods; -} Il2CppReflectionMonoEventInfo; -typedef struct Il2CppReflectionField -{ - Il2CppObject object; - Il2CppClass *klass; - FieldInfo *field; - Il2CppString *name; - Il2CppReflectionType *type; - uint32_t attrs; -} Il2CppReflectionField; -typedef struct Il2CppReflectionProperty -{ - Il2CppObject object; - Il2CppClass *klass; - const PropertyInfo *property; -} Il2CppReflectionProperty; -typedef struct Il2CppReflectionMethod -{ - Il2CppObject object; - const MethodInfo *method; - Il2CppString *name; - Il2CppReflectionType *reftype; -} Il2CppReflectionMethod; -typedef struct Il2CppReflectionGenericMethod -{ - Il2CppReflectionMethod base; -} Il2CppReflectionGenericMethod; -typedef struct Il2CppMethodInfo -{ - Il2CppReflectionType *parent; - Il2CppReflectionType *ret; - uint32_t attrs; - uint32_t implattrs; - uint32_t callconv; -} Il2CppMethodInfo; -typedef struct Il2CppPropertyInfo -{ - Il2CppReflectionType* parent; - Il2CppReflectionType* declaringType; - Il2CppString *name; - Il2CppReflectionMethod *get; - Il2CppReflectionMethod *set; - uint32_t attrs; -} Il2CppPropertyInfo; -typedef struct Il2CppReflectionParameter -{ - Il2CppObject object; - uint32_t AttrsImpl; - Il2CppReflectionType *ClassImpl; - Il2CppObject *DefaultValueImpl; - Il2CppObject *MemberImpl; - Il2CppString *NameImpl; - int32_t PositionImpl; - Il2CppObject* MarshalAs; -} Il2CppReflectionParameter; -typedef struct Il2CppReflectionModule -{ - Il2CppObject obj; - const Il2CppImage* image; - Il2CppReflectionAssembly* assembly; - Il2CppString* fqname; - Il2CppString* name; - Il2CppString* scopename; - uint8_t is_resource; - uint32_t token; -} Il2CppReflectionModule; -typedef struct Il2CppReflectionAssemblyName -{ - Il2CppObject obj; - Il2CppString *name; - Il2CppString *codebase; - int32_t major, minor, build, revision; - Il2CppObject *cultureInfo; - uint32_t flags; - uint32_t hashalg; - Il2CppObject *keypair; - Il2CppArray *publicKey; - Il2CppArray *keyToken; - uint32_t versioncompat; - Il2CppObject *version; - uint32_t processor_architecture; - uint32_t contentType; -} Il2CppReflectionAssemblyName; -typedef struct Il2CppReflectionAssembly -{ - Il2CppObject object; - const Il2CppAssembly *assembly; - Il2CppObject *evidence; - Il2CppObject *resolve_event_holder; - Il2CppObject *minimum; - Il2CppObject *optional; - Il2CppObject *refuse; - Il2CppObject *granted; - Il2CppObject *denied; - uint8_t from_byte_array; - Il2CppString *name; -} Il2CppReflectionAssembly; -typedef struct Il2CppReflectionMarshal -{ - Il2CppObject object; - int32_t count; - int32_t type; - int32_t eltype; - Il2CppString* guid; - Il2CppString* mcookie; - Il2CppString* marshaltype; - Il2CppObject* marshaltyperef; - int32_t param_num; - uint8_t has_size; -} Il2CppReflectionMarshal; -typedef struct Il2CppReflectionPointer -{ - Il2CppObject object; - void* data; - Il2CppReflectionType* type; -} Il2CppReflectionPointer; -typedef struct Il2CppThreadName -{ - Il2CppChar* chars; - int32_t unused; - int32_t length; -} Il2CppThreadName; -typedef struct -{ - uint32_t ref; - void (*destructor)(void* data); -} Il2CppRefCount; -typedef struct -{ - Il2CppRefCount ref; - void* synch_cs; -} Il2CppLongLivedThreadData; -typedef struct Il2CppInternalThread -{ - Il2CppObject obj; - int lock_thread_id; - void* handle; - void* native_handle; - Il2CppThreadName name; - uint32_t state; - Il2CppObject* abort_exc; - int abort_state_handle; - uint64_t tid; - intptr_t debugger_thread; - void** static_data; - void* runtime_thread_info; - Il2CppObject* current_appcontext; - Il2CppObject* root_domain_thread; - Il2CppArray* _serialized_principal; - int _serialized_principal_version; - void* appdomain_refs; - int32_t interruption_requested; - void* longlived; - uint8_t threadpool_thread; - uint8_t thread_interrupt_requested; - int stack_size; - uint8_t apartment_state; - int critical_region_level; - int managed_id; - uint32_t small_id; - void* manage_callback; - intptr_t flags; - void* thread_pinning_ref; - void* abort_protected_block_count; - int32_t priority; - void* owned_mutexes; - void * suspended; - int32_t self_suspended; - size_t thread_state; - void* unused[3]; - void* last; -} Il2CppInternalThread; -typedef struct Il2CppIOSelectorJob -{ - Il2CppObject object; - int32_t operation; - Il2CppObject *callback; - Il2CppObject *state; -} Il2CppIOSelectorJob; -typedef enum -{ - Il2Cpp_CallType_Sync = 0, - Il2Cpp_CallType_BeginInvoke = 1, - Il2Cpp_CallType_EndInvoke = 2, - Il2Cpp_CallType_OneWay = 3 -} Il2CppCallType; -typedef struct Il2CppMethodMessage -{ - Il2CppObject obj; - Il2CppReflectionMethod *method; - Il2CppArray *args; - Il2CppArray *names; - Il2CppArray *arg_types; - Il2CppObject *ctx; - Il2CppObject *rval; - Il2CppObject *exc; - Il2CppAsyncResult *async_result; - uint32_t call_type; -} Il2CppMethodMessage; -typedef struct Il2CppAppDomainSetup -{ - Il2CppObject object; - Il2CppString* application_base; - Il2CppString* application_name; - Il2CppString* cache_path; - Il2CppString* configuration_file; - Il2CppString* dynamic_base; - Il2CppString* license_file; - Il2CppString* private_bin_path; - Il2CppString* private_bin_path_probe; - Il2CppString* shadow_copy_directories; - Il2CppString* shadow_copy_files; - uint8_t publisher_policy; - uint8_t path_changed; - int loader_optimization; - uint8_t disallow_binding_redirects; - uint8_t disallow_code_downloads; - Il2CppObject* activation_arguments; - Il2CppObject* domain_initializer; - Il2CppObject* application_trust; - Il2CppArray* domain_initializer_args; - uint8_t disallow_appbase_probe; - Il2CppArray* configuration_bytes; - Il2CppArray* serialized_non_primitives; -} Il2CppAppDomainSetup; -typedef struct Il2CppThread -{ - Il2CppObject obj; - Il2CppInternalThread* internal_thread; - Il2CppObject* start_obj; - Il2CppException* pending_exception; - Il2CppObject* principal; - int32_t principal_version; - Il2CppDelegate* delegate; - Il2CppObject* executionContext; - uint8_t executionContextBelongsToOuterScope; -} Il2CppThread; -typedef struct Il2CppException -{ - Il2CppObject object; - Il2CppString* className; - Il2CppString* message; - Il2CppObject* _data; - struct Il2CppException* inner_ex; - Il2CppString* _helpURL; - Il2CppArray* trace_ips; - Il2CppString* stack_trace; - Il2CppString* remote_stack_trace; - int remote_stack_index; - Il2CppObject* _dynamicMethods; - il2cpp_hresult_t hresult; - Il2CppString* source; - Il2CppObject* safeSerializationManager; - Il2CppArray* captured_traces; - Il2CppArray* native_trace_ips; - int32_t caught_in_unmanaged; -} Il2CppException; -typedef struct Il2CppSystemException -{ - Il2CppException base; -} Il2CppSystemException; -typedef struct Il2CppArgumentException -{ - Il2CppException base; - Il2CppString *argName; -} Il2CppArgumentException; -typedef struct Il2CppTypedRef -{ - const Il2CppType *type; - void* value; - Il2CppClass *klass; -} Il2CppTypedRef; -typedef struct Il2CppDelegate -{ - Il2CppObject object; - Il2CppMethodPointer method_ptr; - Il2CppMethodPointer invoke_impl; - Il2CppObject *target; - const MethodInfo *method; - void* delegate_trampoline; - intptr_t extraArg; - uint8_t **method_code; - void* interp_method; - void* interp_invoke_impl; - Il2CppReflectionMethod *method_info; - Il2CppReflectionMethod *original_method_info; - Il2CppObject *data; - uint8_t method_is_virtual; -} Il2CppDelegate; -typedef struct Il2CppMulticastDelegate -{ - Il2CppDelegate delegate; - Il2CppArray *delegates; -} Il2CppMulticastDelegate; -typedef struct Il2CppMarshalByRefObject -{ - Il2CppObject obj; - Il2CppObject *identity; -} Il2CppMarshalByRefObject; -typedef void* Il2CppFullySharedGenericAny; -typedef void* Il2CppFullySharedGenericStruct; -typedef struct Il2CppAppDomain -{ - Il2CppMarshalByRefObject mbr; - Il2CppDomain *data; -} Il2CppAppDomain; -typedef struct Il2CppStackFrame -{ - Il2CppObject obj; - int32_t il_offset; - int32_t native_offset; - uint64_t methodAddress; - uint32_t methodIndex; - Il2CppReflectionMethod *method; - Il2CppString *filename; - int32_t line; - int32_t column; - Il2CppString *internal_method_name; -} Il2CppStackFrame; -typedef struct Il2CppDateTimeFormatInfo -{ - Il2CppObject obj; - Il2CppObject* CultureData; - Il2CppString* Name; - Il2CppString* LangName; - Il2CppObject* CompareInfo; - Il2CppObject* CultureInfo; - Il2CppString* AMDesignator; - Il2CppString* PMDesignator; - Il2CppString* DateSeparator; - Il2CppString* GeneralShortTimePattern; - Il2CppString* GeneralLongTimePattern; - Il2CppString* TimeSeparator; - Il2CppString* MonthDayPattern; - Il2CppString* DateTimeOffsetPattern; - Il2CppObject* Calendar; - uint32_t FirstDayOfWeek; - uint32_t CalendarWeekRule; - Il2CppString* FullDateTimePattern; - Il2CppArray* AbbreviatedDayNames; - Il2CppArray* ShortDayNames; - Il2CppArray* DayNames; - Il2CppArray* AbbreviatedMonthNames; - Il2CppArray* MonthNames; - Il2CppArray* GenitiveMonthNames; - Il2CppArray* GenitiveAbbreviatedMonthNames; - Il2CppArray* LeapYearMonthNames; - Il2CppString* LongDatePattern; - Il2CppString* ShortDatePattern; - Il2CppString* YearMonthPattern; - Il2CppString* LongTimePattern; - Il2CppString* ShortTimePattern; - Il2CppArray* YearMonthPatterns; - Il2CppArray* ShortDatePatterns; - Il2CppArray* LongDatePatterns; - Il2CppArray* ShortTimePatterns; - Il2CppArray* LongTimePatterns; - Il2CppArray* EraNames; - Il2CppArray* AbbrevEraNames; - Il2CppArray* AbbrevEnglishEraNames; - Il2CppArray* OptionalCalendars; - uint8_t readOnly; - int32_t FormatFlags; - int32_t CultureID; - uint8_t UseUserOverride; - uint8_t UseCalendarInfo; - int32_t DataItem; - uint8_t IsDefaultCalendar; - Il2CppArray* DateWords; - Il2CppString* FullTimeSpanPositivePattern; - Il2CppString* FullTimeSpanNegativePattern; - Il2CppArray* dtfiTokenHash; -} Il2CppDateTimeFormatInfo; -typedef struct Il2CppNumberFormatInfo -{ - Il2CppObject obj; - Il2CppArray* numberGroupSizes; - Il2CppArray* currencyGroupSizes; - Il2CppArray* percentGroupSizes; - Il2CppString* positiveSign; - Il2CppString* negativeSign; - Il2CppString* numberDecimalSeparator; - Il2CppString* numberGroupSeparator; - Il2CppString* currencyGroupSeparator; - Il2CppString* currencyDecimalSeparator; - Il2CppString* currencySymbol; - Il2CppString* ansiCurrencySymbol; - Il2CppString* naNSymbol; - Il2CppString* positiveInfinitySymbol; - Il2CppString* negativeInfinitySymbol; - Il2CppString* percentDecimalSeparator; - Il2CppString* percentGroupSeparator; - Il2CppString* percentSymbol; - Il2CppString* perMilleSymbol; - Il2CppArray* nativeDigits; - int dataItem; - int numberDecimalDigits; - int currencyDecimalDigits; - int currencyPositivePattern; - int currencyNegativePattern; - int numberNegativePattern; - int percentPositivePattern; - int percentNegativePattern; - int percentDecimalDigits; - int digitSubstitution; - uint8_t readOnly; - uint8_t useUserOverride; - uint8_t isInvariant; - uint8_t validForParseAsNumber; - uint8_t validForParseAsCurrency; -} Il2CppNumberFormatInfo; -typedef struct NumberFormatEntryManaged -{ - int32_t currency_decimal_digits; - int32_t currency_decimal_separator; - int32_t currency_group_separator; - int32_t currency_group_sizes0; - int32_t currency_group_sizes1; - int32_t currency_negative_pattern; - int32_t currency_positive_pattern; - int32_t currency_symbol; - int32_t nan_symbol; - int32_t negative_infinity_symbol; - int32_t negative_sign; - int32_t number_decimal_digits; - int32_t number_decimal_separator; - int32_t number_group_separator; - int32_t number_group_sizes0; - int32_t number_group_sizes1; - int32_t number_negative_pattern; - int32_t per_mille_symbol; - int32_t percent_negative_pattern; - int32_t percent_positive_pattern; - int32_t percent_symbol; - int32_t positive_infinity_symbol; - int32_t positive_sign; -} NumberFormatEntryManaged; -typedef struct Il2CppCultureData -{ - Il2CppObject obj; - Il2CppString *AMDesignator; - Il2CppString *PMDesignator; - Il2CppString *TimeSeparator; - Il2CppArray *LongTimePatterns; - Il2CppArray *ShortTimePatterns; - uint32_t FirstDayOfWeek; - uint32_t CalendarWeekRule; -} Il2CppCultureData; -typedef struct Il2CppCalendarData -{ - Il2CppObject obj; - Il2CppString *NativeName; - Il2CppArray *ShortDatePatterns; - Il2CppArray *YearMonthPatterns; - Il2CppArray *LongDatePatterns; - Il2CppString *MonthDayPattern; - Il2CppArray *EraNames; - Il2CppArray *AbbreviatedEraNames; - Il2CppArray *AbbreviatedEnglishEraNames; - Il2CppArray *DayNames; - Il2CppArray *AbbreviatedDayNames; - Il2CppArray *SuperShortDayNames; - Il2CppArray *MonthNames; - Il2CppArray *AbbreviatedMonthNames; - Il2CppArray *GenitiveMonthNames; - Il2CppArray *GenitiveAbbreviatedMonthNames; -} Il2CppCalendarData; -typedef struct Il2CppCultureInfo -{ - Il2CppObject obj; - uint8_t is_read_only; - int32_t lcid; - int32_t parent_lcid; - int32_t datetime_index; - int32_t number_index; - int32_t default_calendar_type; - uint8_t use_user_override; - Il2CppNumberFormatInfo* number_format; - Il2CppDateTimeFormatInfo* datetime_format; - Il2CppObject* textinfo; - Il2CppString* name; - Il2CppString* englishname; - Il2CppString* nativename; - Il2CppString* iso3lang; - Il2CppString* iso2lang; - Il2CppString* win3lang; - Il2CppString* territory; - Il2CppArray* native_calendar_names; - Il2CppString* compareinfo; - const void* text_info_data; - int dataItem; - Il2CppObject* calendar; - Il2CppObject* parent_culture; - uint8_t constructed; - Il2CppArray* cached_serialized_form; - Il2CppObject* cultureData; - uint8_t isInherited; -} Il2CppCultureInfo; -typedef struct Il2CppRegionInfo -{ - Il2CppObject obj; - int32_t geo_id; - Il2CppString* iso2name; - Il2CppString* iso3name; - Il2CppString* win3name; - Il2CppString* english_name; - Il2CppString* native_name; - Il2CppString* currency_symbol; - Il2CppString* iso_currency_symbol; - Il2CppString* currency_english_name; - Il2CppString* currency_native_name; -} Il2CppRegionInfo; -typedef struct Il2CppSafeHandle -{ - Il2CppObject base; - void* handle; - int state; - uint8_t owns_handle; - uint8_t fullyInitialized; -} Il2CppSafeHandle; -typedef struct Il2CppStringBuilder Il2CppStringBuilder; -typedef struct Il2CppStringBuilder -{ - Il2CppObject object; - Il2CppArray* chunkChars; - struct Il2CppStringBuilder* chunkPrevious; - int chunkLength; - int chunkOffset; - int maxCapacity; -} Il2CppStringBuilder; -typedef struct Il2CppSocketAddress -{ - Il2CppObject base; - int m_Size; - Il2CppArray* data; - uint8_t m_changed; - int m_hash; -} Il2CppSocketAddress; -typedef struct Il2CppSortKey -{ - Il2CppObject base; - Il2CppString *str; - Il2CppArray *key; - int32_t options; - int32_t lcid; -} Il2CppSortKey; -typedef struct Il2CppErrorWrapper -{ - Il2CppObject base; - int32_t errorCode; -} Il2CppErrorWrapper; -typedef struct Il2CppAsyncResult -{ - Il2CppObject base; - Il2CppObject *async_state; - Il2CppWaitHandle *handle; - Il2CppDelegate *async_delegate; - void* data; - Il2CppAsyncCall *object_data; - uint8_t sync_completed; - uint8_t completed; - uint8_t endinvoke_called; - Il2CppObject *async_callback; - Il2CppObject *execution_context; - Il2CppObject *original_context; -} Il2CppAsyncResult; -typedef struct Il2CppAsyncCall -{ - Il2CppObject base; - Il2CppMethodMessage *msg; - MethodInfo *cb_method; - Il2CppDelegate *cb_target; - Il2CppObject *state; - Il2CppObject *res; - Il2CppArray *out_args; -} Il2CppAsyncCall; -typedef struct Il2CppExceptionWrapper Il2CppExceptionWrapper; -typedef struct Il2CppExceptionWrapper -{ - Il2CppException* ex; -} Il2CppExceptionWrapper; -typedef struct Il2CppIOAsyncResult -{ - Il2CppObject base; - Il2CppDelegate* callback; - Il2CppObject* state; - Il2CppWaitHandle* wait_handle; - uint8_t completed_synchronously; - uint8_t completed; -} Il2CppIOAsyncResult; -typedef struct Il2CppSocketAsyncResult -{ - Il2CppIOAsyncResult base; - Il2CppObject* socket; - int32_t operation; - Il2CppException* delayedException; - Il2CppObject* endPoint; - Il2CppArray* buffer; - int32_t offset; - int32_t size; - int32_t socket_flags; - Il2CppObject* acceptSocket; - Il2CppArray* addresses; - int32_t port; - Il2CppObject* buffers; - uint8_t reuseSocket; - int32_t currentAddress; - Il2CppObject* acceptedSocket; - int32_t total; - int32_t error; - int32_t endCalled; -} Il2CppSocketAsyncResult; -typedef enum Il2CppResourceLocation -{ - IL2CPP_RESOURCE_LOCATION_EMBEDDED = 1, - IL2CPP_RESOURCE_LOCATION_ANOTHER_ASSEMBLY = 2, - IL2CPP_RESOURCE_LOCATION_IN_MANIFEST = 4 -} Il2CppResourceLocation; -typedef struct Il2CppManifestResourceInfo -{ - Il2CppObject object; - Il2CppReflectionAssembly* assembly; - Il2CppString* filename; - uint32_t location; -} Il2CppManifestResourceInfo; -typedef struct Il2CppAppContext -{ - Il2CppObject obj; - int32_t domain_id; - int32_t context_id; - void* static_data; -} Il2CppAppContext; -typedef struct Il2CppDecimal -{ - uint16_t reserved; - union - { - struct - { - uint8_t scale; - uint8_t sign; - } u; - uint16_t signscale; - } u; - uint32_t Hi32; - union - { - struct - { - uint32_t Lo32; - uint32_t Mid32; - } v; - uint64_t Lo64; - } v; -} Il2CppDecimal; -typedef struct Il2CppDouble -{ - uint32_t mantLo : 32; - uint32_t mantHi : 20; - uint32_t exp : 11; - uint32_t sign : 1; -} Il2CppDouble; -typedef union Il2CppDouble_double -{ - Il2CppDouble s; - double d; -} Il2CppDouble_double; -typedef enum Il2CppDecimalCompareResult -{ - IL2CPP_DECIMAL_CMP_LT = -1, - IL2CPP_DECIMAL_CMP_EQ, - IL2CPP_DECIMAL_CMP_GT -} Il2CppDecimalCompareResult; -typedef struct Il2CppSingle -{ - uint32_t mant : 23; - uint32_t exp : 8; - uint32_t sign : 1; -} Il2CppSingle; -typedef union Il2CppSingle_float -{ - Il2CppSingle s; - float f; -} Il2CppSingle_float; -typedef struct Il2CppByReference -{ - intptr_t value; -} Il2CppByReference; diff --git a/Il2CppInspector.Common/IL2CPP/Metadata.cs b/Il2CppInspector.Common/IL2CPP/Metadata.cs index 28c25658..c71578a4 100644 --- a/Il2CppInspector.Common/IL2CPP/Metadata.cs +++ b/Il2CppInspector.Common/IL2CPP/Metadata.cs @@ -195,25 +195,6 @@ private void Initialize() Header.AttributeDataRangeSize / Sizeof()); } - 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, 0, MetadataVersions.Tag2023); - - Methods = ReadVersionedObjectArray(Header.MethodsOffset, - Header.MethodsSize / Sizeof()); - } - } - } - // Get all metadata strings var pluginGetStringsResult = PluginHooks.GetStrings(this); if (pluginGetStringsResult.IsDataModified && !pluginGetStringsResult.IsInvalid) diff --git a/Il2CppInspector.Common/Next/Metadata/Il2CppMethodDefinition.cs b/Il2CppInspector.Common/Next/Metadata/Il2CppMethodDefinition.cs index 7e131f00..b8cb72af 100644 --- a/Il2CppInspector.Common/Next/Metadata/Il2CppMethodDefinition.cs +++ b/Il2CppInspector.Common/Next/Metadata/Il2CppMethodDefinition.cs @@ -49,10 +49,6 @@ public partial record struct Il2CppMethodDefinition public ushort Slot { get; private set; } public ushort ParameterCount { get; private set; } - [VersionCondition(EqualTo = "29.0", IncludingTag = "2023")] - [VersionCondition(EqualTo = "31.0", IncludingTag = "2023")] - public bool IsUnmanagedCallersOnly { get; private set; } - public readonly MethodAttributes Attributes => (MethodAttributes)Flags; public readonly MethodImplAttributes ImplAttributes => (MethodImplAttributes)ImplFlags; diff --git a/Il2CppInspector.Common/Next/MetadataVersions.cs b/Il2CppInspector.Common/Next/MetadataVersions.cs index 383ab0ac..11e9bf1c 100644 --- a/Il2CppInspector.Common/Next/MetadataVersions.cs +++ b/Il2CppInspector.Common/Next/MetadataVersions.cs @@ -22,10 +22,10 @@ public static class MetadataVersions public static readonly StructVersion V271 = new(27, 1); public static readonly StructVersion V272 = new(27, 2); - // These two versions have three variations: + // These two versions have two variations: public static readonly StructVersion V290 = new(29); public static readonly StructVersion V310 = new(31); + // No tag - 29.0/31.0 public static readonly string Tag2022 = "2022"; // 29.1/31.1 - public static readonly string Tag2023 = "2023"; // 29.2/31.2 } \ No newline at end of file From e67f85629943aabe8037d25be99a61da984407cb Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Sun, 1 Sep 2024 02:02:13 +0200 Subject: [PATCH 12/27] also remove 29.2 from the readme --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index fa195bd4..2d8dbb28 100644 --- a/README.md +++ b/README.md @@ -756,7 +756,6 @@ Unity version | IL2CPP version | Support 2021.1.0-2021.1.x | 27.2 | Working 2021.2.0-2021.2.x | 29 | Working 2021.3.0+ | 29.1 | Working -2023.2.0a22 | 29.2 | Working 2022.3.33+ | 31(.1) | Working Please refer to the companion repository https://github.com/nneonneo/Il2CppVersions if you would like to track the changes between each IL2CPP release version. From 095bfa16e82689cc0792d3bbf427df93af33465b Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Sun, 13 Oct 2024 21:54:06 +0200 Subject: [PATCH 13/27] remove loading of packed dlls - this was a very unsafe feature --- .../FileFormatStreams/PEReader.cs | 66 ------------------- 1 file changed, 66 deletions(-) diff --git a/Il2CppInspector.Common/FileFormatStreams/PEReader.cs b/Il2CppInspector.Common/FileFormatStreams/PEReader.cs index 648ccec2..16a8d0fa 100644 --- a/Il2CppInspector.Common/FileFormatStreams/PEReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/PEReader.cs @@ -140,76 +140,10 @@ protected override bool Init() { return true; } - // Load DLL into memory and save it as a new PE stream - private void load() { - // Check that the process is running in the same word size as the DLL - // One way round this in future would be to spawn a new process of the correct word size - if ((Environment.Is64BitProcess && Bits == 32) || (!Environment.Is64BitProcess && Bits == 64)) - throw new InvalidOperationException($"Cannot unpack a {Bits}-bit DLL from within a {(Environment.Is64BitProcess ? 64 : 32)}-bit process. Use the {Bits}-version of Il2CppInspector to unpack this DLL."); - - // Get file path - // This error should never occur with the bundled CLI and GUI; only when used as a library by a 3rd party tool - if (LoadOptions == null || !(LoadOptions.BinaryFilePath is string dllPath)) - throw new InvalidOperationException("To load a packed PE file, you must specify the DLL file path in LoadOptions"); - - // Attempt to load DLL and run startup functions - // NOTE: This can cause a CSE (AccessViolation) for certain types of protection - // so only try to unpack as the final load strategy - IntPtr hModule = LoadLibrary(dllPath); - if (hModule == IntPtr.Zero) { - var lastErrorCode = Marshal.GetLastWin32Error(); - throw new InvalidOperationException($"Unable to load the DLL for unpacking: error code {lastErrorCode}"); - } - - // Maximum image size - var size = sections.Last().VirtualAddress + sections.Last().VirtualSize; - - // Allocate memory for unpacked image - var peBytes = new byte[size]; - - // Copy relevant sections from unmanaged memory - foreach (var section in sections.Where(s => wantedSectionTypes.Keys.Contains(s.Characteristics))) - Marshal.Copy(IntPtr.Add(hModule, (int) section.VirtualAddress), peBytes, (int) section.VirtualAddress, (int) section.VirtualSize); - - // Decrease reference count for unload - FreeLibrary(hModule); - - // Rebase - pe.ImageBase = (ulong) hModule.ToInt64(); - - // Rewrite sections to match memory layout - foreach (var section in sections) { - section.PointerToRawData = section.VirtualAddress; - section.SizeOfRawData = section.VirtualSize; - } - - // Truncate memory stream at start of COFF header - var endOfSignature = ReadUInt32(0x3C) + 4; // DOS header + 4-byte PE signature - SetLength(endOfSignature); - - // Re-write the stream (the headers are only necessary in case the user wants to save) - Position = endOfSignature; - WriteObject(coff); - if (Bits == 32) WriteObject((PEOptHeader32) pe); - else WriteObject((PEOptHeader64) pe); - WriteArray(sections); - Write(peBytes, (int) Position, peBytes.Length - (int) Position); - - IsModified = true; - } - // Raw file / unpacked file load strategies public override IEnumerable TryNextLoadStrategy() { // First load strategy: the regular file yield return this; - - // Second load strategy: load the DLL into memory to unpack it - if (mightBePacked) { - Console.WriteLine("IL2CPP binary appears to be packed - attempting to unpack and retrying"); - StatusUpdate("Unpacking binary"); - load(); - yield return this; - } } public override uint[] GetFunctionTable() { From d59f67216a53ae085fef1a8992cf8c4c450b78fc Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Sun, 13 Oct 2024 21:54:59 +0200 Subject: [PATCH 14/27] support auto-recovering type indices from type handles fixes loading of memory-dumped v29+ libraries since those replacee their class indices on load with a pointer to the corresponding type --- Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs | 47 +++++++++++++++++-- .../Next/BinaryMetadata/Il2CppTypeEnum.cs | 31 ++++++++++++ 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs b/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs index ab7c7aa0..64b087fb 100644 --- a/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs +++ b/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs @@ -6,15 +6,12 @@ All rights reserved. */ using Il2CppInspector.Next; -using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.IO; -using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using Il2CppInspector.Next.BinaryMetadata; +using Il2CppInspector.Next.Metadata; using VersionedSerialization; namespace Il2CppInspector @@ -370,6 +367,48 @@ private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) TypeReferences = Image.ReadMappedVersionedObjectPointerArray(MetadataRegistration.Types, (int)MetadataRegistration.TypesCount); + if (TypeReferences.Any(x => + x.Type.IsTypeDefinitionEnum() + && (uint)x.Data.KlassIndex >= (uint)Metadata.Types.Length)) + { + // This is a memory-dumped binary. + // We need to fix the remapped type indices from their pointer form back to the indices. + var baseDefinitionPtr = ulong.MaxValue; + var baseGenericPtr = ulong.MaxValue; + + foreach (var entry in TypeReferences) + { + if (entry.Type.IsTypeDefinitionEnum()) + { + baseDefinitionPtr = Math.Min(baseDefinitionPtr, entry.Data.Type.PointerValue); + } + else if (entry.Type.IsGenericParameterEnum()) + { + baseGenericPtr = Math.Min(baseGenericPtr, entry.Data.GenericParameterHandle.PointerValue); + } + + } + + var definitionSize = (ulong)Il2CppTypeDefinition.Size(Image.Version); + var genericParameterSize = (ulong)Il2CppGenericParameter.Size(Image.Version); + + var builder = ImmutableArray.CreateBuilder(TypeReferences.Length); + for (var i = 0; i < TypeReferences.Length; i++) + { + var type = TypeReferences[i]; + if (type.Type.IsTypeDefinitionEnum()) + { + type.Data.Value = (type.Data.Type.PointerValue - baseDefinitionPtr) / definitionSize; + } + else if (type.Type.IsGenericParameterEnum()) + { + type.Data.Value = (type.Data.Type.PointerValue - baseGenericPtr) / genericParameterSize; + } + builder.Add(type); + } + TypeReferences = builder.MoveToImmutable(); + } + // Custom attribute constructors (function pointers) // This is managed in Il2CppInspector for metadata >= 27 if (Image.Version < MetadataVersions.V270) { diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTypeEnum.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTypeEnum.cs index 0c441424..7466ad9c 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTypeEnum.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppTypeEnum.cs @@ -42,4 +42,35 @@ public enum Il2CppTypeEnum : byte IL2CPP_TYPE_ENUM = 0x55, IL2CPP_TYPE_IL2CPP_TYPE_INDEX = 0xff +} + +public static class Il2CppTypeEnumExtensions +{ + public static bool IsTypeDefinitionEnum(this Il2CppTypeEnum value) + => value + is Il2CppTypeEnum.IL2CPP_TYPE_VOID + or Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN + or Il2CppTypeEnum.IL2CPP_TYPE_CHAR + or Il2CppTypeEnum.IL2CPP_TYPE_I1 + or Il2CppTypeEnum.IL2CPP_TYPE_U1 + or Il2CppTypeEnum.IL2CPP_TYPE_I2 + or Il2CppTypeEnum.IL2CPP_TYPE_U2 + or Il2CppTypeEnum.IL2CPP_TYPE_I4 + or Il2CppTypeEnum.IL2CPP_TYPE_U4 + or Il2CppTypeEnum.IL2CPP_TYPE_I8 + or Il2CppTypeEnum.IL2CPP_TYPE_U8 + or Il2CppTypeEnum.IL2CPP_TYPE_R4 + or Il2CppTypeEnum.IL2CPP_TYPE_R8 + or Il2CppTypeEnum.IL2CPP_TYPE_STRING + or Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE + or Il2CppTypeEnum.IL2CPP_TYPE_CLASS + or Il2CppTypeEnum.IL2CPP_TYPE_I + or Il2CppTypeEnum.IL2CPP_TYPE_U + or Il2CppTypeEnum.IL2CPP_TYPE_OBJECT + or Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF; + + public static bool IsGenericParameterEnum(this Il2CppTypeEnum value) + => value + is Il2CppTypeEnum.IL2CPP_TYPE_VAR + or Il2CppTypeEnum.IL2CPP_TYPE_MVAR; } \ No newline at end of file From daa80bcffed7fedfcd77cf9539c02f310bc88272 Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Sat, 26 Oct 2024 19:56:49 +0200 Subject: [PATCH 15/27] support loading PEs without an export table --- Il2CppInspector.Common/FileFormatStreams/PEReader.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Il2CppInspector.Common/FileFormatStreams/PEReader.cs b/Il2CppInspector.Common/FileFormatStreams/PEReader.cs index 16a8d0fa..f014c53a 100644 --- a/Il2CppInspector.Common/FileFormatStreams/PEReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/PEReader.cs @@ -161,12 +161,17 @@ public override uint[] GetFunctionTable() { return addrs.ToArray(); } - public override IEnumerable GetExports() { + public override IEnumerable GetExports() + { + var exportDirectory = pe.DataDirectory[0]; + if (exportDirectory.Size == 0) + return []; + // Get exports table - var ETStart = pe.DataDirectory[0].VirtualAddress + pe.ImageBase; + var exportTableStart = exportDirectory.VirtualAddress + pe.ImageBase; // Get export RVAs - var exportDirectoryTable = ReadObject(MapVATR(ETStart)); + var exportDirectoryTable = ReadObject(MapVATR(exportTableStart)); var exportCount = (int) exportDirectoryTable.NumberOfFunctions; var exportAddresses = ReadArray(MapVATR(exportDirectoryTable.AddressOfFunctions + pe.ImageBase), exportCount); var exports = exportAddresses.Select((a, i) => new Export { From 88959793886a6189181cd57b4affea9e33375a73 Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Sun, 27 Oct 2024 11:24:14 +0100 Subject: [PATCH 16/27] also read UnresolvedVirtualCallCount on regular v31 --- .../Next/BinaryMetadata/Il2CppCodeRegistration.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeRegistration.cs b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeRegistration.cs index c8a07308..89912e09 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeRegistration.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppCodeRegistration.cs @@ -71,6 +71,7 @@ public partial record struct Il2CppCodeRegistration [NativeInteger] [VersionCondition(GreaterThan = "22.0", LessThan = "27.2")] [VersionCondition(EqualTo = "29.0", IncludingTag = "")] + [VersionCondition(EqualTo = "31.0", IncludingTag = "")] public int UnresolvedVirtualCallCount; [NativeInteger] From 792268f52f95bc379afc9dd6f7b70d7aa9f3821e Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Thu, 7 Nov 2024 12:51:58 +0100 Subject: [PATCH 17/27] Disable plugin loading for now --- Il2CppInspector.Common/Plugins/Internal/PluginManager.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Il2CppInspector.Common/Plugins/Internal/PluginManager.cs b/Il2CppInspector.Common/Plugins/Internal/PluginManager.cs index e146c359..991be6ca 100644 --- a/Il2CppInspector.Common/Plugins/Internal/PluginManager.cs +++ b/Il2CppInspector.Common/Plugins/Internal/PluginManager.cs @@ -113,7 +113,8 @@ public partial class PluginManager // Global enable/disable flag for entire plugin system // If set to false, all plugins will be unloaded // Disable this if you want to create standalone apps using the API but without plugins - private static bool _enabled = true; + private static bool _enabled = false; + public static bool Enabled { get => _enabled; set { @@ -123,7 +124,7 @@ public static bool Enabled { } // All of the detected plugins, including invalid/incompatible/non-loaded plugins - public ObservableCollection ManagedPlugins { get; } = new ObservableCollection(); + public ObservableCollection ManagedPlugins { get; } = []; // All of the plugins that are loaded and available for use public static IEnumerable AvailablePlugins => AsInstance.ManagedPlugins.Where(p => p.Available).Select(p => p.Plugin); @@ -136,7 +137,7 @@ public static Dictionary Plugins => AsInstance.ManagedPlugins.Where(p => p.Available).ToDictionary(p => p.Plugin.Id, p => p); // The relative path from the executable that we'll search for plugins - private static string pluginFolder = Path.GetFullPath(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + Path.DirectorySeparatorChar + "plugins"); + private static string pluginFolder = Path.GetFullPath(Path.GetDirectoryName(Environment.ProcessPath) + Path.DirectorySeparatorChar + "plugins"); // A placeholder plugin to be used when the real plugin cannot be loaded for some reason private class InvalidPlugin : IPlugin From 5715760e8ba0d4eb1af3d0809ff3fe9e65134435 Mon Sep 17 00:00:00 2001 From: Luke <17146677+LukeFZ@users.noreply.github.com> Date: Fri, 8 Nov 2024 23:31:40 +0100 Subject: [PATCH 18/27] Overhaul disassembler script + add Binary Ninja target (#12) * Overhaul diassembler scripts: - No longer defines top level functions - Split into three classes: StatusHandler (like before), DisassemblerInterface (for interfacing with the used program API), ScriptContext (for definiting general functions that use the disassembler interface) - Add type annotations to all class methods and remove 2.7 compatibility stuff (Ghidra now supports Python 3 so this is unnecessary anymore) - Disassembler backends are now responsible for launching metadata/script processing, to better support disassembler differences - String handling is back in the base ScriptContext class, disassembler interfaces opt into the fake string segment creation and fall back to the old method if it isn't supported * Add Binary Ninja disassembler script backend This uses the new backend-controlled execution to launch metadata processing on a background thread to keep the ui responsive * make binary ninja script use own _BINARYNINJA_ define and add define helpers to header * Update README to account for new script and binary ninja backend * implement fake string segment functions for binary ninja but don't advertise support * also cache API function types in binary ninja backend * fix ida script and disable folders again --- .../Outputs/CppScaffolding.cs | 15 +- .../Outputs/PythonScript.cs | 13 +- .../ScriptResources/Targets/BinaryNinja.py | 284 +++++++++++++++ .../Outputs/ScriptResources/Targets/Ghidra.py | 210 ++++++------ .../Outputs/ScriptResources/Targets/IDA.py | 324 +++++++++--------- .../Outputs/ScriptResources/shared-main.py | 182 ---------- .../ScriptResources/shared-preamble.py | 14 - .../Outputs/ScriptResources/shared_base.py | 290 ++++++++++++++++ README.md | 40 +-- 9 files changed, 887 insertions(+), 485 deletions(-) create mode 100644 Il2CppInspector.Common/Outputs/ScriptResources/Targets/BinaryNinja.py delete mode 100644 Il2CppInspector.Common/Outputs/ScriptResources/shared-main.py delete mode 100644 Il2CppInspector.Common/Outputs/ScriptResources/shared-preamble.py create mode 100644 Il2CppInspector.Common/Outputs/ScriptResources/shared_base.py diff --git a/Il2CppInspector.Common/Outputs/CppScaffolding.cs b/Il2CppInspector.Common/Outputs/CppScaffolding.cs index d19f84e0..4f15aaa3 100644 --- a/Il2CppInspector.Common/Outputs/CppScaffolding.cs +++ b/Il2CppInspector.Common/Outputs/CppScaffolding.cs @@ -38,14 +38,15 @@ public void WriteTypes(string typeHeaderFile) { using var fs = new FileStream(typeHeaderFile, FileMode.Create); _writer = new StreamWriter(fs, Encoding.ASCII); - const string decompilerIfDef = "#if !defined(_GHIDRA_) && !defined(_IDA_) && !defined(_IDACLANG_)"; - using (_writer) { writeHeader(); // Write primitive type definitions for when we're not including other headers writeCode($""" + #define IS_LIBCLANG_DECOMPILER (defined(_IDACLANG_) || defined(_BINARYNINJA_)) + #define IS_DECOMPILER (defined(_GHIDRA_) || defined(_IDA_) || IS_LIBCLANG_DECOMPILER) + #if defined(_GHIDRA_) || defined(_IDA_) typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; @@ -57,7 +58,7 @@ public void WriteTypes(string typeHeaderFile) { typedef __int64 int64_t; #endif - #ifdef _IDACLANG_ + #if IS_LIBCLANG_DECOMPILER typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; @@ -68,13 +69,13 @@ public void WriteTypes(string typeHeaderFile) { typedef long int64_t; #endif - #if defined(_GHIDRA_) || defined(_IDACLANG_) + #if defined(_GHIDRA_) || IS_LIBCLANG_DECOMPILER typedef int{_model.Package.BinaryImage.Bits}_t intptr_t; typedef uint{_model.Package.BinaryImage.Bits}_t uintptr_t; typedef uint{_model.Package.BinaryImage.Bits}_t size_t; #endif - {decompilerIfDef} + #if !IS_DECOMPILER #define _CPLUSPLUS_ #endif """); @@ -114,7 +115,7 @@ typedef union better_il2cpp_array_size_t } // C does not support namespaces - writeCode($"{decompilerIfDef}"); + writeCode("#if !IS_DECOMPILER"); writeCode("namespace app {"); writeCode("#endif"); writeLine(""); @@ -124,7 +125,7 @@ typedef union better_il2cpp_array_size_t writeTypesForGroup("Application types from usages", "types_from_usages"); writeTypesForGroup("Application unused value types", "unused_concrete_types"); - writeCode($"{decompilerIfDef}"); + writeCode("#if !IS_DECOMPILER"); writeCode("}"); writeCode("#endif"); } diff --git a/Il2CppInspector.Common/Outputs/PythonScript.cs b/Il2CppInspector.Common/Outputs/PythonScript.cs index 48cf5aa4..04295a51 100644 --- a/Il2CppInspector.Common/Outputs/PythonScript.cs +++ b/Il2CppInspector.Common/Outputs/PythonScript.cs @@ -22,7 +22,9 @@ public class PythonScript public static IEnumerable GetAvailableTargets() { var ns = typeof(PythonScript).Namespace + ".ScriptResources.Targets"; var res = ResourceHelper.GetNamesForNamespace(ns); - return res.Select(s => Path.GetFileNameWithoutExtension(s.Substring(ns.Length + 1))).OrderBy(s => s); + return res + .Select(s => Path.GetFileNameWithoutExtension(s[(ns.Length + 1)..])) + .OrderBy(s => s); } // Output script file @@ -52,12 +54,11 @@ public void WriteScriptToFile(string outputFile, string target, string existingT var jsonMetadataRelativePath = getRelativePath(outputFile, jsonMetadataFile); - var ns = typeof(PythonScript).Namespace + ".ScriptResources"; - var preamble = ResourceHelper.GetText(ns + ".shared-preamble.py"); - var main = ResourceHelper.GetText(ns + ".shared-main.py"); - var api = ResourceHelper.GetText($"{ns}.Targets.{target}.py"); + var ns = $"{typeof(PythonScript).Namespace}.ScriptResources"; + var baseScipt = ResourceHelper.GetText($"{ns}.shared_base.py"); + var impl = ResourceHelper.GetText($"{ns}.Targets.{target}.py"); - var script = string.Join("\n", new [] { preamble, api, main }) + var script = string.Join("\n", baseScipt, impl) .Replace("%SCRIPTFILENAME%", Path.GetFileName(outputFile)) .Replace("%TYPE_HEADER_RELATIVE_PATH%", typeHeaderRelativePath.ToEscapedString()) .Replace("%JSON_METADATA_RELATIVE_PATH%", jsonMetadataRelativePath.ToEscapedString()) diff --git a/Il2CppInspector.Common/Outputs/ScriptResources/Targets/BinaryNinja.py b/Il2CppInspector.Common/Outputs/ScriptResources/Targets/BinaryNinja.py new file mode 100644 index 00000000..10af829b --- /dev/null +++ b/Il2CppInspector.Common/Outputs/ScriptResources/Targets/BinaryNinja.py @@ -0,0 +1,284 @@ +from binaryninja import * + +#try: +# from typing import TYPE_CHECKING +# if TYPE_CHECKING: +# from ..shared_base import BaseStatusHandler, BaseDisassemblerInterface, ScriptContext +# import json +# import os +# import sys +# from datetime import datetime +#except: +# pass + +CURRENT_PATH = os.path.dirname(os.path.realpath(__file__)) + +class BinaryNinjaDisassemblerInterface(BaseDisassemblerInterface): + # this is implemented, + # however the write API does not seem to work properly here (possibly a bug), + # so this is disabled for now + supports_fake_string_segment: bool = False + + _status: BaseStatusHandler + + _view: BinaryView + _undo_id: str + _components: dict[str, Component] + _type_cache: dict[str, Type] + _function_type_cache: dict[str, Type] + + _address_size: int + _endianness: Literal["little", "big"] + + def __init__(self, status: BaseStatusHandler): + self._status = status + + def _get_or_create_type(self, type: str) -> Type: + if type.startswith("struct "): + type = type[len("struct "):] + elif type.startswith("class "): + type = type[len("class "):] + + if type in self._type_cache: + return self._type_cache[type] + + if type.endswith("*"): + base_type = self._get_or_create_type(type[:-1].strip()) + + parsed = PointerType.create(self._view.arch, base_type) # type: ignore + else: + parsed = self._view.get_type_by_name(type) + if parsed is None: + parsed, errors = self._view.parse_type_string(type) + + self._type_cache[type] = parsed + return parsed + + def get_script_directory(self) -> str: + return CURRENT_PATH + + def on_start(self): + self._view = bv # type: ignore + self._undo_id = self._view.begin_undo_actions() + self._view.set_analysis_hold(True) + self._components = {} + self._type_cache = {} + self._function_type_cache = {} + + self._address_size = self._view.address_size + self._endianness = "little" if self._view.endianness == Endianness.LittleEndian else "big" + + self._status.update_step("Parsing header") + + with open(os.path.join(self.get_script_directory(), "il2cpp.h"), "r") as f: + parsed_types, errors = TypeParser.default.parse_types_from_source( + f.read(), + "il2cpp.h", + self._view.platform if self._view.platform is not None else Platform["windows-x86_64"], + self._view, + [ + "--target=x86_64-pc-linux", + "-x", "c++", + "-D_BINARYNINJA_=1" + ] + ) + + if parsed_types is None: + log_error("Failed to import header") + log_error(errors) + return + + self._status.update_step("Importing header types", len(parsed_types.types)) + + def import_progress_func(progress: int, total: int): + self._status.update_progress(1) + return True + + self._view.define_user_types([(x.name, x.type) for x in parsed_types.types], import_progress_func) + + def on_finish(self): + self._view.commit_undo_actions(self._undo_id) + self._view.set_analysis_hold(False) + self._view.update_analysis() + + def define_function(self, address: int, end: int | None = None): + if self._view.get_function_at(address) is not None: + return + + self._view.create_user_function(address) + + def define_data_array(self, address: int, type: str, count: int): + parsed_type = self._get_or_create_type(type) + array_type = ArrayType.create(parsed_type, count) + var = self._view.get_data_var_at(address) + if var is None: + self._view.define_user_data_var(address, array_type) + else: + var.type = array_type + + def set_data_type(self, address: int, type: str): + var = self._view.get_data_var_at(address) + dtype = self._get_or_create_type(type) + if var is None: + self._view.define_user_data_var(address, dtype) + else: + var.type = dtype + + def set_function_type(self, address: int, type: str): + function = self._view.get_function_at(address) + if function is None: + return + + if type in self._function_type_cache: + function.type = self._function_type_cache[type] # type: ignore + else: + #log_info(f"skipping function type setting for {address}, {type}") + #pass + function.type = type.replace("this", "`this`") + + def set_data_comment(self, address: int, cmt: str): + self._view.set_comment_at(address, cmt) + + def set_function_comment(self, address: int, cmt: str): + function = self._view.get_function_at(address) + if function is None: + return + + function.comment = cmt + + def set_data_name(self, address: int, name: str): + var = self._view.get_data_var_at(address) + if var is None: + return + + if name.startswith("_Z"): + type, demangled = demangle_gnu3(self._view.arch, name, self._view) + var.name = get_qualified_name(demangled) + else: + var.name = name + + def set_function_name(self, address: int, name: str): + function = self._view.get_function_at(address) + if function is None: + return + + if name.startswith("_Z"): + type, demangled = demangle_gnu3(self._view.arch, name, self._view) + function.name = get_qualified_name(demangled) + #function.type = type - this does not work due to the generated types not being namespaced. :( + else: + function.name = name + + def add_cross_reference(self, from_address: int, to_address: int): + self._view.add_user_data_ref(from_address, to_address) + + def import_c_typedef(self, type_def: str): + self._view.define_user_type(None, type_def) + + # optional + def _get_or_create_component(self, name: str): + if name in self._components: + return self._components[name] + + current = name + if current.count("/") != 0: + split_idx = current.rindex("/") + parent, child = current[:split_idx], current[split_idx:] + parent = self._get_or_create_component(name) + component = self._view.create_component(child, parent) + else: + component = self._view.create_component(name) + + self._components[name] = component + return component + + def add_function_to_group(self, address: int, group: str): + return + function = self._view.get_function_at(address) + if function is None: + return + + self._get_or_create_component(group).add_function(function) + + def cache_function_types(self, signatures: list[str]): + function_sigs = set(signatures) + if len(function_sigs) == 0: + return + + typestr = ";\n".join(function_sigs).replace("this", "_this") + ";" + res = self._view.parse_types_from_string(typestr) + for function_sig, function in zip(function_sigs, res.functions.values()): # type: ignore + self._function_type_cache[function_sig] = function + + # only required if supports_fake_string_segment == True + def create_fake_segment(self, name: str, size: int) -> int: + last_end_addr = self._view.mapped_address_ranges[-1].end + if last_end_addr % 0x1000 != 0: + last_end_addr += (0x1000 - (last_end_addr % 0x1000)) + + self._view.add_user_segment(last_end_addr, size, 0, 0, SegmentFlag.SegmentContainsData) + self._view.add_user_section(name, last_end_addr, size, SectionSemantics.ReadOnlyDataSectionSemantics) + return last_end_addr + + def write_string(self, address: int, value: str): + self._view.write(address, value.encode() + b"\x00") + + def write_address(self, address: int, value: int): + self._view.write(address, value.to_bytes(self._address_size, self._endianness)) + + +class BinaryNinjaStatusHandler(BaseStatusHandler): + def __init__(self, thread: BackgroundTaskThread): + self.step = "Initializing" + self.max_items = 0 + self.current_items = 0 + self.start_time = datetime.now() + self.step_start_time = self.start_time + self.last_updated_time = datetime.min + self._thread = thread + + def initialize(self): pass + + def update(self): + if self.was_cancelled(): + raise RuntimeError("Cancelled script.") + + current_time = datetime.now() + if 0.5 > (current_time - self.last_updated_time).total_seconds(): + return + + self.last_updated_time = current_time + + step_time = current_time - self.step_start_time + total_time = current_time - self.start_time + self._thread.progress = f"Processing IL2CPP metadata: {self.step} ({self.current_items}/{self.max_items}), elapsed: {step_time} ({total_time})" + + def update_step(self, step, max_items = 0): + self.step = step + self.max_items = max_items + self.current_items = 0 + self.step_start_time = datetime.now() + self.last_updated_time = datetime.min + self.update() + + def update_progress(self, new_progress = 1): + self.current_items += new_progress + self.update() + + def was_cancelled(self): return False + + def close(self): + pass + +# Entry point +class Il2CppTask(BackgroundTaskThread): + def __init__(self): + BackgroundTaskThread.__init__(self, "Processing IL2CPP metadata...", False) + + def run(self): + status = BinaryNinjaStatusHandler(self) + backend = BinaryNinjaDisassemblerInterface(status) + context = ScriptContext(backend, status) + context.process() + +Il2CppTask().start() \ No newline at end of file diff --git a/Il2CppInspector.Common/Outputs/ScriptResources/Targets/Ghidra.py b/Il2CppInspector.Common/Outputs/ScriptResources/Targets/Ghidra.py index ce897927..85448956 100644 --- a/Il2CppInspector.Common/Outputs/ScriptResources/Targets/Ghidra.py +++ b/Il2CppInspector.Common/Outputs/ScriptResources/Targets/Ghidra.py @@ -1,106 +1,124 @@ # Ghidra-specific implementation from ghidra.app.cmd.function import ApplyFunctionSignatureCmd -from ghidra.app.script import GhidraScriptUtil from ghidra.app.util.cparser.C import CParserUtils from ghidra.program.model.data import ArrayDataType from ghidra.program.model.symbol import SourceType from ghidra.program.model.symbol import RefType from ghidra.app.cmd.label import DemanglerCmd -xrefs = currentProgram.getReferenceManager() - -def set_name(addr, name): - if not name.startswith("_ZN"): - createLabel(toAddr(addr), name, True) - return - cmd = DemanglerCmd(currentAddress.getAddress(hex(addr)), name) - if not cmd.applyTo(currentProgram, monitor): - print("Failed to apply demangled name to %s at %s due %s, falling back to mangled" % (name, hex(addr), cmd.getStatusMsg())) - createLabel(toAddr(addr), name, True) - -def make_function(start, end = None): - addr = toAddr(start) - # Don't override existing functions - fn = getFunctionAt(addr) - if fn is None: - # Create new function if none exists - createFunction(addr, None) - -def make_array(addr, numItems, cppType): - if cppType.startswith('struct '): - cppType = cppType[7:] - - t = getDataTypes(cppType)[0] - a = ArrayDataType(t, numItems, t.getLength()) - addr = toAddr(addr) - removeDataAt(addr) - createData(addr, a) - -def define_code(code): - # Code declarations are not supported in Ghidra - # This only affects string literals for metadata version < 19 - # TODO: Replace with creating a DataType for enums +#try: +# from typing import TYPE_CHECKING +# if TYPE_CHECKING: +# from ..shared_base import BaseStatusHandler, BaseDisassemblerInterface, ScriptContext +# import json +# import os +# import sys +# from datetime import datetime +#except: +# pass + +class GhidraDisassemblerInterface(BaseDisassemblerInterface): + supports_fake_string_segment = False + + def get_script_directory(self) -> str: + return getSourceFile().getParentFile().toString() + + def on_start(self): + self.xrefs = currentProgram.getReferenceManager() + + # Check that the user has parsed the C headers first + if len(getDataTypes('Il2CppObject')) == 0: + print('STOP! You must import the generated C header file (%TYPE_HEADER_RELATIVE_PATH%) before running this script.') + print('See https://github.com/djkaty/Il2CppInspector/blob/master/README.md#adding-metadata-to-your-ghidra-workflow for instructions.') + sys.exit() + + # Ghidra sets the image base for ELF to 0x100000 for some reason + # https://github.com/NationalSecurityAgency/ghidra/issues/1020 + # Make sure that the base address is 0 + # Without this, Ghidra may not analyze the binary correctly and you will just waste your time + # If 0 doesn't work for you, replace it with the base address from the output of the CLI or GUI + if currentProgram.getExecutableFormat().endswith('(ELF)'): + currentProgram.setImageBase(toAddr(0), True) + + # Don't trigger decompiler + setAnalysisOption(currentProgram, "Call Convention ID", "false") + + def on_finish(self): + pass + + def define_function(self, address: int, end: int | None = None): + address = toAddr(address) + # Don't override existing functions + fn = getFunctionAt(address) + if fn is None: + # Create new function if none exists + createFunction(address, None) + + def define_data_array(self, address: int, type: str, count: int): + if type.startswith('struct '): + type = type[7:] + + t = getDataTypes(type)[0] + a = ArrayDataType(t, count, t.getLength()) + address = toAddr(address) + removeDataAt(address) + createData(address, a) + + def set_data_type(self, address: int, type: str): + if type.startswith('struct '): + type = type[7:] + + try: + t = getDataTypes(type)[0] + address = toAddr(address) + removeDataAt(address) + createData(address, t) + except: + print("Failed to set type: %s" % type) + + def set_function_type(self, address: int, type: str): + make_function(address) + typeSig = CParserUtils.parseSignature(None, currentProgram, type) + ApplyFunctionSignatureCmd(toAddr(address), typeSig, SourceType.USER_DEFINED, False, True).applyTo(currentProgram) + + def set_data_comment(self, address: int, cmt: str): + setEOLComment(toAddr(address), cmt) + + def set_function_comment(self, address: int, cmt: str): + setPlateComment(toAddr(address), cmt) + + def set_data_name(self, address: int, name: str): + address = toAddr(address) + + if len(name) > 2000: + print("Name length exceeds 2000 characters, skipping (%s)" % name) + return + + if not name.startswith("_ZN"): + createLabel(address, name, True) + return + + cmd = DemanglerCmd(address, name) + if not cmd.applyTo(currentProgram, monitor): + print(f"Failed to apply demangled name to {name} at {address} due {cmd.getStatusMsg()}, falling back to mangled") + createLabel(address, name, True) + + def set_function_name(self, address: int, name: str): + return self.set_data_name(address, name) + + def add_cross_reference(self, from_address: int, to_address: int): + self.xrefs.addMemoryReference(toAddr(from_address), toAddr(to_address), RefType.DATA, SourceType.USER_DEFINED, 0) + + def import_c_typedef(self, type_def: str): + # Code declarations are not supported in Ghidra + # This only affects string literals for metadata version < 19 + # TODO: Replace with creating a DataType for enums + pass + +class GhidraStatusHandler(BaseStatusHandler): pass -def set_function_type(addr, sig): - make_function(addr) - typeSig = CParserUtils.parseSignature(None, currentProgram, sig) - ApplyFunctionSignatureCmd(toAddr(addr), typeSig, SourceType.USER_DEFINED, False, True).applyTo(currentProgram) - -def set_type(addr, cppType): - if cppType.startswith('struct '): - cppType = cppType[7:] - - try: - t = getDataTypes(cppType)[0] - addr = toAddr(addr) - removeDataAt(addr) - createData(addr, t) - except: - print("Failed to set type: %s" % cppType) - -def set_comment(addr, text): - setEOLComment(toAddr(addr), text) - -def set_header_comment(addr, text): - setPlateComment(toAddr(addr), text) - -def script_prologue(status): - # Check that the user has parsed the C headers first - if len(getDataTypes('Il2CppObject')) == 0: - print('STOP! You must import the generated C header file (%TYPE_HEADER_RELATIVE_PATH%) before running this script.') - print('See https://github.com/djkaty/Il2CppInspector/blob/master/README.md#adding-metadata-to-your-ghidra-workflow for instructions.') - sys.exit() - - # Ghidra sets the image base for ELF to 0x100000 for some reason - # https://github.com/NationalSecurityAgency/ghidra/issues/1020 - # Make sure that the base address is 0 - # Without this, Ghidra may not analyze the binary correctly and you will just waste your time - # If 0 doesn't work for you, replace it with the base address from the output of the CLI or GUI - if currentProgram.getExecutableFormat().endswith('(ELF)'): - currentProgram.setImageBase(toAddr(0), True) - - # Don't trigger decompiler - setAnalysisOption(currentProgram, "Call Convention ID", "false") - -def get_script_directory(): return getSourceFile().getParentFile().toString() - -def script_epilogue(status): pass -def add_function_to_group(addr, group): pass -def add_xref(addr, to): - xrefs.addMemoryReference(currentAddress.getAddress(hex(addr)), currentAddress.getAddress(hex(to)), RefType.DATA, SourceType.USER_DEFINED, 0) - -def process_string_literals(status, data): - for d in jsonData['stringLiterals']: - define_string(d) - - # I don't know how to make inline strings in Ghidra - # Just revert back original impl - addr = parse_address(d) - set_name(addr, d['name']) - set_type(addr, r'struct String *') - set_comment(addr, d['string']) - - status.update_progress() - -class StatusHandler(BaseStatusHandler): pass +status = GhidraStatusHandler() +backend = GhidraDisassemblerInterface() +context = ScriptContext(backend, status) +context.process() \ No newline at end of file diff --git a/Il2CppInspector.Common/Outputs/ScriptResources/Targets/IDA.py b/Il2CppInspector.Common/Outputs/ScriptResources/Targets/IDA.py index ace0f7f9..d73ba13b 100644 --- a/Il2CppInspector.Common/Outputs/ScriptResources/Targets/IDA.py +++ b/Il2CppInspector.Common/Outputs/ScriptResources/Targets/IDA.py @@ -8,6 +8,8 @@ import ida_ida import ida_ua import ida_segment +import ida_funcs +import ida_xref try: # 7.7+ import ida_srclang @@ -23,206 +25,205 @@ except ImportError: FOLDERS_AVAILABLE = False -cached_genflags = 0 -skip_make_function = False -func_dirtree = None -is_32_bit = False -fake_segments_base = None - -def script_prologue(status): - global cached_genflags, skip_make_function, func_dirtree, is_32_bit, fake_segments_base - # Disable autoanalysis - cached_genflags = ida_ida.inf_get_genflags() - ida_ida.inf_set_genflags(cached_genflags & ~ida_ida.INFFL_AUTO) - - # Unload type libraries we know to cause issues - like the c++ linux one - PLATFORMS = ["x86", "x64", "arm", "arm64"] - PROBLEMATIC_TYPELIBS = ["gnulnx"] - - for lib in PROBLEMATIC_TYPELIBS: - for platform in PLATFORMS: - ida_typeinf.del_til(f"{lib}_{platform}") - - # Set name mangling to GCC 3.x and display demangled as default - ida_ida.inf_set_demnames(ida_ida.DEMNAM_GCC3 | ida_ida.DEMNAM_NAME) - - status.update_step('Processing Types') - - if IDACLANG_AVAILABLE: - header_path = os.path.join(get_script_directory(), "%TYPE_HEADER_RELATIVE_PATH%") - ida_srclang.set_parser_argv("clang", "-target x86_64-pc-linux -x c++ -D_IDACLANG_=1") # -target required for 8.3+ - ida_srclang.parse_decls_with_parser("clang", None, header_path, True) - else: - original_macros = ida_typeinf.get_c_macros() - ida_typeinf.set_c_macros(original_macros + ";_IDA_=1") - ida_typeinf.idc_parse_types(os.path.join(get_script_directory(), "%TYPE_HEADER_RELATIVE_PATH%"), ida_typeinf.PT_FILE) - ida_typeinf.set_c_macros(original_macros) - - # Skip make_function on Windows GameAssembly.dll files due to them predefining all functions through pdata which makes the method very slow - skip_make_function = ida_segment.get_segm_by_name(".pdata") is not None - if skip_make_function: - print(".pdata section found, skipping function boundaries") - - if FOLDERS_AVAILABLE: - func_dirtree = ida_dirtree.get_std_dirtree(ida_dirtree.DIRTREE_FUNCS) - - is_32_bit = ida_ida.inf_is_32bit_exactly() +#try: +# from typing import TYPE_CHECKING +# if TYPE_CHECKING: +# from ..shared_base import BaseStatusHandler, BaseDisassemblerInterface, ScriptContext +# import json +# import os +# from datetime import datetime +#except: +# pass + +TINFO_DEFINITE = 0x0001 # These only exist in idc for some reason, so we redefine it here +DEFAULT_TIL: "til_t" = None # type: ignore + +class IDADisassemblerInterface(BaseDisassemblerInterface): + supports_fake_string_segment = True + + _status: BaseStatusHandler + + _type_cache: dict + _folders: list + + _function_dirtree: "ida_dirtree.dirtree_t" + _cached_genflags: int + _skip_function_creation: bool + _is_32_bit: bool + _fake_segments_base: int + + def __init__(self, status: BaseStatusHandler): + self._status = status -def script_epilogue(status): - # Reenable auto-analysis - global cached_genflags - ida_ida.inf_set_genflags(cached_genflags) + self._type_cache = {} + self._folders = [] -# Utility methods + self._cached_genflags = 0 + self._skip_function_creation = False + self._is_32_bit = False + self._fake_segments_base = 0 -def set_name(addr, name): - ida_name.set_name(addr, name, ida_name.SN_NOWARN | ida_name.SN_NOCHECK | ida_name.SN_FORCE) + def _get_type(self, type: str): + if type not in self._type_cache: + info = ida_typeinf.idc_parse_decl(DEFAULT_TIL, type, ida_typeinf.PT_RAWARGS) + if info is None: + print(f"Failed to create type {type}.") + return None -def make_function(start, end = None): - global skip_make_function - if skip_make_function: - return + self._type_cache[type] = info[1:] - ida_bytes.del_items(start, ida_bytes.DELIT_SIMPLE, 12) # Undefine x bytes which should hopefully be enough for the first instruction - ida_ua.create_insn(start) # Create instruction at start - if not ida_funcs.add_func(start, end if end is not None else ida_idaapi.BADADDR): # This fails if the function doesn't start with an instruction - print(f"failed to mark function {hex(start)}-{hex(end) if end is not None else '???'} as function") + return self._type_cache[type] -TYPE_CACHE = {} + def get_script_directory(self) -> str: + return os.path.dirname(os.path.realpath(__file__)) -def get_type(typeName): - if typeName not in TYPE_CACHE: - info = ida_typeinf.idc_parse_decl(None, typeName, ida_typeinf.PT_RAWARGS) - if info is None: - print(f"Failed to create type {typeName}.") - return None + def on_start(self): + # Disable autoanalysis + self._cached_genflags = ida_ida.inf_get_genflags() + ida_ida.inf_set_genflags(self._cached_genflags & ~ida_ida.INFFL_AUTO) - TYPE_CACHE[typeName] = info[1:] + # Unload type libraries we know to cause issues - like the c++ linux one + PLATFORMS = ["x86", "x64", "arm", "arm64"] + PROBLEMATIC_TYPELIBS = ["gnulnx"] - return TYPE_CACHE[typeName] + for lib in PROBLEMATIC_TYPELIBS: + for platform in PLATFORMS: + ida_typeinf.del_til(f"{lib}_{platform}") -TINFO_DEFINITE = 0x0001 # These only exist in idc for some reason, so we redefine it here + # Set name mangling to GCC 3.x and display demangled as default + ida_ida.inf_set_demnames(ida_ida.DEMNAM_GCC3 | ida_ida.DEMNAM_NAME) -def set_type(addr, cppType): - cppType += ';' + self._status.update_step('Processing Types') - info = get_type(cppType) - if info is None: - return + if IDACLANG_AVAILABLE: + header_path = os.path.join(self.get_script_directory(), "%TYPE_HEADER_RELATIVE_PATH%") + ida_srclang.set_parser_argv("clang", "-target x86_64-pc-linux -x c++ -D_IDACLANG_=1") # -target required for 8.3+ + ida_srclang.parse_decls_with_parser("clang", None, header_path, True) + else: + original_macros = ida_typeinf.get_c_macros() + ida_typeinf.set_c_macros(original_macros + ";_IDA_=1") + ida_typeinf.idc_parse_types(os.path.join(self.get_script_directory(), "%TYPE_HEADER_RELATIVE_PATH%"), ida_typeinf.PT_FILE) + ida_typeinf.set_c_macros(original_macros) - if ida_typeinf.apply_type(None, info[0], info[1], addr, TINFO_DEFINITE) is None: - print(f"set_type({hex(addr)}, {cppType}); failed!") + # Skip make_function on Windows GameAssembly.dll files due to them predefining all functions through pdata which makes the method very slow + skip_make_function = ida_segment.get_segm_by_name(".pdata") is not None + if skip_make_function: + print(".pdata section found, skipping function boundaries") -def set_function_type(addr, sig): - set_type(addr, sig) + if FOLDERS_AVAILABLE: + self._function_dirtree = ida_dirtree.get_std_dirtree(ida_dirtree.DIRTREE_FUNCS) -def make_array(addr, numItems, cppType): - set_type(addr, cppType) + self._is_32_bit = ida_ida.inf_is_32bit_exactly() - flags = ida_bytes.get_flags(addr) - if ida_bytes.is_struct(flags): - opinfo = ida_nalt.opinfo_t() - ida_bytes.get_opcode(opinfo, addr, 0, flags) - entrySize = ida_bytes.get_data_elsize(addr, flags, opinfo) - tid = opinfo.tid - else: - entrySize = ida_bytes.get_item_size(addr) - tid = ida_idaapi.BADADDR + def on_finish(self): + ida_ida.inf_set_genflags(self._cached_genflags) - ida_bytes.create_data(addr, flags, numItems * entrySize, tid) + def define_function(self, address: int, end: int | None = None): + if self._skip_function_creation: + return + + ida_bytes.del_items(address, ida_bytes.DELIT_SIMPLE, 12) # Undefine x bytes which should hopefully be enough for the first instruction + ida_ua.create_insn(address) # Create instruction at start + if not ida_funcs.add_func(address, end if end is not None else ida_idaapi.BADADDR): # This fails if the function doesn't start with an instruction + print(f"failed to mark function {hex(address)}-{hex(end) if end is not None else '???'} as function") -def define_code(code): - ida_typeinf.idc_parse_types(code) + def define_data_array(self, address: int, type: str, count: int): + self.set_data_type(address, type) -def set_comment(addr, comment, repeatable = True): - ida_bytes.set_cmt(addr, comment, repeatable) + flags = ida_bytes.get_flags(address) + if ida_bytes.is_struct(flags): + opinfo = ida_nalt.opinfo_t() + ida_bytes.get_opinfo(opinfo, address, 0, flags) + entrySize = ida_bytes.get_data_elsize(address, flags, opinfo) + tid = opinfo.tid + else: + entrySize = ida_bytes.get_item_size(address) + tid = ida_idaapi.BADADDR -def set_header_comment(addr, comment): - func = ida_funcs.get_func(addr) - if func is None: - return + ida_bytes.create_data(address, flags, count * entrySize, tid) - ida_funcs.set_func_cmt(func, comment, True) + def set_data_type(self, address: int, type: str): + type += ';' + + info = self._get_type(type) + if info is None: + return -def get_script_directory(): - return os.path.dirname(os.path.realpath(__file__)) + if ida_typeinf.apply_type(DEFAULT_TIL, info[0], info[1], address, TINFO_DEFINITE) is None: + print(f"set_type({hex(address)}, {type}); failed!") -folders = [] -def add_function_to_group(addr, group): - global func_dirtree, folders - return + def set_function_type(self, address: int, type: str): + self.set_data_type(address, type) - if not FOLDERS_AVAILABLE: - return + def set_data_comment(self, address: int, cmt: str): + ida_bytes.set_cmt(address, cmt, False) - if group not in folders: - folders.append(group) - func_dirtree.mkdir(group) + def set_function_comment(self, address: int, cmt: str): + func = ida_funcs.get_func(address) + if func is None: + return - name = ida_funcs.get_func_name(addr) - func_dirtree.rename(name, f"{group}/{name}") + ida_funcs.set_func_cmt(func, cmt, True) -def add_xref(addr, to): - ida_xref.add_dref(addr, to, ida_xref.XREF_USER | ida_xref.dr_I) + def set_data_name(self, address: int, name: str): + ida_name.set_name(address, name, ida_name.SN_NOWARN | ida_name.SN_NOCHECK | ida_name.SN_FORCE) -def write_string(addr, string): - encoded_string = string.encode() + b'\x00' - string_length = len(encoded_string) - ida_bytes.put_bytes(addr, encoded_string) - ida_bytes.create_strlit(addr, string_length, ida_nalt.STRTYPE_C) + def set_function_name(self, address: int, name: str): + self.set_data_name(address, name) -def write_address(addr, value): - global is_32_bit + def add_cross_reference(self, from_address: int, to_address: int): + ida_xref.add_dref(from_address, to_address, ida_xref.XREF_USER | ida_xref.dr_I) - if is_32_bit: - ida_bytes.put_dword(addr, value) - else: - ida_bytes.put_qword(addr, value) + def import_c_typedef(self, type_def: str): + ida_typeinf.idc_parse_types(type_def, 0) -def create_fake_segment(name, size): - global is_32_bit + # optional + def add_function_to_group(self, address: int, group: str): + if not FOLDERS_AVAILABLE or True: # enable at your own risk - this is slow + return - start = ida_ida.inf_get_max_ea() - end = start + size + if group not in self._folders: + self._folders.append(group) + self._function_dirtree.mkdir(group) - ida_segment.add_segm(0, start, end, name, "DATA") - segment = ida_segment.get_segm_by_name(name) - segment.bitness = 1 if is_32_bit else 2 - segment.perm = ida_segment.SEGPERM_READ - segment.update() + name = ida_funcs.get_func_name(address) + self._function_dirtree.rename(name, f"{group}/{name}") - return start + # only required if supports_fake_string_segment == True + def create_fake_segment(self, name: str, size: int) -> int: + start = ida_ida.inf_get_max_ea() + end = start + size -def process_string_literals(status, data): - total_string_length = 0 - for d in data['stringLiterals']: - total_string_length += len(d["string"]) + 1 - - aligned_length = total_string_length + (4096 - (total_string_length % 4096)) - segment_base = create_fake_segment(".fake_strings", aligned_length) + ida_segment.add_segm(0, start, end, name, "DATA") + segment = ida_segment.get_segm_by_name(name) + segment.bitness = 1 if self._is_32_bit else 2 + segment.perm = ida_segment.SEGPERM_READ + segment.update() - current_string_address = segment_base - for d in data['stringLiterals']: - define_string(d) + return start - ref_addr = parse_address(d) - write_string(current_string_address, d["string"]) - write_address(ref_addr, current_string_address) - set_type(ref_addr, r'const char* const') + def write_string(self, address: int, value: str): + encoded_string = value.encode() + b'\x00' + string_length = len(encoded_string) + ida_bytes.put_bytes(address, encoded_string) + ida_bytes.create_strlit(address, string_length, ida_nalt.STRTYPE_C) - current_string_address += len(d["string"]) + 1 - status.update_progress() + def write_address(self, address: int, value: int): + if self._is_32_bit: + ida_bytes.put_dword(address, value) + else: + ida_bytes.put_qword(address, value) # Status handler -class StatusHandler(BaseStatusHandler): +class IDAStatusHandler(BaseStatusHandler): def __init__(self): self.step = "Initializing" self.max_items = 0 self.current_items = 0 - self.start_time = datetime.datetime.now() + self.start_time = datetime.now() self.step_start_time = self.start_time - self.last_updated_time = datetime.datetime.min + self.last_updated_time = datetime.min def initialize(self): ida_kernwin.show_wait_box("Processing") @@ -231,7 +232,7 @@ def update(self): if self.was_cancelled(): raise RuntimeError("Cancelled script.") - current_time = datetime.datetime.now() + current_time = datetime.now() if 0.5 > (current_time - self.last_updated_time).total_seconds(): return @@ -254,8 +255,8 @@ def update_step(self, step, max_items = 0): self.step = step self.max_items = max_items self.current_items = 0 - self.step_start_time = datetime.datetime.now() - self.last_updated_time = datetime.datetime.min + self.step_start_time = datetime.now() + self.last_updated_time = datetime.min self.update() def update_progress(self, new_progress = 1): @@ -265,5 +266,10 @@ def update_progress(self, new_progress = 1): def was_cancelled(self): return ida_kernwin.user_cancelled() - def close(self): - ida_kernwin.hide_wait_box() \ No newline at end of file + def shutdown(self): + ida_kernwin.hide_wait_box() + +status = IDAStatusHandler() +backend = IDADisassemblerInterface(status) +context = ScriptContext(backend, status) +context.process() \ No newline at end of file diff --git a/Il2CppInspector.Common/Outputs/ScriptResources/shared-main.py b/Il2CppInspector.Common/Outputs/ScriptResources/shared-main.py deleted file mode 100644 index ee02b631..00000000 --- a/Il2CppInspector.Common/Outputs/ScriptResources/shared-main.py +++ /dev/null @@ -1,182 +0,0 @@ -# Shared interface -def from_hex(addr): return int(addr, 0) - -def parse_address(d): return from_hex(d['virtualAddress']) - -def define_il_method(jsonDef): - addr = parse_address(jsonDef) - set_name(addr, jsonDef['name']) - set_function_type(addr, jsonDef['signature']) - set_header_comment(addr, jsonDef['dotNetSignature']) - add_function_to_group(addr, jsonDef['group']) - -def define_il_method_info(jsonDef): - addr = parse_address(jsonDef) - set_name(addr, jsonDef['name']) - set_comment(addr, jsonDef['dotNetSignature']) - set_type(addr, r'struct MethodInfo *') - if 'methodAddress' in jsonDef: - add_xref(from_hex(jsonDef["methodAddress"]), addr) - - -def define_cpp_function(jsonDef): - addr = parse_address(jsonDef) - set_name(addr, jsonDef['name']) - set_function_type(addr, jsonDef['signature']) - -def define_string(jsonDef): - addr = parse_address(jsonDef) - set_name(addr, jsonDef['name']) - set_comment(addr, jsonDef['string']) - -def define_field(addr, name, type, ilType = None): - addr = from_hex(addr) - set_name(addr, name) - set_type(addr, type) - if ilType is not None: - set_comment(addr, ilType) - -def define_field_from_json(jsonDef): - define_field(jsonDef['virtualAddress'], jsonDef['name'], jsonDef['type'], jsonDef['dotNetType']) - -def define_array(jsonDef): - addr = parse_address(jsonDef) - make_array(addr, int(jsonDef['count']), jsonDef['type']) - set_name(addr, jsonDef['name']) - -def define_field_with_value(jsonDef): - addr = parse_address(jsonDef) - set_name(addr, jsonDef['name']) - set_comment(addr, jsonDef['value']) - -# Process JSON -def process_json(jsonData, status): - # Function boundaries - functionAddresses = jsonData['functionAddresses'] - functionAddresses.sort() - count = len(functionAddresses) - - status.update_step('Processing function boundaries', count) - for i in range(count): - start = from_hex(functionAddresses[i]) - if start == 0: - status.update_progress() - continue - - end = from_hex(functionAddresses[i + 1]) if i + 1 != count else None - - make_function(start, end) - status.update_progress() - - # Method definitions - status.update_step('Processing method definitions', len(jsonData['methodDefinitions'])) - for d in jsonData['methodDefinitions']: - define_il_method(d) - status.update_progress() - - # Constructed generic methods - status.update_step('Processing constructed generic methods', len(jsonData['constructedGenericMethods'])) - for d in jsonData['constructedGenericMethods']: - define_il_method(d) - status.update_progress() - - # Custom attributes generators - status.update_step('Processing custom attributes generators', len(jsonData['customAttributesGenerators'])) - for d in jsonData['customAttributesGenerators']: - define_cpp_function(d) - status.update_progress() - - # Method.Invoke thunks - status.update_step('Processing Method.Invoke thunks', len(jsonData['methodInvokers'])) - for d in jsonData['methodInvokers']: - define_cpp_function(d) - status.update_progress() - - # String literals for version >= 19 - if 'virtualAddress' in jsonData['stringLiterals'][0]: - status.update_step('Processing string literals (V19+)', len(jsonData['stringLiterals'])) - - process_string_literals(status, jsonData) - - # String literals for version < 19 - else: - status.update_step('Processing string literals (pre-V19)') - litDecl = 'enum StringLiteralIndex {\n' - for d in jsonData['stringLiterals']: - litDecl += " " + d['name'] + ",\n" - litDecl += '};\n' - define_code(litDecl) - - # Il2CppClass (TypeInfo) pointers - status.update_step('Processing Il2CppClass (TypeInfo) pointers', len(jsonData['typeInfoPointers'])) - for d in jsonData['typeInfoPointers']: - define_field_from_json(d) - status.update_progress() - - # Il2CppType (TypeRef) pointers - status.update_step('Processing Il2CppType (TypeRef) pointers', len(jsonData['typeRefPointers'])) - for d in jsonData['typeRefPointers']: - define_field(d['virtualAddress'], d['name'], r'struct Il2CppType *', d['dotNetType']) - status.update_progress() - - # MethodInfo pointers - status.update_step('Processing MethodInfo pointers', len(jsonData['methodInfoPointers'])) - for d in jsonData['methodInfoPointers']: - define_il_method_info(d) - status.update_progress() - - # FieldInfo pointers, add the contents as a comment - status.update_step('Processing FieldInfo pointers', len(jsonData['fields'])) - for d in jsonData['fields']: - define_field_with_value(d) - status.update_progress() - - # FieldRva pointers, add the contents as a comment - status.update_step('Processing FieldRva pointers', len(jsonData['fieldRvas'])) - for d in jsonData['fieldRvas']: - define_field_with_value(d) - status.update_progress() - - # IL2CPP type metadata - status.update_step('Processing IL2CPP type metadata', len(jsonData['typeMetadata'])) - for d in jsonData['typeMetadata']: - define_field(d['virtualAddress'], d['name'], d['type']) - - # IL2CPP function metadata - status.update_step('Processing IL2CPP function metadata', len(jsonData['functionMetadata'])) - for d in jsonData['functionMetadata']: - define_cpp_function(d) - - # IL2CPP array metadata - status.update_step('Processing IL2CPP array metadata', len(jsonData['arrayMetadata'])) - for d in jsonData['arrayMetadata']: - define_array(d) - - # IL2CPP API functions - status.update_step('Processing IL2CPP API functions', len(jsonData['apis'])) - for d in jsonData['apis']: - define_cpp_function(d) - -# Entry point -print('Generated script file by Il2CppInspectorRedux - https://github.com/LukeFZ (Original Il2CppInspector by http://www.djkaty.com - https://github.com/djkaty)') -status = StatusHandler() -status.initialize() - -try: - start_time = datetime.datetime.now() - - status.update_step("Running script prologue") - script_prologue(status) - - with open(os.path.join(get_script_directory(), "%JSON_METADATA_RELATIVE_PATH%"), "r") as jsonFile: - status.update_step("Loading JSON metadata") - jsonData = json.load(jsonFile)['addressMap'] - process_json(jsonData, status) - - status.update_step("Running script epilogue") - script_epilogue(status) - - status.update_step('Script execution complete.') - print("Took: %s" % (datetime.datetime.now() - start_time)) -except RuntimeError: pass -finally: status.close() diff --git a/Il2CppInspector.Common/Outputs/ScriptResources/shared-preamble.py b/Il2CppInspector.Common/Outputs/ScriptResources/shared-preamble.py deleted file mode 100644 index 7310b918..00000000 --- a/Il2CppInspector.Common/Outputs/ScriptResources/shared-preamble.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated script file by Il2CppInspectorRedux - https://github.com/LukeFZ (Original Il2CppInspector by http://www.djkaty.com - https://github.com/djkaty) -# Target Unity version: %TARGET_UNITY_VERSION% - -import json -import os -import sys -import datetime - -class BaseStatusHandler: - def initialize(self): pass - def update_step(self, name, max_items = 0): print(name) - def update_progress(self, progress = 1): pass - def was_cancelled(self): return False - def close(self): pass diff --git a/Il2CppInspector.Common/Outputs/ScriptResources/shared_base.py b/Il2CppInspector.Common/Outputs/ScriptResources/shared_base.py new file mode 100644 index 00000000..e5ca8daf --- /dev/null +++ b/Il2CppInspector.Common/Outputs/ScriptResources/shared_base.py @@ -0,0 +1,290 @@ +# Generated script file by Il2CppInspectorRedux - https://github.com/LukeFZ (Original Il2CppInspector by http://www.djkaty.com - https://github.com/djkaty) +# Target Unity version: %TARGET_UNITY_VERSION% + +import json +import os +from datetime import datetime +import abc + +class BaseStatusHandler(abc.ABC): + def initialize(self): pass + def shutdown(self): pass + + def update_step(self, name: str, max_items: int = 0): print(name) + def update_progress(self, progress: int = 1): pass + + def was_cancelled(self): return False + +class BaseDisassemblerInterface(abc.ABC): + supports_fake_string_segment: bool = False + + @abc.abstractmethod + def get_script_directory(self) -> str: return "" + + @abc.abstractmethod + def on_start(self): pass + + @abc.abstractmethod + def on_finish(self): pass + + @abc.abstractmethod + def define_function(self, address: int, end: int | None = None): pass + + @abc.abstractmethod + def define_data_array(self, address: int, type: str, count: int): pass + + @abc.abstractmethod + def set_data_type(self, address: int, type: str): pass + + @abc.abstractmethod + def set_function_type(self, address: int, type: str): pass + + @abc.abstractmethod + def set_data_comment(self, address: int, cmt: str): pass + + @abc.abstractmethod + def set_function_comment(self, address: int, cmt: str): pass + + @abc.abstractmethod + def set_data_name(self, address: int, name: str): pass + + @abc.abstractmethod + def set_function_name(self, address: int, name: str): pass + + @abc.abstractmethod + def add_cross_reference(self, from_address: int, to_address: int): pass + + @abc.abstractmethod + def import_c_typedef(self, type_def: str): pass + + # optional + def add_function_to_group(self, address: int, group: str): pass + def cache_function_types(self, function_types: list[str]): pass + + # only required if supports_fake_string_segment == True + def create_fake_segment(self, name: str, size: int) -> int: return 0 + + def write_string(self, address: int, value: str): pass + def write_address(self, address: int, value: int): pass + +class ScriptContext: + _backend: BaseDisassemblerInterface + _status: BaseStatusHandler + + def __init__(self, backend: BaseDisassemblerInterface, status: BaseStatusHandler) -> None: + self._backend = backend + self._status = status + + def from_hex(self, addr: str): + return int(addr, 0) + + def parse_address(self, d: dict): + return self.from_hex(d['virtualAddress']) + + def define_il_method(self, definition: dict): + addr = self.parse_address(definition) + self._backend.set_function_name(addr, definition['name']) + self._backend.set_function_type(addr, definition['signature']) + self._backend.set_function_comment(addr, definition['dotNetSignature']) + self._backend.add_function_to_group(addr, definition['group']) + + def define_il_method_info(self, definition: dict): + addr = self.parse_address(definition) + self._backend.set_data_type(addr, r'struct MethodInfo *') + self._backend.set_data_name(addr, definition['name']) + self._backend.set_data_comment(addr, definition['dotNetSignature']) + if 'methodAddress' in definition: + method_addr = self.from_hex(definition["methodAddress"]) + self._backend.add_cross_reference(method_addr, addr) + + def define_cpp_function(self, definition: dict): + addr = self.parse_address(definition) + self._backend.set_function_name(addr, definition['name']) + self._backend.set_function_type(addr, definition['signature']) + + def define_string(self, definition: dict): + addr = self.parse_address(definition) + self._backend.set_data_type(addr, r'struct String *') + self._backend.set_data_name(addr, definition['name']) + self._backend.set_data_comment(addr, definition['string']) + + def define_field(self, addr: str, name: str, type: str, il_type: str | None = None): + address = self.from_hex(addr) + self._backend.set_data_type(address, type) + self._backend.set_data_name(address, name) + if il_type is not None: + self._backend.set_data_comment(address, il_type) + + def define_field_from_json(self, definition: dict): + self.define_field(definition['virtualAddress'], definition['name'], definition['type'], definition['dotNetType']) + + def define_array(self, definition: dict): + addr = self.parse_address(definition) + self._backend.define_data_array(addr, definition['type'], int(definition['count'])) + self._backend.set_data_name(addr, definition['name']) + + def define_field_with_value(self, definition: dict): + addr = self.parse_address(definition) + self._backend.set_data_name(addr, definition['name']) + self._backend.set_data_comment(addr, definition['value']) + + def process_metadata(self, metadata: dict): + # Function boundaries + function_addresses = metadata['functionAddresses'] + function_addresses.sort() + count = len(function_addresses) + + self._status.update_step('Processing function boundaries', count) + for i in range(count): + start = self.from_hex(function_addresses[i]) + if start == 0: + self._status.update_progress() + continue + + end = self.from_hex(function_addresses[i + 1]) if i + 1 != count else None + + self._backend.define_function(start, end) + self._status.update_progress() + + # Method definitions + self._status.update_step('Processing method definitions', len(metadata['methodDefinitions'])) + self._backend.cache_function_types([x["signature"] for x in metadata['methodDefinitions']]) + for d in metadata['methodDefinitions']: + self.define_il_method(d) + self._status.update_progress() + + # Constructed generic methods + self._status.update_step('Processing constructed generic methods', len(metadata['constructedGenericMethods'])) + self._backend.cache_function_types([x["signature"] for x in metadata['constructedGenericMethods']]) + for d in metadata['constructedGenericMethods']: + self.define_il_method(d) + self._status.update_progress() + + # Custom attributes generators + self._status.update_step('Processing custom attributes generators', len(metadata['customAttributesGenerators'])) + self._backend.cache_function_types([x["signature"] for x in metadata['customAttributesGenerators']]) + for d in metadata['customAttributesGenerators']: + self.define_cpp_function(d) + self._status.update_progress() + + # Method.Invoke thunks + self._status.update_step('Processing Method.Invoke thunks', len(metadata['methodInvokers'])) + self._backend.cache_function_types([x["signature"] for x in metadata['methodInvokers']]) + for d in metadata['methodInvokers']: + self.define_cpp_function(d) + self._status.update_progress() + + # String literals for version >= 19 + if 'virtualAddress' in metadata['stringLiterals'][0]: + self._status.update_step('Processing string literals (V19+)', len(metadata['stringLiterals'])) + + if self._backend.supports_fake_string_segment: + total_string_length = 0 + for d in metadata['stringLiterals']: + total_string_length += len(d["string"]) + 1 + + aligned_length = total_string_length + (4096 - (total_string_length % 4096)) + segment_base = self._backend.create_fake_segment(".fake_strings", aligned_length) + + current_string_address = segment_base + for d in metadata['stringLiterals']: + self.define_string(d) + + ref_addr = self.parse_address(d) + self._backend.write_string(current_string_address, d["string"]) + self._backend.set_data_type(ref_addr, r'const char* const') + self._backend.write_address(ref_addr, current_string_address) + + current_string_address += len(d["string"]) + 1 + self._status.update_progress() + else: + for d in metadata['stringLiterals']: + self.define_string(d) + self._status.update_progress() + + # String literals for version < 19 + else: + self._status.update_step('Processing string literals (pre-V19)') + litDecl = 'enum StringLiteralIndex {\n' + for d in metadata['stringLiterals']: + litDecl += " " + d['name'] + ",\n" + litDecl += '};\n' + + self._backend.import_c_typedef(litDecl) + + # Il2CppClass (TypeInfo) pointers + self._status.update_step('Processing Il2CppClass (TypeInfo) pointers', len(metadata['typeInfoPointers'])) + for d in metadata['typeInfoPointers']: + self.define_field_from_json(d) + self._status.update_progress() + + # Il2CppType (TypeRef) pointers + self._status.update_step('Processing Il2CppType (TypeRef) pointers', len(metadata['typeRefPointers'])) + for d in metadata['typeRefPointers']: + self.define_field(d['virtualAddress'], d['name'], r'struct Il2CppType *', d['dotNetType']) + self._status.update_progress() + + # MethodInfo pointers + self._status.update_step('Processing MethodInfo pointers', len(metadata['methodInfoPointers'])) + for d in metadata['methodInfoPointers']: + self.define_il_method_info(d) + self._status.update_progress() + + # FieldInfo pointers, add the contents as a comment + self._status.update_step('Processing FieldInfo pointers', len(metadata['fields'])) + for d in metadata['fields']: + self.define_field_with_value(d) + self._status.update_progress() + + # FieldRva pointers, add the contents as a comment + self._status.update_step('Processing FieldRva pointers', len(metadata['fieldRvas'])) + for d in metadata['fieldRvas']: + self.define_field_with_value(d) + self._status.update_progress() + + # IL2CPP type metadata + self._status.update_step('Processing IL2CPP type metadata', len(metadata['typeMetadata'])) + for d in metadata['typeMetadata']: + self.define_field(d['virtualAddress'], d['name'], d['type']) + + # IL2CPP function metadata + self._status.update_step('Processing IL2CPP function metadata', len(metadata['functionMetadata'])) + for d in metadata['functionMetadata']: + self.define_cpp_function(d) + + # IL2CPP array metadata + self._status.update_step('Processing IL2CPP array metadata', len(metadata['arrayMetadata'])) + for d in metadata['arrayMetadata']: + self.define_array(d) + + # IL2CPP API functions + self._status.update_step('Processing IL2CPP API functions', len(metadata['apis'])) + self._backend.cache_function_types([x["signature"] for x in metadata['apis']]) + for d in metadata['apis']: + self.define_cpp_function(d) + + def process(self): + self._status.initialize() + + try: + start_time = datetime.now() + + self._status.update_step("Running script prologue") + self._backend.on_start() + + metadata_path = os.path.join(self._backend.get_script_directory(), "%JSON_METADATA_RELATIVE_PATH%") + with open(metadata_path, "r") as f: + self._status.update_step("Loading JSON metadata") + metadata = json.load(f)['addressMap'] + self.process_metadata(metadata) + + self._status.update_step("Running script epilogue") + self._backend.on_finish() + + self._status.update_step('Script execution complete.') + + end_time = datetime.now() + print(f"Took: {end_time - start_time}") + + except RuntimeError: pass + finally: self._status.shutdown() \ No newline at end of file diff --git a/README.md b/README.md index 2d8dbb28..d30dbe64 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ This is a continuation of [Il2CppInspector, by djkaty](https://github.com/djkaty - Automatic unloading of conflicting type libraries - Addition of custom fake string segment to show string literal contents in decompiler - A fake xref between MethodInfo instances and their corresponding method to quickly get the correct function +* Binary Ninja script output, with all of the IDA-exclusive features ### Main features @@ -36,7 +37,7 @@ This is a continuation of [Il2CppInspector, by djkaty](https://github.com/djkaty * Create **[C++ scaffolding](#creating-c-scaffolding-or-a-dll-injection-project)** for all types, methods, function pointers and API functions in an IL2CPP application for use in x64dbg, Cydia Substrate etc. -* Create **[IDA](#adding-metadata-to-your-ida-workflow) and [Ghidra](#adding-metadata-to-your-ghidra-workflow) Python scripts** to populate symbol, function and type information; includes API hooks to [implement scripts for other targets](#extending-il2cppinspectors-python-output-to-support-other-targets) +* Create **[IDA](#adding-metadata-to-your-ida-workflow), [Ghidra](#adding-metadata-to-your-ghidra-workflow) or [Binary Ninja](#adding-metadata-to-your-binary-ninja-workflow) Python scripts** to populate symbol, function and type information; includes API hooks to [implement scripts for other targets](#extending-il2cppinspectors-python-output-to-support-other-targets) * Create Visual Studio **[C++ DLL injection projects](#dll-injection-workflow)** directly from IL2CPP files @@ -68,7 +69,6 @@ This is a continuation of [Il2CppInspector, by djkaty](https://github.com/djkaty * Tested with [every release of IL2CPP](#version-support) since Unity 5.3.0 - ### Tutorials and Guides You can read more about how IL2CPP works in my series IL2CPP Reverse Engineering: @@ -124,7 +124,7 @@ Nice to have: * Automatically defeats certain basic obfuscation methods * Test chassis for automated integration testing of IL2CPP binaries -Class library targets .NET 8. Built with Visual Studio 2019. +Class library targets .NET 8. Built with Visual Studio 2022. **NOTE**: Il2CppInspector is not a decompiler. It can provide you with the structure of an application and function addresses for every method so that you can easily jump straight to methods of interest in your disassembler. It does not attempt to recover the entire source code of the application. @@ -132,7 +132,7 @@ Class library targets .NET 8. Built with Visual Studio 2019. ``` git clone --recursive https://github.com/LukeFZ/Il2CppInspectorRedux -cd Il2CppInspector +cd Il2CppInspectorRedux ``` ##### Windows @@ -181,9 +181,9 @@ Get all current plugins (optional): For other operating systems supporting .NET Core, add `-r xxx` to the final command where `xxx` is a RID from https://docs.microsoft.com/en-us/dotnet/articles/core/rid-catalog -The output binary for command-line usage is placed in `Il2CppInspector/Il2CppInspector.CLI/bin/Release/net8.0/[win|osx|linux]-x64/publish/Il2CppInspector.exe`. +The output binary for command-line usage is placed in `Il2CppInspectorRedux/Il2CppInspector.CLI/bin/Release/net8.0/[win|osx|linux]-x64/publish/Il2CppInspector.exe`. -The output binary for Windows GUI is placed in `Il2CppInspector/Il2CppInspector.GUI/bin/Release/net8.0-windows/win-x64/publish/Il2CppInspector.exe`. +The output binary for Windows GUI is placed in `Il2CppInspectorRedux/Il2CppInspector.GUI/bin/Release/net8.0-windows/win-x64/publish/Il2CppInspector.exe`. The `plugins` folder should be placed in the same folder as `Il2CppInspector.exe`. @@ -389,6 +389,12 @@ Example Ghidra C++ decompilation after applying Il2CppInspector: ![Il2CppInspector annotated Ghidra project](docs/Ghidra_Preview.png) +### Adding metadata to your Binary Ninja workflow + +Import your binary into Binary Ninja, and let the initial analysis complete. +Then run the generated *il2cpp.py* using the "File > Run script..." menu option. +You can view the current script progress in the bottom left corner, alongside the total elapsed time. + ### Creating C++ scaffolding or a DLL injection project Il2CppInspector generates a series of C++ source files which you can use in a variety of ways, for example: @@ -706,23 +712,14 @@ You can find out more about plugins, and browse the source code of current plugi ### Extending Il2CppInspector's Python output to support other targets -The current version of Il2CppInspector can output Python scripts targeting the IDA and Ghidra disassemblers. - -When Il2CppInspector generates such a script, it generates a concatenation of a shared block of code (`Outputs/ScriptResources/shared-main.py`) which parses the JSON metadata and dispatches it to a set of implementation-specific functions to be processed, and a block of code specific to the target application which implements these functions (a file from `Outputs/ScriptResources/Targets`). - -If you would like to add support for a new target application, create a new Python script in `Outputs/ScriptResources/Targets` with the nane `.py` and implement the following functions: +The current version of Il2CppInspector can output Python scripts targeting the IDA, Ghidra and Binary Ninja disassemblers. -- `CustomInitializer()` - perform any custom initialization required for the target before applying the metadata -- `DefineCode(code)` - parse and apply the specified C++ declaration text (this is not required for Unity 5.3.2 and later; if you don't need to support earlier versions, just specify `pass` as the implementation) -- `GetScriptDirectory()` - retrieve the directory that the Python script is running in. This will normally be `os.path.dirname(os.path.realpath(__file__))` -- `MakeFunction(start, name=None)` - define address `start` as the start of a function, optionally with name `name` -- `SetComment(addr, text)` - place the comment `text` at address `addr` -- `SetFunctionType(addr, sig)` - parse the C++ function signature in `sig` and apply it to the function at address `addr` -- `SetHeaderComment(addr, text)` - place the header/plate/pre-item comment `text` at address `addr` -- `SetName(addr, name)` - set the symbol (or label or name) of address `addr` to `name` -- `SetType(addr, type)` - define address `addr` to be an item of the C++ type `type` +When Il2CppInspector generates such a script, it generates a concatenation of a shared block of code (`Outputs/ScriptResources/shared_base_.py`) which parses the JSON metadata and dispatches it to a set of implementation-specific functions to be processed, and a block of code specific to the target application which implements these functions (a file from `Outputs/ScriptResources/Targets`). -Refer to the source code of `IDA.py` and `Ghidra.py` for examples. +If you would like to add support for a new target application, create a new Python script in `Outputs/ScriptResources/Targets` with the nane `.py` and implement the *BaseDisassemblerInterface* class. +If you also want to have an updating status display, you also need to implement the *BaseStatusHandler* class. +Your target implementation also needs to dispatch analysis at the end, by constructing a *ScriptContext* instance with your disassembler and status implementations, then calling the `process` methon it. +For a simple version of this you can view the *IDA* and *Ghidra* targets, and the *BinaryNinja* one for a more specific analysis dispatch. When you add a new target and re-compile Il2CppInspector: @@ -815,6 +812,7 @@ The following books and documents were also very helpful: - [ARM Architecture Reference Manual ARMv8-A](https://developer.arm.com/docs/ddi0487/latest) - [Intel 64 and IA-32 Architectures Software Developer's Manual](https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf) - [Ghidra API documentation](https://ghidra.re/ghidra_docs/api/) +- [Binary Ninja API documentation](https://docs.binary.ninja/dev) Pizza spinner animation in the GUI made by Chris Gannon - https://gannon.tv/ From 08431b774a4cf41e243809e117d2ee0874186291 Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Fri, 8 Nov 2024 23:33:13 +0100 Subject: [PATCH 19/27] Fix metadata usage issues caused by it being a value type now --- .../IL2CPP/Il2CppInspector.cs | 17 ++++++++--------- Il2CppInspector.Common/IL2CPP/MetadataUsage.cs | 4 +--- Il2CppInspector.Common/Model/AppModel.cs | 3 ++- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs b/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs index b0ed4014..3ffc7491 100644 --- a/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs +++ b/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs @@ -108,13 +108,13 @@ private List buildMetadataUsages() return buildLateBindingMetadataUsages(); // Version >= 19 && < 27 - var usages = new Dictionary(); + var usages = new Dictionary(); foreach (var metadataUsageList in Metadata.MetadataUsageLists) { for (var i = 0; i < metadataUsageList.Count; i++) { var metadataUsagePair = Metadata.MetadataUsagePairs[metadataUsageList.Start + i]; - usages.TryAdd(metadataUsagePair.DestinationIndex, MetadataUsage.FromEncodedIndex(this, metadataUsagePair.EncodedSourceIndex)); + usages.TryAdd(metadataUsagePair.DestinationIndex, metadataUsagePair.EncodedSourceIndex); } } @@ -123,10 +123,12 @@ private List buildMetadataUsages() // so we have to calculate the correct number of usages above before reading the usage address list from the binary var count = usages.Keys.Max() + 1; var addresses = Binary.Image.ReadMappedUWordArray(Binary.MetadataRegistration.MetadataUsages, (int) count); - foreach (var usage in usages) - usage.Value.SetAddress(addresses[usage.Key]); - return usages.Values.ToList(); + var metadataUsages = new List(); + foreach (var (index, encodedUsage) in usages) + metadataUsages.Add(MetadataUsage.FromEncodedIndex(this, encodedUsage, addresses[index])); + + return metadataUsages; } private List buildLateBindingMetadataUsages() @@ -149,10 +151,7 @@ private List buildLateBindingMetadataUsages() if (CheckMetadataUsageSanity(usage) && BinaryImage.TryMapFileOffsetToVA(i * ((uint)BinaryImage.Bits / 8), out var va)) - { - usage.SetAddress(va); - usages.Add(usage); - } + usages.Add(MetadataUsage.FromEncodedIndex(this, encodedToken, va)); } } diff --git a/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs b/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs index 5123ab4c..e5b86eb7 100644 --- a/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs +++ b/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs @@ -25,7 +25,7 @@ public record struct MetadataUsage { public MetadataUsageType Type { get; } public int SourceIndex { get; } - public ulong VirtualAddress { get; private set; } + public ulong VirtualAddress { get; } public readonly bool IsValid => Type != 0; @@ -55,7 +55,5 @@ public static MetadataUsage FromEncodedIndex(Il2CppInspector package, uint encod } return new MetadataUsage(usageType, (int)index, virtualAddress); } - - public void SetAddress(ulong virtualAddress) => VirtualAddress = virtualAddress; } } \ No newline at end of file diff --git a/Il2CppInspector.Common/Model/AppModel.cs b/Il2CppInspector.Common/Model/AppModel.cs index 1f3b3e36..923e6ee3 100644 --- a/Il2CppInspector.Common/Model/AppModel.cs +++ b/Il2CppInspector.Common/Model/AppModel.cs @@ -105,7 +105,8 @@ private string Group { } // Initialize - public AppModel(TypeModel model, bool makeDefaultBuild = true) { + public AppModel(TypeModel model, bool makeDefaultBuild = true) + { // Save .NET type model TypeModel = model; From e0e8d052eafe04ac27d98bd1f4e8ce7b69da2423 Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Sat, 9 Nov 2024 15:43:22 +0100 Subject: [PATCH 20/27] make TryMapVATR overrideable and implement it for ELFs --- .../FileFormatStreams/ElfReader.cs | 21 +++++++++++++++++-- .../FileFormatStreams/FileFormatStream.cs | 2 +- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Il2CppInspector.Common/FileFormatStreams/ElfReader.cs b/Il2CppInspector.Common/FileFormatStreams/ElfReader.cs index cab3c399..62d56547 100644 --- a/Il2CppInspector.Common/FileFormatStreams/ElfReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/ElfReader.cs @@ -528,11 +528,28 @@ public override IEnumerable
GetSections() { // Note if uiAddr is a valid segment but filesz < memsz and the adjusted uiAddr falls between the range of filesz and memsz, // an exception will be thrown. This area of memory is assumed to contain all zeroes. public override uint MapVATR(ulong uiAddr) { + // Additions in the argument to MapVATR may cause an overflow which should be discarded for 32-bit files + if (!TryMapVATR(uiAddr, out var offset)) + throw new InvalidOperationException("Failed to map virtual address"); + + return offset; + } + + public override bool TryMapVATR(ulong uiAddr, out uint fileOffset) + { // Additions in the argument to MapVATR may cause an overflow which should be discarded for 32-bit files if (Bits == 32) uiAddr &= 0xffff_ffff; - var program_header_table = this.PHT.First(x => uiAddr >= conv.ULong(x.p_vaddr) && uiAddr <= conv.ULong(conv.Add(x.p_vaddr, x.p_filesz))); - return (uint) (uiAddr - conv.ULong(conv.Sub(program_header_table.p_vaddr, program_header_table.p_offset))); + + var phtEntry = PHT.FirstOrDefault(x => uiAddr >= conv.ULong(x.p_vaddr) && uiAddr <= conv.ULong(conv.Add(x.p_vaddr, x.p_filesz))); + if (phtEntry == null) + { + fileOffset = 0; + return false; + } + + fileOffset = (uint)(uiAddr - conv.ULong(conv.Sub(phtEntry.p_vaddr, phtEntry.p_offset))); + return true; } public override ulong MapFileOffsetToVA(uint offset) { diff --git a/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs b/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs index f7db93fa..22a61f16 100644 --- a/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs +++ b/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs @@ -284,7 +284,7 @@ public bool TryGetSections(out IEnumerable
sections) { public virtual uint MapVATR(ulong uiAddr) => (uint) uiAddr; // Try to map an RVA to an offset in the file image - public bool TryMapVATR(ulong uiAddr, out uint fileOffset) { + public virtual bool TryMapVATR(ulong uiAddr, out uint fileOffset) { try { fileOffset = MapVATR(uiAddr); return true; From 3982e5fd9929a20bd15afb37e668ec197054d4c8 Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Sat, 9 Nov 2024 15:43:39 +0100 Subject: [PATCH 21/27] Make field offset reading use TryMapVATR to reduce exceptions --- .../IL2CPP/Il2CppInspector.cs | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs b/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs index 3ffc7491..9ee970f0 100644 --- a/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs +++ b/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs @@ -203,19 +203,21 @@ public Il2CppInspector(Il2CppBinary binary, Metadata metadata) { for (var i = 0; i < TypeDefinitions.Length; i++) { var def = TypeDefinitions[i]; var pFieldOffsets = Binary.FieldOffsetPointers[i]; - if (pFieldOffsets != 0) { - bool available = true; - + if (pFieldOffsets != 0) + { // If the target address range is not mapped in the file, assume zeroes - try { - BinaryImage.Position = BinaryImage.MapVATR((ulong) pFieldOffsets); + if (BinaryImage.TryMapVATR((ulong)pFieldOffsets, out var fieldOffsetPosition)) + { + BinaryImage.Position = fieldOffsetPosition; + var fieldOffsets = BinaryImage.ReadArray(def.FieldCount); + for (var fieldIndex = 0; fieldIndex < def.FieldCount; fieldIndex++) + offsets.Add(def.FieldIndex + fieldIndex, fieldOffsets[fieldIndex]); } - catch (InvalidOperationException) { - available = false; + else + { + for (var fieldIndex = 0; fieldIndex < def.FieldCount; fieldIndex++) + offsets.Add(def.FieldIndex + fieldIndex, 0); } - - for (var f = 0; f < def.FieldCount; f++) - offsets.Add(def.FieldIndex + f, available? BinaryImage.ReadUInt32() : 0); } } From bcbf4f47e2df80f73ddf593e9b199c5a372b8c62 Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Sat, 9 Nov 2024 15:44:11 +0100 Subject: [PATCH 22/27] Fix NRE in Assembly ctor on < v24.2 --- Il2CppInspector.Common/Reflection/Assembly.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Il2CppInspector.Common/Reflection/Assembly.cs b/Il2CppInspector.Common/Reflection/Assembly.cs index 30b0b101..260a5801 100644 --- a/Il2CppInspector.Common/Reflection/Assembly.cs +++ b/Il2CppInspector.Common/Reflection/Assembly.cs @@ -71,8 +71,9 @@ public Assembly(TypeModel model, int imageIndex) { // TODO: Generate EntryPoint method from entryPointIndex } - // Find corresponding module (we'll need this for method pointers) - ModuleDefinition = Model.Package.Modules[ShortName]; + // Find corresponding module (we'll need this for method pointers on V24.2+) + if (Model.Package.Modules != null) + ModuleDefinition = Model.Package.Modules[ShortName]; // Generate types in DefinedTypes from typeStart to typeStart+typeCount-1 for (var t = ImageDefinition.TypeStart; t < ImageDefinition.TypeStart + ImageDefinition.TypeCount; t++) { From 5cd94784f56f40d2f21ebe46f79912d5fc3d952e Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Thu, 14 Nov 2024 07:42:27 +0100 Subject: [PATCH 23/27] Update actions workflow to produce cross-platform CLI binaries, update readme to reflect .net 9 changes --- .github/workflows/build.yml | 76 +++++++++++++++++++++++-------------- README.md | 6 +-- 2 files changed, 51 insertions(+), 31 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 98dc063b..d0dfe0bf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,21 +1,20 @@ name: Il2CppInspectorRedux Build -on: - push: - branches: [ master ] - - workflow_dispatch: +on: [push, workflow_dispatch] jobs: - build: + build-gui: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true - - uses: microsoft/setup-msbuild@v1.1 + - name: Setup .NET SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '9.0.x' - uses: actions/cache@v3 with: @@ -25,33 +24,54 @@ jobs: ${{ runner.os }}-nuget- - name: Restore NuGet packages - run: nuget restore + run: dotnet restore -r net9.0-windows - name: Build GUI - run: msbuild /t:Il2CppInspector_GUI:publish /p:Configuration=Release /p:Platform="Any CPU" /p:TargetFramework=net9.0-windows /p:SelfContained=false /verbosity:minimal - - - name: Build CLI - run: msbuild /t:Il2CppInspector_CLI:publish /p:Configuration=Release /p:Platform="Any CPU" /p:TargetFramework=net9.0 /p:SelfContained=false /verbosity:minimal - - - name: Add Plugins folder (GUI) - shell: pwsh - working-directory: Il2CppInspector.GUI/bin/Release/net8.0-windows/win-x64/publish - run: ../../../../../../get-plugins.ps1 - - - name: Add Plugins folder (CLI) - shell: pwsh - working-directory: Il2CppInspector.CLI/bin/Release/net8.0/win-x64/publish - run: ../../../../../../get-plugins.ps1 + run: dotnet publish ./Il2CppInspector.GUI/Il2CppInspector.GUI.csproj -c Release -r net9.0-windows --no-self-contained - name: Upload GUI Artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Il2CppInspectorRedux.GUI path: Il2CppInspector.GUI/bin/Release/net9.0-windows/win-x64/publish - - name: Upload CLI Artifact - uses: actions/upload-artifact@v2 + build-cli: + runs-on: ubuntu-latest + strategy: + matrix: + dotnet-version: [ '9.0.x' ] + rid: ['win-x64', 'linux-x64', 'linux-arm64', 'osx-x64', 'osx-arm64'] + + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '9.0.x' + + - uses: actions/cache@v3 + with: + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} + restore-keys: | + ${{ runner.os }}-nuget- + + - name: Setup .NET SDK ${{ matrix.dotnet-version }} + uses: actions/setup-dotnet@v3 with: - name: Il2CppInspectorRedux.CLI - path: Il2CppInspector.CLI/bin/Release/net9.0/win-x64/publish + dotnet-version: ${{ matrix.dotnet-version }} + + - name: Install dependencies + run: dotnet restore -r ${{ matrix.rid }} + - name: Build & Publish + run: dotnet publish ./Il2CppInspector.CLI/Il2CppInspector.CLI.csproj -c Release --no-self-contained --no-restore -o ./${{ matrix.rid }} -r ${{ matrix.rid }} + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: Il2CppInspectorRedux.CLI-${{ matrix.rid }}.zip + path: ./${{ matrix.rid }} diff --git a/README.md b/README.md index d30dbe64..83071257 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ Nice to have: * Automatically defeats certain basic obfuscation methods * Test chassis for automated integration testing of IL2CPP binaries -Class library targets .NET 8. Built with Visual Studio 2022. +Class library targets .NET 9. Built with Visual Studio 2022. **NOTE**: Il2CppInspector is not a decompiler. It can provide you with the structure of an application and function addresses for every method so that you can easily jump straight to methods of interest in your disassembler. It does not attempt to recover the entire source code of the application. @@ -181,9 +181,9 @@ Get all current plugins (optional): For other operating systems supporting .NET Core, add `-r xxx` to the final command where `xxx` is a RID from https://docs.microsoft.com/en-us/dotnet/articles/core/rid-catalog -The output binary for command-line usage is placed in `Il2CppInspectorRedux/Il2CppInspector.CLI/bin/Release/net8.0/[win|osx|linux]-x64/publish/Il2CppInspector.exe`. +The output binary for command-line usage is placed in `Il2CppInspectorRedux/Il2CppInspector.CLI/bin/Release/net9.0/[win|osx|linux]-x64/publish/Il2CppInspector.exe`. -The output binary for Windows GUI is placed in `Il2CppInspectorRedux/Il2CppInspector.GUI/bin/Release/net8.0-windows/win-x64/publish/Il2CppInspector.exe`. +The output binary for Windows GUI is placed in `Il2CppInspectorRedux/Il2CppInspector.GUI/bin/Release/net9.0-windows/win-x64/publish/Il2CppInspector.exe`. The `plugins` folder should be placed in the same folder as `Il2CppInspector.exe`. From 226d0dfd1d1b09d0f7b3e33df89692f3eaaa9f6f Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Thu, 14 Nov 2024 07:47:55 +0100 Subject: [PATCH 24/27] workflow: only restore packages for projects that are being built --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d0dfe0bf..389c3641 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: ${{ runner.os }}-nuget- - name: Restore NuGet packages - run: dotnet restore -r net9.0-windows + run: dotnet restore -r win-x64 ./Il2CppInspector.GUI - name: Build GUI run: dotnet publish ./Il2CppInspector.GUI/Il2CppInspector.GUI.csproj -c Release -r net9.0-windows --no-self-contained @@ -65,10 +65,10 @@ jobs: dotnet-version: ${{ matrix.dotnet-version }} - name: Install dependencies - run: dotnet restore -r ${{ matrix.rid }} + run: dotnet restore -r ${{ matrix.rid }} ./Il2CppInspector.CLI - name: Build & Publish - run: dotnet publish ./Il2CppInspector.CLI/Il2CppInspector.CLI.csproj -c Release --no-self-contained --no-restore -o ./${{ matrix.rid }} -r ${{ matrix.rid }} + run: dotnet publish -c Release --no-self-contained --no-restore -o ./${{ matrix.rid }} -r ${{ matrix.rid }} ./Il2CppInspector.CLI/Il2CppInspector.CLI.csproj - name: Upload artifacts uses: actions/upload-artifact@v4 From 2bd24b9c127d95a0768ff80b6c3503faec5508e1 Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Thu, 14 Nov 2024 07:53:14 +0100 Subject: [PATCH 25/27] workflow: tweak caching and fix gui compilation --- .github/workflows/build.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 389c3641..976ab9fa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,18 +16,18 @@ jobs: with: dotnet-version: '9.0.x' - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ~/.nuget/packages - key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} + key: ${{ runner.os }}-nuget-gui-${{ hashFiles('**/packages.lock.json') }} restore-keys: | - ${{ runner.os }}-nuget- + ${{ runner.os }}-nuget-gui- - name: Restore NuGet packages run: dotnet restore -r win-x64 ./Il2CppInspector.GUI - name: Build GUI - run: dotnet publish ./Il2CppInspector.GUI/Il2CppInspector.GUI.csproj -c Release -r net9.0-windows --no-self-contained + run: dotnet publish ./Il2CppInspector.GUI/Il2CppInspector.GUI.csproj -c Release -r win-x64 --no-self-contained - name: Upload GUI Artifact uses: actions/upload-artifact@v4 @@ -55,9 +55,9 @@ jobs: - uses: actions/cache@v3 with: path: ~/.nuget/packages - key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} + key: ${{ runner.os }}-nuget-cli-${{ matrix.rid }}-${{ hashFiles('**/packages.lock.json') }} restore-keys: | - ${{ runner.os }}-nuget- + ${{ runner.os }}-nuget-cli-${{ matrix.rid }}- - name: Setup .NET SDK ${{ matrix.dotnet-version }} uses: actions/setup-dotnet@v3 From e6307d009cebdc3b86e1dad2ebc4530da1688e87 Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Thu, 14 Nov 2024 08:03:22 +0100 Subject: [PATCH 26/27] workflow: remove double .zip in CLI artifact name --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 976ab9fa..8604c986 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -73,5 +73,5 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@v4 with: - name: Il2CppInspectorRedux.CLI-${{ matrix.rid }}.zip + name: Il2CppInspectorRedux.CLI-${{ matrix.rid }} path: ./${{ matrix.rid }} From d43157cab5a9c2c33d906f4eeebc11ccfdc21253 Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Thu, 14 Nov 2024 14:29:06 +0100 Subject: [PATCH 27/27] 29/31.2 don't actually exist, this logic is not needed --- Il2CppInspector.Common/IL2CPP/Metadata.cs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/Il2CppInspector.Common/IL2CPP/Metadata.cs b/Il2CppInspector.Common/IL2CPP/Metadata.cs index 5de9ed4b..c71578a4 100644 --- a/Il2CppInspector.Common/IL2CPP/Metadata.cs +++ b/Il2CppInspector.Common/IL2CPP/Metadata.cs @@ -195,25 +195,6 @@ private void Initialize() Header.AttributeDataRangeSize / Sizeof()); } - if (Version is 29 or 31) - { - // 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 += 0.2; - - Methods = ReadArray(Header.methodsOffset, - Header.methodsCount / Sizeof(typeof(Il2CppMethodDefinition))); - } - } - } - // Get all metadata strings var pluginGetStringsResult = PluginHooks.GetStrings(this); if (pluginGetStringsResult.IsDataModified && !pluginGetStringsResult.IsInvalid)