2017-09-16 18:38:58 +00:00
using System ;
2017-05-12 04:34:18 +00:00
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
2017-09-16 18:38:58 +00:00
using PKHeX.Core ;
2017-05-12 04:34:18 +00:00
namespace PKHeX.WinForms
{
public static class PathUtilWindows
{
2017-09-16 18:38:58 +00:00
/// <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>
2018-05-22 01:20:08 +00:00
public static string Get3DSLocation ( bool skipFirstDrive = true ) = > FindConsoleRootFolder ( "Nintendo 3DS" , skipFirstDrive ) ;
/// <summary>
/// Gets the Switch'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 folder.</returns>
public static string GetSwitchLocation ( bool skipFirstDrive = true ) = > FindConsoleRootFolder ( "Nintendo" , skipFirstDrive ) ;
private static string FindConsoleRootFolder ( string path , bool skipFirstDrive )
2017-05-12 04:34:18 +00:00
{
try
{
2017-09-16 18:38:58 +00:00
// Skip first drive (some users still have floppy drives and would chew up time!)
2018-05-22 01:20:08 +00:00
IEnumerable < string > DriveList = Environment . GetLogicalDrives ( ) ;
2017-09-16 18:38:58 +00:00
if ( skipFirstDrive )
DriveList = DriveList . Skip ( 1 ) ;
2018-05-22 01:20:08 +00:00
return DriveList
. Select ( drive = > Path . Combine ( drive , path ) )
. FirstOrDefault ( Directory . Exists ) ;
2017-05-12 04:34:18 +00:00
}
catch { }
return null ;
}
2017-05-16 23:27:43 +00:00
/// <summary>
/// Gets a list of 3DS save backup paths for the storage device.
/// </summary>
/// <param name="root">Root location of device</param>
/// <returns>List of possible 3DS save backup paths.</returns>
2017-09-16 18:38:58 +00:00
public static IEnumerable < string > Get3DSBackupPaths ( string root )
2017-05-16 23:27:43 +00:00
{
2017-09-16 18:38:58 +00:00
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" ) ;
2017-10-05 16:40:41 +00:00
yield return Path . Combine ( root , "3ds" , "Checkpoint" , "saves" ) ;
2017-05-16 23:27:43 +00:00
}
2018-05-22 01:20:08 +00:00
/// <summary>
/// Gets a list of Switch save backup paths for the storage device.
/// </summary>
/// <param name="root">Root location of device</param>
/// <returns>List of possible 3DS save backup paths.</returns>
public static IEnumerable < string > GetSwitchBackupPaths ( string root )
{
yield return Path . Combine ( root , "switch" , "Checkpoint" , "saves" ) ;
}
2017-05-12 04:34:18 +00:00
/// <summary>
2017-09-16 18:38:58 +00:00
/// Finds a compatible save file that was most recently saved (by file write time).
2017-05-12 04:34:18 +00:00
/// </summary>
2017-06-21 01:38:33 +00:00
/// <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>
2017-06-18 01:37:19 +00:00
public static bool DetectSaveFile ( out string path , params string [ ] extra )
2017-05-12 04:34:18 +00:00
{
2017-09-16 18:38:58 +00:00
var foldersToCheck = extra . Where ( f = > f ? . Length > 0 ) ;
2017-05-12 04:34:18 +00:00
2017-09-16 18:38:58 +00:00
string path3DS = Path . GetPathRoot ( Get3DSLocation ( ) ) ;
2017-05-16 23:27:43 +00:00
if ( path3DS ! = null ) // check for Homebrew/CFW backups
2017-09-16 18:38:58 +00:00
foldersToCheck = foldersToCheck . Concat ( Get3DSBackupPaths ( path3DS ) ) ;
2017-05-12 04:34:18 +00:00
2018-05-22 01:20:08 +00:00
string pathNX = Path . GetPathRoot ( GetSwitchLocation ( ) ) ;
if ( pathNX ! = null ) // check for Homebrew/CFW backups
foldersToCheck = foldersToCheck . Concat ( GetSwitchBackupPaths ( pathNX ) ) ;
2017-09-16 18:38:58 +00:00
path = null ;
List < string > possiblePaths = new List < string > ( ) ;
foreach ( var folder in foldersToCheck )
2017-05-12 04:34:18 +00:00
{
2017-09-16 18:38:58 +00:00
if ( ! SaveUtil . GetSavesFromFolder ( folder , true , out IEnumerable < string > files ) )
2017-05-12 04:34:18 +00:00
{
2017-09-16 18:38:58 +00:00
if ( files ! = null ) // can be null if folder doesn't exist
2017-06-21 01:38:33 +00:00
{
2017-06-21 05:19:04 +00:00
path = string . Join ( Environment . NewLine , files ) ; // `files` contains the error message
2017-06-21 01:38:33 +00:00
return false ;
}
2017-05-12 04:34:18 +00:00
}
2017-06-21 05:19:04 +00:00
if ( files ! = null )
possiblePaths . AddRange ( files ) ;
2017-05-12 04:34:18 +00:00
}
// return newest save file path that is valid
foreach ( var file in possiblePaths . OrderByDescending ( f = > new FileInfo ( f ) . LastWriteTime ) )
{
try
{
var data = File . ReadAllBytes ( file ) ;
2017-06-18 01:37:19 +00:00
var sav = SaveUtil . GetVariantSAV ( data ) ;
2017-05-12 04:34:18 +00:00
if ( sav ? . ChecksumsValid ! = true )
continue ;
path = file ;
return true ;
}
catch ( Exception e )
{
path = e . Message + Environment . NewLine + file ;
return false ;
}
}
return true ;
}
}
}