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] 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 74a68dd..dab3cfb 100644 --- a/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs +++ b/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs @@ -6,10 +6,12 @@ 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 @@ namespace Il2CppInspector 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 @@ namespace Il2CppInspector } } - 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 @@ namespace Il2CppInspector 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 c4ff255..38d27f5 100644 --- a/Il2CppInspector.Common/FileFormatStreams/UBReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/UBReader.cs @@ -38,7 +38,7 @@ namespace Il2CppInspector 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 63116d1..7040ff7 100644 --- a/Il2CppInspector.Common/IL2CPP/Metadata.cs +++ b/Il2CppInspector.Common/IL2CPP/Metadata.cs @@ -17,7 +17,7 @@ using VersionedSerialization; 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 7b518ac..508576f 100644 --- a/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppType.cs +++ b/Il2CppInspector.Common/Next/BinaryMetadata/Il2CppType.cs @@ -76,7 +76,7 @@ public record struct Il2CppType : IReadable 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 0000000..a5fae3e --- /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 f93ab69..83068e7 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 a3a1113..db0de19 100644 --- a/Il2CppInspector.Common/Next/Pointer.cs +++ b/Il2CppInspector.Common/Next/Pointer.cs @@ -25,13 +25,13 @@ public struct Pointer(ulong value = 0) : IReadable, IEquatable> wh 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 5ab1c96..ca37280 100644 --- a/VersionedSerialization.Generator/ObjectSerializationGenerator.cs +++ b/VersionedSerialization.Generator/ObjectSerializationGenerator.cs @@ -213,13 +213,13 @@ namespace VersionedSerialization.Generator 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 75b5208..c216fc2 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 93912ed..58b00a3 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 class ReaderExtensions 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 78dfab5..de26c03 100644 --- a/VersionedSerialization/SpanReader.cs +++ b/VersionedSerialization/SpanReader.cs @@ -40,7 +40,7 @@ public ref struct SpanReader(ReadOnlySpan data, int offset = 0, bool littl } [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 ref struct SpanReader(ReadOnlySpan data, int offset = 0, bool littl } [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 ref struct SpanReader(ReadOnlySpan data, int offset = 0, bool littl } [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 ref struct SpanReader(ReadOnlySpan data, int offset = 0, bool littl } [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) {