mirror of
https://github.com/kwsch/PKHeX
synced 2025-02-16 21:38:40 +00:00
Misc refactoring
add docs, move some data fetching to more appropriate class remove old XP memecrypto support handling, was previously removed due to net standard/core split refactor memecrypto to handle multiple save sizes (USUM won't be the same size save file); placeholder -1 for USUM size
This commit is contained in:
parent
599a67a5a0
commit
7efd771bf4
10 changed files with 158 additions and 230 deletions
|
@ -567,7 +567,7 @@ namespace PKHeX.Core
|
|||
/// <param name="gen">Generation to get location names for.</param>
|
||||
/// <param name="bankID">BankID used to choose the text bank.</param>
|
||||
/// <returns>List of location names.</returns>
|
||||
public static string[] GetLocationNames(int gen, int bankID)
|
||||
private static string[] GetLocationNames(int gen, int bankID)
|
||||
{
|
||||
switch (gen)
|
||||
{
|
||||
|
@ -611,5 +611,51 @@ namespace PKHeX.Core
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the location name for the specified parameters.
|
||||
/// </summary>
|
||||
/// <param name="eggmet">Location is from the <see cref="PKM.Egg_Location"/></param>
|
||||
/// <param name="locval">Location value</param>
|
||||
/// <param name="format">Current <see cref="PKM.Format"/></param>
|
||||
/// <param name="generation"><see cref="PKM.GenNumber"/> of origin</param>
|
||||
/// <returns>Location name</returns>
|
||||
public static string GetLocationName(bool eggmet, int locval, int format, int generation)
|
||||
{
|
||||
int gen = -1;
|
||||
int bankID = 0;
|
||||
|
||||
if (format == 2)
|
||||
gen = 2;
|
||||
else if (format == 3)
|
||||
gen = 3;
|
||||
else if (generation == 4 && (eggmet || format == 4)) // 4
|
||||
{
|
||||
const int size = 1000;
|
||||
bankID = locval / size;
|
||||
gen = 4;
|
||||
locval %= size;
|
||||
}
|
||||
else // 5-7+
|
||||
{
|
||||
const int size = 10000;
|
||||
bankID = locval / size;
|
||||
|
||||
int g = generation;
|
||||
if (g >= 5)
|
||||
gen = g;
|
||||
else if (format >= 5)
|
||||
gen = format;
|
||||
|
||||
locval %= size;
|
||||
if (bankID >= 3)
|
||||
locval -= 1;
|
||||
}
|
||||
|
||||
var bank = GetLocationNames(gen, bankID);
|
||||
if (bank == null || bank.Length <= locval)
|
||||
return string.Empty;
|
||||
return bank[locval];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,15 +168,18 @@ namespace PKHeX.Core
|
|||
};
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Species name lists indexed by the <see cref="PKM.Language"/> value.
|
||||
/// </summary>
|
||||
public static readonly string[][] SpeciesLang =
|
||||
{
|
||||
Util.GetSpeciesList("ja"), // none
|
||||
Util.GetSpeciesList("ja"), // 0 (unused, invalid)
|
||||
Util.GetSpeciesList("ja"), // 1
|
||||
Util.GetSpeciesList("en"), // 2
|
||||
Util.GetSpeciesList("fr"), // 3
|
||||
Util.GetSpeciesList("it"), // 4
|
||||
Util.GetSpeciesList("de"), // 5
|
||||
Util.GetSpeciesList("es"), // none
|
||||
Util.GetSpeciesList("es"), // 6 (reserved for Gen3 KO?, unused)
|
||||
Util.GetSpeciesList("es"), // 7
|
||||
Util.GetSpeciesList("ko"), // 8
|
||||
Util.GetSpeciesList("zh"), // 9 Simplified
|
||||
|
@ -570,6 +573,14 @@ namespace PKHeX.Core
|
|||
ivs[i] = (ivs[i] & 0x1E) + hpivs[type, i];
|
||||
return ivs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hidden Power IV values (even or odd) to achieve a specified Hidden Power Type
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There are other IV combinations to achieve the same Hidden Power Type.
|
||||
/// These are just precomputed for fast modification.
|
||||
/// </remarks>
|
||||
public static readonly int[,] hpivs = {
|
||||
{ 1, 1, 0, 0, 0, 0 }, // Fighting
|
||||
{ 0, 0, 0, 0, 0, 1 }, // Flying
|
||||
|
@ -801,46 +812,19 @@ namespace PKHeX.Core
|
|||
}
|
||||
|
||||
// Extensions
|
||||
/// <summary>
|
||||
/// Gets the Location Name for the <see cref="PKM"/>
|
||||
/// </summary>
|
||||
/// <param name="pk">PKM to fetch data for</param>
|
||||
/// <param name="eggmet">Location requested is the egg obtained location, not met location.</param>
|
||||
/// <returns>Location string</returns>
|
||||
public static string GetLocationString(this PKM pk, bool eggmet)
|
||||
{
|
||||
if (pk.Format < 2)
|
||||
return "";
|
||||
|
||||
int gen = -1;
|
||||
int bankID = 0;
|
||||
int locval = eggmet ? pk.Egg_Location : pk.Met_Location;
|
||||
|
||||
if (pk.Format == 2)
|
||||
gen = 2;
|
||||
else if (pk.Format == 3)
|
||||
gen = 3;
|
||||
else if (pk.Gen4 && (eggmet || pk.Format == 4)) // 4
|
||||
{
|
||||
const int size = 1000;
|
||||
bankID = locval/size;
|
||||
gen = 4;
|
||||
locval %= size;
|
||||
}
|
||||
else // 5-7+
|
||||
{
|
||||
const int size = 10000;
|
||||
bankID = locval/size;
|
||||
|
||||
int g = pk.GenNumber;
|
||||
if (g >= 5)
|
||||
gen = g;
|
||||
else if (pk.Format >= 5)
|
||||
gen = pk.Format;
|
||||
|
||||
locval %= size;
|
||||
if (bankID >= 3)
|
||||
locval -= 1;
|
||||
}
|
||||
|
||||
var bank = GameInfo.GetLocationNames(gen, bankID);
|
||||
if (bank == null || bank.Length <= locval)
|
||||
return "";
|
||||
return bank[locval];
|
||||
return GameInfo.GetLocationName(eggmet, locval, pk.Format, pk.GenNumber);
|
||||
}
|
||||
public static string[] GetQRLines(this PKM pkm)
|
||||
{
|
||||
|
|
|
@ -1250,14 +1250,6 @@ namespace PKHeX.Core
|
|||
{
|
||||
var r = new StringBuilder();
|
||||
|
||||
// MemeCrypto check
|
||||
if (RequiresMemeCrypto && !MemeCrypto.CanUseMemeCrypto())
|
||||
{
|
||||
r.AppendLine("Platform does not support required cryptography providers.");
|
||||
r.AppendLine("Checksum will be broken until the file is saved using an OS without FIPS compliance enabled or a newer OS.");
|
||||
r.AppendLine();
|
||||
}
|
||||
|
||||
// FFFF checks
|
||||
byte[] FFFF = Enumerable.Repeat((byte)0xFF, 0x200).ToArray();
|
||||
for (int i = 0; i < Data.Length / 0x200; i++)
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
public const int BEEF = 0x42454546;
|
||||
|
||||
public const int SIZE_G7USUM = -1;
|
||||
public const int SIZE_G7SM = 0x6BE00;
|
||||
public const int SIZE_G6XY = 0x65600;
|
||||
public const int SIZE_G6ORAS = 0x76000;
|
||||
|
@ -47,10 +48,10 @@ namespace PKHeX.Core
|
|||
SIZE_G1RAW, SIZE_G1BAT
|
||||
};
|
||||
|
||||
public static readonly byte[] FOOTER_DSV = Encoding.ASCII.GetBytes("|-DESMUME SAVE-|");
|
||||
public static readonly string[] HEADER_COLO = { "GC6J","GC6E","GC6P" }; // NTSC-J, NTSC-U, PAL
|
||||
public static readonly string[] HEADER_XD = { "GXXJ","GXXE","GXXP" }; // NTSC-J, NTSC-U, PAL
|
||||
public static readonly string[] HEADER_RSBOX = { "GPXJ","GPXE","GPXP" }; // NTSC-J, NTSC-U, PAL
|
||||
private static readonly byte[] FOOTER_DSV = Encoding.ASCII.GetBytes("|-DESMUME SAVE-|");
|
||||
internal static readonly string[] HEADER_COLO = { "GC6J","GC6E","GC6P" }; // NTSC-J, NTSC-U, PAL
|
||||
internal static readonly string[] HEADER_XD = { "GXXJ","GXXE","GXXP" }; // NTSC-J, NTSC-U, PAL
|
||||
internal static readonly string[] HEADER_RSBOX = { "GPXJ","GPXE","GPXP" }; // NTSC-J, NTSC-U, PAL
|
||||
|
||||
/// <summary>Determines the generation of the given save data.</summary>
|
||||
/// <param name="data">Save data of which to determine the generation</param>
|
||||
|
@ -562,11 +563,11 @@ namespace PKHeX.Core
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the full path of the most recent file based on LastWriteTime.
|
||||
/// Retrieves the full path of the most recent file based on <see cref="FileInfo.LastWriteTime"/>.
|
||||
/// </summary>
|
||||
/// <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 save files that match criteria. If this function returns false, the error message, or null if the directory could not be found</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>
|
||||
/// <returns>Boolean indicating whether or not operation was successful.</returns>
|
||||
public static bool GetSavesFromFolder(string folderPath, bool deep, out IEnumerable<string> result)
|
||||
{
|
||||
|
@ -684,7 +685,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
public static byte[] Resign7(byte[] sav7)
|
||||
{
|
||||
return MemeCrypto.Resign(sav7, false);
|
||||
return MemeCrypto.Resign(sav7);
|
||||
}
|
||||
/// <summary>Calculates the 32bit checksum over an input byte array. Used in GBA save files.</summary>
|
||||
/// <param name="data">Input byte array</param>
|
||||
|
|
|
@ -3,7 +3,6 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
|
@ -12,19 +11,17 @@ namespace PKHeX.Core
|
|||
private static byte[] AESECBEncrypt(byte[] key, byte[] data)
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
using (var aes = Aes.Create())
|
||||
{
|
||||
using (var aes = Util.GetAesProvider())
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Padding = PaddingMode.None;
|
||||
|
||||
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(key, new byte[0x10]), CryptoStreamMode.Write))
|
||||
{
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Padding = PaddingMode.None;
|
||||
cs.Write(data, 0, data.Length);
|
||||
cs.FlushFinalBlock();
|
||||
|
||||
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(key, new byte[0x10]), CryptoStreamMode.Write))
|
||||
{
|
||||
cs.Write(data, 0, data.Length);
|
||||
cs.FlushFinalBlock();
|
||||
|
||||
return ms.ToArray();
|
||||
}
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,19 +29,17 @@ namespace PKHeX.Core
|
|||
private static byte[] AESECBDecrypt(byte[] key, byte[] data)
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
using (var aes = Aes.Create())
|
||||
{
|
||||
using (var aes = Util.GetAesProvider())
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Padding = PaddingMode.None;
|
||||
|
||||
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(key, new byte[0x10]), CryptoStreamMode.Write))
|
||||
{
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Padding = PaddingMode.None;
|
||||
cs.Write(data, 0, data.Length);
|
||||
cs.FlushFinalBlock();
|
||||
|
||||
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(key, new byte[0x10]), CryptoStreamMode.Write))
|
||||
{
|
||||
cs.Write(data, 0, data.Length);
|
||||
cs.FlushFinalBlock();
|
||||
|
||||
return ms.ToArray();
|
||||
}
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -171,10 +166,9 @@ namespace PKHeX.Core
|
|||
private static byte[] ReverseCrypt(byte[] input, int meme_ofs, int memeindex)
|
||||
{
|
||||
var output = (byte[])input.Clone();
|
||||
|
||||
var memekey = MemeKeys[memeindex];
|
||||
|
||||
using (var sha1 = Util.GetSHA1Provider())
|
||||
using (var sha1 = SHA1.Create())
|
||||
{
|
||||
var enc = new byte[0x60];
|
||||
Array.Copy(input, meme_ofs, enc, 0, 0x60);
|
||||
|
@ -202,6 +196,7 @@ namespace PKHeX.Core
|
|||
return null;
|
||||
}
|
||||
|
||||
private const uint POKE = 0x454B4F50;
|
||||
public static byte[] VerifyMemeData(byte[] input)
|
||||
{
|
||||
if (input.Length < 0x60)
|
||||
|
@ -211,7 +206,7 @@ namespace PKHeX.Core
|
|||
|
||||
for (var i = input.Length - 8; i >= 0; i--)
|
||||
{
|
||||
if (BitConverter.ToUInt32(input, i) != 0x454B4F50 ||
|
||||
if (BitConverter.ToUInt32(input, i) != POKE ||
|
||||
BitConverter.ToUInt32(input, i + 4) >= MemeKeys.Length) continue;
|
||||
|
||||
meme_ofs = i - 0x60;
|
||||
|
@ -236,7 +231,7 @@ namespace PKHeX.Core
|
|||
if (input.Length < 0x60)
|
||||
throw new ArgumentException("Bad Meme input!");
|
||||
const int memeindex = 3;
|
||||
using (var sha1 = Util.GetSHA1Provider())
|
||||
using (var sha1 = SHA1.Create())
|
||||
{
|
||||
var key = sha1.ComputeHash(MemeKeys[memeindex].DER.Concat(input.Take(input.Length - 0x60)).ToArray()).Take(0x10).ToArray();
|
||||
|
||||
|
@ -251,66 +246,40 @@ namespace PKHeX.Core
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resigns save data.
|
||||
/// Resigns save data.
|
||||
/// </summary>
|
||||
/// <param name="sav7">The save data to resign.</param>
|
||||
/// <param name="throwIfUnsupported">
|
||||
/// If true, throw an <see cref="InvalidOperationException" /> if MemeCrypto is
|
||||
/// unsupported. If false, calling this function will have no effect.
|
||||
/// </param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if the current platform has FIPS mode enabled on a platform that
|
||||
/// does not support the required crypto service providers.
|
||||
/// </exception>
|
||||
/// <returns>The resigned save data.</returns>
|
||||
public static byte[] Resign(byte[] sav7, bool throwIfUnsupported = true)
|
||||
public static byte[] Resign(byte[] sav7)
|
||||
{
|
||||
if (sav7 == null || sav7.Length != 0x6BE00)
|
||||
if (sav7 == null || sav7.Length != SaveUtil.SIZE_G7SM && sav7.Length != SaveUtil.SIZE_G7USUM)
|
||||
return null;
|
||||
|
||||
try
|
||||
// Save Chunks are 0x200 bytes each; Memecrypto signature is 0x100 bytes into the 2nd to last chunk.
|
||||
int ChecksumTableOffset = sav7.Length - 0x200;
|
||||
int MemeCryptoOffset = ChecksumTableOffset - 0x100;
|
||||
const int ChecksumSignatureLength = 0x140;
|
||||
const int MemeCryptoSignatureLength = 0x80;
|
||||
|
||||
var outSav = (byte[])sav7.Clone();
|
||||
|
||||
using (var sha256 = SHA256.Create())
|
||||
{
|
||||
var outSav = (byte[])sav7.Clone();
|
||||
// Store current signature
|
||||
var CurSig = new byte[MemeCryptoSignatureLength];
|
||||
Buffer.BlockCopy(sav7, MemeCryptoOffset, CurSig, 0, MemeCryptoSignatureLength);
|
||||
|
||||
using (var sha256 = Util.GetSHA256Provider())
|
||||
{
|
||||
var CurSig = new byte[0x80];
|
||||
Array.Copy(sav7, 0x6BB00, CurSig, 0, 0x80);
|
||||
var ChecksumTableSignature = new byte[ChecksumSignatureLength];
|
||||
Buffer.BlockCopy(sav7, ChecksumTableOffset, ChecksumTableSignature, 0, ChecksumSignatureLength);
|
||||
|
||||
var ChecksumTable = new byte[0x140];
|
||||
Array.Copy(sav7, 0x6BC00, ChecksumTable, 0, 0x140);
|
||||
var newSig = new byte[MemeCryptoSignatureLength];
|
||||
sha256.ComputeHash(ChecksumTableSignature).CopyTo(newSig, 0);
|
||||
var memeSig = VerifyMemeData(CurSig);
|
||||
if (memeSig != null)
|
||||
Buffer.BlockCopy(memeSig, 0x20, newSig, 0x20, 0x60);
|
||||
|
||||
var newSig = new byte[0x80];
|
||||
sha256.ComputeHash(ChecksumTable).CopyTo(newSig, 0);
|
||||
var memeSig = VerifyMemeData(CurSig);
|
||||
if (memeSig != null)
|
||||
Array.Copy(memeSig, 0x20, newSig, 0x20, 0x60);
|
||||
|
||||
SignMemeData(newSig).CopyTo(outSav, 0x6BB00);
|
||||
}
|
||||
return outSav;
|
||||
SignMemeData(newSig).CopyTo(outSav, MemeCryptoOffset);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
if (throwIfUnsupported)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
return (byte[])sav7.Clone();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CanUseMemeCrypto()
|
||||
{
|
||||
try
|
||||
{
|
||||
Util.GetSHA256Provider();
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return outSav;
|
||||
}
|
||||
|
||||
#region Meme Key Data
|
||||
|
@ -400,13 +369,5 @@ namespace PKHeX.Core
|
|||
x[i] = (byte)(b1[i] ^ b2[i]);
|
||||
return x;
|
||||
}
|
||||
|
||||
public static string ToHexString(this byte[] ba)
|
||||
{
|
||||
var hex = new StringBuilder(ba.Length * 2);
|
||||
foreach (var b in ba)
|
||||
hex.AppendFormat("{0:X2}", b);
|
||||
return hex.ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
using System;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public partial class Util
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="SHA1CryptoServiceProvider"/>, or <see cref="SHA1Managed"/> if not supported by the current platform.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown if FIPS mode is enabled on a platform that does not support <see cref="SHA1CryptoServiceProvider"/>.</exception>
|
||||
public static SHA1 GetSHA1Provider()
|
||||
{
|
||||
return SHA1.Create();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="SHA256CryptoServiceProvider"/>, or <see cref="SHA256Managed"/> if not supported by the current platform.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown if FIPS mode is enabled on a platform that does not support <see cref="SHA256CryptoServiceProvider"/>.</exception>
|
||||
public static SHA256 GetSHA256Provider()
|
||||
{
|
||||
return SHA256.Create();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="AesCryptoServiceProvider"/>, or <see cref="AesManaged"/> if not supported by the current platform.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown if FIPS mode is enabled on a platform that does not support <see cref="AesCryptoServiceProvider"/>.</exception>
|
||||
public static Aes GetAesProvider()
|
||||
{
|
||||
return Aes.Create();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -743,7 +743,7 @@ namespace PKHeX.WinForms
|
|||
if (sav == null || sav.Version == GameVersion.Invalid)
|
||||
{ WinFormsUtil.Error("Invalid save file loaded. Aborting.", path); return; }
|
||||
|
||||
if (!SanityCheckSAV(ref sav, path))
|
||||
if (!SanityCheckSAV(ref sav))
|
||||
return;
|
||||
StoreLegalSaveGameData(sav);
|
||||
PKMUtil.Initialize(sav); // refresh sprite generator
|
||||
|
@ -834,17 +834,8 @@ namespace PKHeX.WinForms
|
|||
"If the path is a removable disk (SD card), please ensure the write protection switch is not set.");
|
||||
return false;
|
||||
}
|
||||
private static bool SanityCheckSAV(ref SaveFile sav, string path)
|
||||
private static bool SanityCheckSAV(ref SaveFile sav)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(path)) // If path is null, this is the default save
|
||||
{
|
||||
if (sav.RequiresMemeCrypto && !MemeCrypto.CanUseMemeCrypto())
|
||||
{
|
||||
WinFormsUtil.Error("Your platform does not support the required cryptography components.",
|
||||
"In order to be able to save your changes, you must either upgrade to a newer version of Windows or disable FIPS compliance mode.");
|
||||
// Don't abort loading; user can still view save and fix checksum on another platform.
|
||||
}
|
||||
}
|
||||
// Finish setting up the save file.
|
||||
if (sav.Generation == 1)
|
||||
{
|
||||
|
|
|
@ -1,21 +1,33 @@
|
|||
using PKHeX.Core;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using PKHeX.Core;
|
||||
|
||||
namespace PKHeX.WinForms
|
||||
{
|
||||
public static class PathUtilWindows
|
||||
{
|
||||
public static string Get3DSLocation()
|
||||
/// <summary>
|
||||
/// Gets the 3DS's root folder, usually from an inserted SD card.
|
||||
/// </summary>
|
||||
/// <param name="skipFirstDrive">Optional parameter to skip the first drive.
|
||||
/// The first drive is usually the system hard drive, or can be a floppy disk drive (slower to check, never has expected data).</param>
|
||||
/// <returns>Folder path pointing to the Nintendo 3DS folder.</returns>
|
||||
public static string Get3DSLocation(bool skipFirstDrive = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
string[] DriveList = Environment.GetLogicalDrives();
|
||||
for (int i = 1; i < DriveList.Length; i++) // Skip first drive (some users still have floppy drives and would chew up time!)
|
||||
IEnumerable<string> DriveList = Environment.GetLogicalDrives();
|
||||
|
||||
// Skip first drive (some users still have floppy drives and would chew up time!)
|
||||
if (skipFirstDrive)
|
||||
DriveList = DriveList.Skip(1);
|
||||
|
||||
foreach (var drive in DriveList)
|
||||
{
|
||||
string potentialPath = Path.Combine(DriveList[i], "Nintendo 3DS");
|
||||
string potentialPath = Path.Combine(drive, "Nintendo 3DS");
|
||||
if (Directory.Exists(potentialPath))
|
||||
return potentialPath;
|
||||
}
|
||||
|
@ -29,41 +41,37 @@ namespace PKHeX.WinForms
|
|||
/// </summary>
|
||||
/// <param name="root">Root location of device</param>
|
||||
/// <returns>List of possible 3DS save backup paths.</returns>
|
||||
public static string[] Get3DSBackupPaths(string root)
|
||||
public static IEnumerable<string> Get3DSBackupPaths(string root)
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
Path.Combine(root, "saveDataBackup"),
|
||||
Path.Combine(root, "filer", "UserSaveData"),
|
||||
Path.Combine(root, "JKSV", "Saves"),
|
||||
Path.Combine(root, "TWLSaveTool"),
|
||||
Path.Combine(root, "fbi", "save"),
|
||||
Path.Combine(root, "gm9", "out"),
|
||||
Path.Combine(root, "3ds", "data", "JKSM", "Saves"),
|
||||
};
|
||||
yield return Path.Combine(root, "saveDataBackup");
|
||||
yield return Path.Combine(root, "filer", "UserSaveData");
|
||||
yield return Path.Combine(root, "JKSV", "Saves");
|
||||
yield return Path.Combine(root, "TWLSaveTool");
|
||||
yield return Path.Combine(root, "fbi", "save");
|
||||
yield return Path.Combine(root, "gm9", "out");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detects a save file.
|
||||
/// Finds a compatible save file that was most recently saved (by file write time).
|
||||
/// </summary>
|
||||
/// <param name="path">If this function returns true, full path of a save file or null if no path could be found. If this function returns false, this parameter will be set to the error message.</param>
|
||||
/// <param name="extra">Paths to check in addition to the default paths</param>
|
||||
/// <returns>A boolean indicating whether or not a file was detected</returns>
|
||||
public static bool DetectSaveFile(out string path, params string[] extra)
|
||||
{
|
||||
var foldersToCheck = extra.Where(f => f?.Length > 0);
|
||||
|
||||
string path3DS = Path.GetPathRoot(Get3DSLocation());
|
||||
List<string> possiblePaths = new List<string>();
|
||||
List<string> foldersToCheck = new List<string>(extra.Where(f => f?.Length > 0));
|
||||
path = null;
|
||||
|
||||
if (path3DS != null) // check for Homebrew/CFW backups
|
||||
foldersToCheck.AddRange(Get3DSBackupPaths(path3DS));
|
||||
foldersToCheck = foldersToCheck.Concat(Get3DSBackupPaths(path3DS));
|
||||
|
||||
foreach (var p in foldersToCheck)
|
||||
path = null;
|
||||
List<string> possiblePaths = new List<string>();
|
||||
foreach (var folder in foldersToCheck)
|
||||
{
|
||||
if (!SaveUtil.GetSavesFromFolder(p, true, out IEnumerable<string> files))
|
||||
if (!SaveUtil.GetSavesFromFolder(folder, true, out IEnumerable<string> files))
|
||||
{
|
||||
if (files != null) // Could be null if `p` doesn't exist
|
||||
if (files != null) // can be null if folder doesn't exist
|
||||
{
|
||||
path = string.Join(Environment.NewLine, files); // `files` contains the error message
|
||||
return false;
|
||||
|
|
|
@ -77,7 +77,6 @@
|
|||
<Compile Include="PKM\PIDIVTests.cs" />
|
||||
<Compile Include="PKM\PKMTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Saves\Substructures\MemeCryptoTests.cs" />
|
||||
<Compile Include="Util\DateUtilTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -90,7 +89,9 @@
|
|||
<Name>PKHeX.WinForms</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<Folder Include="Saves\Substructures\" />
|
||||
</ItemGroup>
|
||||
<Choose>
|
||||
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using PKHeX.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PKHeX.Tests.Saves.Substructures
|
||||
{
|
||||
[TestClass]
|
||||
public class MemeCryptoTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void CanUseMemeCrypto()
|
||||
{
|
||||
Assert.IsTrue(MemeCrypto.CanUseMemeCrypto());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue