diff --git a/PKHeX.Core/Saves/Blocks/BlockInfoRSBOX.cs b/PKHeX.Core/Saves/Blocks/BlockInfoRSBOX.cs
index 5f5c51af4..d6f5386e7 100644
--- a/PKHeX.Core/Saves/Blocks/BlockInfoRSBOX.cs
+++ b/PKHeX.Core/Saves/Blocks/BlockInfoRSBOX.cs
@@ -40,15 +40,7 @@ private uint GetChecksum(byte[] data)
int start = Offset + 4;
int end = start + Length - 4;
- return GetChecksum(data, start, end);
- }
-
- private static uint GetChecksum(byte[] data, int start, int end)
- {
- ushort chk = 0; // initial value
- for (int j = start; j < end; j += 2)
- chk += BigEndian.ToUInt16(data, j);
- return (uint)(chk << 16 | (ushort)(0xF004 - chk));
+ return Checksums.CheckSum16BigInvert(data, start, end);
}
}
-}
\ No newline at end of file
+}
diff --git a/PKHeX.Core/Saves/Util/Checksums.cs b/PKHeX.Core/Saves/Util/Checksums.cs
index 99969f9d4..d57a7e28d 100644
--- a/PKHeX.Core/Saves/Util/Checksums.cs
+++ b/PKHeX.Core/Saves/Util/Checksums.cs
@@ -129,5 +129,18 @@ public static ushort CheckSum16(byte[] data, int start, int length, ushort initi
/// Initial value for checksum
/// Checksum
public static ushort CheckSum32(byte[] data, uint initial = 0) => CheckSum32(data, 0, data.Length, initial);
+
+ /// 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)
+ {
+ 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));
+ }
}
-}
\ No newline at end of file
+}
diff --git a/PKHeX.Core/Saves/Util/SaveUtil.cs b/PKHeX.Core/Saves/Util/SaveUtil.cs
index 6e607ab04..98213b48f 100644
--- a/PKHeX.Core/Saves/Util/SaveUtil.cs
+++ b/PKHeX.Core/Saves/Util/SaveUtil.cs
@@ -273,20 +273,20 @@ private static GameVersion GetIsG3SAV(byte[] data)
const int blockCount = 14;
const int blockSize = 0x1000;
int ofs = blockCount * blockSize * s;
- int[] order = new int[blockCount];
+ var order = new short[blockCount];
for (int i = 0; i < order.Length; i++)
- order[i] = BitConverter.ToUInt16(data, (i * blockSize) + 0xFF4 + ofs);
+ order[i] = BitConverter.ToInt16(data, (i * blockSize) + 0xFF4 + ofs);
- if (Array.FindIndex(order, i => i > 0xD) >= 0) // invalid block ID
+ if (Array.FindIndex(order, i => (uint)i > 0xD) >= 0) // invalid block ID
continue;
- int block0 = Array.IndexOf(order, 0);
+ int block0 = Array.IndexOf(order, (short)0);
// Sometimes not all blocks are present (start of game), yielding multiple block0's.
// Real 0th block comes before block1.
if (order[0] == 1 && block0 != order.Length - 1)
continue;
- if (Array.FindIndex(order, v => v != 0) < 0) // all blocks are 0
+ if (Array.TrueForAll(order, v => v == 0)) // all blocks are 0
continue;
// Detect RS/E/FRLG
return SAV3.GetVersion(data, (blockSize * block0) + ofs);
@@ -302,21 +302,11 @@ private static GameVersion GetIsG3BOXSAV(byte[] data)
if (data.Length is not SIZE_G3BOX)
return Invalid;
- byte[] sav = data;
-
// Verify first checksum
- ushort chk = 0; // initial value
- var ofs = data.Length - SIZE_G3BOX + 0x2000;
- for (int i = 0x4; i < 0x1FFC; i += 2)
- chk += BigEndian.ToUInt16(sav, ofs + i);
-
- ushort chkA = chk;
- ushort chkB = (ushort)(0xF004 - chkA);
-
- ushort CHK_A = BigEndian.ToUInt16(sav, ofs + 0);
- ushort CHK_B = BigEndian.ToUInt16(sav, ofs + 2);
-
- return CHK_A == chkA && CHK_B == chkB ? RSBOX : Invalid;
+ const int offset = 0x2000;
+ var chk = Checksums.CheckSum16BigInvert(data, offset + 4, offset + 0x1FFC);
+ var actual = BigEndian.ToUInt32(data, offset);
+ return chk == actual ? RSBOX : Invalid;
}
/// Checks to see if the data belongs to a Colosseum save
@@ -328,10 +318,10 @@ private static GameVersion GetIsG3COLOSAV(byte[] data)
return Invalid;
// Check the intro bytes for each save slot
- int offset = data.Length - SIZE_G3COLO;
+ const int offset = 0x6000;
for (int i = 0; i < 3; i++)
{
- var ofs = 0x6000 + offset + (0x1E000 * i);
+ var ofs = offset + (0x1E000 * i);
if (BitConverter.ToUInt32(data, ofs) != 0x00000101)
return Invalid;
}
@@ -347,10 +337,10 @@ private static GameVersion GetIsG3XDSAV(byte[] data)
return Invalid;
// Check the intro bytes for each save slot
- int offset = data.Length - SIZE_G3XD;
+ const int offset = 0x6000;
for (int i = 0; i < 2; i++)
{
- var ofs = 0x6000 + offset + (0x28000 * i);
+ var ofs = offset + (0x28000 * i);
if ((BitConverter.ToUInt32(data, ofs) & 0xFFFE_FFFF) != 0x00000101)
return Invalid;
}