diff --git a/PKHeX.Core/Saves/Util/Recognition/ISaveHandler.cs b/PKHeX.Core/Saves/Util/Recognition/ISaveHandler.cs
index 3a94b65c2..a95697544 100644
--- a/PKHeX.Core/Saves/Util/Recognition/ISaveHandler.cs
+++ b/PKHeX.Core/Saves/Util/Recognition/ISaveHandler.cs
@@ -1,5 +1,6 @@
namespace PKHeX.Core
{
+#if !(EXCLUDE_EMULATOR_FORMATS && EXCLUDE_HACKS)
///
/// Provides handling for recognizing atypical save file formats.
///
@@ -19,4 +20,21 @@ public interface ISaveHandler
/// Null if not a valid save file for this handler's format. Returns an object containing header, footer, and inner data references.
SaveHandlerSplitResult? TrySplit(byte[] input);
}
+#endif
+
+#if !EXCLUDE_HACKS
+ ///
+ /// Provides handling for recognizing atypical save file formats.
+ ///
+ public interface ISaveReader : ISaveHandler
+ {
+ ///
+ /// Reads a save file from the
+ ///
+ /// Raw input data
+ /// Optional file path.
+ /// Save File object, or null if invalid. Check if it is compatible first.
+ SaveFile? ReadSaveFile(byte[] data, string? path = null);
+ }
+#endif
}
diff --git a/PKHeX.Core/Saves/Util/SaveUtil.cs b/PKHeX.Core/Saves/Util/SaveUtil.cs
index 8d1698a3b..6e607ab04 100644
--- a/PKHeX.Core/Saves/Util/SaveUtil.cs
+++ b/PKHeX.Core/Saves/Util/SaveUtil.cs
@@ -61,6 +61,15 @@ public static class SaveUtil
private static readonly SaveHandlerGCI DolphinHandler = new();
+#if !EXCLUDE_HACKS
+ ///
+ /// Specialized readers for loading save files from non-standard games (e.g. hacks).
+ ///
+ // ReSharper disable once CollectionNeverUpdated.Global
+ public static readonly List CustomSaveReaders = new();
+#endif
+
+#if !EXCLUDE_EMULATOR_FORMATS
///
/// Pre-formatters for loading save files from non-standard formats (e.g. emulators).
///
@@ -70,6 +79,7 @@ public static class SaveUtil
new SaveHandlerDeSmuME(),
new SaveHandlerARDS(),
};
+#endif
internal static readonly HashSet SizesSWSH = new()
{
@@ -487,7 +497,7 @@ private static GameVersion GetIsG8SAV(byte[] data)
public static SaveFile? GetVariantSAV(string path)
{
var data = File.ReadAllBytes(path);
- var sav = GetVariantSAV(data);
+ var sav = GetVariantSAV(data, path);
if (sav == null)
return null;
sav.Metadata.SetExtraInfo(path);
@@ -496,13 +506,27 @@ private static GameVersion GetIsG8SAV(byte[] data)
/// Creates an instance of a SaveFile using the given save data.
/// Save data from which to create a SaveFile.
+ /// Optional save file path, may help initialize a non-standard save file format.
/// An appropriate type of save file for the given data, or null if the save data is invalid.
- public static SaveFile? GetVariantSAV(byte[] data)
+ public static SaveFile? GetVariantSAV(byte[] data, string? path = null)
{
+#if !EXCLUDE_HACKS
+ foreach (var h in CustomSaveReaders)
+ {
+ if (!h.IsRecognized(data.Length))
+ continue;
+
+ var custom = h.ReadSaveFile(data, path);
+ if (custom != null)
+ return custom;
+ }
+#endif
+
var sav = GetVariantSAVInternal(data);
if (sav != null)
return sav;
+#if !EXCLUDE_EMULATOR_FORMATS
foreach (var h in Handlers)
{
if (!h.IsRecognized(data.Length))
@@ -519,6 +543,7 @@ private static GameVersion GetIsG8SAV(byte[] data)
sav.Metadata.SetExtraInfo(split.Header, split.Footer);
return sav;
}
+#endif
// unrecognized.
return null;