From 64428539bdb69f8b1ae29a3e202eae0e5adf0be5 Mon Sep 17 00:00:00 2001 From: Kurt Date: Sat, 23 May 2020 22:17:03 -0700 Subject: [PATCH] Fix sav4 active block detect Closes #2697 ty @Ammako ! lazy slice check has flaws; just compare the major/minor counters in the save footer and check the uninitialized cases nobody is gonna save the game 2^32 times, but check for the overflow case too! first ff'd is preferred when the second is ff'd-1! --- PKHeX.Core/Saves/SAV4.cs | 24 +----------- PKHeX.Core/Saves/SAV4BlockDetection.cs | 53 ++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 22 deletions(-) create mode 100644 PKHeX.Core/Saves/SAV4BlockDetection.cs diff --git a/PKHeX.Core/Saves/SAV4.cs b/PKHeX.Core/Saves/SAV4.cs index 47fd480e5..10c440d7a 100644 --- a/PKHeX.Core/Saves/SAV4.cs +++ b/PKHeX.Core/Saves/SAV4.cs @@ -149,30 +149,10 @@ public override string ChecksumInfo private static int GetActiveBlock(byte[] data, int begin, int length) { - // Check to see if the save is initialized completely - // if the block is not initialized, fall back to the other save. - var start = begin; - if (data.IsRangeAll((byte)0, start, 10) || data.IsRangeAll((byte)0xFF, start, 10)) - return 1; - start += PartitionSize; // check other save - if (data.IsRangeAll((byte)0, start, 10) || data.IsRangeAll((byte)0xFF, start, 10)) - return 0; - - // Fall back to highest value save counter - return GetActiveBlockViaCounter(data, begin, length); + int offset = begin + length - 0x14; + return SAV4BlockDetection.CompareFooters(data, offset, offset + PartitionSize); } - private static int GetActiveBlockViaCounter(byte[] data, int begin, int length) - { - int ofs = GetBlockSaveCounterOffset(begin, length); - var block0 = BitConverter.ToUInt16(data, ofs); - var block1 = BitConverter.ToUInt16(data, ofs + PartitionSize); - bool first = block0 >= block1; - return first ? 0 : 1; - } - - private static int GetBlockSaveCounterOffset(int start, int length) => start + length - 0x10; - protected int WondercardFlags = int.MinValue; protected int AdventureInfo = int.MinValue; protected int Seal = int.MinValue; diff --git a/PKHeX.Core/Saves/SAV4BlockDetection.cs b/PKHeX.Core/Saves/SAV4BlockDetection.cs new file mode 100644 index 000000000..c186dc8d0 --- /dev/null +++ b/PKHeX.Core/Saves/SAV4BlockDetection.cs @@ -0,0 +1,53 @@ +using System; + +namespace PKHeX.Core +{ + /// + /// Finds the index of the most recent save block for blocks. + /// + public static class SAV4BlockDetection + { + private const int First = 0; + private const int Second = 1; + private const int Same = 2; + + /// + /// Compares the footers of the two blocks to determine which is newest. + /// + /// 0=Primary, 1=Secondary. + public static int CompareFooters(byte[] data, int offset1, int offset2) + { + // Major Counters + var major1 = BitConverter.ToUInt32(data, offset1); + var major2 = BitConverter.ToUInt32(data, offset2); + var result1 = CompareCounters(major1, major2); + if (result1 == First) + return First; + if (result1 == Second) + return Second; + + // Minor Counters + var minor1 = BitConverter.ToUInt32(data, offset1 + 4); + var minor2 = BitConverter.ToUInt32(data, offset2 + 4); + var result2 = CompareCounters(minor1, minor2); + return result2 == Second ? Second : First; // Same -> First, shouldn't happen for valid saves. + } + + private static int CompareCounters(uint counter1, uint counter2) + { + // Uninitialized + if (counter1 == uint.MaxValue && counter2 == 0) + return Second; + if (counter1 == 0 && counter2 == uint.MaxValue) + return First; + + // Different + if (counter1 > counter2) + return First; + if (counter1 < counter2) + return Second; + + return Same; + } + } +}