Convert checksum operations to span-based

Fix SAV.Data references in SAV_Misc4 to use the General block instead
This commit is contained in:
Kurt 2021-05-14 12:30:40 -07:00
parent 014835ed14
commit 871f9b0627
17 changed files with 61 additions and 68 deletions

View file

@ -50,19 +50,19 @@ namespace PKHeX.Core
public sealed class BlockInfo6 : BlockInfo3DS
{
public BlockInfo6(int bo, uint id, int ofs, int len) : base(bo, id, ofs, len) { }
protected override ushort GetChecksum(byte[] data) => Checksums.CRC16_CCITT(data, Offset, Length);
protected override ushort GetChecksum(byte[] data) => Checksums.CRC16_CCITT(new ReadOnlySpan<byte>(data, Offset, Length));
}
public sealed class BlockInfo7 : BlockInfo3DS
{
public BlockInfo7(int bo, uint id, int ofs, int len) : base(bo, id, ofs, len) { }
protected override ushort GetChecksum(byte[] data) => Checksums.CRC16(data, Offset, Length);
protected override ushort GetChecksum(byte[] data) => Checksums.CRC16Invert(new ReadOnlySpan<byte>(data, Offset, Length));
}
public sealed class BlockInfo7b : BlockInfo3DS
{
public BlockInfo7b(int bo, uint id, int ofs, int len) : base(bo, id, ofs, len) { }
protected override ushort GetChecksum(byte[] data) => Checksums.CRC16NoInvert(data, Offset, Length);
protected override ushort GetChecksum(byte[] data) => Checksums.CRC16NoInvert(new ReadOnlySpan<byte>(data, Offset, Length));
}
public static class BlockInfoBEEFUtil

View file

@ -18,7 +18,7 @@ namespace PKHeX.Core
ChecksumMirror = chkMirror;
}
private ushort GetChecksum(byte[] data) => Checksums.CRC16_CCITT(data, Offset, Length);
private ushort GetChecksum(byte[] data) => Checksums.CRC16_CCITT(new ReadOnlySpan<byte>(data, Offset, Length));
protected override bool ChecksumValid(byte[] data)
{

View file

@ -1,3 +1,5 @@
using System;
namespace PKHeX.Core
{
/// <summary>
@ -6,15 +8,16 @@ namespace PKHeX.Core
public sealed class BlockInfoRSBOX : BlockInfo
{
public readonly uint SaveCount;
public readonly uint Checksum;
public readonly uint OriginalChecksum;
private const int ChecksumRegionSize = 0x1FF8;
public BlockInfoRSBOX(byte[] data, int offset)
{
Offset = offset;
Length = 0x1FFC;
Length = 4 + ChecksumRegionSize;
// Values stored in Big Endian format
Checksum = BigEndian.ToUInt32(data, Offset + 0);
OriginalChecksum = BigEndian.ToUInt32(data, Offset);
ID = BigEndian.ToUInt32(data, Offset + 4);
SaveCount = BigEndian.ToUInt32(data, Offset + 8);
}
@ -38,9 +41,8 @@ namespace PKHeX.Core
private uint GetChecksum(byte[] data)
{
int start = Offset + 4;
int end = start + Length - 4;
return Checksums.CheckSum16BigInvert(data, start, end);
var span = new ReadOnlySpan<byte>(data, start, ChecksumRegionSize);
return Checksums.CheckSum16BigInvert(span);
}
}
}

View file

@ -75,7 +75,7 @@ namespace PKHeX.Core
{
var boxOfs = GetBoxOffset(i) - ListHeaderSize;
var size = BoxSize - 2;
var chk = Checksums.CheckSum16(Data, boxOfs, size);
var chk = Checksums.CheckSum16(new ReadOnlySpan<byte>(Data, boxOfs, size));
var actual = BigEndian.ToUInt16(Data, boxOfs + size);
return chk == actual;
}
@ -84,7 +84,7 @@ namespace PKHeX.Core
{
var boxOfs = GetBoxOffset(i) - ListHeaderSize;
var size = BoxSize - 2;
var chk = Checksums.CheckSum16(Data, boxOfs, size);
var chk = Checksums.CheckSum16(new ReadOnlySpan<byte>(Data, boxOfs, size));
BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size);
}

