using System; using System.Collections.Generic; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; /// /// Gen4 Extra Block Info /// public sealed class BlockInfo4 : BlockInfo { private const int SIZE_FOOTER = 0x10; private readonly int FooterOffset; public BlockInfo4(uint id, int offset, int length) { ID = id; Offset = offset; Length = length; FooterOffset = offset + length - SIZE_FOOTER; } public uint GetKey(ReadOnlySpan data) => ReadUInt32LittleEndian(data[Offset..]); public uint GetMagic(ReadOnlySpan data) => ReadUInt32LittleEndian(data[FooterOffset..]); public uint GetRevision(ReadOnlySpan data) => ReadUInt32LittleEndian(data[(FooterOffset + 0x4)..]); public int GetSize(ReadOnlySpan data) => ReadInt32LittleEndian(data[(FooterOffset + 0x8)..]); public ushort GetID(ReadOnlySpan data) => ReadUInt16LittleEndian(data[(FooterOffset + 0xC)..]); private ushort GetChecksum(ReadOnlySpan data) => Checksums.CRC16_CCITT(data.Slice(Offset, Length - 2)); private bool IsInitialized(ReadOnlySpan data) { return (ID == 0 && GetRevision(data) != 0xFFFFFFFF) || (ID != 0 && GetKey(data) != 0xFFFFFFFF); } public bool SizeValid(ReadOnlySpan data) { return GetSize(data) == Length; } protected override bool ChecksumValid(ReadOnlySpan data) { if (!IsInitialized(data)) return true; ushort chk = GetChecksum(data); if (chk != ReadUInt16LittleEndian(data[(FooterOffset + 14)..])) return false; return true; } public bool IsValid(ReadOnlySpan data) { return IsInitialized(data) && SizeValid(data) && ChecksumValid(data); } protected override void SetChecksum(Span data) { if (!IsInitialized(data)) return; ushort chk = GetChecksum(data); WriteUInt16LittleEndian(data[(FooterOffset + 14)..], chk); } private void SetMagic(Span data, uint magic) { if (!IsInitialized(data)) return; WriteUInt32LittleEndian(data[FooterOffset..], magic); } public static void SetMagics(IEnumerable blocks, Span data, uint magic) { foreach (var b in blocks) b.SetMagic(data, magic); } } public static partial class Extensions { public static void SetMagics(this IEnumerable blocks, Span data, uint magic) => BlockInfo4.SetMagics(blocks, data, magic); }