Add MemoryCard detection as latest sav, pkmdb

This commit is contained in:
Kurt 2023-05-01 16:51:17 -07:00
parent e5cc0ffd6b
commit 3285ecada9
12 changed files with 111 additions and 24 deletions

View file

@ -58,6 +58,8 @@ public static class SlotInfoLoader
private static void AddPartyData(SaveFile sav, ConcurrentBag<SlotCache> db)
{
var count = sav.PartyCount;
if ((uint)count > 6)
count = 6;
for (var index = 0; index < count; index++)
{
var pk = sav.GetPartySlotAtIndex(index);

View file

@ -47,7 +47,7 @@ public sealed class SAV3GCMemoryCard
0x4000000, // 128MB
};
public static bool IsMemoryCardSize(long Size) => ValidMemoryCardSizes.Contains((int)Size);
public static bool IsMemoryCardSize(long size) => ValidMemoryCardSizes.Contains((int)size);
public static bool IsMemoryCardSize(ReadOnlySpan<byte> Data)
{

View file

@ -12,7 +12,7 @@ public interface ISaveHandler
/// </summary>
/// <param name="size">File size</param>
/// <returns>True if recognized, false if not recognized.</returns>
bool IsRecognized(int size);
bool IsRecognized(long size);
/// <summary>
/// Tries splitting up the <see cref="input"/> into header/footer/data components. Returns null if not a valid save file for this handler.

View file

@ -10,7 +10,7 @@ public sealed class SaveHandlerARDS : ISaveHandler
private const int sizeHeader = 0xA4;
private const int ExpectedSize = SaveUtil.SIZE_G4RAW + sizeHeader; // 0x800A4
public bool IsRecognized(int size) => size is ExpectedSize;
public bool IsRecognized(long size) => size is ExpectedSize;
public SaveHandlerSplitResult TrySplit(ReadOnlySpan<byte> input)
{

View file

@ -19,7 +19,7 @@ public sealed class SaveHandlerBizHawk : ISaveHandler
return _0x0b == _0x14;
}
public bool IsRecognized(int size) => SaveUtil.IsSizeValidNoHandler(size - sizeFooter);
public bool IsRecognized(long size) => SaveUtil.IsSizeValidNoHandler(size - sizeFooter);
public SaveHandlerSplitResult? TrySplit(ReadOnlySpan<byte> input)
{

View file

@ -26,7 +26,7 @@ public sealed class SaveHandlerDeSmuME : ISaveHandler
return true;
}
public bool IsRecognized(int size) => size is ExpectedSize;
public bool IsRecognized(long size) => size is ExpectedSize;
public SaveHandlerSplitResult? TrySplit(ReadOnlySpan<byte> input)
{

View file

@ -38,7 +38,7 @@ public sealed class SaveHandlerGCI : ISaveHandler
return true;
}
public bool IsRecognized(int size) => size is SIZE_G3BOXGCI or SIZE_G3COLOGCI or SIZE_G3XDGCI;
public bool IsRecognized(long size) => size is SIZE_G3BOXGCI or SIZE_G3COLOGCI or SIZE_G3XDGCI;
public SaveHandlerSplitResult? TrySplit(ReadOnlySpan<byte> input)
{

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
@ -164,7 +164,7 @@ public static class SaveFinder
/// <inheritdoc cref="GetSaveFiles"/>
public static IEnumerable<SaveFile> DetectSaveFiles() => GetSaveFiles(Environment.GetLogicalDrives(), true, CustomBackupPaths, true);
/// <inheritdoc cref="TryDetectSaveFile(out PKHeX.Core.SaveFile?)"/>
/// <inheritdoc cref="TryDetectSaveFile(out SaveFile?)"/>
public static bool TryDetectSaveFile([NotNullWhen(true)] out SaveFile? sav) => TryDetectSaveFile(Environment.GetLogicalDrives(), out sav);
public static bool TryDetectSaveFile(IReadOnlyList<string> drives, [NotNullWhen(true)] out SaveFile? sav)

View file

@ -115,7 +115,7 @@ public static class SaveUtil
};
#endif
private static readonly HashSet<int> SizesSV = new()
private static readonly HashSet<long> SizesSV = new()
{
SIZE_G9_0, SIZE_G9_0a,
SIZE_G9_1, SIZE_G9_1a,
@ -129,17 +129,17 @@ public static class SaveUtil
SIZE_G9_3P0, SIZE_G9_3P1,
};
private static readonly HashSet<int> SizesSWSH = new()
private static readonly HashSet<long> SizesSWSH = new()
{
SIZE_G8SWSH, SIZE_G8SWSH_1, SIZE_G8SWSH_2, SIZE_G8SWSH_2B, SIZE_G8SWSH_3, SIZE_G8SWSH_3A, SIZE_G8SWSH_3B, SIZE_G8SWSH_3C,
};
private static readonly HashSet<int> SizesGen2 = new()
private static readonly HashSet<long> SizesGen2 = new()
{
SIZE_G2RAW_U, SIZE_G2VC_U, SIZE_G2BAT_U, SIZE_G2EMU_U, SIZE_G2RAW_J, SIZE_G2BAT_J, SIZE_G2EMU_J, SIZE_G2VC_J,
};
private static readonly HashSet<int> Sizes = new(SizesGen2.Concat(SizesSWSH).Concat(SizesSV))
private static readonly HashSet<long> Sizes = new(SizesGen2.Concat(SizesSWSH).Concat(SizesSV))
{
SIZE_G8LA, SIZE_G8LA_1, SIZE_G8BDSP, SIZE_G8BDSP_1, SIZE_G8BDSP_2, SIZE_G8BDSP_3,
// SizesSWSH covers gen8 sizes since there's so many
@ -858,10 +858,9 @@ public static class SaveUtil
try
{
var searchOption = deep ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
// force evaluation so that an invalid path will throw before we return true/false.
// EnumerateFiles throws an exception while iterating, which won't be caught by the try-catch here.
var files = Directory.GetFiles(folderPath, "*", searchOption);
result = files.Where(f => !(ignoreBackups && IsBackup(f)) && IsSizeValid(FileUtil.GetFileSize(f)));
var files = Directory.EnumerateFiles(folderPath, "*", searchOption)
.IterateSafe(log: z => System.Diagnostics.Debug.WriteLine(z));
result = FilterSaveFiles(ignoreBackups, files);
return true;
}
catch (Exception ex)
@ -876,24 +875,47 @@ public static class SaveUtil
}
}
public static bool IsBackup(string path) => Path.GetFileNameWithoutExtension(path).Equals("backup", StringComparison.OrdinalIgnoreCase) || Path.GetExtension(path) is ".bak";
private static IEnumerable<string> FilterSaveFiles(bool ignoreBackups, IEnumerable<string> files)
{
foreach (string file in files)
{
if (ignoreBackups && IsBackup(file))
continue;
var size = FileUtil.GetFileSize(file);
if (!IsSizeValid(size))
continue;
yield return file;
}
}
public static bool IsBackup(string path)
{
var fn = Path.GetFileNameWithoutExtension(path);
if (fn == "backup")
return true;
var ext = Path.GetExtension(path);
return ext == ".bak";
}
/// <summary>
/// Determines whether the save data size is valid for automatically detecting saves.
/// </summary>
/// <param name="size">Size in bytes of the save data</param>
/// <returns>A boolean indicating whether or not the save data size is valid.</returns>
public static bool IsSizeValid(int size) => IsSizeValidNoHandler(size) || IsSizeValidHandler(size);
public static bool IsSizeValid(long size) => IsSizeValidNoHandler(size) || IsSizeValidHandler(size) || SAV3GCMemoryCard.IsMemoryCardSize(size);
/// <summary>
/// Determines whether the save data size is valid for automatically detecting saves.
/// </summary>
/// <remarks>Only checks the <see cref="Handlers"/> list.</remarks>
public static bool IsSizeValidHandler(int size) => Handlers.Any(z => z.IsRecognized(size));
public static bool IsSizeValidHandler(long size) => Handlers.Any(z => z.IsRecognized(size));
/// <summary>
/// Determines whether the save data size is valid for automatically detecting saves.
/// </summary>
/// <remarks>Does not check the <see cref="Handlers"/> list.</remarks>
public static bool IsSizeValidNoHandler(int size) => Sizes.Contains(size);
public static bool IsSizeValidNoHandler(long size) => Sizes.Contains(size);
}

