2019-02-15 19:46:46 +00:00
using System.Collections.Generic ;
2017-02-04 22:58:30 +00:00
using System.IO ;
using System.Linq ;
2018-07-21 04:32:33 +00:00
2018-04-07 04:23:09 +00:00
using static PKHeX . Core . MessageStrings ;
2017-02-04 22:58:30 +00:00
2018-07-21 04:32:33 +00:00
namespace PKHeX.Core
2017-02-04 22:58:30 +00:00
{
/// <summary>
/// Contains extension methods for use with a <see cref="SaveFile"/>.
/// </summary>
2018-07-21 04:32:33 +00:00
public static class BoxUtil
2017-02-04 22:58:30 +00:00
{
/// <summary>
/// Dumps a folder of files to the <see cref="SaveFile"/>.
/// </summary>
2020-06-17 02:46:22 +00:00
/// <param name="sav"><see cref="SaveFile"/> that is being dumped from.</param>
2017-02-04 22:58:30 +00:00
/// <param name="path">Folder to store <see cref="PKM"/> files.</param>
/// <param name="boxFolders">Option to save in child folders with the Box Name as the folder name.</param>
2019-02-10 04:19:55 +00:00
/// <returns>-1 if aborted, otherwise the amount of files dumped.</returns>
2020-06-17 02:46:22 +00:00
public static int DumpBoxes ( this SaveFile sav , string path , bool boxFolders = false )
2017-02-04 22:58:30 +00:00
{
2020-06-17 02:46:22 +00:00
if ( ! sav . HasBox )
2019-02-10 04:19:55 +00:00
return - 1 ;
2017-02-04 22:58:30 +00:00
2020-06-17 02:46:22 +00:00
var boxData = sav . BoxData ;
2019-02-23 22:58:48 +00:00
var ctr = 0 ;
2020-06-17 02:46:22 +00:00
foreach ( var pk in boxData )
2017-02-04 22:58:30 +00:00
{
if ( pk . Species = = 0 | | ! pk . Valid )
continue ;
2020-06-17 02:46:22 +00:00
var boxFolder = path ;
2017-02-04 22:58:30 +00:00
if ( boxFolders )
{
2020-06-17 02:46:22 +00:00
var boxName = Util . CleanFileName ( sav . GetBoxName ( pk . Box - 1 ) ) ;
boxFolder = Path . Combine ( path , boxName ) ;
Directory . CreateDirectory ( boxFolder ) ;
2017-02-04 22:58:30 +00:00
}
2019-02-23 22:58:48 +00:00
var fileName = Util . CleanFileName ( pk . FileName ) ;
2020-06-17 02:46:22 +00:00
var fn = Path . Combine ( boxFolder , fileName ) ;
2019-02-23 22:58:48 +00:00
if ( File . Exists ( fn ) )
continue ;
2019-11-16 22:03:25 +00:00
File . WriteAllBytes ( fn , pk . DecryptedPartyData ) ;
2019-02-23 22:58:48 +00:00
ctr + + ;
2017-02-04 22:58:30 +00:00
}
2019-02-10 04:19:55 +00:00
return ctr ;
2017-02-04 22:58:30 +00:00
}
2017-06-23 01:51:46 +00:00
/// <summary>
/// Dumps the <see cref="SaveFile.BoxData"/> to a folder with individual decrypted files.
/// </summary>
2020-06-17 02:46:22 +00:00
/// <param name="sav"><see cref="SaveFile"/> that is being dumped from.</param>
2017-06-23 01:51:46 +00:00
/// <param name="path">Folder to store <see cref="PKM"/> files.</param>
/// <param name="currentBox">Box contents to be dumped.</param>
2019-02-10 04:19:55 +00:00
/// <returns>-1 if aborted, otherwise the amount of files dumped.</returns>
2020-06-17 02:46:22 +00:00
public static int DumpBox ( this SaveFile sav , string path , int currentBox )
2017-03-21 07:21:03 +00:00
{
2020-06-17 02:46:22 +00:00
if ( ! sav . HasBox )
2019-02-10 04:19:55 +00:00
return - 1 ;
2017-03-21 07:21:03 +00:00
2020-06-17 02:46:22 +00:00
var boxData = sav . BoxData ;
2019-02-23 22:58:48 +00:00
var ctr = 0 ;
2020-06-17 02:46:22 +00:00
foreach ( var pk in boxData )
2017-03-21 07:21:03 +00:00
{
2018-05-22 01:28:54 +00:00
if ( pk . Species = = 0 | | ! pk . Valid | | pk . Box - 1 ! = currentBox )
2017-03-21 07:21:03 +00:00
continue ;
2019-02-23 22:58:48 +00:00
var fileName = Path . Combine ( path , Util . CleanFileName ( pk . FileName ) ) ;
if ( File . Exists ( fileName ) )
continue ;
2019-11-16 22:03:25 +00:00
File . WriteAllBytes ( fileName , pk . DecryptedPartyData ) ;
2017-03-21 07:21:03 +00:00
ctr + + ;
}
2019-02-10 04:19:55 +00:00
return ctr ;
2017-03-21 07:21:03 +00:00
}
2017-02-04 22:58:30 +00:00
/// <summary>
/// Loads a folder of files to the <see cref="SaveFile"/>.
/// </summary>
2020-06-17 02:46:22 +00:00
/// <param name="sav"><see cref="SaveFile"/> to load folder to.</param>
2017-02-04 22:58:30 +00:00
/// <param name="path">Folder to load <see cref="PKM"/> files from. Files are only loaded from the top directory.</param>
/// <param name="result">Result message from the method.</param>
/// <param name="boxStart">First box to start loading to. All prior boxes are not modified.</param>
/// <param name="boxClear">Instruction to clear boxes after the starting box.</param>
2018-12-29 01:58:13 +00:00
/// <param name="overwrite">Overwrite existing full slots. If true, will only overwrite empty slots.</param>
2017-02-04 22:58:30 +00:00
/// <param name="noSetb">Bypass option to not modify <see cref="PKM"/> properties when setting to Save File.</param>
2018-02-01 07:37:01 +00:00
/// <param name="all">Enumerate all files even in sub-folders.</param>
2019-02-23 22:37:06 +00:00
/// <returns>Count of files imported.</returns>
2020-06-17 02:46:22 +00:00
public static int LoadBoxes ( this SaveFile sav , string path , out string result , int boxStart = 0 , bool boxClear = false , bool overwrite = false , PKMImportSetting noSetb = PKMImportSetting . UseDefault , bool all = false )
2017-02-04 22:58:30 +00:00
{
2018-02-01 07:37:01 +00:00
if ( string . IsNullOrWhiteSpace ( path ) | | ! Directory . Exists ( path ) )
2019-02-23 22:37:06 +00:00
{ result = MsgSaveBoxExportPathInvalid ; return - 1 ; }
2018-02-01 07:37:01 +00:00
2020-06-17 02:46:22 +00:00
var option = all ? SearchOption . AllDirectories : SearchOption . TopDirectoryOnly ;
var files = Directory . EnumerateFiles ( path , "*.*" , option ) ;
return sav . LoadBoxes ( files , out result , boxStart , boxClear , overwrite , noSetb ) ;
2018-02-01 07:37:01 +00:00
}
2018-07-29 20:27:48 +00:00
2018-02-01 07:37:01 +00:00
/// <summary>
/// Loads a folder of files to the <see cref="SaveFile"/>.
/// </summary>
2020-06-17 02:46:22 +00:00
/// <param name="sav"><see cref="SaveFile"/> to load folder to.</param>
/// <param name="files">Files to load <see cref="PKM"/> files from.</param>
2018-02-01 07:37:01 +00:00
/// <param name="result">Result message from the method.</param>
/// <param name="boxStart">First box to start loading to. All prior boxes are not modified.</param>
/// <param name="boxClear">Instruction to clear boxes after the starting box.</param>
2018-12-29 01:58:13 +00:00
/// <param name="overwrite">Overwrite existing full slots. If true, will only overwrite empty slots.</param>
2018-02-01 07:37:01 +00:00
/// <param name="noSetb">Bypass option to not modify <see cref="PKM"/> properties when setting to Save File.</param>
2019-02-23 22:37:06 +00:00
/// <returns>Count of files imported.</returns>
2020-06-17 02:46:22 +00:00
public static int LoadBoxes ( this SaveFile sav , IEnumerable < string > files , out string result , int boxStart = 0 , bool boxClear = false , bool overwrite = false , PKMImportSetting noSetb = PKMImportSetting . UseDefault )
2018-02-01 07:37:01 +00:00
{
2020-06-17 02:46:22 +00:00
var pks = GetPossiblePKMsFromPaths ( sav , files ) ;
return sav . LoadBoxes ( pks , out result , boxStart , boxClear , overwrite , noSetb ) ;
2018-02-01 07:37:01 +00:00
}
2018-07-29 20:27:48 +00:00
2018-02-01 07:37:01 +00:00
/// <summary>
/// Loads a folder of files to the <see cref="SaveFile"/>.
/// </summary>
2020-06-17 02:46:22 +00:00
/// <param name="sav"><see cref="SaveFile"/> to load folder to.</param>
2019-02-23 22:37:06 +00:00
/// <param name="encounters">Encounters to create <see cref="PKM"/> files from.</param>
2018-02-01 07:37:01 +00:00
/// <param name="result">Result message from the method.</param>
/// <param name="boxStart">First box to start loading to. All prior boxes are not modified.</param>
/// <param name="boxClear">Instruction to clear boxes after the starting box.</param>
2018-12-29 01:58:13 +00:00
/// <param name="overwrite">Overwrite existing full slots. If true, will only overwrite empty slots.</param>
2018-02-01 07:37:01 +00:00
/// <param name="noSetb">Bypass option to not modify <see cref="PKM"/> properties when setting to Save File.</param>
2019-02-23 22:37:06 +00:00
/// <returns>Count of files imported.</returns>
2020-06-17 02:46:22 +00:00
public static int LoadBoxes ( this SaveFile sav , IEnumerable < IEncounterable > encounters , out string result , int boxStart = 0 , bool boxClear = false , bool overwrite = false , PKMImportSetting noSetb = PKMImportSetting . UseDefault )
2018-02-01 07:37:01 +00:00
{
2020-06-17 02:46:22 +00:00
var pks = encounters . Select ( z = > z . ConvertToPKM ( sav ) ) ;
return sav . LoadBoxes ( pks , out result , boxStart , boxClear , overwrite , noSetb ) ;
2018-02-01 07:37:01 +00:00
}
2018-07-29 20:27:48 +00:00
2018-02-01 07:37:01 +00:00
/// <summary>
/// Loads a folder of files to the <see cref="SaveFile"/>.
/// </summary>
2020-06-17 02:46:22 +00:00
/// <param name="sav"><see cref="SaveFile"/> to load folder to.</param>
2018-02-01 07:37:01 +00:00
/// <param name="pks">Unconverted <see cref="PKM"/> objects to load.</param>
/// <param name="result">Result message from the method.</param>
/// <param name="boxStart">First box to start loading to. All prior boxes are not modified.</param>
/// <param name="boxClear">Instruction to clear boxes after the starting box.</param>
2018-12-29 01:58:13 +00:00
/// <param name="overwrite">Overwrite existing full slots. If true, will only overwrite empty slots.</param>
2018-02-01 07:37:01 +00:00
/// <param name="noSetb">Bypass option to not modify <see cref="PKM"/> properties when setting to Save File.</param>
/// <returns>True if any files are imported.</returns>
2020-06-17 02:46:22 +00:00
public static int LoadBoxes ( this SaveFile sav , IEnumerable < PKM > pks , out string result , int boxStart = 0 , bool boxClear = false , bool overwrite = false , PKMImportSetting noSetb = PKMImportSetting . UseDefault )
2018-02-01 07:37:01 +00:00
{
2020-06-17 02:46:22 +00:00
if ( ! sav . HasBox )
2019-02-23 22:37:06 +00:00
{ result = MsgSaveBoxFailNone ; return - 1 ; }
2018-02-01 07:37:01 +00:00
2020-06-17 02:46:22 +00:00
var compat = sav . GetCompatible ( pks ) ;
2017-02-04 22:58:30 +00:00
if ( boxClear )
2020-06-17 02:46:22 +00:00
sav . ClearBoxes ( boxStart ) ;
2017-02-04 22:58:30 +00:00
2020-06-17 02:46:22 +00:00
int ctr = sav . ImportPKMs ( compat , overwrite , boxStart , noSetb ) ;
2018-02-01 07:37:01 +00:00
if ( ctr < = 0 )
2017-02-04 22:58:30 +00:00
{
2018-04-07 04:23:09 +00:00
result = MsgSaveBoxImportNoFiles ;
2019-02-23 22:37:06 +00:00
return - 1 ;
2018-02-01 07:37:01 +00:00
}
2017-02-04 22:58:30 +00:00
2018-04-07 04:23:09 +00:00
result = string . Format ( MsgSaveBoxImportSuccess , ctr ) ;
2019-02-23 22:37:06 +00:00
return ctr ;
2018-02-01 07:37:01 +00:00
}
2017-02-04 22:58:30 +00:00
2020-06-17 02:46:22 +00:00
public static IEnumerable < PKM > GetPKMsFromPaths ( IEnumerable < string > files , int generation )
2018-02-01 07:37:01 +00:00
{
2020-06-17 02:46:22 +00:00
var result = files
2018-02-01 07:37:01 +00:00
. Where ( file = > PKX . IsPKM ( new FileInfo ( file ) . Length ) )
. Select ( File . ReadAllBytes )
PKHeX.Core Nullable cleanup (#2401)
* Handle some nullable cases
Refactor MysteryGift into a second abstract class (backed by a byte array, or fake data)
Make some classes have explicit constructors instead of { } initialization
* Handle bits more obviously without null
* Make SaveFile.BAK explicitly readonly again
* merge constructor methods to have readonly fields
* Inline some properties
* More nullable handling
* Rearrange box actions
define straightforward classes to not have any null properties
* Make extrabyte reference array immutable
* Move tooltip creation to designer
* Rearrange some logic to reduce nesting
* Cache generated fonts
* Split mystery gift album purpose
* Handle more tooltips
* Disallow null setters
* Don't capture RNG object, only type enum
* Unify learnset objects
Now have readonly properties which are never null
don't new() empty learnsets (>800 Learnset objects no longer created,
total of 2400 objects since we also new() a move & level array)
optimize g1/2 reader for early abort case
* Access rewrite
Initialize blocks in a separate object, and get via that object
removes a couple hundred "might be null" warnings since blocks are now readonly getters
some block references have been relocated, but interfaces should expose all that's needed
put HoF6 controls in a groupbox, and disable
* Readonly personal data
* IVs non nullable for mystery gift
* Explicitly initialize forced encounter moves
* Make shadow objects readonly & non-null
Put murkrow fix in binary data resource, instead of on startup
* Assign dex form fetch on constructor
Fixes legality parsing edge cases
also handle cxd parse for valid; exit before exception is thrown in FrameGenerator
* Remove unnecessary null checks
* Keep empty value until init
SetPouch sets the value to an actual one during load, but whatever
* Readonly team lock data
* Readonly locks
Put locked encounters at bottom (favor unlocked)
* Mail readonly data / offset
Rearrange some call flow and pass defaults
Add fake classes for SaveDataEditor mocking
Always party size, no need to check twice in stat editor
use a fake save file as initial data for savedata editor, and for
gamedata (wow i found a usage)
constrain eventwork editor to struct variable types (uint, int, etc),
thus preventing null assignment errors
2019-10-17 01:47:31 +00:00
. Select ( data = > PKMConverter . GetPKMfromBytes ( data , prefer : generation ) ) ;
foreach ( var pkm in result )
{
if ( pkm ! = null )
yield return pkm ;
}
2018-02-01 07:37:01 +00:00
}
2018-11-23 20:22:51 +00:00
2020-06-17 02:46:22 +00:00
private static IEnumerable < PKM > GetPossiblePKMsFromPaths ( SaveFile sav , IEnumerable < string > files )
2018-11-23 20:22:51 +00:00
{
2020-06-17 02:46:22 +00:00
foreach ( var f in files )
2018-11-23 20:22:51 +00:00
{
var obj = FileUtil . GetSupportedFile ( f , sav ) ;
switch ( obj )
{
case PKM pk :
yield return pk ;
break ;
2020-12-22 06:33:48 +00:00
case MysteryGift { IsPokémon : true } g :
2018-11-23 20:22:51 +00:00
yield return g . ConvertToPKM ( sav ) ;
break ;
case GP1 g when g . Species ! = 0 :
yield return g . ConvertToPB7 ( sav ) ;
break ;
2020-11-04 04:12:42 +00:00
case IPokeGroup g :
foreach ( var p in g . Contents )
yield return p ;
break ;
2018-11-23 20:22:51 +00:00
}
}
}
2020-09-19 05:11:13 +00:00
/// <summary>
/// Gets box names for all boxes in the save file.
/// </summary>
/// <param name="sav"><see cref="SaveFile"/> that box names are being dumped for.</param>
/// <returns>Returns default English box names in the event the save file does not have names (not exportable), or fails to return a box name.</returns>
public static string [ ] GetBoxNames ( SaveFile sav )
{
int count = sav . BoxCount ;
var result = new string [ count ] ;
2020-12-05 13:36:23 +00:00
if ( ! sav . State . Exportable )
2020-09-19 05:11:13 +00:00
{
for ( int i = 0 ; i < count ; i + + )
result [ i ] = $"Box {i + 1}" ;
return result ;
}
for ( int i = 0 ; i < count ; i + + )
{
try { result [ i ] = sav . GetBoxName ( i ) ; }
#pragma warning disable CA1031 // Do not catch general exception types
catch { result [ i ] = $"Box {i + 1}" ; }
#pragma warning restore CA1031 // Do not catch general exception types
}
return result ;
}
2017-02-04 22:58:30 +00:00
}
}