View file

@ -60,7 +60,7 @@ namespace PKHeX.Core
{
var boxOfs = GetBoxOffset(i) - ListHeaderSize;
const int size = BoxSizeJ - 2;
var chk = Checksums.CheckSum16(Data, boxOfs, size);
var chk = Checksums.CheckSum16(new ReadOnlySpan<byte>(Data, boxOfs, size));
var actual = BigEndian.ToUInt16(Data, boxOfs + size);
return chk == actual;
}
@ -69,7 +69,7 @@ namespace PKHeX.Core
{
var boxOfs = GetBoxOffset(i) - ListHeaderSize;
const int size = BoxSizeJ - 2;
var chk = Checksums.CheckSum16(Data, boxOfs, size);
var chk = Checksums.CheckSum16(new ReadOnlySpan<byte>(Data, boxOfs, size));
BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size);
}

View file

@ -73,7 +73,7 @@ namespace PKHeX.Core
{
var boxOfs = GetBoxOffset(i) - ListHeaderSizeBox;
var size = BoxSize - 2;
var chk = Checksums.CheckSum16(Data, boxOfs, size);
var chk = Checksums.CheckSum16(new ReadOnlySpan<byte>(Data, boxOfs, size));
var actual = BigEndian.ToUInt16(Data, boxOfs + size);
return chk == actual;
}
@ -109,7 +109,7 @@ namespace PKHeX.Core
{
var boxOfs = GetBoxOffset(i) - ListHeaderSizeBox;
var size = BoxSize - 2;
var chk = Checksums.CheckSum16(Data, boxOfs, size);
var chk = Checksums.CheckSum16(new ReadOnlySpan<byte>(Data, boxOfs, size));
BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size);
}

View file

@ -104,7 +104,7 @@ namespace PKHeX.Core
// Checksums
protected abstract int FooterSize { get; }
private ushort CalcBlockChecksum(byte[] data) => Checksums.CRC16_CCITT(data, 0, data.Length - FooterSize);
private ushort CalcBlockChecksum(byte[] data) => Checksums.CRC16_CCITT(new ReadOnlySpan<byte>(data, 0, data.Length - FooterSize));
private static ushort GetBlockChecksumSaved(byte[] data) => BitConverter.ToUInt16(data, data.Length - 2);
private bool GetBlockChecksumValid(byte[] data) => CalcBlockChecksum(data) == GetBlockChecksumSaved(data);

View file

@ -175,7 +175,7 @@ namespace PKHeX.Core
chkbytes.CopyTo(Data, footer + 2); // checksum
chkbytes.CopyTo(Data, footer + 0x100); // second checksum
dlcfooter.CopyTo(Data, footer + 0x102);
ushort skinchkval = Checksums.CRC16_CCITT(Data, footer + 0x100, 4);
ushort skinchkval = Checksums.CRC16_CCITT(new ReadOnlySpan<byte>(Data, footer + 0x100, 4));
BitConverter.GetBytes(skinchkval).CopyTo(Data, footer + 0x112);
// Indicate in the save file that data is present

View file

@ -12,7 +12,7 @@ namespace PKHeX.Core
public override PersonalTable Personal => PersonalTable.RS;
public override IReadOnlyList<ushort> HeldItems => Legal.HeldItems_RS;
protected override SaveFile CloneInternal() => new Bank3((byte[])Data.Clone());
public override string PlayTimeString => Checksums.CRC16(Data, 0, Data.Length).ToString("X4");
public override string PlayTimeString => Checksums.CRC16Invert(Data).ToString("X4");
protected internal override string ShortSummary => PlayTimeString;
public override string Extension => ".gst";

View file

@ -12,7 +12,7 @@ namespace PKHeX.Core
public override PersonalTable Personal => PersonalTable.HGSS;
public override IReadOnlyList<ushort> HeldItems => Legal.HeldItems_HGSS;
protected override SaveFile CloneInternal() => new Bank4((byte[])Data.Clone());
public override string PlayTimeString => Checksums.CRC16(Data, 0, Data.Length).ToString("X4");
public override string PlayTimeString => Checksums.CRC16Invert(Data).ToString("X4");
protected internal override string ShortSummary => PlayTimeString;
public override string Extension => ".stk";

