diff --git a/PKHeX.Core/Saves/SAV3GCMemoryCard.cs b/PKHeX.Core/Saves/SAV3GCMemoryCard.cs
index 17b89e35a..9ce86589c 100644
--- a/PKHeX.Core/Saves/SAV3GCMemoryCard.cs
+++ b/PKHeX.Core/Saves/SAV3GCMemoryCard.cs
@@ -65,6 +65,8 @@ public sealed class SAV3GCMemoryCard
private static int BlockAlloc => BLOCK_SIZE * BlockAlloc_Block;
private static int BlockAllocBAK => BLOCK_SIZE * BlockAlloc_Block;
+ public SAV3GCMemoryCard(byte[] data) => Data = data;
+
// Checksums
private void GetChecksum(int block, int offset, int length, out ushort csum, out ushort inv_csum)
{
@@ -178,9 +180,8 @@ private void RestoreBackup()
Array.Copy(Data, BlockAllocBackup_Block*BLOCK_SIZE, Data, BlockAlloc_Block*BLOCK_SIZE, BLOCK_SIZE);
}
- public GCMemoryCardState LoadMemoryCardFile(byte[] data)
+ public GCMemoryCardState GetMemoryCardState()
{
- Data = data;
if (!IsMemoryCardSize(Data))
return GCMemoryCardState.Invalid; // Invalid size
diff --git a/PKHeX.Core/Util/FileUtil.cs b/PKHeX.Core/Util/FileUtil.cs
new file mode 100644
index 000000000..580247bc0
--- /dev/null
+++ b/PKHeX.Core/Util/FileUtil.cs
@@ -0,0 +1,182 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+
+namespace PKHeX.Core
+{
+ ///
+ /// Logic for detecting supported binary object formats.
+ ///
+ public static class FileUtil
+ {
+ ///
+ /// Attempts to get a binary object from the provided path.
+ ///
+ ///
+ /// Reference savefile used for PC Binary compatibility checks.
+ /// Supported file object reference, null if none found.
+ public static object GetSupportedFile(string path, SaveFile reference = null)
+ {
+ try
+ {
+ var fi = new FileInfo(path);
+ if (IsFileTooBig(fi.Length) || IsFileTooSmall(fi.Length))
+ return null;
+
+ var data = File.ReadAllBytes(path);
+ var ext = Path.GetExtension(path);
+ return GetSupportedFile(data, ext, reference);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(MessageStrings.MsgFileInUse);
+ Debug.WriteLine(e.Message);
+ return null;
+ }
+ }
+
+ ///
+ /// Attempts to get a binary object from the provided inputs.
+ ///
+ /// Binary data for the file.
+ /// File extension used as a hint.
+ /// Reference savefile used for PC Binary compatibility checks.
+ /// Supported file object reference, null if none found.
+ public static object GetSupportedFile(byte[] data, string ext, SaveFile reference = null)
+ {
+ if (TryGetSAV(data, out var sav))
+ return sav;
+ if (TryGetMemoryCard(data, out var mc))
+ return mc;
+ if (TryGetPKM(data, out var pk, ext))
+ return pk;
+ if (TryGetPCBoxBin(data, out IEnumerable pks, reference))
+ return pks;
+ if (TryGetBattleVideo(data, out BattleVideo bv))
+ return bv;
+ if (TryGetMysteryGift(data, out MysteryGift g, ext))
+ return g;
+ return null;
+ }
+
+ ///
+ /// Checks if the length is too big to be a detectable file.
+ ///
+ /// File size
+ public static bool IsFileTooBig(long length)
+ {
+ if (length <= 0x100000)
+ return false;
+ if (length == SaveUtil.SIZE_G4BR)
+ return false;
+ if (SAV3GCMemoryCard.IsMemoryCardSize(length))
+ return false; // pbr/GC have size > 1MB
+ return true;
+ }
+
+ ///
+ /// Checks if the length is too small to be a detectable file.
+ ///
+ /// File size
+ public static bool IsFileTooSmall(long length) => length < 0x20;
+
+ ///
+ /// Tries to get an object from the input parameters.
+ ///
+ /// Binary data
+ /// Output result
+ /// True if file object reference is valid, false if none found.
+ public static bool TryGetSAV(byte[] data, out SaveFile sav)
+ {
+ sav = SaveUtil.GetVariantSAV(data);
+ return sav != null;
+ }
+
+ ///
+ /// Tries to get an object from the input parameters.
+ ///
+ /// Binary data
+ /// Output result
+ /// True if file object reference is valid, false if none found.
+ public static bool TryGetMemoryCard(byte[] data, out SAV3GCMemoryCard memcard)
+ {
+ if (!SAV3GCMemoryCard.IsMemoryCardSize(data))
+ {
+ memcard = null;
+ return false;
+ }
+ memcard = new SAV3GCMemoryCard(data);
+ return true;
+ }
+
+ ///
+ /// Tries to get an object from the input parameters.
+ ///
+ /// Binary data
+ /// Output result
+ /// Format hint
+ /// Reference savefile used for PC Binary compatibility checks.
+ /// True if file object reference is valid, false if none found.
+ public static bool TryGetPKM(byte[] data, out PKM pk, string ext, ITrainerInfo sav = null)
+ {
+ if (ext == ".pgt") // size collision with pk6
+ {
+ pk = default(PKM);
+ return false;
+ }
+ var format = PKX.GetPKMFormatFromExtension(ext, sav?.Generation ?? 6);
+ pk = PKMConverter.GetPKMfromBytes(data, prefer: format);
+ return pk != null;
+ }
+ ///
+ /// Tries to get an object from the input parameters.
+ ///
+ /// Binary data
+ /// Output result
+ /// Reference savefile used for PC Binary compatibility checks.
+ /// True if file object reference is valid, false if none found.
+ public static bool TryGetPCBoxBin(byte[] data, out IEnumerable pkms, SaveFile SAV)
+ {
+ if (SAV == null)
+ {
+ pkms = Enumerable.Empty();
+ return false;
+ }
+ var length = data.Length;
+ if (PKX.IsPKM(length / SAV.SlotCount) || PKX.IsPKM(length / SAV.BoxSlotCount))
+ {
+ pkms = PKX.GetPKMDataFromConcatenatedBinary(data, length);
+ return true;
+ }
+ pkms = Enumerable.Empty();
+ return false;
+ }
+
+ ///
+ /// Tries to get a object from the input parameters.
+ ///
+ /// Binary data
+ /// Output result
+ /// True if file object reference is valid, false if none found.
+ public static bool TryGetBattleVideo(byte[] data, out BattleVideo bv)
+ {
+ bv = BattleVideo.GetVariantBattleVideo(data);
+ return bv != null;
+ }
+
+ ///
+ /// Tries to get a object from the input parameters.
+ ///
+ /// Binary data
+ /// Output result
+ /// Format hint
+ /// True if file object reference is valid, false if none found.
+ public static bool TryGetMysteryGift(byte[] data, out MysteryGift mg, string ext)
+ {
+ mg = MysteryGift.GetMysteryGift(data, ext);
+ return mg != null;
+ }
+ }
+}
diff --git a/PKHeX.WinForms/MainWindow/Main.cs b/PKHeX.WinForms/MainWindow/Main.cs
index 13a23ee43..25f35057c 100644
--- a/PKHeX.WinForms/MainWindow/Main.cs
+++ b/PKHeX.WinForms/MainWindow/Main.cs
@@ -502,12 +502,12 @@ private void OpenQuick(string path, bool force = false)
return;
string ext = Path.GetExtension(path);
- if (fi.Length > 0x10009C && fi.Length != SaveUtil.SIZE_G4BR && !SAV3GCMemoryCard.IsMemoryCardSize(fi.Length)) // pbr/GC have size > 1MB
+ if (FileUtil.IsFileTooBig(fi.Length))
{
WinFormsUtil.Error(MsgFileSizeLarge + Environment.NewLine + string.Format(MsgFileSize, fi.Length), path);
return;
}
- if (fi.Length < 32)
+ if (FileUtil.IsFileTooSmall(fi.Length))
{
WinFormsUtil.Error(MsgFileSizeSmall + Environment.NewLine + string.Format(MsgFileSize, fi.Length), path);
return;
@@ -525,19 +525,8 @@ private void OpenQuick(string path, bool force = false)
private void OpenFile(byte[] input, string path, string ext)
{
- if (TryLoadXorpadSAV(input, path))
- return;
- if (TryLoadSAV(input, path))
- return;
- if (TryLoadMemoryCard(input, path))
- return;
- if (TryLoadPKM(input, ext))
- return;
- if (TryLoadPCBoxBin(input))
- return;
- if (TryLoadBattleVideo(input))
- return;
- if (TryLoadMysteryGift(input, path, ext))
+ var obj = FileUtil.GetSupportedFile(input, ext, C_SAV.SAV);
+ if (obj != null && LoadFile(obj, path))
return;
WinFormsUtil.Error(MsgFileUnsupported,
@@ -545,103 +534,46 @@ private void OpenFile(byte[] input, string path, string ext)
$"{string.Format(MsgFileSize, input.Length)}{Environment.NewLine}{input.Length} bytes (0x{input.Length:X4})");
}
- private bool TryLoadXorpadSAV(byte[] input, string path)
+ private bool LoadFile(object input, string path)
{
- if (input.Length == 0x10009C) // Resize to 1MB
+ if (input == null)
+ return false;
+
+ switch (input)
{
- Array.Copy(input, 0x9C, input, 0, 0x100000);
- Array.Resize(ref input, 0x100000);
+ case PKM pk: return OpenPKM(pk);
+ case SaveFile s: return OpenSAV(s, path);
+ case BattleVideo b: return OpenBattleVideo(b);
+ case MysteryGift g: return OpenMysteryGift(g, path);
+ case IEnumerable pkms: return OpenPCBoxBin(pkms);
+
+ case SAV3GCMemoryCard gc:
+ if (!CheckGCMemoryCard(gc, path))
+ return true;
+ var mcsav = SaveUtil.GetVariantSAV(gc);
+ return OpenSAV(mcsav, path);
}
- if (input.Length != 0x100000)
- return false;
- if (OpenXOR(input, path)) // Check if we can load the save via xorpad
- return true;
-
- if (BitConverter.ToUInt64(input, 0x10) != 0) // encrypted save
- {
- WinFormsUtil.Error(MsgFileLoadEncrypted + Environment.NewLine + MsgFileLoadEncryptedFail, path);
- return true;
- }
-
- DialogResult sdr = WinFormsUtil.Prompt(MessageBoxButtons.YesNoCancel, MsgFileLoadXorpad1, MsgFileLoadXorpad2);
- if (sdr == DialogResult.Cancel)
- return true;
-
- int savshift = sdr == DialogResult.Yes ? 0 : 0x7F000;
- var psdata = SaveUtil.GetMainFromSaveContainer(input, savshift);
- if (psdata != null)
- return TryLoadSAV(psdata, path);
-
- WinFormsUtil.Error(MsgFileLoadSaveFail, path);
- return true;
+ return false;
}
- private bool TryLoadSAV(byte[] input, string path)
+ private bool OpenPKM(PKM pk)
{
- var sav = SaveUtil.GetVariantSAV(input);
- if (sav == null)
- return false;
- OpenSAV(sav, path);
- return true;
- }
-
- private bool TryLoadMemoryCard(byte[] input, string path)
- {
- if (!SAV3GCMemoryCard.IsMemoryCardSize(input))
- return false;
- SAV3GCMemoryCard MC = CheckGCMemoryCard(input, path);
- if (MC == null)
- return false;
- var sav = SaveUtil.GetVariantSAV(MC);
- if (sav == null)
- return false;
- OpenSAV(sav, path);
- return true;
- }
-
- private bool TryLoadPKM(byte[] input, string ext)
- {
- if (ext == ".pgt") // size collision with pk6
- return false;
- var pk = PKMConverter.GetPKMfromBytes(input, prefer: PKX.GetPKMFormatFromExtension(ext, C_SAV.SAV.Generation));
- if (pk == null)
- return false;
-
+ pk = PKMConverter.ConvertToType(pk, C_SAV.SAV.PKMType, out string c);
+ Debug.WriteLine(c);
PKME_Tabs.PopulateFields(pk);
return true;
}
- private bool TryLoadPCBoxBin(byte[] input)
+ private bool OpenBattleVideo(BattleVideo b)
{
- if (!C_SAV.IsPCBoxBin(input.Length))
- return false;
- if (!C_SAV.OpenPCBoxBin(input, out string c))
- {
- WinFormsUtil.Alert(MsgFileLoadIncompatible, c);
- return true;
- }
-
- WinFormsUtil.Alert(c);
- return true;
- }
-
- private bool TryLoadBattleVideo(byte[] input)
- {
- if (!BattleVideo.IsValid(input))
- return false;
-
- BattleVideo b = BattleVideo.GetVariantBattleVideo(input);
bool result = C_SAV.OpenBattleVideo(b, out string c);
WinFormsUtil.Alert(c);
Debug.WriteLine(c);
return result;
}
- private bool TryLoadMysteryGift(byte[] input, string path, string ext)
+ private bool OpenMysteryGift(MysteryGift tg, string path)
{
- var tg = MysteryGift.GetMysteryGift(input, ext);
- if (tg == null)
- return false;
if (!tg.IsPokémon)
{
WinFormsUtil.Alert(MsgPKMMysteryGiftFail, path);
@@ -662,24 +594,15 @@ private bool TryLoadMysteryGift(byte[] input, string path, string ext)
return true;
}
- private bool OpenXOR(byte[] input, string path)
+ private bool OpenPCBoxBin(IEnumerable pkms)
{
- // try to get a save file via xorpad in same folder
- var folder = new DirectoryInfo(path).Parent.FullName;
- var pads = Directory.EnumerateFiles(folder);
- var s = SaveUtil.GetSAVfromXORpads(input, pads);
-
- if (s == null) // failed to find xorpad in path folder
+ if (!C_SAV.OpenPCBoxBin(pkms.SelectMany(z => z).ToArray(), out string c))
{
- // try again
- pads = Directory.EnumerateFiles(WorkingDirectory);
- s = SaveUtil.GetSAVfromXORpads(input, pads);
+ WinFormsUtil.Alert(MsgFileLoadIncompatible, c);
+ return true;
}
- if (s == null)
- return false; // failed
-
- OpenSAV(s, s.FileName);
+ WinFormsUtil.Alert(c);
return true;
}
@@ -699,30 +622,29 @@ private static GameVersion SelectMemoryCardSaveGame(SAV3GCMemoryCard MC)
return dialog.Result;
}
- private static SAV3GCMemoryCard CheckGCMemoryCard(byte[] Data, string path)
+ private static bool CheckGCMemoryCard(SAV3GCMemoryCard MC, string path)
{
- var MC = new SAV3GCMemoryCard();
- var MCState = MC.LoadMemoryCardFile(Data);
- switch (MCState)
+ var state = MC.GetMemoryCardState();
+ switch (state)
{
- default: { WinFormsUtil.Error(MsgFileGameCubeBad, path); return null; }
- case GCMemoryCardState.NoPkmSaveGame: { WinFormsUtil.Error(MsgFileGameCubeNoGames, path); return null; }
+ default: { WinFormsUtil.Error(MsgFileGameCubeBad, path); return false; }
+ case GCMemoryCardState.NoPkmSaveGame: { WinFormsUtil.Error(MsgFileGameCubeNoGames, path); return false; }
case GCMemoryCardState.DuplicateCOLO:
case GCMemoryCardState.DuplicateXD:
- case GCMemoryCardState.DuplicateRSBOX: { WinFormsUtil.Error(MsgFileGameCubeDuplicate, path); return null; }
+ case GCMemoryCardState.DuplicateRSBOX: { WinFormsUtil.Error(MsgFileGameCubeDuplicate, path); return false; }
case GCMemoryCardState.MultipleSaveGame:
{
GameVersion Game = SelectMemoryCardSaveGame(MC);
if (Game == GameVersion.Invalid) //Cancel
- return null;
+ return false;
MC.SelectSaveGame(Game);
break;
}
- case GCMemoryCardState.SaveGameCOLO: MC.SelectSaveGame(GameVersion.COLO); break;
- case GCMemoryCardState.SaveGameXD: MC.SelectSaveGame(GameVersion.XD); break;
- case GCMemoryCardState.SaveGameRSBOX: MC.SelectSaveGame(GameVersion.RSBOX); break;
+ case GCMemoryCardState.SaveGameCOLO: MC.SelectSaveGame(GameVersion.COLO); break;
+ case GCMemoryCardState.SaveGameXD: MC.SelectSaveGame(GameVersion.XD); break;
+ case GCMemoryCardState.SaveGameRSBOX: MC.SelectSaveGame(GameVersion.RSBOX); break;
}
- return MC;
+ return true;
}
private static void StoreLegalSaveGameData(SaveFile sav)
@@ -748,14 +670,14 @@ private static PKM LoadTemplate(SaveFile sav)
return PKMConverter.ConvertToType(pk, sav.BlankPKM.GetType(), out path); // no sneaky plz; reuse string
}
- private void OpenSAV(SaveFile sav, string path)
+ private bool OpenSAV(SaveFile sav, string path)
{
if (sav == null || sav.Version == GameVersion.Invalid)
- { WinFormsUtil.Error(MsgFileLoadSaveLoadFail, path); return; }
+ { WinFormsUtil.Error(MsgFileLoadSaveLoadFail, path); return true; }
sav.SetFileInfo(path);
if (!SanityCheckSAV(ref sav))
- return;
+ return true;
StoreLegalSaveGameData(sav);
PKMConverter.Trainer = sav;
SpriteUtil.Spriter.Initialize(sav); // refresh sprite generator
@@ -778,6 +700,7 @@ private void OpenSAV(SaveFile sav, string path)
Menu_ShowdownExportCurrentBox.Visible = sav.HasBox;
SystemSounds.Beep.Play();
+ return true;
}
private void ResetSAVPKMEditors(SaveFile sav)
@@ -1010,10 +933,16 @@ private void ImportQRToTabs(string url)
return;
var sav = C_SAV.SAV;
- if (TryLoadPKM(input, sav.Generation.ToString()))
+ if (FileUtil.TryGetPKM(input, out var pk, sav.Generation.ToString(), sav))
+ {
+ OpenPKM(pk);
return;
- if (TryLoadMysteryGift(input, url, null))
+ }
+ if (FileUtil.TryGetMysteryGift(input, out var mg, url))
+ {
+ OpenMysteryGift(mg, url);
return;
+ }
WinFormsUtil.Alert(MsgQRDecodeFail, string.Format(MsgQRDecodeSize, input.Length));
}