From f672ac42fe94e5caf06da078b33996e0d22e6281 Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Thu, 19 Mar 2026 13:54:46 +0100 Subject: [PATCH] slim down nso reader classes --- .../FileFormatStreams/NsoReader.cs | 74 +++++++++---------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/Il2CppInspector.Common/FileFormatStreams/NsoReader.cs b/Il2CppInspector.Common/FileFormatStreams/NsoReader.cs index d17aa5e..7e44259 100644 --- a/Il2CppInspector.Common/FileFormatStreams/NsoReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/NsoReader.cs @@ -7,26 +7,32 @@ using System.Collections.Frozen; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Text; +using Il2CppInspector.Next; using VersionedSerialization; namespace Il2CppInspector; -internal abstract class MemoryBackingBuffer : IDisposable +// These are slimmed-down versions of the project the loader originated from, +// which is why the API might look a bit weird +internal class ByteArrayBackingBuffer : IDisposable { - public abstract bool Initialized { get; protected set; } + [MemberNotNullWhen(true, nameof(Buffer))] + public bool Initialized { get; protected set; } public bool IsLittleEndian { get; protected set; } public bool Is32Bit { get; protected set; } public ulong BaseAddress { get; protected set; } + public byte[]? Buffer { get; private set; } - protected abstract Span Data { get; } + protected Span Data => Buffer.AsSpan(); - public virtual void Initialize(long capacity, bool littleEndian, bool is32Bit, ulong baseAddress) + public void Initialize(long capacity, bool littleEndian, bool is32Bit, ulong baseAddress) { IsLittleEndian = littleEndian; Is32Bit = is32Bit; BaseAddress = baseAddress; + Buffer = new byte[capacity]; + Initialized = true; } protected int TranslateVaToRva(ulong address) @@ -107,23 +113,6 @@ internal abstract class MemoryBackingBuffer : IDisposable } } -internal class ByteArrayBackingBuffer : MemoryBackingBuffer -{ - [MemberNotNullWhen(true, nameof(Buffer))] - public override bool Initialized { get; protected set; } - - public byte[]? Buffer { get; private set; } - - protected override Span Data => Buffer.AsSpan(); - - public override void Initialize(long capacity, bool littleEndian, bool is32Bit, ulong baseAddress) - { - base.Initialize(capacity, littleEndian, is32Bit, baseAddress); - Buffer = new byte[capacity]; - Initialized = true; - } -} - public class NsoReader : FileFormatStream { public override string DefaultFilename => "main"; @@ -146,11 +135,11 @@ public class NsoReader : FileFormatStream protected override bool Init() { - var reader = VersionedSerialization.Reader.LittleEndian(GetBuffer()); - var magic = reader.ReadPrimitive(); + var magic = ReadPrimitive(); if (magic != NsoHeader.ExpectedMagic) return false; + Position = 0; LoadInternal(); Position = 0; @@ -160,24 +149,28 @@ public class NsoReader : FileFormatStream } private readonly ByteArrayBackingBuffer _mappedExecutable = new(); - private bool _is32Bit; private FrozenDictionary _dynamicEntries = FrozenDictionary.Empty; private ImmutableArray<(ulong Start, ulong End)> _relocationEntryRegions = []; + private bool _is32Bit; private void LoadInternal() { - var reader = VersionedSerialization.Reader.LittleEndian(GetBuffer()); - var header = reader.ReadVersionedObject(); + var header = ReadVersionedObject(); var totalLength = header.TextSegment.Size + header.RoSegment.Size + header.DataSegment.Size + header.BssSize; _mappedExecutable.Initialize(totalLength, true, false, 0); - LoadSegment(ref reader, in header.TextSegment, header.TextFileSize, header.Flags.HasFlag(SegmentFlags.TextCompress)); - LoadSegment(ref reader, in header.RoSegment, header.RoFileSize, header.Flags.HasFlag(SegmentFlags.RoCompress)); - LoadSegment(ref reader, in header.DataSegment, header.DataFileSize, header.Flags.HasFlag(SegmentFlags.DataCompress)); + LoadSegment(in header.TextSegment, header.TextFileSize, header.Flags.HasFlag(SegmentFlags.TextCompress)); + LoadSegment(in header.RoSegment, header.RoFileSize, header.Flags.HasFlag(SegmentFlags.RoCompress)); + LoadSegment(in header.DataSegment, header.DataFileSize, header.Flags.HasFlag(SegmentFlags.DataCompress)); - var modInfoHeader = _mappedExecutable.ReadObject(header.TextSegment.MemoryOffset, default); + LoadMappedExecutable(header.TextSegment.MemoryOffset); + } + + private void LoadMappedExecutable(uint textSegmentRva) + { + var modInfoHeader = _mappedExecutable.ReadObject(textSegmentRva, default); var modHeader = _mappedExecutable.ReadObject(modInfoHeader.ModOffset, default); Debug.Assert(modHeader.ValidMagic); @@ -190,6 +183,8 @@ public class NsoReader : FileFormatStream var currentDynamicOffset = modInfoHeader.ModOffset + modHeader.DynamicOffset; var dynamicEntries = new Dictionary(); + var dynamicEntrySize = (uint)DynamicEntry.StructSize(config: new ReaderConfig(Is32Bit)); + while (true) { var entry = _mappedExecutable.ReadObject(currentDynamicOffset, default); @@ -197,7 +192,7 @@ public class NsoReader : FileFormatStream break; dynamicEntries[entry.Tag] = entry.Value; - currentDynamicOffset += 2 * (Is32Bit ? 4u : 8u); + currentDynamicOffset += dynamicEntrySize; } _dynamicEntries = dynamicEntries.ToFrozenDictionary(); @@ -205,10 +200,12 @@ public class NsoReader : FileFormatStream ApplyRelocations(); } - private void LoadSegment(ref Reader reader, ref readonly SegmentHeader header, uint fileSize, bool isCompressed) - where T : ISeekableReader, allows ref struct + private void LoadSegment(ref readonly SegmentHeader header, uint fileSize, bool isCompressed) { - reader.Offset = checked((int)header.FileOffset); + var reader = new Reader(this) + { + Offset = checked((int)header.FileOffset) + }; var data = reader.ReadBytes(checked((int)fileSize)); if (!isCompressed) @@ -234,9 +231,6 @@ public class NsoReader : FileFormatStream // Copied and trimmed down from ElfBinary private void ApplyRelocations() { - if (_mappedExecutable == null) - throw new InvalidOperationException("Cannot apply relocations without executable being mapped"); - if (!_dynamicEntries.TryGetValue(DynamicTag.DT_SYMTAB, out var symtabAddress) || !_dynamicEntries.TryGetValue(DynamicTag.DT_SYMENT, out var symtabEntrySize)) return; @@ -313,6 +307,10 @@ public class NsoReader : FileFormatStream { _mappedExecutable.WriteNUInt(offset, value); } + else + { + Debug.Assert(false); + } } }