From 871f9b0627f1bd6915b5ae97b4a0747be4f92529 Mon Sep 17 00:00:00 2001 From: Kurt Date: Fri, 14 May 2021 12:30:40 -0700 Subject: [PATCH] Convert checksum operations to span-based Fix SAV.Data references in SAV_Misc4 to use the General block instead --- PKHeX.Core/Saves/Blocks/BlockInfo3DS.cs | 6 +-- PKHeX.Core/Saves/Blocks/BlockInfoNDS.cs | 2 +- PKHeX.Core/Saves/Blocks/BlockInfoRSBOX.cs | 14 ++--- PKHeX.Core/Saves/SAV1Stadium.cs | 4 +- PKHeX.Core/Saves/SAV1StadiumJ.cs | 4 +- PKHeX.Core/Saves/SAV2Stadium.cs | 4 +- PKHeX.Core/Saves/SAV4.cs | 2 +- PKHeX.Core/Saves/SAV5.cs | 2 +- PKHeX.Core/Saves/Storage/Bank3.cs | 2 +- PKHeX.Core/Saves/Storage/Bank4.cs | 2 +- PKHeX.Core/Saves/Storage/BulkStorage.cs | 2 +- PKHeX.Core/Saves/Storage/SAV4Ranch.cs | 2 +- .../Saves/Substructures/Gen6/LinkBlock6.cs | 2 +- PKHeX.Core/Saves/Substructures/Gen7/QR7.cs | 5 +- PKHeX.Core/Saves/Util/Checksums.cs | 53 +++++++------------ PKHeX.Core/Saves/Util/SaveUtil.cs | 7 +-- .../Subforms/Save Editors/Gen4/SAV_Misc4.cs | 16 +++--- 17 files changed, 61 insertions(+), 68 deletions(-) diff --git a/PKHeX.Core/Saves/Blocks/BlockInfo3DS.cs b/PKHeX.Core/Saves/Blocks/BlockInfo3DS.cs index 61a61c5d2..b900d43b3 100644 --- a/PKHeX.Core/Saves/Blocks/BlockInfo3DS.cs +++ b/PKHeX.Core/Saves/Blocks/BlockInfo3DS.cs @@ -50,19 +50,19 @@ protected override void SetChecksum(byte[] data) 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(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(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(data, Offset, Length)); } public static class BlockInfoBEEFUtil diff --git a/PKHeX.Core/Saves/Blocks/BlockInfoNDS.cs b/PKHeX.Core/Saves/Blocks/BlockInfoNDS.cs index 4e0959703..ef3526a97 100644 --- a/PKHeX.Core/Saves/Blocks/BlockInfoNDS.cs +++ b/PKHeX.Core/Saves/Blocks/BlockInfoNDS.cs @@ -18,7 +18,7 @@ public BlockInfoNDS(int offset, int length, int chkOffset, int chkMirror) ChecksumMirror = chkMirror; } - private ushort GetChecksum(byte[] data) => Checksums.CRC16_CCITT(data, Offset, Length); + private ushort GetChecksum(byte[] data) => Checksums.CRC16_CCITT(new ReadOnlySpan(data, Offset, Length)); protected override bool ChecksumValid(byte[] data) { diff --git a/PKHeX.Core/Saves/Blocks/BlockInfoRSBOX.cs b/PKHeX.Core/Saves/Blocks/BlockInfoRSBOX.cs index d6f5386e7..205439916 100644 --- a/PKHeX.Core/Saves/Blocks/BlockInfoRSBOX.cs +++ b/PKHeX.Core/Saves/Blocks/BlockInfoRSBOX.cs @@ -1,3 +1,5 @@ +using System; + namespace PKHeX.Core { /// @@ -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 @@ protected override void SetChecksum(byte[] data) private uint GetChecksum(byte[] data) { int start = Offset + 4; - int end = start + Length - 4; - - return Checksums.CheckSum16BigInvert(data, start, end); + var span = new ReadOnlySpan(data, start, ChecksumRegionSize); + return Checksums.CheckSum16BigInvert(span); } } } diff --git a/PKHeX.Core/Saves/SAV1Stadium.cs b/PKHeX.Core/Saves/SAV1Stadium.cs index 1f9af6abe..ff27bf6df 100644 --- a/PKHeX.Core/Saves/SAV1Stadium.cs +++ b/PKHeX.Core/Saves/SAV1Stadium.cs @@ -75,7 +75,7 @@ protected override bool GetIsBoxChecksumValid(int i) { var boxOfs = GetBoxOffset(i) - ListHeaderSize; var size = BoxSize - 2; - var chk = Checksums.CheckSum16(Data, boxOfs, size); + var chk = Checksums.CheckSum16(new ReadOnlySpan(Data, boxOfs, size)); var actual = BigEndian.ToUInt16(Data, boxOfs + size); return chk == actual; } @@ -84,7 +84,7 @@ protected override void SetBoxChecksum(int i) { var boxOfs = GetBoxOffset(i) - ListHeaderSize; var size = BoxSize - 2; - var chk = Checksums.CheckSum16(Data, boxOfs, size); + var chk = Checksums.CheckSum16(new ReadOnlySpan(Data, boxOfs, size)); BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size); } diff --git a/PKHeX.Core/Saves/SAV1StadiumJ.cs b/PKHeX.Core/Saves/SAV1StadiumJ.cs index 32a93a528..df0bbf24b 100644 --- a/PKHeX.Core/Saves/SAV1StadiumJ.cs +++ b/PKHeX.Core/Saves/SAV1StadiumJ.cs @@ -60,7 +60,7 @@ protected override bool GetIsBoxChecksumValid(int i) { var boxOfs = GetBoxOffset(i) - ListHeaderSize; const int size = BoxSizeJ - 2; - var chk = Checksums.CheckSum16(Data, boxOfs, size); + var chk = Checksums.CheckSum16(new ReadOnlySpan(Data, boxOfs, size)); var actual = BigEndian.ToUInt16(Data, boxOfs + size); return chk == actual; } @@ -69,7 +69,7 @@ protected override void SetBoxChecksum(int i) { var boxOfs = GetBoxOffset(i) - ListHeaderSize; const int size = BoxSizeJ - 2; - var chk = Checksums.CheckSum16(Data, boxOfs, size); + var chk = Checksums.CheckSum16(new ReadOnlySpan(Data, boxOfs, size)); BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size); } diff --git a/PKHeX.Core/Saves/SAV2Stadium.cs b/PKHeX.Core/Saves/SAV2Stadium.cs index 87efac0bb..595fa5805 100644 --- a/PKHeX.Core/Saves/SAV2Stadium.cs +++ b/PKHeX.Core/Saves/SAV2Stadium.cs @@ -73,7 +73,7 @@ protected override bool GetIsBoxChecksumValid(int i) { var boxOfs = GetBoxOffset(i) - ListHeaderSizeBox; var size = BoxSize - 2; - var chk = Checksums.CheckSum16(Data, boxOfs, size); + var chk = Checksums.CheckSum16(new ReadOnlySpan(Data, boxOfs, size)); var actual = BigEndian.ToUInt16(Data, boxOfs + size); return chk == actual; } @@ -109,7 +109,7 @@ protected override void SetBoxChecksum(int i) { var boxOfs = GetBoxOffset(i) - ListHeaderSizeBox; var size = BoxSize - 2; - var chk = Checksums.CheckSum16(Data, boxOfs, size); + var chk = Checksums.CheckSum16(new ReadOnlySpan(Data, boxOfs, size)); BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size); } diff --git a/PKHeX.Core/Saves/SAV4.cs b/PKHeX.Core/Saves/SAV4.cs index c11a766b9..d7266c1c2 100644 --- a/PKHeX.Core/Saves/SAV4.cs +++ b/PKHeX.Core/Saves/SAV4.cs @@ -104,7 +104,7 @@ public override void CopyChangesFrom(SaveFile sav) // 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(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); diff --git a/PKHeX.Core/Saves/SAV5.cs b/PKHeX.Core/Saves/SAV5.cs index 19e76edac..73fd511e5 100644 --- a/PKHeX.Core/Saves/SAV5.cs +++ b/PKHeX.Core/Saves/SAV5.cs @@ -175,7 +175,7 @@ public byte[] CGearSkinData 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(Data, footer + 0x100, 4)); BitConverter.GetBytes(skinchkval).CopyTo(Data, footer + 0x112); // Indicate in the save file that data is present diff --git a/PKHeX.Core/Saves/Storage/Bank3.cs b/PKHeX.Core/Saves/Storage/Bank3.cs index 35b4424f3..4fe67522c 100644 --- a/PKHeX.Core/Saves/Storage/Bank3.cs +++ b/PKHeX.Core/Saves/Storage/Bank3.cs @@ -12,7 +12,7 @@ public sealed class Bank3 : BulkStorage public override PersonalTable Personal => PersonalTable.RS; public override IReadOnlyList 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"; diff --git a/PKHeX.Core/Saves/Storage/Bank4.cs b/PKHeX.Core/Saves/Storage/Bank4.cs index 4abd7bafd..9f0230b70 100644 --- a/PKHeX.Core/Saves/Storage/Bank4.cs +++ b/PKHeX.Core/Saves/Storage/Bank4.cs @@ -12,7 +12,7 @@ public sealed class Bank4 : BulkStorage public override PersonalTable Personal => PersonalTable.HGSS; public override IReadOnlyList 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"; diff --git a/PKHeX.Core/Saves/Storage/BulkStorage.cs b/PKHeX.Core/Saves/Storage/BulkStorage.cs index 099f01436..52ea07724 100644 --- a/PKHeX.Core/Saves/Storage/BulkStorage.cs +++ b/PKHeX.Core/Saves/Storage/BulkStorage.cs @@ -21,7 +21,7 @@ protected BulkStorage(byte[] data, Type t, int start, int slotsPerBox = 30) : ba 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(Data, Box, Data.Length - Box)):X4}"; public override string Extension => ".bin"; public sealed override bool ChecksumsValid => true; public sealed override string ChecksumInfo => "No Info."; diff --git a/PKHeX.Core/Saves/Storage/SAV4Ranch.cs b/PKHeX.Core/Saves/Storage/SAV4Ranch.cs index 57a1cfa9d..eb96d2132 100644 --- a/PKHeX.Core/Saves/Storage/SAV4Ranch.cs +++ b/PKHeX.Core/Saves/Storage/SAV4Ranch.cs @@ -22,7 +22,7 @@ public sealed class SAV4Ranch : BulkStorage, ISaveFileRevision public override PersonalTable Personal => PersonalTable.Pt; public override IReadOnlyList 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"; diff --git a/PKHeX.Core/Saves/Substructures/Gen6/LinkBlock6.cs b/PKHeX.Core/Saves/Substructures/Gen6/LinkBlock6.cs index 904a1d3a7..e696359e2 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/LinkBlock6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/LinkBlock6.cs @@ -22,7 +22,7 @@ public void SetLinkInfo(PL6 pl6) 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(Data, Offset + 0x200, 0xC48 - 4 - 0x200)); // [app,chk) private int GetChecksumOffset() => Offset + 0xC48 - 4; diff --git a/PKHeX.Core/Saves/Substructures/Gen7/QR7.cs b/PKHeX.Core/Saves/Substructures/Gen7/QR7.cs index fe876563f..8a3691dfd 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/QR7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/QR7.cs @@ -89,7 +89,10 @@ public static byte[] GenerateQRData(PK7 pk7, int box = 0, int slot = 0, int num_ 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(data, 0, 0x1A0); + var chk = Checksums.CRC16Invert(span); + BitConverter.GetBytes(chk).CopyTo(data, 0x1A0); return data; } } diff --git a/PKHeX.Core/Saves/Util/Checksums.cs b/PKHeX.Core/Saves/Util/Checksums.cs index d57a7e28d..bf4603f35 100644 --- a/PKHeX.Core/Saves/Util/Checksums.cs +++ b/PKHeX.Core/Saves/Util/Checksums.cs @@ -9,17 +9,14 @@ public static class Checksums { /// Calculates the CRC16-CCITT checksum over an input byte array. /// Input byte array - /// Starting point for checksum - /// /// Checksum - public static ushort CRC16_CCITT(byte[] data, int start, int length) + public static ushort CRC16_CCITT(ReadOnlySpan 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 @@ public static ushort CRC16_CCITT(byte[] data, int start, int length) return (ushort)(top << 8 | bot); } - /// Calculates the CRC16-CCITT checksum over an input byte array. - /// Input byte array - /// Checksum - 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 @@ public static ushort CRC16_CCITT(byte[] data, int start, int length) /// Calculates the 16bit checksum over an input byte array. /// Input byte array - /// Offset to start checksum at - /// Length of array to checksum /// Initial value for checksum /// Checksum - public static ushort CRC16(byte[] data, int start, int length, ushort initial) + private static ushort CRC16(ReadOnlySpan 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; } /// Calculates the 16bit checksum over an input byte array. /// Input byte array - /// Offset to start checksum at - /// Length of array to checksum - /// Checksum - public static ushort CRC16(byte[] data, int start, int length) => (ushort)~CRC16(data, start, length, unchecked((ushort)~0)); + public static ushort CRC16Invert(ReadOnlySpan data) => (ushort)~CRC16(data, unchecked((ushort)~0)); /// Calculates the 16bit checksum over an input byte array. /// Input byte array - /// Offset to start checksum at - /// Length of array to checksum - /// Checksum - public static ushort CRC16NoInvert(byte[] data, int start, int length) => CRC16(data, start, length, 0); + public static ushort CRC16NoInvert(ReadOnlySpan data) => CRC16(data, 0); /// Calculates the 32bit checksum over an input byte array. Used in GBA save files. /// Input byte array @@ -112,15 +96,13 @@ public static ushort CheckSum32(byte[] data, int start, int length, uint initial /// Calculates the 16bit checksum over an input byte array. Used in N64 Stadium save files. /// Input byte array - /// Offset to start checksum at - /// Length of array to checksum /// Initial value for checksum /// Checksum - public static ushort CheckSum16(byte[] data, int start, int length, ushort initial = 0) + public static ushort CheckSum16(ReadOnlySpan 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 @@ public static ushort CheckSum16(byte[] data, int start, int length, ushort initi /// Calculates the 32bit checksum over an input byte array. Used in GC R/S BOX. /// Input byte array - /// Offset to start checksum at - /// Exclusive end offset to finish the checksum at /// Checksum - public static uint CheckSum16BigInvert(byte[] data, int start, int end) + public static uint CheckSum16BigInvert(ReadOnlySpan 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)); } } } diff --git a/PKHeX.Core/Saves/Util/SaveUtil.cs b/PKHeX.Core/Saves/Util/SaveUtil.cs index 64a1424a0..02d56c974 100644 --- a/PKHeX.Core/Saves/Util/SaveUtil.cs +++ b/PKHeX.Core/Saves/Util/SaveUtil.cs @@ -318,7 +318,8 @@ private static GameVersion GetIsG3BOXSAV(byte[] data) // Verify first checksum const int offset = 0x2000; - var chk = Checksums.CheckSum16BigInvert(data, offset + 4, offset + 0x1FFC); + var span = new ReadOnlySpan(data, offset + 4, 0x1FF8); + var chk = Checksums.CheckSum16BigInvert(span); var actual = BigEndian.ToUInt32(data, offset); return chk == actual ? RSBOX : Invalid; } @@ -417,11 +418,11 @@ private static GameVersion GetIsG5SAV(byte[] data) // 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(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(data, SIZE_G5B2W2 - 0x100, 0x94)); if (chk2 == actual2) return B2W2; return Invalid; diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen4/SAV_Misc4.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen4/SAV_Misc4.cs index 557dbac53..872240222 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen4/SAV_Misc4.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen4/SAV_Misc4.cs @@ -472,13 +472,13 @@ private void ReadBattleFrontier() 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 @@ private void ReadBattleFrontier() // 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 @@ private void SaveBattleFrontier() } if (HallStatUpdated) - BitConverter.GetBytes(Checksums.CRC16_CCITT(SAV.Data, ofsHallStat, 0xBAE)).CopyTo(SAV.Data, ofsHallStat + 0xBAE); + BitConverter.GetBytes(Checksums.CRC16_CCITT(new ReadOnlySpan(SAV.General, ofsHallStat, 0xBAE))).CopyTo(SAV.General, ofsHallStat + 0xBAE); } private void SetPrints() @@ -746,7 +750,7 @@ private void GetHallStat() 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 @@ private void NUD_HallStreaks_ValueChanged(object sender, EventArgs e) { 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