2016-06-20 04:22:43 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
2017-01-08 07:54:09 +00:00
|
|
|
|
namespace PKHeX.Core
|
2016-06-20 04:22:43 +00:00
|
|
|
|
{
|
|
|
|
|
// Base Class for Save Files
|
|
|
|
|
public abstract class SaveFile
|
|
|
|
|
{
|
2017-01-08 07:54:09 +00:00
|
|
|
|
public static bool SetUpdateDex = true;
|
|
|
|
|
public static bool SetUpdatePKM = true;
|
2016-06-20 04:22:43 +00:00
|
|
|
|
|
|
|
|
|
// General Object Properties
|
|
|
|
|
public byte[] Data;
|
|
|
|
|
public bool Edited;
|
|
|
|
|
public string FileName, FilePath;
|
|
|
|
|
public abstract string BAKName { 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; }
|
2016-06-28 06:03:57 +00:00
|
|
|
|
public byte[] Footer { protected get; set; } = new byte[0]; // .dsv
|
2016-09-19 05:47:31 +00:00
|
|
|
|
public byte[] Header { protected get; set; } = new byte[0]; // .gci
|
2016-10-12 02:11:24 +00:00
|
|
|
|
public bool Japanese { get; set; }
|
2016-12-08 06:57:08 +00:00
|
|
|
|
public string PlayTimeString => $"{PlayedHours}ː{PlayedMinutes:00}ː{PlayedSeconds:00}"; // not :
|
2016-10-12 02:11:24 +00:00
|
|
|
|
public virtual bool IndeterminateGame => false;
|
|
|
|
|
public virtual bool IndeterminateSubVersion => false;
|
2017-01-05 06:22:50 +00:00
|
|
|
|
public abstract string Extension { get; }
|
|
|
|
|
public virtual string[] PKMExtensions => PKM.Extensions.Where(f =>
|
|
|
|
|
{
|
|
|
|
|
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-04-02 14:53:46 +00:00
|
|
|
|
public virtual bool IsMemoryCardSave => false;
|
2016-06-20 04:22:43 +00:00
|
|
|
|
// General PKM Properties
|
2016-09-26 23:14:11 +00:00
|
|
|
|
public abstract Type PKMType { get; }
|
2016-06-20 04:22:43 +00:00
|
|
|
|
public abstract PKM getPKM(byte[] data);
|
|
|
|
|
public abstract PKM BlankPKM { get; }
|
|
|
|
|
public abstract byte[] decryptPKM(byte[] data);
|
|
|
|
|
public abstract int SIZE_STORED { get; }
|
|
|
|
|
public abstract int SIZE_PARTY { get; }
|
|
|
|
|
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
|
2017-04-21 02:38:48 +00:00
|
|
|
|
public virtual byte[] Write(bool DSV, bool GCI)
|
2017-04-02 14:53:46 +00:00
|
|
|
|
{
|
|
|
|
|
return Write(DSV);
|
|
|
|
|
}
|
2016-07-28 15:52:04 +00:00
|
|
|
|
public virtual byte[] Write(bool DSV)
|
2016-06-20 04:22:43 +00:00
|
|
|
|
{
|
|
|
|
|
setChecksums();
|
2016-06-20 05:11:53 +00:00
|
|
|
|
if (Footer.Length > 0 && DSV)
|
|
|
|
|
return Data.Concat(Footer).ToArray();
|
2016-09-19 05:47:31 +00:00
|
|
|
|
if (Header.Length > 0)
|
|
|
|
|
return Header.Concat(Data).ToArray();
|
2016-06-20 04:22:43 +00:00
|
|
|
|
return Data;
|
|
|
|
|
}
|
|
|
|
|
public virtual string MiscSaveChecks() { return ""; }
|
|
|
|
|
public virtual string MiscSaveInfo() { return ""; }
|
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
|
|
|
|
|
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
|
|
|
|
|
2016-06-21 01:58:06 +00:00
|
|
|
|
public virtual int MaxMoveID => int.MaxValue;
|
|
|
|
|
public virtual int MaxSpeciesID => int.MaxValue;
|
|
|
|
|
public virtual int MaxAbilityID => int.MaxValue;
|
|
|
|
|
public virtual int MaxItemID => int.MaxValue;
|
|
|
|
|
public virtual int MaxBallID => int.MaxValue;
|
|
|
|
|
public virtual int MaxGameID => int.MaxValue;
|
|
|
|
|
|
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 HasPuff => Puff > -1;
|
|
|
|
|
public bool HasPSS => PSS > -1;
|
|
|
|
|
public bool HasOPower => OPower > -1;
|
|
|
|
|
public bool HasJPEG => JPEGData != null;
|
|
|
|
|
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;
|
2016-10-29 18:32:21 +00:00
|
|
|
|
public virtual bool HasBoxWallpapers => getBoxWallpaperOffset(0) > -1;
|
2016-06-20 04:22:43 +00:00
|
|
|
|
public virtual bool HasSUBE => SUBE > -1 && !ORAS;
|
|
|
|
|
public virtual bool HasGeolocation => false;
|
|
|
|
|
public bool HasPokeBlock => ORAS && !ORASDEMO;
|
|
|
|
|
public bool HasEvents => EventFlags != null;
|
2016-07-27 03:18:48 +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;
|
|
|
|
|
public virtual int MaxShadowID => 0;
|
2016-06-20 04:22:43 +00:00
|
|
|
|
|
|
|
|
|
// Offsets
|
2016-09-26 23:14:11 +00:00
|
|
|
|
protected virtual 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 Puff { 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
|
|
|
|
|
public PKM[] BoxData
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2016-08-28 10:18:22 +00:00
|
|
|
|
PKM[] data = new PKM[BoxCount*BoxSlotCount];
|
2016-06-20 04:22:43 +00:00
|
|
|
|
for (int i = 0; i < data.Length; i++)
|
|
|
|
|
{
|
2016-08-28 10:18:22 +00:00
|
|
|
|
data[i] = getStoredSlot(getBoxOffset(i/BoxSlotCount) + SIZE_STORED*(i%BoxSlotCount));
|
2016-12-08 06:57:08 +00:00
|
|
|
|
data[i].Identifier = $"{getBoxName(i/BoxSlotCount)}:{i%BoxSlotCount + 1:00}";
|
2016-08-28 10:18:22 +00:00
|
|
|
|
data[i].Box = i/BoxSlotCount + 1;
|
|
|
|
|
data[i].Slot = i%BoxSlotCount + 1;
|
2016-12-16 17:03:36 +00:00
|
|
|
|
data[i].Locked = getIsSlotLocked(data[i].Box, data[i].Slot);
|
2016-06-20 04:22:43 +00:00
|
|
|
|
}
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (value == null)
|
|
|
|
|
throw new ArgumentNullException();
|
2016-08-28 10:18:22 +00:00
|
|
|
|
if (value.Length != BoxCount*BoxSlotCount)
|
|
|
|
|
throw new ArgumentException($"Expected {BoxCount*BoxSlotCount}, got {value.Length}");
|
2016-06-20 04:22:43 +00:00
|
|
|
|
if (value.Any(pk => PKMType != pk.GetType()))
|
|
|
|
|
throw new ArgumentException($"Not {PKMType} array.");
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < value.Length; i++)
|
2016-08-28 10:18:22 +00:00
|
|
|
|
setStoredSlot(value[i], getBoxOffset(i/BoxSlotCount) + SIZE_STORED*(i%BoxSlotCount));
|
2016-06-20 04:22:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public PKM[] PartyData
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
PKM[] data = new PKM[PartyCount];
|
|
|
|
|
for (int i = 0; i < data.Length; i++)
|
2016-07-28 01:51:11 +00:00
|
|
|
|
data[i] = getPartySlot(getPartyOffset(i));
|
2016-06-20 04:22:43 +00:00
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (value == null)
|
|
|
|
|
throw new ArgumentNullException();
|
|
|
|
|
if (value.Length == 0 || value.Length > 6)
|
|
|
|
|
throw new ArgumentException("Expected 1-6, got " + value.Length);
|
|
|
|
|
if (value.Any(pk => PKMType != pk.GetType()))
|
|
|
|
|
throw new ArgumentException($"Not {PKMType} array.");
|
|
|
|
|
if (value[0].Species == 0)
|
2017-05-23 04:55:05 +00:00
|
|
|
|
Console.WriteLine($"Empty first slot, received {value.Length}.");
|
2016-06-20 04:22:43 +00:00
|
|
|
|
|
|
|
|
|
PKM[] newParty = value.Where(pk => pk.Species != 0).ToArray();
|
|
|
|
|
|
|
|
|
|
PartyCount = newParty.Length;
|
|
|
|
|
Array.Resize(ref newParty, 6);
|
|
|
|
|
|
|
|
|
|
for (int i = PartyCount; i < newParty.Length; i++)
|
|
|
|
|
newParty[i] = BlankPKM;
|
|
|
|
|
for (int i = 0; i < newParty.Length; i++)
|
|
|
|
|
setPartySlot(newParty[i], getPartyOffset(i));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public PKM[] BattleBoxData
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2016-10-20 01:19:01 +00:00
|
|
|
|
if (!HasBattleBox)
|
2016-06-20 04:22:43 +00:00
|
|
|
|
return new PKM[0];
|
|
|
|
|
|
|
|
|
|
PKM[] data = new PKM[6];
|
|
|
|
|
for (int i = 0; i < data.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
data[i] = getStoredSlot(BattleBox + SIZE_STORED * i);
|
2016-12-16 07:17:17 +00:00
|
|
|
|
data[i].Locked = BattleBoxLocked;
|
2016-06-20 04:22:43 +00:00
|
|
|
|
if (data[i].Species == 0)
|
|
|
|
|
return data.Take(i).ToArray();
|
|
|
|
|
}
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool[] EventFlags
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (EventFlagMax < 0)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
bool[] Flags = new bool[EventFlagMax];
|
|
|
|
|
for (int i = 0; i < Flags.Length; i++)
|
2017-05-26 09:05:58 +00:00
|
|
|
|
Flags[i] = (Data[EventFlag + (i >> 3)] >> (i & 7) & 0x1) == 1;
|
2016-06-20 04:22:43 +00:00
|
|
|
|
return Flags;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (EventFlagMax < 0)
|
|
|
|
|
return;
|
|
|
|
|
if (value.Length != EventFlagMax)
|
|
|
|
|
return;
|
|
|
|
|
|
2017-05-23 04:55:05 +00:00
|
|
|
|
byte[] data = new byte[value.Length>>3];
|
2016-06-20 04:22:43 +00:00
|
|
|
|
for (int i = 0; i < value.Length; i++)
|
|
|
|
|
if (value[i])
|
|
|
|
|
data[i >> 3] |= (byte)(1 << (i & 7));
|
|
|
|
|
|
|
|
|
|
data.CopyTo(Data, EventFlag);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public ushort[] EventConsts
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (EventConstMax < 0)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
ushort[] Constants = new ushort[EventConstMax];
|
|
|
|
|
for (int i = 0; i < Constants.Length; i++)
|
2016-08-04 00:56:15 +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++)
|
|
|
|
|
BitConverter.GetBytes(value[i]).CopyTo(Data, EventConst + i * 2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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
|
2017-05-13 03:32:36 +00:00
|
|
|
|
protected virtual bool[] MysteryGiftReceivedFlags { get => null; set { } }
|
|
|
|
|
protected virtual MysteryGift[] MysteryGiftCards { get => null; set { } }
|
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 { } }
|
2016-06-20 04:22:43 +00:00
|
|
|
|
public virtual string JPEGTitle => null;
|
|
|
|
|
public virtual byte[] JPEGData => null;
|
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 { } }
|
2016-09-19 05:47:31 +00:00
|
|
|
|
public virtual ushort TID { get; set; }
|
|
|
|
|
public virtual ushort SID { get; set; }
|
2016-11-19 01:19:16 +00:00
|
|
|
|
public int TrainerID7 => (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; }
|
2017-05-23 04:55:05 +00:00
|
|
|
|
public 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 { } }
|
2016-06-20 05:11:53 +00:00
|
|
|
|
|
2016-06-20 04:22:43 +00:00
|
|
|
|
// Varied Methods
|
|
|
|
|
protected abstract void setChecksums();
|
|
|
|
|
public abstract int getBoxOffset(int box);
|
|
|
|
|
public abstract int getPartyOffset(int slot);
|
|
|
|
|
public abstract string getBoxName(int box);
|
|
|
|
|
public abstract void setBoxName(int box, string val);
|
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 { } }
|
|
|
|
|
public virtual ulong? Secure1 { get => null; set { } }
|
|
|
|
|
public virtual ulong? Secure2 { 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;
|
2016-09-19 05:47:31 +00:00
|
|
|
|
public virtual int getDaycareSlotOffset(int loc, int slot) { return -1; }
|
|
|
|
|
public virtual uint? getDaycareEXP(int loc, int slot) { return null; }
|
2016-10-30 02:11:47 +00:00
|
|
|
|
public virtual string getDaycareRNGSeed(int loc) { return null; }
|
2016-09-10 20:40:03 +00:00
|
|
|
|
public virtual bool? getDaycareHasEgg(int loc) { return null; }
|
2016-09-19 05:47:31 +00:00
|
|
|
|
public virtual bool? getDaycareOccupied(int loc, int slot) { return null; }
|
|
|
|
|
|
|
|
|
|
public virtual void setDaycareEXP(int loc, int slot, uint EXP) { }
|
2016-10-30 02:11:47 +00:00
|
|
|
|
public virtual void setDaycareRNGSeed(int loc, string seed) { }
|
2016-06-20 04:22:43 +00:00
|
|
|
|
public virtual void setDaycareHasEgg(int loc, bool hasEgg) { }
|
2016-09-19 05:47:31 +00:00
|
|
|
|
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 { } }
|
|
|
|
|
public virtual byte[] BoxFlags { get => null; set { } }
|
|
|
|
|
public virtual int CurrentBox { get => 0; set { } }
|
2016-12-16 07:17:17 +00:00
|
|
|
|
protected int[] LockedSlots = new int[0];
|
2016-12-27 00:41:12 +00:00
|
|
|
|
protected int[] TeamSlots = new int[0];
|
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);
|
|
|
|
|
if (LockedSlots.Any(slot => min <= slot && slot < max)) // slots locked within operation range
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
int len = BoxSlotCount*SIZE_STORED;
|
|
|
|
|
byte[] boxdata = getData(getBoxOffset(0), len*BoxCount); // get all boxes
|
|
|
|
|
string[] boxNames = new int[BoxCount].Select((x, i) => getBoxName(i)).ToArray();
|
|
|
|
|
int[] boxWallpapers = new int[BoxCount].Select((x, i) => getBoxWallpaper(i)).ToArray();
|
|
|
|
|
|
|
|
|
|
min /= BoxSlotCount;
|
|
|
|
|
max /= BoxSlotCount;
|
|
|
|
|
|
|
|
|
|
// 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++;
|
|
|
|
|
}
|
|
|
|
|
Array.Copy(boxdata, len*i, Data, getBoxOffset(b), len);
|
|
|
|
|
setBoxName(b, boxNames[i]);
|
|
|
|
|
setBoxWallpaper(b, boxWallpapers[i]);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
int min = BoxSlotCount * box1;
|
|
|
|
|
int max = BoxSlotCount * box1 + BoxSlotCount;
|
2017-04-20 03:54:49 +00:00
|
|
|
|
if (LockedSlots.Any(slot => min <= slot && slot < max)) // locked slot within box
|
|
|
|
|
return false;
|
|
|
|
|
if (TeamSlots.Any(slot => min <= slot && slot < max)) // team slot within box
|
2017-01-07 05:22:42 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
min = BoxSlotCount * box2;
|
|
|
|
|
max = BoxSlotCount * box2 + BoxSlotCount;
|
2017-04-20 03:54:49 +00:00
|
|
|
|
if (LockedSlots.Any(slot => min <= slot && slot < max)) // locked slot within box
|
|
|
|
|
return false;
|
|
|
|
|
if (TeamSlots.Any(slot => min <= slot && slot < max)) // team slot within box
|
2017-01-07 05:22:42 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Data
|
|
|
|
|
int b1o = getBoxOffset(box1);
|
|
|
|
|
int b2o = getBoxOffset(box2);
|
|
|
|
|
int len = BoxSlotCount*SIZE_STORED;
|
|
|
|
|
byte[] b1 = new byte[len];
|
|
|
|
|
Array.Copy(Data, b1o, b1, 0, len);
|
|
|
|
|
Array.Copy(Data, b2o, Data, b1o, len);
|
|
|
|
|
Array.Copy(b1, 0, Data, b2o, len);
|
|
|
|
|
|
|
|
|
|
// Name
|
|
|
|
|
string b1n = getBoxName(box1);
|
|
|
|
|
setBoxName(box1, getBoxName(box2));
|
|
|
|
|
setBoxName(box2, b1n);
|
|
|
|
|
|
|
|
|
|
// Wallpaper
|
|
|
|
|
int b1w = getBoxWallpaper(box1);
|
|
|
|
|
setBoxWallpaper(box1, getBoxWallpaper(box2));
|
|
|
|
|
setBoxWallpaper(box2, b1w);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2016-10-29 18:32:21 +00:00
|
|
|
|
|
|
|
|
|
protected virtual int getBoxWallpaperOffset(int box) { return -1; }
|
2017-05-24 04:38:15 +00:00
|
|
|
|
public virtual int getBoxWallpaper(int box)
|
2016-10-29 18:32:21 +00:00
|
|
|
|
{
|
|
|
|
|
int offset = getBoxWallpaperOffset(box);
|
|
|
|
|
if (offset < 0 || box > BoxCount)
|
|
|
|
|
return box;
|
|
|
|
|
return Data[offset];
|
|
|
|
|
}
|
|
|
|
|
public virtual void setBoxWallpaper(int box, int val)
|
|
|
|
|
{
|
|
|
|
|
int offset = getBoxWallpaperOffset(box);
|
|
|
|
|
if (offset < 0 || box > BoxCount)
|
|
|
|
|
return;
|
|
|
|
|
Data[offset] = (byte)val;
|
|
|
|
|
}
|
2016-09-19 05:47:31 +00:00
|
|
|
|
|
2016-10-01 23:52:29 +00:00
|
|
|
|
public virtual PKM getPartySlot(int offset)
|
2016-06-20 04:22:43 +00:00
|
|
|
|
{
|
|
|
|
|
return getPKM(decryptPKM(getData(offset, SIZE_PARTY)));
|
|
|
|
|
}
|
2016-10-01 23:52:29 +00:00
|
|
|
|
public virtual PKM getStoredSlot(int offset)
|
2016-06-20 04:22:43 +00:00
|
|
|
|
{
|
|
|
|
|
return getPKM(decryptPKM(getData(offset, SIZE_STORED)));
|
|
|
|
|
}
|
|
|
|
|
public void setPartySlot(PKM pkm, int offset, bool? trade = null, bool? dex = null)
|
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
setPKM(pkm);
|
|
|
|
|
if (dex ?? SetUpdateDex)
|
|
|
|
|
setDex(pkm);
|
2017-06-07 05:13:12 +00:00
|
|
|
|
setPartyValues(pkm, isParty: true);
|
2016-06-20 04:22:43 +00:00
|
|
|
|
|
2016-07-05 03:04:43 +00:00
|
|
|
|
for (int i = 0; i < 6; i++)
|
|
|
|
|
if (getPartyOffset(i) == offset)
|
|
|
|
|
if (PartyCount <= i)
|
|
|
|
|
PartyCount = i + 1;
|
|
|
|
|
|
2016-06-20 04:22:43 +00:00
|
|
|
|
setData(pkm.EncryptedPartyData, offset);
|
|
|
|
|
Edited = true;
|
|
|
|
|
}
|
2016-08-29 20:27:17 +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)
|
|
|
|
|
setPKM(pkm);
|
|
|
|
|
if (dex ?? SetUpdateDex)
|
|
|
|
|
setDex(pkm);
|
2017-06-07 05:13:12 +00:00
|
|
|
|
setPartyValues(pkm, isParty: false);
|
2016-06-20 04:22:43 +00:00
|
|
|
|
setData(pkm.EncryptedBoxData, offset);
|
|
|
|
|
Edited = true;
|
|
|
|
|
}
|
2016-07-05 03:04:43 +00:00
|
|
|
|
public void deletePartySlot(int slot)
|
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
int slotTo = getPartyOffset(i - 1);
|
|
|
|
|
int slotFrom = getPartyOffset(i);
|
|
|
|
|
setData(getData(slotFrom, SIZE_PARTY), slotTo);
|
|
|
|
|
}
|
|
|
|
|
setStoredSlot(BlankPKM, getPartyOffset(5), false, false);
|
|
|
|
|
PartyCount -= 1;
|
|
|
|
|
}
|
2016-12-16 07:17:17 +00:00
|
|
|
|
public virtual bool getIsSlotLocked(int box, int slot) { return false; }
|
|
|
|
|
public bool getBoxHasLockedSlot(int BoxStart, int BoxEnd)
|
|
|
|
|
{
|
|
|
|
|
return LockedSlots.Any(slot => BoxStart*BoxSlotCount <= slot && slot < (BoxEnd + 1)*BoxSlotCount);
|
|
|
|
|
}
|
2016-12-27 00:41:12 +00:00
|
|
|
|
public virtual bool getIsTeamSet(int box, int slot) { return false; }
|
2016-06-20 04:22:43 +00:00
|
|
|
|
|
|
|
|
|
public void sortBoxes(int BoxStart = 0, int BoxEnd = -1)
|
|
|
|
|
{
|
|
|
|
|
PKM[] BD = BoxData;
|
2016-08-28 10:18:22 +00:00
|
|
|
|
var Section = BD.Skip(BoxStart*BoxSlotCount);
|
2016-06-20 04:22:43 +00:00
|
|
|
|
if (BoxEnd > BoxStart)
|
2016-08-28 10:18:22 +00:00
|
|
|
|
Section = Section.Take(BoxSlotCount*(BoxEnd - BoxStart));
|
2016-06-20 04:22:43 +00:00
|
|
|
|
|
|
|
|
|
var Sorted = Section
|
|
|
|
|
.OrderBy(p => p.Species == 0) // empty slots at end
|
|
|
|
|
.ThenBy(p => p.IsEgg) // eggs to the end
|
|
|
|
|
.ThenBy(p => p.Species) // species sorted
|
2016-10-01 23:52:29 +00:00
|
|
|
|
.ThenBy(p => p.AltForm) // altforms sorted
|
2017-06-02 15:54:08 +00:00
|
|
|
|
.ThenBy(p => p.Gender) // gender sorted
|
2016-06-20 04:22:43 +00:00
|
|
|
|
.ThenBy(p => p.IsNicknamed).ToArray();
|
|
|
|
|
|
2016-08-28 10:18:22 +00:00
|
|
|
|
Array.Copy(Sorted, 0, BD, BoxStart*BoxSlotCount, Sorted.Length);
|
2016-06-20 04:22:43 +00:00
|
|
|
|
BoxData = BD;
|
|
|
|
|
}
|
|
|
|
|
public void resetBoxes(int BoxStart = 0, int BoxEnd = -1)
|
|
|
|
|
{
|
|
|
|
|
if (BoxEnd < 0)
|
|
|
|
|
BoxEnd = BoxCount;
|
2017-03-04 17:38:39 +00:00
|
|
|
|
|
|
|
|
|
var blank = BlankPKM.EncryptedBoxData;
|
|
|
|
|
if (this is SAV3RSBox)
|
|
|
|
|
Array.Resize(ref blank, blank.Length + 4); // 00000 TID/SID at end
|
|
|
|
|
|
2016-06-20 04:22:43 +00:00
|
|
|
|
for (int i = BoxStart; i < BoxEnd; i++)
|
|
|
|
|
{
|
|
|
|
|
int offset = getBoxOffset(i);
|
2016-08-28 10:18:22 +00:00
|
|
|
|
for (int p = 0; p < BoxSlotCount; p++)
|
2017-03-04 17:38:39 +00:00
|
|
|
|
setData(blank, offset + SIZE_STORED * p);
|
2016-06-20 04:22:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] getPCBin() { return BoxData.SelectMany(pk => pk.EncryptedBoxData).ToArray(); }
|
2016-08-28 10:18:22 +00:00
|
|
|
|
public byte[] getBoxBin(int box) { return BoxData.Skip(box*BoxSlotCount).Take(BoxSlotCount).SelectMany(pk => pk.EncryptedBoxData).ToArray(); }
|
2016-06-20 04:22:43 +00:00
|
|
|
|
public bool setPCBin(byte[] data)
|
|
|
|
|
{
|
2016-12-16 07:17:17 +00:00
|
|
|
|
if (LockedSlots.Any())
|
|
|
|
|
return false;
|
2016-06-20 04:22:43 +00:00
|
|
|
|
if (data.Length != getPCBin().Length)
|
|
|
|
|
return false;
|
|
|
|
|
|
2016-09-19 05:47:31 +00:00
|
|
|
|
int len = BlankPKM.EncryptedBoxData.Length;
|
|
|
|
|
|
2016-06-20 04:22:43 +00:00
|
|
|
|
// split up data to individual pkm
|
2016-09-19 05:47:31 +00:00
|
|
|
|
byte[][] pkdata = new byte[data.Length/len][];
|
|
|
|
|
for (int i = 0; i < data.Length; i += len)
|
2016-08-20 06:36:13 +00:00
|
|
|
|
{
|
2016-09-19 05:47:31 +00:00
|
|
|
|
pkdata[i/len] = new byte[len];
|
|
|
|
|
Array.Copy(data, i, pkdata[i/len], 0, len);
|
2016-08-20 06:36:13 +00:00
|
|
|
|
}
|
2016-06-20 04:22:43 +00:00
|
|
|
|
|
|
|
|
|
PKM[] pkms = BoxData;
|
|
|
|
|
for (int i = 0; i < pkms.Length; i++)
|
2016-09-07 02:40:54 +00:00
|
|
|
|
pkms[i] = getPKM(decryptPKM(pkdata[i]));
|
2016-06-20 04:22:43 +00:00
|
|
|
|
BoxData = pkms;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
public bool setBoxBin(byte[] data, int box)
|
|
|
|
|
{
|
2016-12-16 07:17:17 +00:00
|
|
|
|
if (LockedSlots.Any(slot => box * BoxSlotCount <= slot && slot < (box + 1) * BoxSlotCount))
|
|
|
|
|
return false;
|
2016-06-20 04:22:43 +00:00
|
|
|
|
if (data.Length != getBoxBin(box).Length)
|
|
|
|
|
return false;
|
|
|
|
|
|
2016-09-19 05:47:31 +00:00
|
|
|
|
int len = BlankPKM.EncryptedBoxData.Length;
|
|
|
|
|
|
|
|
|
|
// split up data to individual pkm
|
|
|
|
|
byte[][] pkdata = new byte[data.Length/len][];
|
|
|
|
|
for (int i = 0; i < data.Length; i += len)
|
2016-08-20 06:36:13 +00:00
|
|
|
|
{
|
2016-09-19 05:47:31 +00:00
|
|
|
|
pkdata[i/len] = new byte[len];
|
|
|
|
|
Array.Copy(data, i, pkdata[i/len], 0, len);
|
2016-08-20 06:36:13 +00:00
|
|
|
|
}
|
2016-06-20 04:22:43 +00:00
|
|
|
|
|
|
|
|
|
PKM[] pkms = BoxData;
|
2016-08-28 10:18:22 +00:00
|
|
|
|
for (int i = 0; i < BoxSlotCount; i++)
|
2016-09-07 02:40:54 +00:00
|
|
|
|
pkms[box*BoxSlotCount + i] = getPKM(decryptPKM(pkdata[i]));
|
2016-06-20 04:22:43 +00:00
|
|
|
|
BoxData = pkms;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-07 05:13:12 +00:00
|
|
|
|
protected virtual void setPartyValues(PKM pkm, bool isParty) { }
|
2016-06-20 04:22:43 +00:00
|
|
|
|
protected virtual void setPKM(PKM pkm) { }
|
2017-03-15 05:41:15 +00:00
|
|
|
|
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-03-17 04:20:06 +00:00
|
|
|
|
public int SeenCount => HasPokeDex ? new bool[MaxSpeciesID].Where((b, i) => getSeen(i+1)).Count() : 0;
|
|
|
|
|
public int CaughtCount => HasPokeDex ? new bool[MaxSpeciesID].Where((b, i) => getCaught(i+1)).Count() : 0;
|
2016-08-30 00:48:05 +00:00
|
|
|
|
|
2016-06-20 04:22:43 +00:00
|
|
|
|
public byte[] getData(int Offset, int Length)
|
|
|
|
|
{
|
2016-08-20 06:36:13 +00:00
|
|
|
|
if (Offset + Length > Data.Length)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
byte[] data = new byte[Length];
|
|
|
|
|
Array.Copy(Data, Offset, data, 0, Length);
|
|
|
|
|
return data;
|
2016-06-20 04:22:43 +00:00
|
|
|
|
}
|
|
|
|
|
public void setData(byte[] input, int Offset)
|
|
|
|
|
{
|
|
|
|
|
input.CopyTo(Data, Offset);
|
|
|
|
|
Edited = true;
|
|
|
|
|
}
|
2016-11-12 15:30:51 +00:00
|
|
|
|
|
2017-04-09 21:06:50 +00:00
|
|
|
|
public abstract string getString(int Offset, int Length);
|
|
|
|
|
public abstract byte[] setString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0);
|
|
|
|
|
|
2016-12-16 07:17:17 +00:00
|
|
|
|
public virtual bool RequiresMemeCrypto => false;
|
2017-04-10 22:59:16 +00:00
|
|
|
|
|
|
|
|
|
public virtual string eBerryName => string.Empty;
|
|
|
|
|
public virtual bool eBerryIsEnigma => true;
|
2016-06-20 04:22:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|