View file

@ -73,18 +73,43 @@ public static class FileUtil
catch { return true; }
}
public static int GetFileSize(string path)
public static long GetFileSize(string path)
{
try
{
var size = new FileInfo(path).Length;
var fi = new FileInfo(path);
var size = fi.Length;
if (size > int.MaxValue)
return -1;
return (int)size;
return size;
}
catch { return -1; } // Bad File / Locked
}
public static IEnumerable<T> IterateSafe<T>(this IEnumerable<T> source, int failOut = 10, Action<Exception>? log = null)
{
using var enumerator = source.GetEnumerator();
int ctr = 0;
while (true)
{
try
{
var next = enumerator.MoveNext();
if (!next)
yield break;
}
catch (Exception ex)
{
log?.Invoke(ex);
if (++ctr >= failOut)
yield break;
continue;
}
ctr = 0;
yield return enumerator.Current;
}
}
private static bool TryGetGP1(byte[] data, [NotNullWhen(true)] out GP1? gp1)
{
gp1 = null;
@ -167,6 +192,16 @@ public static class FileUtil
return true;
}
/// <inheritdoc cref="TryGetMemoryCard(byte[], out SAV3GCMemoryCard?)"/>
public static bool TryGetMemoryCard(string file, [NotNullWhen(true)] out SAV3GCMemoryCard? memcard)
{
memcard = null;
if (!File.Exists(file))
return false;
var data = File.ReadAllBytes(file);
return TryGetMemoryCard(data, out memcard);
}
/// <summary>
/// Tries to get an <see cref="PKM"/> object from the input parameters.
/// </summary>

