fix support for 35+, add support for 39

This commit is contained in:
LukeFZ
2025-09-18 23:36:57 +02:00
parent e5f2fa703d
commit f1a69cafe3
13 changed files with 7433 additions and 140 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -95,7 +95,7 @@ namespace Il2CppInspector
// Set object versioning for Bin2Object from metadata version // Set object versioning for Bin2Object from metadata version
Version = new StructVersion(Header.Version); Version = new StructVersion(Header.Version);
if (Version < MetadataVersions.V160 || Version > MetadataVersions.V380) { if (Version < MetadataVersions.V160 || Version > MetadataVersions.V390) {
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}).");
} }
@@ -142,6 +142,13 @@ namespace Il2CppInspector
throw new InvalidOperationException("Could not determine TypeIndex size based on the metadata header"); throw new InvalidOperationException("Could not determine TypeIndex size based on the metadata header");
var fullTag = $"{tag}_{TypeIndex.TagPrefix}{typeIndexSize}"; var fullTag = $"{tag}_{TypeIndex.TagPrefix}{typeIndexSize}";
if (Version >= MetadataVersions.V390)
{
var parameterIndexSize = GetIndexSize(Header.Parameters.Count);
fullTag += $"_{ParameterIndex.TagPrefix}{parameterIndexSize}";
}
Version = new StructVersion(Version.Major, Version.Minor, fullTag); Version = new StructVersion(Version.Major, Version.Minor, fullTag);
} }
@@ -154,8 +161,11 @@ namespace Il2CppInspector
// in the header after the sanity and version fields, and since it will always point directly to the first byte after the end of the header, // in the header after the sanity and version fields, and since it will always point directly to the first byte after the end of the header,
// 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. // 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) { if (!pluginResult.SkipValidation)
var realHeaderLength = Header.StringLiteralOffset; {
var realHeaderLength = Version >= MetadataVersions.V380
? Header.StringLiterals.Offset
: Header.StringLiteralOffset;
if (realHeaderLength != Sizeof<Il2CppGlobalMetadataHeader>()) { if (realHeaderLength != Sizeof<Il2CppGlobalMetadataHeader>()) {
if (Version == MetadataVersions.V240) { if (Version == MetadataVersions.V240) {
@@ -255,10 +265,17 @@ namespace Il2CppInspector
Strings = pluginGetStringsResult.Strings; Strings = pluginGetStringsResult.Strings;
else { else {
Position = Header.StringOffset; var stringOffset = Version >= MetadataVersions.V380
? Header.Strings.Offset
: Header.StringOffset;
var stringLength = Version >= MetadataVersions.V380
? Header.Strings.SectionSize
: Header.StringSize;
while (Position < Header.StringOffset + Header.StringSize) Position = stringOffset;
Strings.Add((int) Position - Header.StringOffset, ReadNullTerminatedString());
while (Position < stringOffset + stringLength)
Strings.Add((int)Position - stringOffset, ReadNullTerminatedString());
} }
// Get all string literals // Get all string literals
@@ -277,15 +294,17 @@ namespace Il2CppInspector
if (Version >= MetadataVersions.V350) if (Version >= MetadataVersions.V350)
{ {
StringLiterals = new string[stringLiteralList.Length - 1]; var literals = new string[stringLiteralList.Length - 1];
for (var i = 0; i < stringLiteralList.Length; i++) for (var i = 0; i < literals.Length; i++)
{ {
var currentStringDataIndex = stringLiteralList[i].DataIndex; var currentStringDataIndex = stringLiteralList[i].DataIndex;
var nextStringDataIndex = stringLiteralList[i + 1].DataIndex; var nextStringDataIndex = stringLiteralList[i + 1].DataIndex;
var stringLength = nextStringDataIndex - currentStringDataIndex; var stringLength = nextStringDataIndex - currentStringDataIndex;
StringLiterals[i] = ReadFixedLengthString(dataOffset + currentStringDataIndex, stringLength); literals[i] = ReadFixedLengthString(dataOffset + currentStringDataIndex, stringLength);
} }
StringLiterals = literals;
} }
else else
{ {

View File

@@ -2,58 +2,27 @@
namespace Il2CppInspector.Next.Metadata; namespace Il2CppInspector.Next.Metadata;
public struct GenericContainerIndex(int value) : IReadable, IEquatable<GenericContainerIndex> public struct GenericContainerIndex(int value) : IIndexType<GenericContainerIndex>, IReadable, IEquatable<GenericContainerIndex>
{ {
public const string TagPrefix = nameof(GenericContainerIndex); public const string TagPrefix = nameof(GenericContainerIndex);
static string IIndexType<GenericContainerIndex>.TagPrefix => TagPrefix;
static StructVersion IIndexType<GenericContainerIndex>.AddedVersion => MetadataVersions.V390;
private int _value = value; private int _value = value;
public static implicit operator int(GenericContainerIndex idx) => idx._value;
public static implicit operator GenericContainerIndex(int idx) => new(idx);
public static int Size(in StructVersion version = default, bool is32Bit = false) public static int Size(in StructVersion version = default, bool is32Bit = false)
{ => IIndexType<GenericContainerIndex>.IndexSize(version, is32Bit);
if (version >= MetadataVersions.V380
&& version.Tag != null
&& version.Tag.Contains(TagPrefix)
&& !version.Tag.Contains($"{TagPrefix}4"))
{
if (version.Tag.Contains($"{TagPrefix}2"))
return sizeof(ushort);
if (version.Tag.Contains($"{TagPrefix}1"))
return sizeof(byte);
}
return sizeof(int);
}
public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
{ {
if (version >= MetadataVersions.V380 _value = IIndexType<GenericContainerIndex>.ReadIndex(ref reader, in version);
&& version.Tag != null
&& version.Tag.Contains(TagPrefix)
&& !version.Tag.Contains($"{TagPrefix}4"))
{
if (version.Tag.Contains($"{TagPrefix}2"))
{
_value = reader.ReadPrimitive<short>();
_value = _value == ushort.MaxValue ? -1 : _value;
return;
}
if (version.Tag.Contains($"{TagPrefix}1"))
{
_value = reader.ReadPrimitive<byte>();
_value = _value == byte.MaxValue ? -1 : _value;
return;
}
}
_value = reader.ReadPrimitive<int>();
} }
#region Equality operators + ToString #region Operators + ToString
public static implicit operator int(GenericContainerIndex idx) => idx._value;
public static implicit operator GenericContainerIndex(int idx) => new(idx);
public static bool operator ==(GenericContainerIndex left, GenericContainerIndex right) public static bool operator ==(GenericContainerIndex left, GenericContainerIndex right)
=> left._value == right._value; => left._value == right._value;
@@ -61,7 +30,7 @@ public struct GenericContainerIndex(int value) : IReadable, IEquatable<GenericCo
public static bool operator !=(GenericContainerIndex left, GenericContainerIndex right) public static bool operator !=(GenericContainerIndex left, GenericContainerIndex right)
=> !(left == right); => !(left == right);
public readonly override bool Equals(object? obj) public readonly override bool Equals(object obj)
=> obj is GenericContainerIndex other && Equals(other); => obj is GenericContainerIndex other && Equals(other);
public readonly bool Equals(GenericContainerIndex other) public readonly bool Equals(GenericContainerIndex other)

View File

@@ -0,0 +1,55 @@
using VersionedSerialization;
namespace Il2CppInspector.Next.Metadata;
public interface IIndexType<T> where T
: IIndexType<T>, allows ref struct
{
public static abstract string TagPrefix { get; }
public static abstract StructVersion AddedVersion { get; }
private static string TagSize4 => $"{T.TagPrefix}4";
private static string TagSize2 => $"{T.TagPrefix}2";
private static string TagSize1 => $"{T.TagPrefix}1";
private static bool HasCustomSize(in StructVersion version)
=> version >= T.AddedVersion
&& version.Tag != null
&& version.Tag.Contains(T.TagPrefix)
&& !version.Tag.Contains(TagSize4);
public static int IndexSize(in StructVersion version = default, bool is32Bit = false)
{
if (version.Tag != null && HasCustomSize(version))
{
if (version.Tag.Contains(TagSize2))
return sizeof(ushort);
if (version.Tag.Contains(TagSize1))
return sizeof(byte);
}
return sizeof(int);
}
public static int ReadIndex<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
{
if (version.Tag != null && HasCustomSize(version))
{
if (version.Tag.Contains(TagSize2))
{
var value = reader.ReadPrimitive<ushort>();
return value == ushort.MaxValue ? -1 : value;
}
if (version.Tag.Contains(TagSize1))
{
var value = reader.ReadPrimitive<byte>();
return value == byte.MaxValue ? -1 : value;
}
}
return reader.ReadPrimitive<int>();
}
}

View File

@@ -66,7 +66,7 @@ public partial record struct Il2CppGlobalMetadataHeader
[VersionCondition(LessThan = "35.0")] [VersionCondition(LessThan = "35.0")]
public int MethodsSize { get; private set; } public int MethodsSize { get; private set; }
[VersionCondition(GreaterThan = "16.0")] [VersionCondition(GreaterThan = "16.0", LessThan = "35.0")]
[VersionCondition(EqualTo = "16.0")] [VersionCondition(EqualTo = "16.0")]
public int ParameterDefaultValuesOffset { get; private set; } public int ParameterDefaultValuesOffset { get; private set; }

View File

@@ -4,7 +4,6 @@ using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata; namespace Il2CppInspector.Next.Metadata;
using StringIndex = int; using StringIndex = int;
using ParameterIndex = int;
[VersionedStruct] [VersionedStruct]
public partial record struct Il2CppMethodDefinition public partial record struct Il2CppMethodDefinition

View File

@@ -1,6 +1,5 @@
namespace Il2CppInspector.Next.Metadata; namespace Il2CppInspector.Next.Metadata;
using ParameterIndex = int;
using DefaultValueDataIndex = int; using DefaultValueDataIndex = int;
using VersionedSerialization.Attributes; using VersionedSerialization.Attributes;

View File

@@ -0,0 +1,46 @@
using NoisyCowStudios.Bin2Object;
using VersionedSerialization;
namespace Il2CppInspector.Next.Metadata;
public struct ParameterIndex(int value) : IIndexType<ParameterIndex>, IReadable, IEquatable<ParameterIndex>
{
public const string TagPrefix = nameof(ParameterIndex);
static string IIndexType<ParameterIndex>.TagPrefix => TagPrefix;
static StructVersion IIndexType<ParameterIndex>.AddedVersion => MetadataVersions.V390;
private int _value = value;
public static int Size(in StructVersion version = default, bool is32Bit = false)
=> IIndexType<ParameterIndex>.IndexSize(version, is32Bit);
public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
{
_value = IIndexType<ParameterIndex>.ReadIndex(ref reader, in version);
}
#region Operators + ToString
public static implicit operator int(ParameterIndex idx) => idx._value;
public static implicit operator ParameterIndex(int idx) => new(idx);
public static bool operator ==(ParameterIndex left, ParameterIndex right)
=> left._value == right._value;
public static bool operator !=(ParameterIndex left, ParameterIndex right)
=> !(left == right);
public readonly override bool Equals(object obj)
=> obj is ParameterIndex other && Equals(other);
public readonly bool Equals(ParameterIndex other)
=> this == other;
public readonly override int GetHashCode()
=> HashCode.Combine(_value);
public readonly override string ToString() => _value.ToString();
#endregion
}

View File

@@ -2,57 +2,27 @@
namespace Il2CppInspector.Next.Metadata; namespace Il2CppInspector.Next.Metadata;
public struct TypeDefinitionIndex(int value) : IReadable, IEquatable<TypeDefinitionIndex> public struct TypeDefinitionIndex(int value) : IIndexType<TypeDefinitionIndex>, IReadable, IEquatable<TypeDefinitionIndex>
{ {
public const string TagPrefix = nameof(TypeDefinitionIndex); public const string TagPrefix = nameof(TypeDefinitionIndex);
static string IIndexType<TypeDefinitionIndex>.TagPrefix => TagPrefix;
static StructVersion IIndexType<TypeDefinitionIndex>.AddedVersion => MetadataVersions.V390;
private int _value = value; private int _value = value;
public static implicit operator int(TypeDefinitionIndex idx) => idx._value;
public static implicit operator TypeDefinitionIndex(int idx) => new(idx);
public static int Size(in StructVersion version = default, bool is32Bit = false) public static int Size(in StructVersion version = default, bool is32Bit = false)
{ => IIndexType<TypeDefinitionIndex>.IndexSize(version, is32Bit);
if (version >= MetadataVersions.V380
&& version.Tag != null
&& version.Tag.Contains(TagPrefix)
&& !version.Tag.Contains($"{TagPrefix}4"))
{
if (version.Tag.Contains($"{TagPrefix}2"))
return sizeof(ushort);
if (version.Tag.Contains($"{TagPrefix}1"))
return sizeof(byte);
}
return sizeof(int);
}
public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
{ {
if (version >= MetadataVersions.V380 _value = IIndexType<TypeDefinitionIndex>.ReadIndex(ref reader, in version);
&& version.Tag != null
&& version.Tag.Contains(TagPrefix)
&& !version.Tag.Contains($"{TagPrefix}4"))
{
if (version.Tag.Contains($"{TagPrefix}2"))
{
_value = reader.ReadPrimitive<ushort>();
return;
}
if (version.Tag.Contains($"{TagPrefix}1"))
{
_value = reader.ReadPrimitive<byte>();
_value = _value == byte.MaxValue ? -1 : _value;
return;
}
}
_value = reader.ReadPrimitive<int>();
} }
#region Equality operators + ToString #region Operators + ToString
public static implicit operator int(TypeDefinitionIndex idx) => idx._value;
public static implicit operator TypeDefinitionIndex(int idx) => new(idx);
public static bool operator ==(TypeDefinitionIndex left, TypeDefinitionIndex right) public static bool operator ==(TypeDefinitionIndex left, TypeDefinitionIndex right)
=> left._value == right._value; => left._value == right._value;
@@ -60,7 +30,7 @@ public struct TypeDefinitionIndex(int value) : IReadable, IEquatable<TypeDefinit
public static bool operator !=(TypeDefinitionIndex left, TypeDefinitionIndex right) public static bool operator !=(TypeDefinitionIndex left, TypeDefinitionIndex right)
=> !(left == right); => !(left == right);
public readonly override bool Equals(object? obj) public readonly override bool Equals(object obj)
=> obj is TypeDefinitionIndex other && Equals(other); => obj is TypeDefinitionIndex other && Equals(other);
public readonly bool Equals(TypeDefinitionIndex other) public readonly bool Equals(TypeDefinitionIndex other)

View File

@@ -2,58 +2,27 @@
namespace Il2CppInspector.Next.Metadata; namespace Il2CppInspector.Next.Metadata;
public struct TypeIndex(int value) : IReadable, IEquatable<TypeIndex> public struct TypeIndex(int value) : IIndexType<TypeIndex>, IReadable, IEquatable<TypeIndex>
{ {
public const string TagPrefix = nameof(TypeIndex); public const string TagPrefix = nameof(TypeIndex);
static string IIndexType<TypeIndex>.TagPrefix => TagPrefix;
static StructVersion IIndexType<TypeIndex>.AddedVersion => MetadataVersions.V390;
private int _value = value; private int _value = value;
public static implicit operator int(TypeIndex idx) => idx._value;
public static implicit operator TypeIndex(int idx) => new(idx);
public static int Size(in StructVersion version = default, bool is32Bit = false) public static int Size(in StructVersion version = default, bool is32Bit = false)
{ => IIndexType<TypeIndex>.IndexSize(version, is32Bit);
if (version >= MetadataVersions.V380
&& version.Tag != null
&& version.Tag.Contains(TagPrefix)
&& !version.Tag.Contains($"{TagPrefix}4"))
{
if (version.Tag.Contains($"{TagPrefix}2"))
return sizeof(ushort);
if (version.Tag.Contains($"{TagPrefix}1"))
return sizeof(byte);
}
return sizeof(int);
}
public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
{ {
if (version >= MetadataVersions.V380 _value = IIndexType<TypeIndex>.ReadIndex(ref reader, in version);
&& version.Tag != null
&& version.Tag.Contains(TagPrefix)
&& !version.Tag.Contains($"{TagPrefix}4"))
{
if (version.Tag.Contains($"{TagPrefix}2"))
{
_value = reader.ReadPrimitive<ushort>();
_value = _value == ushort.MaxValue ? -1 : _value;
return;
}
if (version.Tag.Contains($"{TagPrefix}1"))
{
_value = reader.ReadPrimitive<byte>();
_value = _value == byte.MaxValue ? -1 : _value;
return;
}
}
_value = reader.ReadPrimitive<int>();
} }
#region Equality operators + ToString #region Operators + ToString
public static implicit operator int(TypeIndex idx) => idx._value;
public static implicit operator TypeIndex(int idx) => new(idx);
public static bool operator ==(TypeIndex left, TypeIndex right) public static bool operator ==(TypeIndex left, TypeIndex right)
=> left._value == right._value; => left._value == right._value;
@@ -61,7 +30,7 @@ public struct TypeIndex(int value) : IReadable, IEquatable<TypeIndex>
public static bool operator !=(TypeIndex left, TypeIndex right) public static bool operator !=(TypeIndex left, TypeIndex right)
=> !(left == right); => !(left == right);
public readonly override bool Equals(object? obj) public readonly override bool Equals(object obj)
=> obj is TypeIndex other && Equals(other); => obj is TypeIndex other && Equals(other);
public readonly bool Equals(TypeIndex other) public readonly bool Equals(TypeIndex other)

View File

@@ -35,4 +35,8 @@ public static class MetadataVersions
// Unity 6000.3.0a5 // Unity 6000.3.0a5
public static readonly StructVersion V380 = new(38); public static readonly StructVersion V380 = new(38);
// NOTE: This version uses tags to specify the size of TypeIndex, TypeDefinitionIndex, and GenericContainerIndex. // NOTE: This version uses tags to specify the size of TypeIndex, TypeDefinitionIndex, and GenericContainerIndex.
// Unity 6000.3.0b1
public static readonly StructVersion V390 = new(39);
// NOTE This version additionally uses a tag to specify the size of ParameterIndex.
} }