View file

@ -21,7 +21,7 @@ namespace PKHeX.Core
protected readonly int SlotsPerBox;
protected internal override string ShortSummary => $"{Checksums.CRC16(Data, Box, Data.Length - Box):X4}";
protected internal override string ShortSummary => $"{Checksums.CRC16Invert(new ReadOnlySpan<byte>(Data, Box, Data.Length - Box)):X4}";
public override string Extension => ".bin";
public sealed override bool ChecksumsValid => true;
public sealed override string ChecksumInfo => "No Info.";

View file

@ -22,7 +22,7 @@ namespace PKHeX.Core
public override PersonalTable Personal => PersonalTable.Pt;
public override IReadOnlyList<ushort> HeldItems => Legal.HeldItems_Pt;
protected override SaveFile CloneInternal() => new SAV4Ranch((byte[])Data.Clone());
public override string PlayTimeString => Checksums.CRC16(Data, 0, Data.Length).ToString("X4");
public override string PlayTimeString => Checksums.CRC16Invert(Data).ToString("X4");
protected internal override string ShortSummary => $"{OT} {PlayTimeString}";
public override string Extension => ".bin";

View file

@ -22,7 +22,7 @@ namespace PKHeX.Core
Checksum = GetCalculatedChecksum(); // [app,chk)
}
private ushort GetCalculatedChecksum() => Checksums.CRC16_CCITT(Data, Offset + 0x200, 0xC48 - 4 - 0x200); // [app,chk)
private ushort GetCalculatedChecksum() => Checksums.CRC16_CCITT(new ReadOnlySpan<byte>(Data, Offset + 0x200, 0xC48 - 4 - 0x200)); // [app,chk)
private int GetChecksumOffset() => Offset + 0xC48 - 4;

View file

@ -89,7 +89,10 @@ namespace PKHeX.Core
pk7.EncryptedPartyData.CopyTo(data, 0x30); // Copy in pokemon data
GetRawQR(pk7.Species, pk7.Form, pk7.IsShiny, pk7.Gender).CopyTo(data, 0x140);
BitConverter.GetBytes(Checksums.CRC16(data, 0, 0x1A0)).CopyTo(data, 0x1A0);
var span = new ReadOnlySpan<byte>(data, 0, 0x1A0);
var chk = Checksums.CRC16Invert(span);
BitConverter.GetBytes(chk).CopyTo(data, 0x1A0);
return data;
}
}

View file