View file

@ -593,6 +593,7 @@ public partial class Main : Form
var mcsav = SaveUtil.GetVariantSAV(gc);
if (mcsav is null)
return false;
mcsav.Metadata.SetExtraInfo(path);
return OpenSAV(mcsav, path);
}
return false;

View file

@ -433,13 +433,40 @@ public partial class SAV_Database : Form
var sav = SaveUtil.GetVariantSAV(file);
if (sav == null)
{
Debug.WriteLine("Unable to load SaveFile: " + file);
if (FileUtil.TryGetMemoryCard(file, out var mc))
TryAddPKMsFromMemoryCard(dbTemp, mc, file);
else
Debug.WriteLine($"Unable to load SaveFile: {file}");
return;
}
SlotInfoLoader.AddFromSaveFile(sav, dbTemp);
}
private static void TryAddPKMsFromMemoryCard(ConcurrentBag<SlotCache> dbTemp, SAV3GCMemoryCard mc, string file)
{
var state = mc.GetMemoryCardState();
if (state == GCMemoryCardState.Invalid)
return;
if (mc.HasCOLO)
TryAdd(dbTemp, mc, file, GameVersion.COLO);
if (mc.HasXD)
TryAdd(dbTemp, mc, file, GameVersion.XD);
if (mc.HasRSBOX)
TryAdd(dbTemp, mc, file, GameVersion.RSBOX);
static void TryAdd(ConcurrentBag<SlotCache> dbTemp, SAV3GCMemoryCard mc, string path, GameVersion game)
{
mc.SelectSaveGame(game);
var sav = SaveUtil.GetVariantSAV(mc);
if (sav is null)
return;
sav.Metadata.SetExtraInfo(path);
SlotInfoLoader.AddFromSaveFile(sav, dbTemp);
}
}
// IO Usage
private void OpenDB(object sender, EventArgs e)
{