mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-04-25 08:10:48 -05:00
Add cancellation to savefile detection calls
5s timeout on detection, roughly
This commit is contained in:
parent
56ab067ad9
commit
5ab6dbc0ac
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
|
|
@ -75,14 +76,14 @@ public void ReadTemplateIfNoEntity(string path)
|
|||
|
||||
private static SaveFile? ReadSettingsDefinedPKM(IStartupSettings startup, PKM pk) => startup.AutoLoadSaveOnStartup switch
|
||||
{
|
||||
AutoLoadSetting.RecentBackup => SaveFinder.DetectSaveFiles().FirstOrDefault(z => z.IsCompatiblePKM(pk)),
|
||||
AutoLoadSetting.RecentBackup => SaveFinder.DetectSaveFiles(new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token).FirstOrDefault(z => z.IsCompatiblePKM(pk)),
|
||||
AutoLoadSetting.LastLoaded => GetMostRecentlyLoaded(startup.RecentlyLoaded).FirstOrDefault(z => z.IsCompatiblePKM(pk)),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
private static SaveFile? ReadSettingsAnyPKM(IStartupSettings startup) => startup.AutoLoadSaveOnStartup switch
|
||||
{
|
||||
AutoLoadSetting.RecentBackup => SaveFinder.DetectSaveFiles().FirstOrDefault(),
|
||||
AutoLoadSetting.RecentBackup => SaveFinder.DetectSaveFiles(new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token).FirstOrDefault(),
|
||||
AutoLoadSetting.LastLoaded => GetMostRecentlyLoaded(startup.RecentlyLoaded).FirstOrDefault(),
|
||||
_ => null,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
|
|
@ -76,21 +77,23 @@ public static IEnumerable<string> GetSwitchBackupPaths(string root)
|
|||
/// Finds a compatible save file that was most recently saved (by file write time).
|
||||
/// </summary>
|
||||
/// <param name="drives">List of drives on the host machine.</param>
|
||||
/// <param name="token">Cancellation token to cancel the operation.</param>
|
||||
/// <param name="extra">Paths to check in addition to the default paths</param>
|
||||
/// <returns>Reference to a valid save file, if any.</returns>
|
||||
public static SaveFile? FindMostRecentSaveFile(IReadOnlyList<string> drives, params string[] extra)
|
||||
=> FindMostRecentSaveFile(drives, (IEnumerable<string>)extra);
|
||||
public static SaveFile? FindMostRecentSaveFile(IReadOnlyList<string> drives, CancellationToken token, params string[] extra)
|
||||
=> FindMostRecentSaveFile(drives, extra, token);
|
||||
|
||||
/// <summary>
|
||||
/// Finds a compatible save file that was most recently saved (by file write time).
|
||||
/// </summary>
|
||||
/// <param name="drives">List of drives on the host machine.</param>
|
||||
/// <param name="extra">Paths to check in addition to the default paths</param>
|
||||
/// <param name="token">Cancellation token to cancel the operation.</param>
|
||||
/// <returns>Reference to a valid save file, if any.</returns>
|
||||
public static SaveFile? FindMostRecentSaveFile(IReadOnlyList<string> drives, IEnumerable<string> extra)
|
||||
public static SaveFile? FindMostRecentSaveFile(IReadOnlyList<string> drives, IEnumerable<string> extra, CancellationToken token)
|
||||
{
|
||||
var foldersToCheck = GetFoldersToCheck(drives, extra);
|
||||
var result = GetSaveFilePathsFromFolders(foldersToCheck, true, out var possiblePaths);
|
||||
var foldersToCheck = GetFoldersToCheck(drives, extra, token);
|
||||
var result = GetSaveFilePathsFromFolders(foldersToCheck, true, out var possiblePaths, token);
|
||||
if (!result)
|
||||
throw new FileNotFoundException(string.Join(Environment.NewLine, possiblePaths)); // `possiblePaths` contains the error message
|
||||
|
||||
|
|
@ -107,11 +110,12 @@ public static IEnumerable<string> GetSwitchBackupPaths(string root)
|
|||
/// <param name="detect">Detect save files stored in common SD card homebrew locations.</param>
|
||||
/// <param name="extra">Paths to check in addition to the default paths</param>
|
||||
/// <param name="ignoreBackups">Option to ignore backup files.</param>
|
||||
/// <param name="token">Cancellation token to cancel the operation.</param>
|
||||
/// <returns>Valid save files, if any.</returns>
|
||||
public static IEnumerable<SaveFile> GetSaveFiles(IReadOnlyList<string> drives, bool detect, IEnumerable<string> extra, bool ignoreBackups)
|
||||
public static IEnumerable<SaveFile> GetSaveFiles(IReadOnlyList<string> drives, bool detect, IEnumerable<string> extra, bool ignoreBackups, CancellationToken token)
|
||||
{
|
||||
var paths = detect ? GetFoldersToCheck(drives, extra) : extra;
|
||||
var result = GetSaveFilePathsFromFolders(paths, ignoreBackups, out var possiblePaths);
|
||||
var paths = detect ? GetFoldersToCheck(drives, extra, token) : extra;
|
||||
var result = GetSaveFilePathsFromFolders(paths, ignoreBackups, out var possiblePaths, token);
|
||||
if (!result)
|
||||
yield break;
|
||||
|
||||
|
|
@ -124,7 +128,7 @@ public static IEnumerable<SaveFile> GetSaveFiles(IReadOnlyList<string> drives, b
|
|||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetFoldersToCheck(IReadOnlyList<string> drives, IEnumerable<string> extra)
|
||||
public static IEnumerable<string> GetFoldersToCheck(IReadOnlyList<string> drives, IEnumerable<string> extra, CancellationToken token)
|
||||
{
|
||||
var foldersToCheck = extra.Where(f => !string.IsNullOrWhiteSpace(f)).Concat(CustomBackupPaths);
|
||||
|
||||
|
|
@ -139,12 +143,15 @@ public static IEnumerable<string> GetFoldersToCheck(IReadOnlyList<string> drives
|
|||
return foldersToCheck;
|
||||
}
|
||||
|
||||
private static bool GetSaveFilePathsFromFolders(IEnumerable<string> foldersToCheck, bool ignoreBackups, out IEnumerable<string> possible)
|
||||
private static bool GetSaveFilePathsFromFolders(IEnumerable<string> foldersToCheck, bool ignoreBackups, out IEnumerable<string> possible, CancellationToken token)
|
||||
{
|
||||
var possiblePaths = new List<string>();
|
||||
foreach (var folder in foldersToCheck)
|
||||
{
|
||||
if (!SaveUtil.GetSavesFromFolder(folder, true, out IEnumerable<string> files, ignoreBackups))
|
||||
if (token.IsCancellationRequested)
|
||||
break;
|
||||
|
||||
if (!SaveUtil.GetSavesFromFolder(token, folder, true, out IEnumerable<string> files, ignoreBackups))
|
||||
{
|
||||
if (files is not string[] msg) // should always return string[]
|
||||
continue;
|
||||
|
|
@ -159,23 +166,25 @@ private static bool GetSaveFilePathsFromFolders(IEnumerable<string> foldersToChe
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="FindMostRecentSaveFile(IReadOnlyList{string},string[])"/>
|
||||
public static SaveFile? FindMostRecentSaveFile() => FindMostRecentSaveFile(Environment.GetLogicalDrives(), CustomBackupPaths);
|
||||
/// <inheritdoc cref="FindMostRecentSaveFile(IReadOnlyList{string},CancellationToken,string[])"/>
|
||||
public static SaveFile? FindMostRecentSaveFile(CancellationToken token) => FindMostRecentSaveFile(DriveList, CustomBackupPaths, token);
|
||||
|
||||
/// <inheritdoc cref="GetSaveFiles"/>
|
||||
public static IEnumerable<SaveFile> DetectSaveFiles() => GetSaveFiles(Environment.GetLogicalDrives(), true, CustomBackupPaths, true);
|
||||
public static IEnumerable<SaveFile> DetectSaveFiles(CancellationToken token) => GetSaveFiles(DriveList, true, CustomBackupPaths, true, token);
|
||||
|
||||
/// <returns>
|
||||
/// True if a valid save file was found, false otherwise.
|
||||
/// </returns>
|
||||
/// <inheritdoc cref="FindMostRecentSaveFile(IReadOnlyList{string},string[])"/>
|
||||
public static bool TryDetectSaveFile([NotNullWhen(true)] out SaveFile? sav) => TryDetectSaveFile(Environment.GetLogicalDrives(), out sav);
|
||||
/// <inheritdoc cref="FindMostRecentSaveFile(IReadOnlyList{string},CancellationToken,string[])"/>
|
||||
public static bool TryDetectSaveFile(CancellationToken token, [NotNullWhen(true)] out SaveFile? sav) => TryDetectSaveFile(token, DriveList, out sav);
|
||||
|
||||
/// <inheritdoc cref="TryDetectSaveFile(out SaveFile)"/>
|
||||
public static bool TryDetectSaveFile(IReadOnlyList<string> drives, [NotNullWhen(true)] out SaveFile? sav)
|
||||
/// <inheritdoc cref="TryDetectSaveFile(CancellationToken, out SaveFile)"/>
|
||||
public static bool TryDetectSaveFile(CancellationToken token, IReadOnlyList<string> drives, [NotNullWhen(true)] out SaveFile? sav)
|
||||
{
|
||||
sav = FindMostRecentSaveFile(drives, CustomBackupPaths);
|
||||
sav = FindMostRecentSaveFile(drives, CustomBackupPaths, token);
|
||||
var path = sav?.Metadata.FilePath;
|
||||
return File.Exists(path);
|
||||
}
|
||||
|
||||
private static string[] DriveList => Environment.GetLogicalDrives();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
using static PKHeX.Core.MessageStrings;
|
||||
using static PKHeX.Core.GameVersion;
|
||||
|
|
@ -848,12 +849,13 @@ public static SaveFile GetBlankSAV(EntityContext context, string trainerName, La
|
|||
/// <summary>
|
||||
/// Retrieves possible save file paths from the provided <see cref="folderPath"/>.
|
||||
/// </summary>
|
||||
/// <param name="token">Cancellation token to cancel the operation.</param>
|
||||
/// <param name="folderPath">Folder to look within</param>
|
||||
/// <param name="deep">Search all subfolders</param>
|
||||
/// <param name="result">If this function returns true, full path of all <see cref="SaveFile"/> that match criteria. If this function returns false, the error message, or null if the directory could not be found</param>
|
||||
/// <param name="ignoreBackups">Option to ignore files with backup names and extensions</param>
|
||||
/// <returns>Boolean indicating if the operation was successful.</returns>
|
||||
public static bool GetSavesFromFolder(string folderPath, bool deep, out IEnumerable<string> result, bool ignoreBackups = true)
|
||||
public static bool GetSavesFromFolder(CancellationToken token, string folderPath, bool deep, out IEnumerable<string> result, bool ignoreBackups = true)
|
||||
{
|
||||
if (!Directory.Exists(folderPath))
|
||||
{
|
||||
|
|
@ -865,7 +867,7 @@ public static bool GetSavesFromFolder(string folderPath, bool deep, out IEnumera
|
|||
var searchOption = deep ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
||||
var files = Directory.EnumerateFiles(folderPath, "*", searchOption)
|
||||
.IterateSafe(log: z => System.Diagnostics.Debug.WriteLine(z));
|
||||
result = FilterSaveFiles(ignoreBackups, files);
|
||||
result = FilterSaveFiles(token, ignoreBackups, files);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -880,10 +882,13 @@ public static bool GetSavesFromFolder(string folderPath, bool deep, out IEnumera
|
|||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<string> FilterSaveFiles(bool ignoreBackups, IEnumerable<string> files)
|
||||
private static IEnumerable<string> FilterSaveFiles(CancellationToken token, bool ignoreBackups, IEnumerable<string> files)
|
||||
{
|
||||
foreach (var file in files)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
yield break;
|
||||
|
||||
if (ignoreBackups && IsBackup(file))
|
||||
continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
using System.Linq;
|
||||
using System.Media;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
|
|
@ -1343,7 +1344,8 @@ private void ClickSaveFileName(object sender, EventArgs e)
|
|||
{
|
||||
try
|
||||
{
|
||||
if (!SaveFinder.TryDetectSaveFile(out var sav))
|
||||
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||
if (!SaveFinder.TryDetectSaveFile(cts.Token, out var sav))
|
||||
return;
|
||||
|
||||
var path = sav.Metadata.FilePath!;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using PKHeX.Core;
|
||||
|
|
@ -39,10 +40,12 @@ public partial class SAV_Database : Form
|
|||
private readonly string Viewed;
|
||||
private const int MAXFORMAT = PKX.Generation;
|
||||
private readonly SummaryPreviewer ShowSet = new();
|
||||
private CancellationTokenSource cts = new();
|
||||
|
||||
public SAV_Database(PKMEditor f1, SAVEditor saveditor)
|
||||
{
|
||||
InitializeComponent();
|
||||
FormClosing += (_, _) => cts.Cancel();
|
||||
WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage);
|
||||
UC_Builder = new EntityInstructionBuilder(() => f1.PreparePKM())
|
||||
{
|
||||
|
|
@ -123,15 +126,16 @@ public SAV_Database(PKMEditor f1, SAVEditor saveditor)
|
|||
// Load Data
|
||||
B_Search.Enabled = false;
|
||||
L_Count.Text = "Loading...";
|
||||
var task = new Task(LoadDatabase);
|
||||
var token = cts.Token;
|
||||
var task = new Task(() => LoadDatabase(token), cts.Token);
|
||||
task.ContinueWith(z =>
|
||||
{
|
||||
if (!z.IsFaulted)
|
||||
if (token.IsCancellationRequested || !z.IsFaulted)
|
||||
return;
|
||||
Invoke((MethodInvoker)(() => L_Count.Text = "Failed."));
|
||||
if (z.Exception is null)
|
||||
return;
|
||||
WinFormsUtil.Error("Loading database failed.", z.Exception.InnerException ?? new Exception(z.Exception.Message));
|
||||
WinFormsUtil.Error("Loading database failed.", z.Exception.InnerException ?? z.Exception.GetBaseException());
|
||||
});
|
||||
task.Start();
|
||||
|
||||
|
|
@ -360,7 +364,7 @@ private sealed class SearchFolderDetail(string path, bool ignoreBackupFiles)
|
|||
public bool IgnoreBackupFiles { get; } = ignoreBackupFiles;
|
||||
}
|
||||
|
||||
private void LoadDatabase()
|
||||
private void LoadDatabase(CancellationToken token)
|
||||
{
|
||||
var settings = Main.Settings;
|
||||
var otherPaths = new List<SearchFolderDetail>();
|
||||
|
|
@ -369,7 +373,9 @@ private void LoadDatabase()
|
|||
if (settings.EntityDb.SearchBackups)
|
||||
otherPaths.Add(new SearchFolderDetail(Main.BackupPath, false));
|
||||
|
||||
RawDB = LoadPKMSaves(DatabasePath, SAV, otherPaths, settings.EntityDb.SearchExtraSavesDeep);
|
||||
RawDB = LoadEntitiesFromFolder(DatabasePath, SAV, otherPaths, settings.EntityDb.SearchExtraSavesDeep, token);
|
||||
if (token.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
// Load stats for pk who do not have any
|
||||
foreach (var entry in RawDB)
|
||||
|
|
@ -381,22 +387,24 @@ private void LoadDatabase()
|
|||
try
|
||||
{
|
||||
while (!IsHandleCreated) { }
|
||||
if (cts.Token.IsCancellationRequested)
|
||||
return;
|
||||
BeginInvoke(new MethodInvoker(() => SetResults(RawDB)));
|
||||
}
|
||||
catch { /* Window Closed? */ }
|
||||
}
|
||||
|
||||
private static List<SlotCache> LoadPKMSaves(string pkmdb, SaveFile sav, List<SearchFolderDetail> otherPaths, bool otherDeep)
|
||||
private static List<SlotCache> LoadEntitiesFromFolder(string databaseFolder, SaveFile sav, List<SearchFolderDetail> otherPaths, bool otherDeep, CancellationToken token)
|
||||
{
|
||||
var dbTemp = new ConcurrentBag<SlotCache>();
|
||||
var extensions = new HashSet<string>(EntityFileExtension.GetExtensionsAll().Select(z => $".{z}"));
|
||||
|
||||
var files = Directory.EnumerateFiles(pkmdb, "*", SearchOption.AllDirectories);
|
||||
var files = Directory.EnumerateFiles(databaseFolder, "*", SearchOption.AllDirectories);
|
||||
Parallel.ForEach(files, file => SlotInfoLoader.AddFromLocalFile(file, dbTemp, sav, extensions));
|
||||
|
||||
foreach (var folder in otherPaths)
|
||||
{
|
||||
if (!SaveUtil.GetSavesFromFolder(folder.Path, otherDeep, out IEnumerable<string> paths, folder.IgnoreBackupFiles))
|
||||
if (!SaveUtil.GetSavesFromFolder(token, folder.Path, otherDeep, out var paths, folder.IgnoreBackupFiles))
|
||||
continue;
|
||||
|
||||
Parallel.ForEach(paths, file => TryAddPKMsFromSaveFilePath(dbTemp, file));
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using PKHeX.Core;
|
||||
|
|
@ -19,23 +20,27 @@ public partial class SAV_FolderList : Form
|
|||
private readonly SortableBindingList<SavePreview> Recent;
|
||||
private readonly SortableBindingList<SavePreview> Backup;
|
||||
private readonly List<Label> TempTranslationLabels = [];
|
||||
private readonly CancellationTokenSource cts = new(TimeSpan.FromSeconds(20));
|
||||
|
||||
public SAV_FolderList(Action<SaveFile> openSaveFile)
|
||||
{
|
||||
InitializeComponent();
|
||||
FormClosing += (_, _) => cts.Cancel();
|
||||
OpenSaveFile = openSaveFile;
|
||||
|
||||
var backups = Main.BackupPath;
|
||||
var drives = Environment.GetLogicalDrives();
|
||||
Paths = GetPathList(drives);
|
||||
Paths = GetPathList(drives, backups);
|
||||
|
||||
dgDataRecent.ContextMenuStrip = GetContextMenu(dgDataRecent);
|
||||
dgDataBackup.ContextMenuStrip = GetContextMenu(dgDataBackup);
|
||||
dgDataRecent.Sorted += (_, _) => GetFilterText(dgDataRecent);
|
||||
dgDataBackup.Sorted += (_, _) => GetFilterText(dgDataBackup);
|
||||
|
||||
var extra = Paths.Select(z => z.Path).Where(z => z != Main.BackupPath).Distinct();
|
||||
var backup = SaveFinder.GetSaveFiles(drives, false, [Main.BackupPath], false);
|
||||
var recent = SaveFinder.GetSaveFiles(drives, false, extra, true).ToList();
|
||||
var token = cts.Token;
|
||||
var extra = Paths.Select(z => z.Path).Where(z => z != backups).Distinct();
|
||||
var backup = SaveFinder.GetSaveFiles(drives, false, [backups], false, token);
|
||||
var recent = SaveFinder.GetSaveFiles(drives, false, extra, true, token).ToList();
|
||||
var loaded = Main.Settings.Startup.RecentlyLoaded
|
||||
.Where(z => recent.All(x => x.Metadata.FilePath != z))
|
||||
.Where(File.Exists).Select(SaveUtil.GetVariantSAV).OfType<SaveFile>();
|
||||
|
|
@ -76,11 +81,11 @@ public SAV_FolderList(Action<SaveFile> openSaveFile)
|
|||
CenterToParent();
|
||||
}
|
||||
|
||||
private static List<INamedFolderPath> GetPathList(IReadOnlyList<string> drives)
|
||||
private static List<INamedFolderPath> GetPathList(IReadOnlyList<string> drives, string backupPath)
|
||||
{
|
||||
List<INamedFolderPath> locs =
|
||||
[
|
||||
new CustomFolderPath(Main.BackupPath, display: "PKHeX Backups"),
|
||||
new CustomFolderPath(backupPath, display: "PKHeX Backups"),
|
||||
..GetUserPaths(), ..GetConsolePaths(drives), ..GetSwitchPaths(drives),
|
||||
];
|
||||
var filtered = locs
|
||||
|
|
|
|||
|
|
@ -291,7 +291,8 @@ public static bool OpenSAVPKMDialog(IEnumerable<string> extensions, out string?
|
|||
{
|
||||
try
|
||||
{
|
||||
var sav = SaveFinder.FindMostRecentSaveFile();
|
||||
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||
var sav = SaveFinder.FindMostRecentSaveFile(cts.Token);
|
||||
return sav?.Metadata.FilePath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user