@ -9,17 +9,14 @@ namespace PKHeX.Core
{
/// <summary>Calculates the CRC16-CCITT checksum over an input byte array.</summary>
/// <param name="data">Input byte array</param>
/// <param name="start">Starting point for checksum</param>
/// <param name="length"></param>
/// <returns>Checksum</returns>
public static ushort CRC16_CCITT(byte[] data, int start, int length)
public static ushort CRC16_CCITT(ReadOnlySpan<byte> data)
{
byte top = 0xFF;
byte bot = 0xFF;
int end = start + length;
for (int i = start; i < end; i++)
foreach (var b in data)
{
var x = data[i] ^ top;
var x = b ^ top;
x ^= (x >> 4);
top = (byte)(bot ^ (x >> 3) ^ (x << 4));
bot = (byte)(x ^ (x << 5));
@ -27,11 +24,6 @@ namespace PKHeX.Core
return (ushort)(top << 8 | bot);
}
/// <summary>Calculates the CRC16-CCITT checksum over an input byte array.</summary>
/// <param name="data">Input byte array</param>
/// <returns>Checksum</returns>
public static ushort CRC16_CCITT(byte[] data) => CRC16_CCITT(data, 0, data.Length);
private static readonly ushort[] crc16 =
{
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
@ -70,31 +62,23 @@ namespace PKHeX.Core
/// <summary>Calculates the 16bit checksum over an input byte array.</summary>
/// <param name="data">Input byte array</param>
/// <param name="start">Offset to start checksum at</param>
/// <param name="length">Length of array to checksum</param>
/// <param name="initial">Initial value for checksum</param>
/// <returns>Checksum</returns>
public static ushort CRC16(byte[] data, int start, int length, ushort initial)
private static ushort CRC16(ReadOnlySpan<byte> data, ushort initial)
{
ushort chk = initial;
for (var i = start; i < start + length; i++)
chk = (ushort)(crc16[(data[i] ^ chk) & 0xFF] ^ chk >> 8);
foreach (var b in data)
chk = (ushort)(crc16[(b ^ chk) & 0xFF] ^ chk >> 8);
return chk;
}
/// <summary>Calculates the 16bit checksum over an input byte array.</summary>
/// <param name="data">Input byte array</param>
/// <param name="start">Offset to start checksum at</param>
/// <param name="length">Length of array to checksum</param>
/// <returns>Checksum</returns>
public static ushort CRC16(byte[] data, int start, int length) => (ushort)~CRC16(data, start, length, unchecked((ushort)~0));
public static ushort CRC16Invert(ReadOnlySpan<byte> data) => (ushort)~CRC16(data, unchecked((ushort)~0));
/// <summary>Calculates the 16bit checksum over an input byte array.</summary>
/// <param name="data">Input byte array</param>
/// <param name="start">Offset to start checksum at</param>
/// <param name="length">Length of array to checksum</param>
/// <returns>Checksum</returns>
public static ushort CRC16NoInvert(byte[] data, int start, int length) => CRC16(data, start, length, 0);
public static ushort CRC16NoInvert(ReadOnlySpan<byte> data) => CRC16(data, 0);
/// <summary>Calculates the 32bit checksum over an input byte array. Used in GBA save files.</summary>
/// <param name="data">Input byte array</param>
@ -112,15 +96,13 @@ namespace PKHeX.Core
/// <summary>Calculates the 16bit checksum over an input byte array. Used in N64 Stadium save files.</summary>
/// <param name="data">Input byte array</param>
/// <param name="start">Offset to start checksum at</param>
/// <param name="length">Length of array to checksum</param>
/// <param name="initial">Initial value for checksum</param>
/// <returns>Checksum</returns>
public static ushort CheckSum16(byte[] data, int start, int length, ushort initial = 0)
public static ushort CheckSum16(ReadOnlySpan<byte> data, ushort initial = 0)
{
ushort acc = initial;
for (int i = 0; i < length; i++)
acc += data[start + i];
foreach (byte b in data)
acc += b;
return acc;
}
@ -132,15 +114,16 @@ namespace PKHeX.Core
/// <summary>Calculates the 32bit checksum over an input byte array. Used in GC R/S BOX.</summary>
/// <param name="data">Input byte array</param>
/// <param name="start">Offset to start checksum at</param>
/// <param name="end">Exclusive end offset to finish the checksum at</param>
/// <returns>Checksum</returns>
public static uint CheckSum16BigInvert(byte[] data, int start, int end)
public static uint CheckSum16BigInvert(ReadOnlySpan<byte> data)
{
ushort chk = 0; // initial value
for (int i = start; i < end; i += 2)
chk += BigEndian.ToUInt16(data, i);
return (uint)(chk << 16 | (ushort)(0xF004 - chk));
while ((data.Length & ~1) != 0)
{
chk += BigEndian.ToUInt16(data);
data = data.Slice(2);
}
return (uint)(chk << 16 | (ushort)(0xF004u - chk));
}
}
}

View file

@ -318,7 +318,8 @@ namespace PKHeX.Core
// Verify first checksum
const int offset = 0x2000;
var chk = Checksums.CheckSum16BigInvert(data, offset + 4, offset + 0x1FFC);
var span = new ReadOnlySpan<byte>(data, offset + 4, 0x1FF8);
var chk = Checksums.CheckSum16BigInvert(span);
var actual = BigEndian.ToUInt32(data, offset);
return chk == actual ? RSBOX : Invalid;
}
@ -417,11 +418,11 @@ namespace PKHeX.Core
// check the checksum block validity; nobody would normally modify this region
ushort chk1 = BitConverter.ToUInt16(data, SIZE_G5BW - 0x100 + 0x8C + 0xE);
ushort actual1 = Checksums.CRC16_CCITT(data, SIZE_G5BW - 0x100, 0x8C);
ushort actual1 = Checksums.CRC16_CCITT(new ReadOnlySpan<byte>(data, SIZE_G5BW - 0x100, 0x8C));
if (chk1 == actual1)
return BW;
ushort chk2 = BitConverter.ToUInt16(data, SIZE_G5B2W2 - 0x100 + 0x94 + 0xE);
ushort actual2 = Checksums.CRC16_CCITT(data, SIZE_G5B2W2 - 0x100, 0x94);
ushort actual2 = Checksums.CRC16_CCITT(new ReadOnlySpan<byte>(data, SIZE_G5B2W2 - 0x100, 0x94));
if (chk2 == actual2)
return B2W2;
return Invalid;

