using System; using System.ComponentModel.DataAnnotations; using System.Diagnostics; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; /// /// Unpacks a BinLinkerAccessor generated file container into individual arrays. /// [DebuggerDisplay($"{{{nameof(Identifier)},nq}}[{{{nameof(Length)},nq}}]")] public readonly ref struct BinLinkerAccessor { /// Backing data object private readonly ReadOnlySpan Data; /// Total count of files available for accessing. public ushort Length => ReadUInt16LittleEndian(Data[2..]); /// Magic identifier for the file. public string Identifier => new([(char)Data[0], (char)Data[1]]); /// /// Retrieves a view of the entry at the requested . /// /// Entry to retrieve. public ReadOnlySpan this[int index] => GetEntry(index); private BinLinkerAccessor(ReadOnlySpan data) => Data = data; private ReadOnlySpan GetEntry(int index) { int offset = 4 + (index * sizeof(int)); // Start and End are both 32-bit integers, sequentially. // Read them in one shot a 64-bit integer and decompose. var startEnd = ReadUInt64LittleEndian(Data[offset..]); int start = (int)startEnd; int end = (int)(startEnd >> 32); return Data[start..end]; } /// /// Sanity checks the input only in DEBUG builds, and returns a new wrapper. /// /// Data reference /// Expected identifier (debug verification only) public static BinLinkerAccessor Get(ReadOnlySpan data, [Length(2, 2)] ReadOnlySpan identifier) { SanityCheckIdentifier(data, identifier); return new BinLinkerAccessor(data); } [Conditional("DEBUG")] private static void SanityCheckIdentifier(ReadOnlySpan data, [Length(2, 2)] ReadOnlySpan identifier) { Debug.Assert(data.Length > 4); Debug.Assert(identifier[0] == data[0] && identifier[1] == data[1]); } }