mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-26 14:00:21 +00:00
Enhance bv5 checksum documentation
add for gen4 so there's no more mystery on that change span to memory for direct injecting to a bv obj
This commit is contained in:
parent
994c063537
commit
d86469266b
5 changed files with 90 additions and 30 deletions
|
@ -193,10 +193,10 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlagProvider37, IBox
|
|||
public abstract AdventureInfo5 AdventureInfo { get; }
|
||||
IEventFlag37 IEventFlagProvider37.EventWork => EventWork;
|
||||
|
||||
public abstract Span<byte> BattleVideoNative { get; }
|
||||
public abstract Span<byte> BattleVideoDownload1 { get; }
|
||||
public abstract Span<byte> BattleVideoDownload2 { get; }
|
||||
public abstract Span<byte> BattleVideoDownload3 { get; }
|
||||
public abstract Memory<byte> BattleVideoNative { get; }
|
||||
public abstract Memory<byte> BattleVideoDownload1 { get; }
|
||||
public abstract Memory<byte> BattleVideoDownload2 { get; }
|
||||
public abstract Memory<byte> BattleVideoDownload3 { get; }
|
||||
|
||||
protected override byte[] GetFinalData()
|
||||
{
|
||||
|
|
|
@ -73,8 +73,8 @@ public sealed class SAV5B2W2 : SAV5, ISaveBlock5B2W2
|
|||
set { if (value.Length == MaxStringLengthOT * 2) value.CopyTo(Data.AsSpan(0x23BA4)); }
|
||||
}
|
||||
|
||||
public override Span<byte> BattleVideoNative => Data.AsSpan(0x4C000, BattleVideo5.SIZE_USED);
|
||||
public override Span<byte> BattleVideoDownload1 => Data.AsSpan(0x4DA00, BattleVideo5.SIZE_USED);
|
||||
public override Span<byte> BattleVideoDownload2 => Data.AsSpan(0x4F400, BattleVideo5.SIZE_USED);
|
||||
public override Span<byte> BattleVideoDownload3 => Data.AsSpan(0x50E00, BattleVideo5.SIZE_USED);
|
||||
public override Memory<byte> BattleVideoNative => Data.AsMemory(0x4C000, BattleVideo5.SIZE_USED);
|
||||
public override Memory<byte> BattleVideoDownload1 => Data.AsMemory(0x4DA00, BattleVideo5.SIZE_USED);
|
||||
public override Memory<byte> BattleVideoDownload2 => Data.AsMemory(0x4F400, BattleVideo5.SIZE_USED);
|
||||
public override Memory<byte> BattleVideoDownload3 => Data.AsMemory(0x50E00, BattleVideo5.SIZE_USED);
|
||||
}
|
||||
|
|
|
@ -57,8 +57,8 @@ public sealed class SAV5BW : SAV5
|
|||
public override WhiteBlack5BW Forest => Blocks.Forest;
|
||||
public override AdventureInfo5 AdventureInfo => Blocks.AdventureInfo;
|
||||
|
||||
public override Span<byte> BattleVideoNative => Data.AsSpan(0x4A000, BattleVideo5.SIZE_USED);
|
||||
public override Span<byte> BattleVideoDownload1 => Data.AsSpan(0x4C000, BattleVideo5.SIZE_USED);
|
||||
public override Span<byte> BattleVideoDownload2 => Data.AsSpan(0x4E000, BattleVideo5.SIZE_USED);
|
||||
public override Span<byte> BattleVideoDownload3 => Data.AsSpan(0x50000, BattleVideo5.SIZE_USED);
|
||||
public override Memory<byte> BattleVideoNative => Data.AsMemory(0x4A000, BattleVideo5.SIZE_USED);
|
||||
public override Memory<byte> BattleVideoDownload1 => Data.AsMemory(0x4C000, BattleVideo5.SIZE_USED);
|
||||
public override Memory<byte> BattleVideoDownload2 => Data.AsMemory(0x4E000, BattleVideo5.SIZE_USED);
|
||||
public override Memory<byte> BattleVideoDownload3 => Data.AsMemory(0x50000, BattleVideo5.SIZE_USED);
|
||||
}
|
||||
|
|
|
@ -19,9 +19,10 @@ public sealed class BattleVideo4(Memory<byte> Raw) : IBattleVideo
|
|||
private Span<byte> Data => Raw.Span[..SIZE_USED];
|
||||
|
||||
public bool IsDecrypted;
|
||||
private const int CryptoStart = 0xE8;
|
||||
private const int CryptoEnd = 0x1D4C;
|
||||
private Span<byte> CryptoData => Data[CryptoStart..CryptoEnd];
|
||||
private Span<byte> CryptoData => Data[0xE8..0x1D4C];
|
||||
private Span<byte> Footer => Data.Slice(SIZE, SIZE_FOOTER);
|
||||
|
||||
public Span<byte> DecryptedChecksumRegion => CryptoData; // ccitt16 -> 0x1D4C
|
||||
|
||||
public byte Generation => 4;
|
||||
public IEnumerable<PKM> Contents => GetTeam(0).Concat(GetTeam(1)); // don't bother with multi-battles
|
||||
|
@ -50,6 +51,8 @@ public sealed class BattleVideo4(Memory<byte> Raw) : IBattleVideo
|
|||
public const int SizeVideoPoke = 0x70;
|
||||
|
||||
public uint Key { get => ReadUInt32LittleEndian(Data); set => WriteUInt32LittleEndian(Data, value); }
|
||||
|
||||
/// <summary> <see cref="CalculateDecryptedChecksum"/> </summary>
|
||||
public ushort Seed { get => ReadUInt16LittleEndian(Data[0x1D4C..]); set => WriteUInt16LittleEndian(Data[0x1D4C..], value); }
|
||||
|
||||
public void Decrypt() => SetDecryptedState(true);
|
||||
|
@ -70,7 +73,7 @@ public sealed class BattleVideo4(Memory<byte> Raw) : IBattleVideo
|
|||
|
||||
#region Footer
|
||||
public bool SizeValid => BlockSize == SIZE;
|
||||
public bool ChecksumValid => Checksum == GetChecksum();
|
||||
public bool ChecksumValid => Checksum == CalculateFooterChecksum();
|
||||
|
||||
public uint Magic { get => ReadUInt32LittleEndian(Footer); set => WriteUInt32LittleEndian(Footer, value); }
|
||||
public uint Revision { get => ReadUInt32LittleEndian(Footer[0x4..]); set => WriteUInt32LittleEndian(Footer[0x4..], value); }
|
||||
|
@ -78,10 +81,30 @@ public sealed class BattleVideo4(Memory<byte> Raw) : IBattleVideo
|
|||
public ushort BlockID { get => ReadUInt16LittleEndian(Footer[0xC..]); set => WriteUInt16LittleEndian(Footer[0xC..], value); }
|
||||
public ushort Checksum { get => ReadUInt16LittleEndian(Footer[0xE..]); set => WriteUInt16LittleEndian(Footer[0xE..], value); }
|
||||
|
||||
private ReadOnlySpan<byte> GetRegion() => Data[..(SIZE + SIZE_FOOTER)];
|
||||
private Span<byte> Footer => Data.Slice(SIZE, SIZE_FOOTER);
|
||||
private ushort GetChecksum() => Checksums.CRC16_CCITT(GetRegion()[..^2]);
|
||||
public void RefreshChecksum() => Checksum = GetChecksum();
|
||||
private ushort CalculateFooterChecksum()
|
||||
{
|
||||
if (IsDecrypted)
|
||||
throw new InvalidOperationException("Cannot calculate checksum when decrypted.");
|
||||
return Checksums.CRC16_CCITT(Data[..^2]);
|
||||
}
|
||||
|
||||
private ushort CalculateDecryptedChecksum()
|
||||
{
|
||||
if (!IsDecrypted)
|
||||
throw new InvalidOperationException("Cannot calculate checksum when encrypted.");
|
||||
return Checksums.CRC16_CCITT(DecryptedChecksumRegion);
|
||||
}
|
||||
|
||||
public void RefreshChecksums()
|
||||
{
|
||||
var state = IsDecrypted;
|
||||
Decrypt();
|
||||
Seed = CalculateDecryptedChecksum();
|
||||
Encrypt();
|
||||
Checksum = CalculateFooterChecksum();
|
||||
SetDecryptedState(state);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Conversion
|
||||
|
|
|
@ -19,11 +19,14 @@ public sealed class BattleVideo5(Memory<byte> Raw) : IBattleVideo
|
|||
private Span<byte> Data => Raw.Span[..SIZE_USED];
|
||||
|
||||
public bool IsDecrypted;
|
||||
private const int CryptoStart = 0xC4;
|
||||
private const int CryptoEnd = 0x18A0;
|
||||
private Span<byte> CryptoData => Data[CryptoStart..CryptoEnd];
|
||||
private Span<byte> CryptoData => Data[0xC4..0x18A0];
|
||||
private Span<byte> Footer => Data.Slice(SIZE, SIZE_FOOTER);
|
||||
|
||||
public byte Generation => 4;
|
||||
public Span<byte> DecryptedChecksumRegion => CryptoData; // ccitt16 -> 0x18A0
|
||||
public Span<byte> EncryptedChecksumRegion => Data[..0x18A4]; // ccitt16 -> 0x18A6
|
||||
public Span<byte> FooterChecksumRegion => Footer[..4]; // ccitt16 -> 0x12
|
||||
|
||||
public byte Generation => 5;
|
||||
public IEnumerable<PKM> Contents => GetTeam(0).Concat(GetTeam(1)); // don't bother with multi-battles
|
||||
public static bool IsValid(ReadOnlySpan<byte> data) => data.Length == SIZE_USED && ReadUInt32LittleEndian(data[0x1908..]) == SIZE_USED;
|
||||
|
||||
|
@ -47,7 +50,13 @@ public sealed class BattleVideo5(Memory<byte> Raw) : IBattleVideo
|
|||
public const int SizeVideoPoke = 0x70;
|
||||
|
||||
public uint Key { get => ReadUInt32LittleEndian(Data); set => WriteUInt32LittleEndian(Data, value); }
|
||||
|
||||
/// <summary> <see cref="CalculateDecryptedChecksum"/> </summary>
|
||||
public ushort Seed { get => ReadUInt16LittleEndian(Data[0x18A0..]); set => WriteUInt16LittleEndian(Data[0x18A0..], value); }
|
||||
// 0000: ^ seed is truncated to u16.
|
||||
public ushort EncryptedCount { get => ReadUInt16LittleEndian(Data[0x18A4..]); set => WriteUInt16LittleEndian(Data[0x18A4..], value); }
|
||||
public ushort EncryptedChecksum { get => ReadUInt16LittleEndian(Data[0x18A6..]); set => WriteUInt16LittleEndian(Data[0x18A6..], value); }
|
||||
// 0xFF padding(?) to 0x1900
|
||||
|
||||
public void Decrypt() => SetDecryptedState(true);
|
||||
public void Encrypt() => SetDecryptedState(false);
|
||||
|
@ -71,15 +80,43 @@ public sealed class BattleVideo5(Memory<byte> Raw) : IBattleVideo
|
|||
|
||||
#region Footer
|
||||
public bool SizeValid => BlockSize == SIZE;
|
||||
public bool ChecksumValid => Checksum == GetChecksum();
|
||||
public bool ChecksumValid => FooterChecksum == CalculateFooterChecksum();
|
||||
|
||||
public ushort EncryptedChecksumCopy { get => ReadUInt16LittleEndian(Footer); set => WriteUInt16LittleEndian(Footer, value); }
|
||||
// 0000
|
||||
public uint FooterCount { get => ReadUInt32LittleEndian(Footer[0x4..]); set => WriteUInt32LittleEndian(Footer[0x4..], value); }
|
||||
public int BlockSize { get => ReadInt32LittleEndian(Footer[0x8..]); set => WriteInt32LittleEndian(Footer[0x8..], value); }
|
||||
public ushort Checksum { get => ReadUInt16LittleEndian(Footer[0x12..]); set => WriteUInt16LittleEndian(Footer[0x12..], value); }
|
||||
public uint BlockID { get => ReadUInt32LittleEndian(Footer[0xC..]); set => WriteUInt32LittleEndian(Footer[0xC..], value); }
|
||||
// 0000
|
||||
public ushort FooterChecksum { get => ReadUInt16LittleEndian(Footer[0x12..]); set => WriteUInt16LittleEndian(Footer[0x12..], value); }
|
||||
|
||||
private ushort CalculateEncryptedChecksum()
|
||||
{
|
||||
if (IsDecrypted)
|
||||
throw new InvalidOperationException("Cannot calculate checksum when decrypted.");
|
||||
return Checksums.CRC16_CCITT(EncryptedChecksumRegion);
|
||||
}
|
||||
|
||||
private ushort CalculateDecryptedChecksum()
|
||||
{
|
||||
if (!IsDecrypted)
|
||||
throw new InvalidOperationException("Cannot calculate checksum when encrypted.");
|
||||
return Checksums.CRC16_CCITT(DecryptedChecksumRegion);
|
||||
}
|
||||
|
||||
private ushort CalculateFooterChecksum() => Checksums.CRC16_CCITT(FooterChecksumRegion);
|
||||
|
||||
public void RefreshChecksums()
|
||||
{
|
||||
var state = IsDecrypted;
|
||||
Decrypt();
|
||||
Seed = CalculateDecryptedChecksum();
|
||||
Encrypt();
|
||||
EncryptedChecksum = CalculateEncryptedChecksum();
|
||||
FooterChecksum = CalculateFooterChecksum();
|
||||
SetDecryptedState(state);
|
||||
}
|
||||
|
||||
private ReadOnlySpan<byte> GetRegion() => Data[..SIZE_USED];
|
||||
private Span<byte> Footer => Data.Slice(SIZE, SIZE_FOOTER);
|
||||
private ushort GetChecksum() => Checksums.CRC16_CCITT(GetRegion()[..^2]);
|
||||
public void RefreshChecksum() => Checksum = GetChecksum();
|
||||
#endregion
|
||||
|
||||
#region Conversion
|
||||
|
|
Loading…
Reference in a new issue