View file

@ -472,13 +472,13 @@ namespace PKHeX.WinForms
bool f = false;
for (int i = 0; i < 2; i++, ofsHallStat += 0x14)
{
var h = BitConverter.ToInt32(SAV.Data, ofsHallStat);
var h = BitConverter.ToInt32(SAV.General, ofsHallStat);
if (h == -1) continue;
for (int j = 0; j < 0x20; j++)
{
for (int k = 0, a = j + 0x20 << 12; k < 2; k++, a += 0x40000)
{
if (h != BitConverter.ToInt32(SAV.Data, a) || BitConverter.ToInt16(SAV.Data, a + 0xBA8) != 0xBA0)
if (h != BitConverter.ToInt32(SAV.General, a) || BitConverter.ToInt16(SAV.General, a + 0xBA8) != 0xBA0)
continue;
f = true;
@ -507,7 +507,11 @@ namespace PKHeX.WinForms
// Fill List
CB_Species.InitializeBinding();
CB_Species.DataSource = new BindingSource(GameInfo.SpeciesDataSource.Skip(1).Where(id => id.Value <= SAV.MaxSpeciesID).ToList(), null);
var speciesList = GameInfo.SpeciesDataSource.ToList();
speciesList.RemoveAt(0);
speciesList.RemoveAll(z => z.Value > SAV.MaxSpeciesID);
CB_Species.DataSource = new BindingSource(speciesList, null);
editing = false;
CB_Stats1.SelectedIndex = 0;
@ -525,7 +529,7 @@ namespace PKHeX.WinForms
}
if (HallStatUpdated)
BitConverter.GetBytes(Checksums.CRC16_CCITT(SAV.Data, ofsHallStat, 0xBAE)).CopyTo(SAV.Data, ofsHallStat + 0xBAE);
BitConverter.GetBytes(Checksums.CRC16_CCITT(new ReadOnlySpan<byte>(SAV.General, ofsHallStat, 0xBAE))).CopyTo(SAV.General, ofsHallStat + 0xBAE);
}
private void SetPrints()
@ -746,7 +750,7 @@ namespace PKHeX.WinForms
if (ofsHallStat > 0)
{
ushort v = BitConverter.ToUInt16(SAV.Data, ofsHallStat + 4 + (0x3DE * CB_Stats2.SelectedIndex) + (species << 1));
ushort v = BitConverter.ToUInt16(SAV.General, ofsHallStat + 4 + (0x3DE * CB_Stats2.SelectedIndex) + (species << 1));
NUD_HallStreaks.Value = v > 9999 ? 9999 : v;
}
}
@ -774,7 +778,7 @@ namespace PKHeX.WinForms
{
if (editing || ofsHallStat < 0)
return;
BitConverter.GetBytes((ushort)NUD_HallStreaks.Value).CopyTo(SAV.Data, ofsHallStat + 4 + (0x3DE * CB_Stats2.SelectedIndex) + (species << 1));
BitConverter.GetBytes((ushort)NUD_HallStreaks.Value).CopyTo(SAV.General, ofsHallStat + 4 + (0x3DE * CB_Stats2.SelectedIndex) + (species << 1));
HallStatUpdated = true;
}
#endregion