mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-10 14:44:24 +00:00
Add MemoryCard detection as latest sav, pkmdb
This commit is contained in:
parent
e5cc0ffd6b
commit
3285ecada9
12 changed files with 111 additions and 24 deletions
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue