2016-06-20 04:22:43 +00:00
using System ;
2017-09-29 05:20:27 +00:00
using System.Collections.Generic ;
2017-07-02 02:43:51 +00:00
using System.Diagnostics ;
2016-06-20 04:22:43 +00:00
using System.Linq ;
2017-01-08 07:54:09 +00:00
namespace PKHeX.Core
2016-06-20 04:22:43 +00:00
{
2017-10-24 06:12:58 +00:00
/// <summary>
/// Base Class for Save Files
/// </summary>
2018-06-15 23:00:28 +00:00
public abstract class SaveFile : ITrainerInfo , IGameValueLimit
2016-06-20 04:22:43 +00:00
{
2017-06-19 05:27:40 +00:00
public static bool SetUpdateDex { protected get ; set ; } = true ;
public static bool SetUpdatePKM { protected get ; set ; } = true ;
2016-06-20 04:22:43 +00:00
// General Object Properties
public byte [ ] Data ;
public bool Edited ;
2018-07-17 04:00:43 +00:00
public string FileName , FilePath , FileFolder ;
2018-07-30 04:51:45 +00:00
public string BAKName = > $"{FileName} [{BAKText}].bak" ;
protected abstract string BAKText { get ; }
2016-06-28 06:03:57 +00:00
public byte [ ] BAK { get ; protected set ; }
public bool Exportable { get ; protected set ; }
2016-06-20 04:22:43 +00:00
public abstract SaveFile Clone ( ) ;
2016-06-20 05:11:53 +00:00
public abstract string Filter { get ; }
2018-08-03 03:11:42 +00:00
public byte [ ] Footer { protected get ; set ; } = Array . Empty < byte > ( ) ; // .dsv
public byte [ ] Header { protected get ; set ; } = Array . Empty < byte > ( ) ; // .gci
2017-06-18 01:37:19 +00:00
public bool Japanese { get ; protected set ; }
2018-07-16 00:48:31 +00:00
public virtual string PlayTimeString = > $"{PlayedHours}ː {PlayedMinutes:00}ː {PlayedSeconds:00}" ; // not :
2018-03-12 01:07:48 +00:00
public bool IndeterminateGame = > Version = = GameVersion . Unknown ;
2017-01-05 06:22:50 +00:00
public abstract string Extension { get ; }
2018-08-03 03:11:42 +00:00
2018-05-12 15:13:39 +00:00
public virtual string [ ] PKMExtensions = > PKM . Extensions . Where ( f = >
2017-01-05 06:22:50 +00:00
{
int gen = f . Last ( ) - 0x30 ;
2017-01-27 17:13:42 +00:00
return 3 < = gen & & gen < = Generation ;
2017-01-05 06:22:50 +00:00
} ) . ToArray ( ) ;
2017-06-18 01:37:19 +00:00
2016-06-20 04:22:43 +00:00
// General PKM Properties
2016-09-26 23:14:11 +00:00
public abstract Type PKMType { get ; }
2017-06-18 01:37:19 +00:00
public abstract PKM GetPKM ( byte [ ] data ) ;
2016-06-20 04:22:43 +00:00
public abstract PKM BlankPKM { get ; }
2017-06-18 01:37:19 +00:00
public abstract byte [ ] DecryptPKM ( byte [ ] data ) ;
2016-06-20 04:22:43 +00:00
public abstract int SIZE_STORED { get ; }
2017-06-18 01:37:19 +00:00
protected abstract int SIZE_PARTY { get ; }
2016-06-20 04:22:43 +00:00
public abstract int MaxEV { get ; }
2016-08-28 10:18:22 +00:00
public virtual int MaxIV = > 31 ;
2016-06-20 04:22:43 +00:00
public ushort [ ] HeldItems { get ; protected set ; }
// General SAV Properties
2019-02-19 05:59:57 +00:00
public byte [ ] Write ( ExportFlags flags = ExportFlags . None )
2017-04-02 14:53:46 +00:00
{
2019-02-19 05:59:57 +00:00
byte [ ] data = GetFinalData ( ) ;
if ( Footer . Length > 0 & & flags . HasFlagFast ( ExportFlags . IncludeFooter ) )
return data . Concat ( Footer ) . ToArray ( ) ;
if ( Header . Length > 0 & & flags . HasFlagFast ( ExportFlags . IncludeHeader ) )
return data . Concat ( Data ) . ToArray ( ) ;
return data ;
2017-04-02 14:53:46 +00:00
}
2018-08-03 03:11:42 +00:00
2019-02-19 05:59:57 +00:00
protected virtual byte [ ] GetFinalData ( )
2016-06-20 04:22:43 +00:00
{
2017-06-18 01:37:19 +00:00
SetChecksums ( ) ;
2016-06-20 04:22:43 +00:00
return Data ;
}
2018-08-03 03:11:42 +00:00
2017-09-29 05:20:27 +00:00
public virtual string MiscSaveChecks ( ) = > string . Empty ;
public virtual string MiscSaveInfo ( ) = > string . Empty ;
2016-06-28 06:03:57 +00:00
public virtual GameVersion Version { get ; protected set ; }
2016-06-20 04:22:43 +00:00
public abstract bool ChecksumsValid { get ; }
public abstract string ChecksumInfo { get ; }
public abstract int Generation { get ; }
2016-07-28 01:59:10 +00:00
public PersonalTable Personal { get ; set ; }
2016-06-20 04:22:43 +00:00
2019-01-07 00:22:45 +00:00
public bool GG = > Data . Length = = SaveUtil . SIZE_G7GG & & GameVersion . GG . Contains ( Version ) ;
2017-09-19 05:36:06 +00:00
public bool USUM = > Data . Length = = SaveUtil . SIZE_G7USUM ;
2016-11-08 16:43:57 +00:00
public bool SM = > Data . Length = = SaveUtil . SIZE_G7SM ;
2016-06-20 04:22:43 +00:00
public bool ORASDEMO = > Data . Length = = SaveUtil . SIZE_G6ORASDEMO ;
2016-08-16 03:57:42 +00:00
public bool ORAS = > Data . Length = = SaveUtil . SIZE_G6ORAS ;
public bool XY = > Data . Length = = SaveUtil . SIZE_G6XY ;
2016-07-26 06:11:17 +00:00
public bool B2W2 = > Version = = GameVersion . B2W2 ;
public bool BW = > Version = = GameVersion . BW ;
public bool HGSS = > Version = = GameVersion . HGSS ;
public bool Pt = > Version = = GameVersion . Pt ;
public bool DP = > Version = = GameVersion . DP ;
public bool E = > Version = = GameVersion . E ;
public bool FRLG = > Version = = GameVersion . FRLG ;
public bool RS = > Version = = GameVersion . RS ;
2016-09-02 21:20:39 +00:00
public bool GSC = > Version = = GameVersion . GS | | Version = = GameVersion . C ;
2017-01-07 05:22:42 +00:00
public bool RBY = > Version = = GameVersion . RBY ;
public bool GameCube = > new [ ] { GameVersion . COLO , GameVersion . XD , GameVersion . RSBOX } . Contains ( Version ) ;
2016-09-02 21:20:39 +00:00
2017-06-18 01:37:19 +00:00
public abstract int MaxMoveID { get ; }
public abstract int MaxSpeciesID { get ; }
public abstract int MaxAbilityID { get ; }
public abstract int MaxItemID { get ; }
public abstract int MaxBallID { get ; }
public abstract int MaxGameID { get ; }
2018-06-15 23:00:28 +00:00
public virtual int MinGameID = > 0 ;
2016-06-21 01:58:06 +00:00
2016-06-20 04:22:43 +00:00
// Flags
public bool HasWondercards = > WondercardData > - 1 ;
public bool HasSuperTrain = > SuperTrain > - 1 ;
public bool HasBerryField = > BerryField > - 1 ;
public bool HasHoF = > HoF > - 1 ;
public bool HasSecretBase = > SecretBase > - 1 ;
public bool HasPSS = > PSS > - 1 ;
public bool HasOPower = > OPower > - 1 ;
2019-02-02 07:26:43 +00:00
public bool HasJPEG = > JPEGData . Length > 0 ;
2016-06-20 04:22:43 +00:00
public bool HasBox = > Box > - 1 ;
2016-07-05 06:52:37 +00:00
public virtual bool HasParty = > Party > - 1 ;
2016-06-20 04:22:43 +00:00
public bool HasBattleBox = > BattleBox > - 1 ;
public bool HasFused = > Fused > - 1 ;
public bool HasGTS = > GTS > - 1 ;
public bool HasDaycare = > Daycare > - 1 ;
2016-07-04 18:56:30 +00:00
public virtual bool HasPokeDex = > PokeDex > - 1 ;
2017-06-18 01:37:19 +00:00
public virtual bool HasBoxWallpapers = > GetBoxWallpaperOffset ( 0 ) > - 1 ;
2017-12-27 23:52:29 +00:00
public virtual bool HasNamableBoxes = > HasBoxWallpapers ;
2016-06-20 04:22:43 +00:00
public bool HasPokeBlock = > ORAS & & ! ORASDEMO ;
2019-02-02 07:26:43 +00:00
public virtual bool HasEvents = > EventFlags . Length ! = 0 ;
2018-08-03 03:11:42 +00:00
public bool HasLink = > ( ORAS & & ! ORASDEMO ) | | XY ;
2016-06-20 04:22:43 +00:00
// Counts
protected virtual int GiftCountMax { get ; } = int . MinValue ;
protected virtual int GiftFlagMax { get ; } = 0x800 ;
protected virtual int EventFlagMax { get ; } = int . MinValue ;
protected virtual int EventConstMax { get ; } = int . MinValue ;
2016-06-26 21:23:41 +00:00
public virtual int DaycareSeedSize { get ; } = 0 ;
2016-06-20 04:22:43 +00:00
public abstract int OTLength { get ; }
public abstract int NickLength { get ; }
2017-02-04 20:13:54 +00:00
public virtual int MaxMoney = > 9999999 ;
public virtual int MaxCoins = > 9999 ;
2016-06-20 04:22:43 +00:00
// Offsets
2018-02-16 01:05:45 +00:00
protected int Box { get ; set ; } = int . MinValue ;
2016-06-20 04:22:43 +00:00
protected int Party { get ; set ; } = int . MinValue ;
protected int Trainer1 { get ; set ; } = int . MinValue ;
protected int Daycare { get ; set ; } = int . MinValue ;
protected int WondercardData { get ; set ; } = int . MinValue ;
protected int PCLayout { get ; set ; } = int . MinValue ;
protected int EventFlag { get ; set ; } = int . MinValue ;
protected int EventConst { get ; set ; } = int . MinValue ;
public int GTS { get ; protected set ; } = int . MinValue ;
public int BattleBox { get ; protected set ; } = int . MinValue ;
public int Fused { get ; protected set ; } = int . MinValue ;
public int SUBE { get ; protected set ; } = int . MinValue ;
public int PokeDex { get ; protected set ; } = int . MinValue ;
public int SuperTrain { get ; protected set ; } = int . MinValue ;
public int SecretBase { get ; protected set ; } = int . MinValue ;
public int PSS { get ; protected set ; } = int . MinValue ;
public int BerryField { get ; protected set ; } = int . MinValue ;
public int OPower { get ; protected set ; } = int . MinValue ;
public int HoF { get ; protected set ; } = int . MinValue ;
// SAV Properties
2017-09-29 05:20:27 +00:00
public IList < PKM > BoxData
2016-06-20 04:22:43 +00:00
{
get
{
2016-08-28 10:18:22 +00:00
PKM [ ] data = new PKM [ BoxCount * BoxSlotCount ] ;
2018-06-17 04:56:16 +00:00
for ( int box = 0 ; box < BoxCount ; box + + )
AddBoxData ( data , box , box * BoxSlotCount ) ;
2016-06-20 04:22:43 +00:00
return data ;
}
set
{
2017-09-29 05:20:27 +00:00
if ( value . Count ! = BoxCount * BoxSlotCount )
throw new ArgumentException ( $"Expected {BoxCount*BoxSlotCount}, got {value.Count}" ) ;
2016-06-20 04:22:43 +00:00
if ( value . Any ( pk = > PKMType ! = pk . GetType ( ) ) )
throw new ArgumentException ( $"Not {PKMType} array." ) ;
2018-05-20 03:48:03 +00:00
for ( int b = 0 ; b < BoxCount ; b + + )
2018-06-17 04:56:16 +00:00
SetBoxData ( value , b , b * BoxSlotCount ) ;
2016-06-20 04:22:43 +00:00
}
}
2018-08-03 03:11:42 +00:00
2019-01-28 05:57:33 +00:00
public void SetBoxData ( IList < PKM > value , int box , int index = 0 )
2018-06-17 04:56:16 +00:00
{
int ofs = GetBoxOffset ( box ) ;
for ( int slot = 0 ; slot < BoxSlotCount ; slot + + , ofs + = SIZE_STORED )
{
2019-01-09 02:31:14 +00:00
var pk = value [ index + slot ] ;
if ( ! pk . StorageFlags . IsOverwriteProtected ( ) )
SetStoredSlot ( pk , ofs ) ;
2018-06-17 04:56:16 +00:00
}
}
2018-08-03 03:11:42 +00:00
2018-06-17 04:56:16 +00:00
public PKM [ ] GetBoxData ( int box )
{
var data = new PKM [ BoxSlotCount ] ;
AddBoxData ( data , box , 0 ) ;
return data ;
}
2018-08-03 03:11:42 +00:00
2018-06-17 04:56:16 +00:00
private void AddBoxData ( IList < PKM > data , int box , int index )
{
int ofs = GetBoxOffset ( box ) ;
var boxName = GetBoxName ( box ) ;
for ( int slot = 0 ; slot < BoxSlotCount ; slot + + , ofs + = SIZE_STORED )
{
int i = slot + index ;
data [ i ] = GetStoredSlot ( ofs ) ;
data [ i ] . Identifier = $"{boxName}:{slot + 1:00}" ;
data [ i ] . Box = box + 1 ;
data [ i ] . Slot = slot + 1 ;
2019-01-09 02:31:14 +00:00
data [ i ] . StorageFlags = GetSlotFlags ( box , slot ) ;
2018-06-17 04:56:16 +00:00
}
}
2017-09-29 05:20:27 +00:00
public IList < PKM > PartyData
2016-06-20 04:22:43 +00:00
{
get
{
PKM [ ] data = new PKM [ PartyCount ] ;
for ( int i = 0 ; i < data . Length ; i + + )
2017-06-18 01:37:19 +00:00
data [ i ] = GetPartySlot ( GetPartyOffset ( i ) ) ;
2016-06-20 04:22:43 +00:00
return data ;
}
set
{
2017-09-29 05:20:27 +00:00
if ( value . Count = = 0 | | value . Count > 6 )
2017-09-30 05:58:25 +00:00
throw new ArgumentException ( $"Expected 1-6, got {value.Count}" ) ;
2016-06-20 04:22:43 +00:00
if ( value . Any ( pk = > PKMType ! = pk . GetType ( ) ) )
throw new ArgumentException ( $"Not {PKMType} array." ) ;
if ( value [ 0 ] . Species = = 0 )
2017-09-29 05:20:27 +00:00
Debug . WriteLine ( $"Empty first slot, received {value.Count}." ) ;
2016-06-20 04:22:43 +00:00
2018-09-25 02:43:59 +00:00
int ctr = 0 ;
foreach ( var exist in value . Where ( pk = > pk . Species ! = 0 ) )
SetPartySlot ( exist , GetPartyOffset ( ctr + + ) ) ;
for ( int i = ctr ; i < 6 ; i + + )
SetPartySlot ( BlankPKM , GetPartyOffset ( i ) ) ;
2016-06-20 04:22:43 +00:00
}
}
2018-08-03 03:11:42 +00:00
2017-09-29 05:20:27 +00:00
public IList < PKM > BattleBoxData
2016-06-20 04:22:43 +00:00
{
get
{
2016-10-20 01:19:01 +00:00
if ( ! HasBattleBox )
2018-08-02 01:30:51 +00:00
return Array . Empty < PKM > ( ) ;
2016-06-20 04:22:43 +00:00
PKM [ ] data = new PKM [ 6 ] ;
for ( int i = 0 ; i < data . Length ; i + + )
{
2019-01-12 06:25:48 +00:00
data [ i ] = GetStoredSlot ( GetBattleBoxOffset ( i ) ) ;
2019-01-09 02:31:14 +00:00
if ( BattleBoxLocked )
data [ i ] . StorageFlags | = StorageSlotFlag . Locked ;
2018-05-20 03:48:03 +00:00
if ( data [ i ] . Species ! = 0 )
continue ;
Array . Resize ( ref data , i ) ;
return data ;
2016-06-20 04:22:43 +00:00
}
return data ;
}
}
2019-01-12 06:25:48 +00:00
public int GetBattleBoxOffset ( int index ) = > BattleBox + ( SIZE_STORED * index ) ;
2017-09-14 03:37:18 +00:00
/// <summary> All Event Flag values for the savegame </summary>
2016-06-20 04:22:43 +00:00
public bool [ ] EventFlags
{
get
{
if ( EventFlagMax < 0 )
2019-02-02 07:26:43 +00:00
return Array . Empty < bool > ( ) ;
2016-06-20 04:22:43 +00:00
bool [ ] Flags = new bool [ EventFlagMax ] ;
for ( int i = 0 ; i < Flags . Length ; i + + )
2017-09-24 23:36:51 +00:00
Flags [ i ] = GetEventFlag ( i ) ;
2016-06-20 04:22:43 +00:00
return Flags ;
}
set
{
if ( EventFlagMax < 0 )
return ;
if ( value . Length ! = EventFlagMax )
return ;
for ( int i = 0 ; i < value . Length ; i + + )
2017-09-24 23:36:51 +00:00
SetEventFlag ( i , value [ i ] ) ;
2016-06-20 04:22:43 +00:00
}
}
2018-08-03 03:11:42 +00:00
2017-09-14 03:37:18 +00:00
/// <summary> All Event Constant values for the savegame </summary>
2016-06-20 04:22:43 +00:00
public ushort [ ] EventConsts
{
get
{
if ( EventConstMax < 0 )
2019-02-02 07:26:43 +00:00
return Array . Empty < ushort > ( ) ;
2016-06-20 04:22:43 +00:00
ushort [ ] Constants = new ushort [ EventConstMax ] ;
for ( int i = 0 ; i < Constants . Length ; i + + )
2018-08-03 03:11:42 +00:00
Constants [ i ] = BitConverter . ToUInt16 ( Data , EventConst + ( i * 2 ) ) ;
2016-06-20 04:22:43 +00:00
return Constants ;
}
set
{
if ( EventConstMax < 0 )
return ;
if ( value . Length ! = EventConstMax )
return ;
for ( int i = 0 ; i < value . Length ; i + + )
2018-08-03 03:11:42 +00:00
BitConverter . GetBytes ( value [ i ] ) . CopyTo ( Data , EventConst + ( i * 2 ) ) ;
2016-06-20 04:22:43 +00:00
}
}
2018-08-03 03:11:42 +00:00
2017-09-14 03:37:18 +00:00
/// <summary>
/// Gets the <see cref="bool"/> status of a desired Event Flag
/// </summary>
/// <param name="flagNumber">Event Flag to check</param>
/// <returns>Flag is Set (true) or not Set (false)</returns>
2018-09-27 04:05:06 +00:00
public virtual bool GetEventFlag ( int flagNumber )
2017-09-14 03:37:18 +00:00
{
2018-09-28 03:01:34 +00:00
if ( flagNumber > = EventFlagMax )
2017-09-14 03:37:18 +00:00
throw new ArgumentException ( $"Event Flag to get ({flagNumber}) is greater than max ({EventFlagMax})." ) ;
2017-09-24 23:36:51 +00:00
return GetFlag ( EventFlag + ( flagNumber > > 3 ) , flagNumber & 7 ) ;
2017-09-14 03:37:18 +00:00
}
/// <summary>
/// Sets the <see cref="bool"/> status of a desired Event Flag
/// </summary>
/// <param name="flagNumber">Event Flag to check</param>
/// <param name="value">Event Flag status to set</param>
/// <remarks>Flag is Set (true) or not Set (false)</remarks>
2018-09-27 04:05:06 +00:00
public virtual void SetEventFlag ( int flagNumber , bool value )
2017-09-14 03:37:18 +00:00
{
2018-09-28 03:01:34 +00:00
if ( flagNumber > = EventFlagMax )
2017-09-14 03:37:18 +00:00
throw new ArgumentException ( $"Event Flag to set ({flagNumber}) is greater than max ({EventFlagMax})." ) ;
2017-09-24 23:36:51 +00:00
SetFlag ( EventFlag + ( flagNumber > > 3 ) , flagNumber & 7 , value ) ;
}
2018-08-03 03:11:42 +00:00
2017-10-19 04:43:42 +00:00
/// <summary>
/// Gets the <see cref="bool"/> status of the Flag at the specified offset and index.
/// </summary>
/// <param name="offset">Offset to read from</param>
/// <param name="bitIndex">Bit index to read</param>
/// <returns>Flag is Set (true) or not Set (false)</returns>
public bool GetFlag ( int offset , int bitIndex )
2017-09-24 23:36:51 +00:00
{
2017-10-19 04:43:42 +00:00
bitIndex & = 7 ; // ensure bit access is 0-7
return ( Data [ offset ] > > bitIndex & 1 ) ! = 0 ;
2017-09-24 23:36:51 +00:00
}
2018-08-03 03:11:42 +00:00
2017-10-19 04:43:42 +00:00
/// <summary>
/// Sets the <see cref="bool"/> status of the Flag at the specified offset and index.
/// </summary>
/// <param name="offset">Offset to read from</param>
/// <param name="bitIndex">Bit index to read</param>
/// <param name="value">Flag status to set</param>
/// <remarks>Flag is Set (true) or not Set (false)</remarks>
public void SetFlag ( int offset , int bitIndex , bool value )
2017-09-24 23:36:51 +00:00
{
2017-10-19 04:43:42 +00:00
bitIndex & = 7 ; // ensure bit access is 0-7
Data [ offset ] & = ( byte ) ~ ( 1 < < bitIndex ) ;
Data [ offset ] | = ( byte ) ( ( value ? 1 : 0 ) < < bitIndex ) ;
2017-09-14 03:37:18 +00:00
}
2016-06-20 04:22:43 +00:00
// Inventory
2016-09-19 05:47:31 +00:00
public virtual InventoryPouch [ ] Inventory { get ; set ; }
2016-06-20 04:22:43 +00:00
protected int OFS_PouchHeldItem { get ; set ; } = int . MinValue ;
protected int OFS_PouchKeyItem { get ; set ; } = int . MinValue ;
protected int OFS_PouchMedicine { get ; set ; } = int . MinValue ;
protected int OFS_PouchTMHM { get ; set ; } = int . MinValue ;
protected int OFS_PouchBerry { get ; set ; } = int . MinValue ;
protected int OFS_PouchBalls { get ; set ; } = int . MinValue ;
protected int OFS_BattleItems { get ; set ; } = int . MinValue ;
protected int OFS_MailItems { get ; set ; } = int . MinValue ;
2016-08-27 11:33:21 +00:00
protected int OFS_PCItem { get ; set ; } = int . MinValue ;
2016-10-20 01:19:01 +00:00
protected int OFS_PouchZCrystals { get ; set ; } = int . MinValue ;
2016-06-20 04:22:43 +00:00
// Mystery Gift
2019-02-02 07:26:43 +00:00
protected virtual bool [ ] MysteryGiftReceivedFlags { get = > Array . Empty < bool > ( ) ; set { } }
protected virtual MysteryGift [ ] MysteryGiftCards { get = > Array . Empty < MysteryGift > ( ) ; set { } }
2018-08-03 03:11:42 +00:00
2016-06-20 04:22:43 +00:00
public virtual MysteryGiftAlbum GiftAlbum
{
2017-05-13 03:32:36 +00:00
get = > new MysteryGiftAlbum
2016-06-20 04:22:43 +00:00
{
2017-05-13 03:32:36 +00:00
Flags = MysteryGiftReceivedFlags ,
Gifts = MysteryGiftCards
} ;
2016-06-20 04:22:43 +00:00
set
{
MysteryGiftReceivedFlags = value . Flags ;
MysteryGiftCards = value . Gifts ;
}
}
2017-05-13 03:32:36 +00:00
public virtual bool BattleBoxLocked { get = > false ; set { } }
2019-02-02 07:26:43 +00:00
public virtual string JPEGTitle = > string . Empty ;
public virtual byte [ ] JPEGData = > Array . Empty < byte > ( ) ;
2017-05-13 03:32:36 +00:00
public virtual int Country { get = > - 1 ; set { } }
public virtual int ConsoleRegion { get = > - 1 ; set { } }
public virtual int SubRegion { get = > - 1 ; set { } }
2016-06-20 04:22:43 +00:00
// Trainer Info
2016-09-19 05:47:31 +00:00
public virtual int Gender { get ; set ; }
2017-05-13 03:32:36 +00:00
public virtual int Language { get = > - 1 ; set { } }
public virtual int Game { get = > - 1 ; set { } }
2018-04-28 18:06:58 +00:00
public virtual int TID { get ; set ; }
public virtual int SID { get ; set ; }
2016-11-19 01:19:16 +00:00
public int TrainerID7 = > ( int ) ( ( uint ) ( TID | ( SID < < 16 ) ) % 1000000 ) ;
2018-07-16 00:48:31 +00:00
public int TrainerSID7 = > ( int ) ( ( uint ) ( TID | ( SID < < 16 ) ) / 1000000 ) ;
2017-05-19 00:51:08 +00:00
public virtual string OT { get ; set ; } = "PKHeX" ;
2016-09-19 05:47:31 +00:00
public virtual int PlayedHours { get ; set ; }
public virtual int PlayedMinutes { get ; set ; }
public virtual int PlayedSeconds { get ; set ; }
2016-06-26 21:23:41 +00:00
public virtual int SecondsToStart { get ; set ; }
public virtual int SecondsToFame { get ; set ; }
2016-09-19 05:47:31 +00:00
public virtual uint Money { get ; set ; }
2016-06-20 04:22:43 +00:00
public abstract int BoxCount { get ; }
2019-01-15 05:31:53 +00:00
public virtual int SlotCount = > BoxCount * BoxSlotCount ;
2016-09-19 05:47:31 +00:00
public virtual int PartyCount { get ; protected set ; }
2017-05-13 03:32:36 +00:00
public virtual int MultiplayerSpriteID { get = > 0 ; set { } }
2018-08-03 03:11:42 +00:00
2017-06-22 03:24:42 +00:00
public bool IsPartyAllEggs ( params int [ ] except )
{
if ( ! HasParty )
return false ;
var party = PartyData ;
2017-09-29 05:20:27 +00:00
return party . Count = = party . Where ( ( t , i ) = > t . IsEgg | | except . Contains ( i ) ) . Count ( ) ;
2017-06-22 03:24:42 +00:00
}
2016-06-20 05:11:53 +00:00
2016-06-20 04:22:43 +00:00
// Varied Methods
2017-06-18 01:37:19 +00:00
protected abstract void SetChecksums ( ) ;
public abstract int GetBoxOffset ( int box ) ;
public abstract int GetPartyOffset ( int slot ) ;
public abstract string GetBoxName ( int box ) ;
2018-05-12 15:13:39 +00:00
public abstract void SetBoxName ( int box , string value ) ;
2016-12-10 20:27:37 +00:00
public virtual int GameSyncIDSize { get ; } = 8 ;
2017-05-13 03:32:36 +00:00
public virtual string GameSyncID { get = > null ; set { } }
2016-06-20 04:22:43 +00:00
// Daycare
public int DaycareIndex = 0 ;
2016-10-23 19:45:27 +00:00
public virtual bool HasTwoDaycares = > false ;
2017-10-18 06:19:34 +00:00
public virtual int GetDaycareSlotOffset ( int loc , int slot ) = > - 1 ;
public virtual uint? GetDaycareEXP ( int loc , int slot ) = > null ;
public virtual string GetDaycareRNGSeed ( int loc ) = > null ;
public virtual bool? IsDaycareHasEgg ( int loc ) = > null ;
public virtual bool? IsDaycareOccupied ( int loc , int slot ) = > null ;
2016-09-19 05:47:31 +00:00
2017-06-18 01:37:19 +00:00
public virtual void SetDaycareEXP ( int loc , int slot , uint EXP ) { }
public virtual void SetDaycareRNGSeed ( int loc , string seed ) { }
public virtual void SetDaycareHasEgg ( int loc , bool hasEgg ) { }
public virtual void SetDaycareOccupied ( int loc , int slot , bool occupied ) { }
2016-06-20 04:22:43 +00:00
// Storage
2016-08-28 10:18:22 +00:00
public virtual int BoxSlotCount = > 30 ;
2017-05-13 03:32:36 +00:00
public virtual int BoxesUnlocked { get = > - 1 ; set { } }
2019-02-02 07:26:43 +00:00
public virtual byte [ ] BoxFlags { get = > Array . Empty < byte > ( ) ; set { } }
2018-11-22 18:55:20 +00:00
public virtual int CurrentBox { get ; set ; }
2018-08-02 01:30:51 +00:00
protected int [ ] TeamSlots = Array . Empty < int > ( ) ;
2018-12-05 06:59:28 +00:00
protected virtual IList < int > [ ] SlotPointers = > new [ ] { TeamSlots } ;
2018-12-05 06:00:57 +00:00
public virtual StorageSlotFlag GetSlotFlags ( int index ) = > StorageSlotFlag . None ;
2018-12-05 06:59:28 +00:00
public StorageSlotFlag GetSlotFlags ( int box , int slot ) = > GetSlotFlags ( ( box * BoxSlotCount ) + slot ) ;
public bool IsSlotLocked ( int box , int slot ) = > GetSlotFlags ( box , slot ) . HasFlagFast ( StorageSlotFlag . Locked ) ;
2018-12-29 01:58:13 +00:00
public bool IsSlotLocked ( int index ) = > GetSlotFlags ( index ) . HasFlagFast ( StorageSlotFlag . Locked ) ;
2018-12-05 06:59:28 +00:00
public bool IsSlotOverwriteProtected ( int box , int slot ) = > GetSlotFlags ( box , slot ) . IsOverwriteProtected ( ) ;
2018-12-29 01:58:13 +00:00
public bool IsSlotOverwriteProtected ( int index ) = > GetSlotFlags ( index ) . IsOverwriteProtected ( ) ;
2018-08-03 03:11:42 +00:00
2017-01-07 05:22:42 +00:00
public bool MoveBox ( int box , int insertBeforeBox )
{
if ( box = = insertBeforeBox ) // no movement required
return true ;
if ( box > = BoxCount | | insertBeforeBox > = BoxCount ) // invalid box positions
return false ;
int pos1 = BoxSlotCount * box ;
int pos2 = BoxSlotCount * insertBeforeBox ;
int min = Math . Min ( pos1 , pos2 ) ;
int max = Math . Max ( pos1 , pos2 ) ;
int len = BoxSlotCount * SIZE_STORED ;
2017-06-18 01:37:19 +00:00
byte [ ] boxdata = GetData ( GetBoxOffset ( 0 ) , len * BoxCount ) ; // get all boxes
2018-05-12 15:41:23 +00:00
string [ ] boxNames = new int [ BoxCount ] . Select ( ( _ , i ) = > GetBoxName ( i ) ) . ToArray ( ) ;
int [ ] boxWallpapers = new int [ BoxCount ] . Select ( ( _ , i ) = > GetBoxWallpaper ( i ) ) . ToArray ( ) ;
2017-01-07 05:22:42 +00:00
min / = BoxSlotCount ;
max / = BoxSlotCount ;
2018-05-12 15:13:39 +00:00
2017-01-07 05:22:42 +00:00
// move all boxes within range to final spot
for ( int i = min , ctr = min ; i < max ; i + + )
{
int b = insertBeforeBox ; // if box is the moved box, move to insertion point, else move to unused box.
if ( i ! = box )
{
if ( insertBeforeBox = = ctr )
+ + ctr ;
b = ctr + + ;
}
2017-09-29 05:20:27 +00:00
Buffer . BlockCopy ( boxdata , len * i , Data , GetBoxOffset ( b ) , len ) ;
2017-06-18 01:37:19 +00:00
SetBoxName ( b , boxNames [ i ] ) ;
SetBoxWallpaper ( b , boxWallpapers [ i ] ) ;
2017-01-07 05:22:42 +00:00
}
2018-11-11 05:04:24 +00:00
SlotPointerUtil . UpdateMove ( box , insertBeforeBox , BoxSlotCount , SlotPointers ) ;
2017-01-07 05:22:42 +00:00
return true ;
}
2018-08-03 03:11:42 +00:00
2017-01-07 05:22:42 +00:00
public bool SwapBox ( int box1 , int box2 )
{
if ( box1 = = box2 ) // no movement required
return true ;
if ( box1 > = BoxCount | | box2 > = BoxCount ) // invalid box positions
return false ;
2017-10-18 06:19:34 +00:00
if ( ! IsBoxAbleToMove ( box1 ) | | ! IsBoxAbleToMove ( box2 ) )
2017-01-07 05:22:42 +00:00
return false ;
// Data
2017-06-18 01:37:19 +00:00
int b1o = GetBoxOffset ( box1 ) ;
int b2o = GetBoxOffset ( box2 ) ;
2017-01-07 05:22:42 +00:00
int len = BoxSlotCount * SIZE_STORED ;
byte [ ] b1 = new byte [ len ] ;
2017-09-29 05:20:27 +00:00
Buffer . BlockCopy ( Data , b1o , b1 , 0 , len ) ;
Buffer . BlockCopy ( Data , b2o , Data , b1o , len ) ;
Buffer . BlockCopy ( b1 , 0 , Data , b2o , len ) ;
2017-01-07 05:22:42 +00:00
// Name
2017-06-18 01:37:19 +00:00
string b1n = GetBoxName ( box1 ) ;
SetBoxName ( box1 , GetBoxName ( box2 ) ) ;
SetBoxName ( box2 , b1n ) ;
2017-01-07 05:22:42 +00:00
// Wallpaper
2017-06-18 01:37:19 +00:00
int b1w = GetBoxWallpaper ( box1 ) ;
SetBoxWallpaper ( box1 , GetBoxWallpaper ( box2 ) ) ;
SetBoxWallpaper ( box2 , b1w ) ;
2018-11-11 05:04:24 +00:00
// Pointers
SlotPointerUtil . UpdateSwap ( box1 , box2 , BoxSlotCount , SlotPointers ) ;
2017-01-07 05:22:42 +00:00
return true ;
}
2018-08-03 03:11:42 +00:00
2017-10-18 06:19:34 +00:00
private bool IsBoxAbleToMove ( int box )
{
int min = BoxSlotCount * box ;
2018-08-03 03:11:42 +00:00
int max = min + BoxSlotCount ;
2018-09-25 02:43:59 +00:00
return ! IsRegionOverwriteProtected ( min , max ) ;
2017-10-18 06:19:34 +00:00
}
2016-10-29 18:32:21 +00:00
2017-12-17 03:52:06 +00:00
protected virtual int GetBoxWallpaperOffset ( int box ) = > - 1 ;
2018-08-03 03:11:42 +00:00
2017-06-18 01:37:19 +00:00
public virtual int GetBoxWallpaper ( int box )
2016-10-29 18:32:21 +00:00
{
2017-06-18 01:37:19 +00:00
int offset = GetBoxWallpaperOffset ( box ) ;
2016-10-29 18:32:21 +00:00
if ( offset < 0 | | box > BoxCount )
return box ;
return Data [ offset ] ;
}
2018-08-03 03:11:42 +00:00
2018-05-12 15:41:23 +00:00
public virtual void SetBoxWallpaper ( int box , int value )
2016-10-29 18:32:21 +00:00
{
2017-06-18 01:37:19 +00:00
int offset = GetBoxWallpaperOffset ( box ) ;
2016-10-29 18:32:21 +00:00
if ( offset < 0 | | box > BoxCount )
return ;
2018-05-12 15:41:23 +00:00
Data [ offset ] = ( byte ) value ;
2016-10-29 18:32:21 +00:00
}
2016-09-19 05:47:31 +00:00
2018-05-20 03:48:03 +00:00
private void GetBoxSlotFromIndex ( int index , out int box , out int slot )
{
box = index / BoxSlotCount ;
if ( box > = BoxCount )
throw new ArgumentOutOfRangeException ( nameof ( index ) ) ;
slot = index % BoxSlotCount ;
}
public PKM GetPartySlotAtIndex ( int index ) = > GetPartySlot ( GetPartyOffset ( index ) ) ;
public PKM GetBoxSlotAtIndex ( int box , int slot ) = > GetStoredSlot ( GetBoxSlotOffset ( box , slot ) ) ;
2018-08-03 03:11:42 +00:00
2018-05-20 03:48:03 +00:00
public PKM GetBoxSlotAtIndex ( int index )
{
GetBoxSlotFromIndex ( index , out int box , out int slot ) ;
return GetBoxSlotAtIndex ( box , slot ) ;
}
2018-08-03 03:11:42 +00:00
public int GetBoxSlotOffset ( int box , int slot ) = > GetBoxOffset ( box ) + ( slot * SIZE_STORED ) ;
2018-05-20 03:48:03 +00:00
public int GetBoxSlotOffset ( int index )
2016-06-20 04:22:43 +00:00
{
2018-05-20 03:48:03 +00:00
GetBoxSlotFromIndex ( index , out int box , out int slot ) ;
return GetBoxSlotOffset ( box , slot ) ;
2016-06-20 04:22:43 +00:00
}
2018-05-20 03:48:03 +00:00
public void SetBoxSlotAtIndex ( PKM pkm , int box , int slot , bool? trade , bool? dex = null ) = > SetStoredSlot ( pkm , GetBoxSlotOffset ( box , slot ) , trade , dex ) ;
public void SetBoxSlotAtIndex ( PKM pkm , int index , bool? trade , bool? dex = null ) = > SetStoredSlot ( pkm , GetBoxSlotOffset ( index ) , trade , dex ) ;
public void SetPartySlotAtIndex ( PKM pkm , int index , bool? trade = null , bool? dex = null ) = > SetPartySlot ( pkm , GetPartyOffset ( index ) , trade , dex ) ;
public virtual PKM GetPartySlot ( int offset ) = > GetPKM ( DecryptPKM ( GetData ( offset , SIZE_PARTY ) ) ) ;
2018-08-03 03:11:42 +00:00
2017-06-18 01:37:19 +00:00
public virtual PKM GetStoredSlot ( int offset )
2016-06-20 04:22:43 +00:00
{
2017-06-18 01:37:19 +00:00
return GetPKM ( DecryptPKM ( GetData ( offset , SIZE_STORED ) ) ) ;
2016-06-20 04:22:43 +00:00
}
2018-08-03 03:11:42 +00:00
2017-06-18 01:37:19 +00:00
public void SetPartySlot ( PKM pkm , int offset , bool? trade = null , bool? dex = null )
2016-06-20 04:22:43 +00:00
{
if ( pkm = = null ) return ;
if ( pkm . GetType ( ) ! = PKMType )
2017-03-17 04:20:06 +00:00
throw new ArgumentException ( $"PKM Format needs to be {PKMType} when setting to this Save File." ) ;
2016-06-20 04:22:43 +00:00
if ( trade ? ? SetUpdatePKM )
2017-06-18 01:37:19 +00:00
SetPKM ( pkm ) ;
2016-06-20 04:22:43 +00:00
if ( dex ? ? SetUpdateDex )
2017-06-18 01:37:19 +00:00
SetDex ( pkm ) ;
SetPartyValues ( pkm , isParty : true ) ;
2016-06-20 04:22:43 +00:00
2017-10-18 06:19:34 +00:00
int i = GetPartyIndex ( offset ) ;
if ( i < = - 1 )
throw new ArgumentException ( "Invalid Party offset provided; unable to resolve party slot index." ) ;
2018-05-12 15:13:39 +00:00
2017-10-18 06:19:34 +00:00
// update party count
if ( pkm . Species ! = 0 )
{
if ( PartyCount < = i )
PartyCount = i + 1 ;
}
else if ( PartyCount > i )
2018-08-03 03:11:42 +00:00
{
2017-10-18 06:19:34 +00:00
PartyCount = i ;
2018-08-03 03:11:42 +00:00
}
2016-07-05 03:04:43 +00:00
2017-06-18 01:37:19 +00:00
SetData ( pkm . EncryptedPartyData , offset ) ;
2016-06-20 04:22:43 +00:00
Edited = true ;
}
2018-08-03 03:11:42 +00:00
2017-10-18 06:19:34 +00:00
private int GetPartyIndex ( int offset )
{
for ( int i = 0 ; i < 6 ; i + + )
2018-08-03 03:11:42 +00:00
{
2017-10-18 06:19:34 +00:00
if ( GetPartyOffset ( i ) = = offset )
return i ;
2018-08-03 03:11:42 +00:00
}
2017-10-18 06:19:34 +00:00
return - 1 ;
}
2018-08-03 03:11:42 +00:00
2017-06-18 01:37:19 +00:00
public virtual void SetStoredSlot ( PKM pkm , int offset , bool? trade = null , bool? dex = null )
2016-06-20 04:22:43 +00:00
{
if ( pkm = = null ) return ;
if ( pkm . GetType ( ) ! = PKMType )
2017-03-17 04:20:06 +00:00
throw new ArgumentException ( $"PKM Format needs to be {PKMType} when setting to this Save File." ) ;
2016-06-20 04:22:43 +00:00
if ( trade ? ? SetUpdatePKM )
2017-06-18 01:37:19 +00:00
SetPKM ( pkm ) ;
2016-06-20 04:22:43 +00:00
if ( dex ? ? SetUpdateDex )
2017-06-18 01:37:19 +00:00
SetDex ( pkm ) ;
SetPartyValues ( pkm , isParty : false ) ;
SetData ( pkm . EncryptedBoxData , offset ) ;
2016-06-20 04:22:43 +00:00
Edited = true ;
}
2018-08-03 03:11:42 +00:00
2017-06-18 01:37:19 +00:00
public void DeletePartySlot ( int slot )
2016-07-05 03:04:43 +00:00
{
if ( PartyCount < = slot ) // beyond party range (or empty data already present)
return ;
// Move all party slots down one
for ( int i = slot + 1 ; i < 6 ; i + + ) // Slide slots down
{
2017-06-18 01:37:19 +00:00
int slotTo = GetPartyOffset ( i - 1 ) ;
int slotFrom = GetPartyOffset ( i ) ;
SetData ( GetData ( slotFrom , SIZE_PARTY ) , slotTo ) ;
2016-07-05 03:04:43 +00:00
}
2017-06-18 01:37:19 +00:00
SetStoredSlot ( BlankPKM , GetPartyOffset ( 5 ) , false , false ) ;
2018-05-12 15:41:23 +00:00
PartyCount - - ;
2016-07-05 03:04:43 +00:00
}
2018-08-03 03:11:42 +00:00
2019-01-15 05:31:53 +00:00
protected virtual bool IsSlotSwapProtected ( int box , int slot ) = > false ;
2018-09-25 02:43:59 +00:00
private bool IsRegionOverwriteProtected ( int min , int max )
{
2018-12-05 06:59:28 +00:00
return SlotPointers . SelectMany ( z = > z )
. Where ( z = > GetSlotFlags ( z ) . IsOverwriteProtected ( ) )
. Any ( slot = > WithinRange ( slot , min , max ) ) ;
2018-09-25 02:43:59 +00:00
}
2018-08-03 03:11:42 +00:00
2018-11-11 05:04:24 +00:00
private static bool WithinRange ( int slot , int min , int max ) = > min < = slot & & slot < max ;
2017-06-18 01:37:19 +00:00
public bool IsAnySlotLockedInBox ( int BoxStart , int BoxEnd )
2016-12-16 07:17:17 +00:00
{
2018-12-05 06:59:28 +00:00
return SlotPointers . SelectMany ( z = > z )
. Where ( z = > GetSlotFlags ( z ) . HasFlagFast ( StorageSlotFlag . Locked ) )
. Any ( slot = > WithinRange ( slot , BoxStart * BoxSlotCount , ( BoxEnd + 1 ) * BoxSlotCount ) ) ;
2016-12-16 07:17:17 +00:00
}
2018-08-03 03:11:42 +00:00
2019-02-21 03:39:49 +00:00
/// <summary>
/// Sorts all <see cref="PKM"/> present within the range specified by <see cref="BoxStart"/> and <see cref="BoxEnd"/> with the provied <see cref="sortMethod"/>.
/// </summary>
/// <param name="BoxStart">Starting box; if not provided, will iterate from the first box.</param>
/// <param name="BoxEnd">Ending box; if not provided, will iterate to the end.</param>
/// <param name="sortMethod">Sorting logic required to order a <see cref="PKM"/> with respect to its peers; if not provided, will use a default sorting method.</param>
/// <param name="reverse">Reverse the sorting order</param>
/// <returns>Count of repositioned <see cref="PKM"/> slots.</returns>
2019-02-21 01:59:54 +00:00
public int SortBoxes ( int BoxStart = 0 , int BoxEnd = - 1 , Func < IEnumerable < PKM > , IEnumerable < PKM > > sortMethod = null , bool reverse = false )
2016-06-20 04:22:43 +00:00
{
2017-09-29 05:20:27 +00:00
var BD = BoxData ;
int start = BoxSlotCount * BoxStart ;
var Section = BD . Skip ( start ) ;
2018-04-24 02:11:55 +00:00
if ( BoxEnd > = BoxStart )
Section = Section . Take ( BoxSlotCount * ( BoxEnd - BoxStart + 1 ) ) ;
2016-06-20 04:22:43 +00:00
2018-11-11 05:04:24 +00:00
Func < int , int , bool > skip = IsSlotSwapProtected ;
Section = Section . Where ( z = > ! skip ( z . Box , z . Slot ) ) ;
2018-04-21 22:07:58 +00:00
var Sorted = ( sortMethod ? ? PKMSorting . OrderBySpecies ) ( Section ) ;
2018-05-01 04:39:12 +00:00
if ( reverse )
Sorted = Sorted . ReverseSort ( ) ;
2016-06-20 04:22:43 +00:00
2018-11-11 05:04:24 +00:00
var result = Sorted . ToArray ( ) ;
var boxclone = new PKM [ BD . Count ] ;
BD . CopyTo ( boxclone , 0 ) ;
2019-02-21 01:59:54 +00:00
int count = result . CopyTo ( boxclone , skip , start ) ;
2018-11-11 05:04:24 +00:00
SlotPointerUtil . UpdateRepointFrom ( boxclone , BD , 0 , SlotPointers ) ;
2019-01-09 02:31:14 +00:00
// clear storage flags to ensure all data is written back
foreach ( var pk in result )
pk . StorageFlags = StorageSlotFlag . None ;
2018-11-11 05:04:24 +00:00
BoxData = boxclone ;
2019-02-21 01:59:54 +00:00
return count ;
2016-06-20 04:22:43 +00:00
}
2018-08-03 03:11:42 +00:00
2019-02-21 03:39:49 +00:00
/// <summary>
/// Removes all <see cref="PKM"/> present within the range specified by <see cref="BoxStart"/> and <see cref="BoxEnd"/> if the provied <see cref="deleteCriteria"/> is satisfied.
/// </summary>
/// <param name="BoxStart">Starting box; if not provided, will iterate from the first box.</param>
/// <param name="BoxEnd">Ending box; if not provided, will iterate to the end.</param>
/// <param name="deleteCriteria">Criteria required to be satisfied for a <see cref="PKM"/> to be deleted; if not provided, will clear if possible.</param>
/// <returns>Count of deleted <see cref="PKM"/> slots.</returns>
2019-02-21 01:59:54 +00:00
public int ClearBoxes ( int BoxStart = 0 , int BoxEnd = - 1 , Func < PKM , bool > deleteCriteria = null )
2016-06-20 04:22:43 +00:00
{
if ( BoxEnd < 0 )
2018-04-24 02:11:55 +00:00
BoxEnd = BoxCount - 1 ;
2017-03-04 17:38:39 +00:00
var blank = BlankPKM . EncryptedBoxData ;
2019-02-21 01:59:54 +00:00
int deleted = 0 ;
2018-04-24 02:11:55 +00:00
for ( int i = BoxStart ; i < = BoxEnd ; i + + )
2016-06-20 04:22:43 +00:00
{
2016-08-28 10:18:22 +00:00
for ( int p = 0 ; p < BoxSlotCount ; p + + )
2018-05-01 00:29:13 +00:00
{
2018-09-25 02:43:59 +00:00
if ( IsSlotOverwriteProtected ( i , p ) )
2018-05-18 05:43:07 +00:00
continue ;
2019-02-02 07:08:03 +00:00
var ofs = GetBoxSlotOffset ( i , p ) ;
2019-02-21 01:59:54 +00:00
if ( ! IsPKMPresent ( ofs ) )
continue ;
2018-05-01 00:29:13 +00:00
if ( deleteCriteria ! = null )
{
var pk = GetStoredSlot ( ofs ) ;
if ( ! deleteCriteria ( pk ) )
continue ;
}
SetData ( blank , ofs ) ;
2019-02-21 01:59:54 +00:00
+ + deleted ;
2018-05-01 00:29:13 +00:00
}
2016-06-20 04:22:43 +00:00
}
2019-02-21 01:59:54 +00:00
return deleted ;
2016-06-20 04:22:43 +00:00
}
2018-08-03 03:11:42 +00:00
2019-02-21 03:39:49 +00:00
/// <summary>
/// Modifies all <see cref="PKM"/> present within the range specified by <see cref="BoxStart"/> and <see cref="BoxEnd"/> with the modification routine provided by <see cref="action"/>.
/// </summary>
/// <param name="action">Modification to perform on a <see cref="PKM"/></param>
/// <param name="BoxStart">Starting box; if not provided, will iterate from the first box.</param>
/// <param name="BoxEnd">Ending box; if not provided, will iterate to the end.</param>
/// <returns>Count of modified <see cref="PKM"/> slots.</returns>
2019-02-21 01:59:54 +00:00
public int ModifyBoxes ( Action < PKM > action , int BoxStart = 0 , int BoxEnd = - 1 )
2018-04-22 19:43:18 +00:00
{
2019-02-21 03:39:49 +00:00
if ( action = = null )
throw new ArgumentException ( nameof ( action ) ) ;
2018-04-22 19:43:18 +00:00
if ( BoxEnd < 0 )
2018-04-24 02:11:55 +00:00
BoxEnd = BoxCount - 1 ;
2019-02-21 01:59:54 +00:00
int modified = 0 ;
2018-04-24 02:11:55 +00:00
for ( int b = BoxStart ; b < = BoxEnd ; b + + )
2018-04-22 19:43:18 +00:00
{
2018-08-03 03:11:42 +00:00
for ( int s = 0 ; s < BoxSlotCount ; s + + )
{
2018-09-25 02:43:59 +00:00
if ( IsSlotOverwriteProtected ( b , s ) )
2018-08-03 03:11:42 +00:00
continue ;
2019-02-21 03:39:49 +00:00
var ofs = GetBoxSlotOffset ( b , s ) ;
if ( ! IsPKMPresent ( ofs ) )
continue ;
var pk = GetStoredSlot ( ofs ) ;
action ( pk ) ;
2019-02-21 01:59:54 +00:00
+ + modified ;
2019-02-21 03:39:49 +00:00
SetStoredSlot ( pk , ofs , false , false ) ;
2018-08-03 03:11:42 +00:00
}
2018-04-22 19:43:18 +00:00
}
2019-02-21 01:59:54 +00:00
return modified ;
2018-04-22 19:43:18 +00:00
}
2016-06-20 04:22:43 +00:00
2017-06-18 01:37:19 +00:00
public byte [ ] PCBinary = > BoxData . SelectMany ( pk = > pk . EncryptedBoxData ) . ToArray ( ) ;
2018-09-16 19:44:00 +00:00
public byte [ ] GetBoxBinary ( int box ) = > GetBoxData ( box ) . SelectMany ( pk = > pk . EncryptedBoxData ) . ToArray ( ) ;
2018-08-03 03:11:42 +00:00
2017-06-18 01:37:19 +00:00
public bool SetPCBinary ( byte [ ] data )
2016-06-20 04:22:43 +00:00
{
2018-12-05 06:59:28 +00:00
if ( IsRegionOverwriteProtected ( 0 , SlotCount ) )
2016-12-16 07:17:17 +00:00
return false ;
2017-06-18 01:37:19 +00:00
if ( data . Length ! = PCBinary . Length )
2016-06-20 04:22:43 +00:00
return false ;
2017-09-29 05:20:27 +00:00
var BD = BoxData ;
var pkdata = PKX . GetPKMDataFromConcatenatedBinary ( data , BlankPKM . EncryptedBoxData . Length ) ;
2018-12-11 04:32:08 +00:00
pkdata . Select ( z = > GetPKM ( DecryptPKM ( z ) ) ) . CopyTo ( BD , IsSlotOverwriteProtected ) ;
2017-09-29 05:20:27 +00:00
BoxData = BD ;
2016-06-20 04:22:43 +00:00
return true ;
}
2018-08-03 03:11:42 +00:00
2017-06-18 01:37:19 +00:00
public bool SetBoxBinary ( byte [ ] data , int box )
2016-06-20 04:22:43 +00:00
{
2017-09-29 05:20:27 +00:00
int start = box * BoxSlotCount ;
int end = start + BoxSlotCount ;
2018-12-05 06:59:28 +00:00
if ( IsRegionOverwriteProtected ( start , end ) )
2016-12-16 07:17:17 +00:00
return false ;
2017-06-18 01:37:19 +00:00
if ( data . Length ! = GetBoxBinary ( box ) . Length )
2016-06-20 04:22:43 +00:00
return false ;
2017-09-29 05:20:27 +00:00
var BD = BoxData ;
var pkdata = PKX . GetPKMDataFromConcatenatedBinary ( data , BlankPKM . EncryptedBoxData . Length ) ;
2018-12-11 04:32:08 +00:00
pkdata . Select ( z = > GetPKM ( DecryptPKM ( z ) ) ) . CopyTo ( BD , IsSlotOverwriteProtected , start ) ;
2017-09-29 05:20:27 +00:00
BoxData = BD ;
2016-06-20 04:22:43 +00:00
return true ;
}
2018-03-26 02:05:49 +00:00
protected virtual void SetPartyValues ( PKM pkm , bool isParty )
{
if ( ! isParty )
return ;
2019-02-09 21:17:00 +00:00
if ( pkm . PartyStatsPresent ) // Stats already present
2018-03-26 02:05:49 +00:00
return ;
pkm . SetStats ( pkm . GetStats ( pkm . PersonalInfo ) ) ;
pkm . Stat_Level = pkm . CurrentLevel ;
}
2018-08-03 03:11:42 +00:00
2017-06-18 01:37:19 +00:00
protected virtual void SetPKM ( PKM pkm ) { }
protected virtual void SetDex ( PKM pkm ) { }
public virtual bool GetSeen ( int species ) = > false ;
public virtual void SetSeen ( int species , bool seen ) { }
public virtual bool GetCaught ( int species ) = > false ;
public virtual void SetCaught ( int species , bool caught ) { }
2017-10-31 16:24:54 +00:00
public int SeenCount = > HasPokeDex ? Enumerable . Range ( 1 , MaxSpeciesID ) . Count ( GetSeen ) : 0 ;
public int CaughtCount = > HasPokeDex ? Enumerable . Range ( 1 , MaxSpeciesID ) . Count ( GetCaught ) : 0 ;
public decimal PercentSeen = > ( decimal ) SeenCount / MaxSpeciesID ;
public decimal PercentCaught = > ( decimal ) CaughtCount / MaxSpeciesID ;
2017-06-18 01:37:19 +00:00
public byte [ ] GetData ( int Offset , int Length )
2016-06-20 04:22:43 +00:00
{
2016-08-20 06:36:13 +00:00
byte [ ] data = new byte [ Length ] ;
2017-09-29 05:20:27 +00:00
Buffer . BlockCopy ( Data , Offset , data , 0 , Length ) ;
2016-08-20 06:36:13 +00:00
return data ;
2016-06-20 04:22:43 +00:00
}
2018-08-03 03:11:42 +00:00
2017-06-18 01:37:19 +00:00
public void SetData ( byte [ ] input , int Offset )
2016-06-20 04:22:43 +00:00
{
input . CopyTo ( Data , Offset ) ;
Edited = true ;
}
2018-08-03 03:11:42 +00:00
2018-07-25 23:00:52 +00:00
public bool IsRangeEmpty ( int Offset , int Length ) = > IsRangeAll ( Offset , Length , 0 ) ;
2018-08-03 03:11:42 +00:00
2018-07-25 23:00:52 +00:00
public bool IsRangeAll ( int Offset , int Length , int value )
2017-12-17 02:29:10 +00:00
{
2018-08-10 04:53:39 +00:00
for ( int i = Offset + Length - 1 ; i > = Offset ; i - - )
{
2018-07-25 23:00:52 +00:00
if ( Data [ i ] ! = value )
2017-12-17 02:29:10 +00:00
return false ;
2018-08-10 04:53:39 +00:00
}
2017-12-17 02:29:10 +00:00
return true ;
}
2018-08-03 03:11:42 +00:00
2019-02-20 04:26:20 +00:00
public virtual bool IsPKMPresent ( int offset ) = > PKX . IsPKMPresent ( Data , offset ) ;
2016-11-12 15:30:51 +00:00
2018-12-27 21:10:55 +00:00
public bool IsStorageFull = > NextOpenBoxSlot ( ) = = StorageFullValue ;
private const int StorageFullValue = - 1 ;
2018-12-27 09:08:42 +00:00
2018-12-27 21:10:55 +00:00
public int NextOpenBoxSlot ( int lastKnownOccupied = - 1 )
2018-12-27 09:08:42 +00:00
{
2018-12-27 21:10:55 +00:00
int count = BoxSlotCount * BoxCount ;
for ( int i = lastKnownOccupied + 1 ; i < count ; i + + )
2018-12-27 09:08:42 +00:00
{
2018-12-27 21:10:55 +00:00
int offset = GetBoxSlotOffset ( i ) ;
if ( ! IsPKMPresent ( offset ) )
return i ;
2018-12-27 09:08:42 +00:00
}
2018-12-27 21:10:55 +00:00
return StorageFullValue ;
2018-12-27 09:08:42 +00:00
}
2018-12-05 06:00:57 +00:00
public abstract string GetString ( byte [ ] data , int offset , int length ) ;
public string GetString ( int offset , int length ) = > GetString ( Data , offset , length ) ;
2017-06-18 01:37:19 +00:00
public abstract byte [ ] SetString ( string value , int maxLength , int PadToSize = 0 , ushort PadWith = 0 ) ;
2017-04-09 21:06:50 +00:00
2018-11-01 22:38:09 +00:00
/// <summary>
/// Compresses the <see cref="BoxData"/> by pulling out the empty storage slots and putting them at the end, retaining all existing data.
/// </summary>
/// <param name="storedCount">Count of actual <see cref="PKM"/> stored.</param>
/// <param name="slotPointers">Important slot pointers that need to be repointed if a slot moves.</param>
/// <returns>True if <see cref="BoxData"/> was updated, false if no update done.</returns>
2018-11-11 05:04:24 +00:00
public bool CompressStorage ( out int storedCount , params IList < int > [ ] slotPointers )
2018-11-01 22:38:09 +00:00
{
// keep track of empty slots, and only write them at the end if slots were shifted (no need otherwise).
var empty = new List < byte [ ] > ( ) ;
bool shiftedSlots = false ;
ushort ctr = 0 ;
int size = SIZE_STORED ;
2018-11-15 06:33:30 +00:00
int count = BoxSlotCount * BoxCount ;
2018-11-01 22:38:09 +00:00
for ( int i = 0 ; i < count ; i + + )
{
2018-11-15 06:33:30 +00:00
int offset = GetBoxSlotOffset ( i ) ;
2018-11-01 22:38:09 +00:00
if ( IsPKMPresent ( offset ) )
{
if ( ctr ! = i ) // copy required
{
shiftedSlots = true ; // appending empty slots afterwards is now required since a rewrite was done
2018-11-15 06:33:30 +00:00
int destOfs = GetBoxSlotOffset ( ctr ) ;
Buffer . BlockCopy ( Data , offset , Data , destOfs , size ) ;
2018-11-11 05:04:24 +00:00
SlotPointerUtil . UpdateRepointFrom ( ctr , i , slotPointers ) ;
2018-11-01 22:38:09 +00:00
}
ctr + + ;
continue ;
}
// pop out an empty slot; save all unused data & preserve order
byte [ ] data = new byte [ size ] ;
Buffer . BlockCopy ( Data , offset , data , 0 , size ) ;
empty . Add ( data ) ;
}
storedCount = ctr ;
if ( ! shiftedSlots )
return false ;
for ( int i = ctr ; i < count ; i + + )
{
var data = empty [ i - ctr ] ;
2018-11-15 06:33:30 +00:00
int offset = GetBoxSlotOffset ( i ) ;
data . CopyTo ( Data , offset ) ;
2018-11-01 22:38:09 +00:00
}
return true ;
}
2016-06-20 04:22:43 +00:00
}
}