diff --git a/PKHeX.Core/Saves/SAV3.cs b/PKHeX.Core/Saves/SAV3.cs index ecc87b4d6..dd17f5942 100644 --- a/PKHeX.Core/Saves/SAV3.cs +++ b/PKHeX.Core/Saves/SAV3.cs @@ -219,9 +219,10 @@ protected override void SetChecksums() { for (int i = 0; i < BLOCK_COUNT; i++) { - byte[] chunk = Data.Skip(ABO + i*SIZE_BLOCK).Take(chunkLength[BlockOrder[i]]).ToArray(); - ushort chk = SaveUtil.CRC32(chunk); - BitConverter.GetBytes(chk).CopyTo(Data, ABO + i*SIZE_BLOCK + 0xFF6); + int ofs = ABO + i * SIZE_BLOCK; + int len = chunkLength[BlockOrder[i]]; + ushort chk = SaveUtil.CRC32(Data, ofs, len); + BitConverter.GetBytes(chk).CopyTo(Data, ofs + 0xFF6); } } public override bool ChecksumsValid @@ -230,9 +231,10 @@ public override bool ChecksumsValid { for (int i = 0; i < BLOCK_COUNT; i++) { - byte[] chunk = Data.Skip(ABO + i * SIZE_BLOCK).Take(chunkLength[BlockOrder[i]]).ToArray(); - ushort chk = SaveUtil.CRC32(chunk); - if (chk != BitConverter.ToUInt16(Data, ABO + i*SIZE_BLOCK + 0xFF6)) + int ofs = ABO + i * SIZE_BLOCK; + int len = chunkLength[BlockOrder[i]]; + ushort chk = SaveUtil.CRC32(Data, ofs, len); + if (chk != BitConverter.ToUInt16(Data, ofs + 0xFF6)) return false; } return true; @@ -245,10 +247,10 @@ public override string ChecksumInfo var list = new List(); for (int i = 0; i < BLOCK_COUNT; i++) { - byte[] chunk = Data.Skip(ABO + i * SIZE_BLOCK).Take(chunkLength[BlockOrder[i]]).ToArray(); - ushort chk = SaveUtil.CRC32(chunk); - ushort old = BitConverter.ToUInt16(Data, ABO + i*SIZE_BLOCK + 0xFF6); - if (chk != old) + int ofs = ABO + i * SIZE_BLOCK; + int len = chunkLength[BlockOrder[i]]; + ushort chk = SaveUtil.CRC32(Data, ofs, len); + if (chk != BitConverter.ToUInt16(Data, ofs + 0xFF6)) list.Add($"Block {BlockOrder[i]:00} @ {i*SIZE_BLOCK:X5} invalid."); } return list.Any() ? string.Join(Environment.NewLine, list) : "Checksums are valid."; diff --git a/PKHeX.Core/Saves/SaveUtil.cs b/PKHeX.Core/Saves/SaveUtil.cs index 011b8f29a..200e79e45 100644 --- a/PKHeX.Core/Saves/SaveUtil.cs +++ b/PKHeX.Core/Saves/SaveUtil.cs @@ -647,20 +647,33 @@ public static ushort CRC16(byte[] data, int start, int length, ushort initial = chk = (ushort) (crc16[(data[i] ^ chk) & 0xFF] ^ chk >> 8); return (ushort)~chk; } + /// Calculates the 32bit checksum over an input byte array. Used in GBA save files. + /// Input byte array + /// Initial value for checksum + /// Checksum + public static ushort CRC16(byte[] data, ushort initial = 0) => CRC16(data, 0, data.Length, initial); public static byte[] Resign7(byte[] sav7) { return MemeCrypto.Resign7(sav7); } /// Calculates the 32bit checksum over an input byte array. Used in GBA save files. /// Input byte array + /// Offset to start checksum at + /// Length of array to checksum + /// Initial value for checksum /// Checksum - public static ushort CRC32(byte[] data) + public static ushort CRC32(byte[] data, int start, int length, uint initial = 0) { - uint val = 0; - for (int i = 0; i < data.Length; i += 4) + uint val = initial; + for (int i = start; i < start + length; i += 4) val += BitConverter.ToUInt32(data, i); return (ushort)(val + (val >> 16)); } + /// Calculates the 32bit checksum over an input byte array. Used in GBA save files. + /// Input byte array + /// Initial value for checksum + /// Checksum + public static ushort CRC32(byte[] data, uint initial = 0) => CRC32(data, 0, data.Length, initial); private static void CheckHeaderFooter(ref byte[] input, ref byte[] header, ref byte[] footer) { if (input.Length > SIZE_G4RAW) // DeSmuME Gen4/5 DSV