using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace PKHeX.Core
{
///
/// Base Class for Save Files
///
public abstract class SaveFile : ITrainerInfo
{
public static bool SetUpdateDex { protected get; set; } = true;
public static bool SetUpdatePKM { protected get; set; } = true;
// General Object Properties
public byte[] Data;
public bool Edited;
public string FileName, FilePath;
public abstract string BAKName { get; }
public byte[] BAK { get; protected set; }
public bool Exportable { get; protected set; }
public abstract SaveFile Clone();
public abstract string Filter { get; }
public byte[] Footer { protected get; set; } = new byte[0]; // .dsv
public byte[] Header { protected get; set; } = new byte[0]; // .gci
public bool Japanese { get; protected set; }
protected virtual string PlayTimeString => $"{PlayedHours}ː{PlayedMinutes:00}ː{PlayedSeconds:00}"; // not :
public bool IndeterminateGame => Version == GameVersion.Unknown;
public virtual bool IndeterminateSubVersion => false;
public abstract string Extension { get; }
public virtual string[] PKMExtensions => PKM.Extensions.Where(f =>
{
int gen = f.Last() - 0x30;
return 3 <= gen && gen <= Generation;
}).ToArray();
// General PKM Properties
public abstract Type PKMType { get; }
public abstract PKM GetPKM(byte[] data);
public abstract PKM BlankPKM { get; }
public abstract byte[] DecryptPKM(byte[] data);
public abstract int SIZE_STORED { get; }
protected abstract int SIZE_PARTY { get; }
public abstract int MaxEV { get; }
public virtual int MaxIV => 31;
public ushort[] HeldItems { get; protected set; }
// General SAV Properties
public virtual byte[] Write(bool DSV, bool GCI)
{
return Write(DSV);
}
protected virtual byte[] Write(bool DSV)
{
SetChecksums();
if (Footer.Length > 0 && DSV)
return Data.Concat(Footer).ToArray();
if (Header.Length > 0)
return Header.Concat(Data).ToArray();
return Data;
}
public virtual string MiscSaveChecks() => string.Empty;
public virtual string MiscSaveInfo() => string.Empty;
public virtual GameVersion Version { get; protected set; }
public abstract bool ChecksumsValid { get; }
public abstract string ChecksumInfo { get; }
public abstract int Generation { get; }
public PersonalTable Personal { get; set; }
public bool USUM => Data.Length == SaveUtil.SIZE_G7USUM;
public bool SM => Data.Length == SaveUtil.SIZE_G7SM;
public bool ORASDEMO => Data.Length == SaveUtil.SIZE_G6ORASDEMO;
public bool ORAS => Data.Length == SaveUtil.SIZE_G6ORAS;
public bool XY => Data.Length == SaveUtil.SIZE_G6XY;
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;
public bool GSC => Version == GameVersion.GS || Version == GameVersion.C;
public bool RBY => Version == GameVersion.RBY;
public bool GameCube => new[] { GameVersion.COLO, GameVersion.XD, GameVersion.RSBOX }.Contains(Version);
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; }
// 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;
public virtual bool HasParty => Party > -1;
public bool HasBattleBox => BattleBox > -1;
public bool HasFused => Fused > -1;
public bool HasGTS => GTS > -1;
public bool HasDaycare => Daycare > -1;
public virtual bool HasPokeDex => PokeDex > -1;
public virtual bool HasBoxWallpapers => GetBoxWallpaperOffset(0) > -1;
public virtual bool HasNamableBoxes => HasBoxWallpapers;
public virtual bool HasSUBE => SUBE > -1 && !ORAS;
public virtual bool HasGeolocation => false;
public bool HasPokeBlock => ORAS && !ORASDEMO;
public bool HasEvents => EventFlags != null;
public bool HasLink => ORAS && !ORASDEMO || XY;
// 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;
public virtual int DaycareSeedSize { get; } = 0;
public abstract int OTLength { get; }
public abstract int NickLength { get; }
public virtual int MaxMoney => 9999999;
public virtual int MaxCoins => 9999;
public virtual int MaxShadowID => 0;
// Offsets
protected int Box { get; set; } = int.MinValue;
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 IList BoxData
{
get
{
PKM[] data = new PKM[BoxCount*BoxSlotCount];
for (int i = 0; i < data.Length; i++)
{
data[i] = GetStoredSlot(GetBoxOffset(i/BoxSlotCount) + SIZE_STORED*(i%BoxSlotCount));
data[i].Identifier = $"{GetBoxName(i/BoxSlotCount)}:{i%BoxSlotCount + 1:00}";
data[i].Box = i/BoxSlotCount + 1;
data[i].Slot = i%BoxSlotCount + 1;
data[i].Locked = IsSlotLocked(data[i].Box, data[i].Slot);
}
return data;
}
set
{
if (value.Count != BoxCount*BoxSlotCount)
throw new ArgumentException($"Expected {BoxCount*BoxSlotCount}, got {value.Count}");
if (value.Any(pk => PKMType != pk.GetType()))
throw new ArgumentException($"Not {PKMType} array.");
for (int i = 0; i < value.Count; i++)
SetStoredSlot(value[i], GetBoxOffset(i/BoxSlotCount) + SIZE_STORED*(i%BoxSlotCount));
}
}
public IList PartyData
{
get
{
PKM[] data = new PKM[PartyCount];
for (int i = 0; i < data.Length; i++)
data[i] = GetPartySlot(GetPartyOffset(i));
return data;
}
set
{
if (value.Count == 0 || value.Count > 6)
throw new ArgumentException($"Expected 1-6, got {value.Count}");
if (value.Any(pk => PKMType != pk.GetType()))
throw new ArgumentException($"Not {PKMType} array.");
if (value[0].Species == 0)
Debug.WriteLine($"Empty first slot, received {value.Count}.");
PKM[] newParty = new PKM[6];
value.Where(pk => pk.Species != 0).CopyTo(newParty);
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 IList BattleBoxData
{
get
{
if (!HasBattleBox)
return new PKM[0];
PKM[] data = new PKM[6];
for (int i = 0; i < data.Length; i++)
{
data[i] = GetStoredSlot(BattleBox + SIZE_STORED * i);
data[i].Locked = BattleBoxLocked;
if (data[i].Species == 0)
return data.Take(i).ToArray();
}
return data;
}
}
/// All Event Flag values for the savegame
public bool[] EventFlags
{
get
{
if (EventFlagMax < 0)
return null;
bool[] Flags = new bool[EventFlagMax];
for (int i = 0; i < Flags.Length; i++)
Flags[i] = GetEventFlag(i);
return Flags;
}
set
{
if (EventFlagMax < 0)
return;
if (value.Length != EventFlagMax)
return;
for (int i = 0; i < value.Length; i++)
SetEventFlag(i, value[i]);
}
}
/// All Event Constant values for the savegame
public ushort[] EventConsts
{
get
{
if (EventConstMax < 0)
return null;
ushort[] Constants = new ushort[EventConstMax];
for (int i = 0; i < Constants.Length; i++)
Constants[i] = BitConverter.ToUInt16(Data, EventConst + i * 2);
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);
}
}
///
/// Gets the status of a desired Event Flag
///
/// Event Flag to check
/// Flag is Set (true) or not Set (false)
public bool GetEventFlag(int flagNumber)
{
if (flagNumber > EventFlagMax)
throw new ArgumentException($"Event Flag to get ({flagNumber}) is greater than max ({EventFlagMax}).");
return GetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7);
}
///
/// Sets the status of a desired Event Flag
///
/// Event Flag to check
/// Event Flag status to set
/// Flag is Set (true) or not Set (false)
public void SetEventFlag(int flagNumber, bool value)
{
if (flagNumber > EventFlagMax)
throw new ArgumentException($"Event Flag to set ({flagNumber}) is greater than max ({EventFlagMax}).");
SetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7, value);
}
///
/// Gets the status of the Flag at the specified offset and index.
///
/// Offset to read from
/// Bit index to read
/// Flag is Set (true) or not Set (false)
public bool GetFlag(int offset, int bitIndex)
{
bitIndex &= 7; // ensure bit access is 0-7
return (Data[offset] >> bitIndex & 1) != 0;
}
///
/// Sets the status of the Flag at the specified offset and index.
///
/// Offset to read from
/// Bit index to read
/// Flag status to set
/// Flag is Set (true) or not Set (false)
public void SetFlag(int offset, int bitIndex, bool value)
{
bitIndex &= 7; // ensure bit access is 0-7
Data[offset] &= (byte)~(1 << bitIndex);
Data[offset] |= (byte)((value ? 1 : 0) << bitIndex);
}
// Inventory
public virtual InventoryPouch[] Inventory { get; set; }
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;
protected int OFS_PCItem { get; set; } = int.MinValue;
protected int OFS_PouchZCrystals { get; set; } = int.MinValue;
// Mystery Gift
protected virtual bool[] MysteryGiftReceivedFlags { get => null; set { } }
protected virtual MysteryGift[] MysteryGiftCards { get => null; set { } }
public virtual MysteryGiftAlbum GiftAlbum
{
get => new MysteryGiftAlbum
{
Flags = MysteryGiftReceivedFlags,
Gifts = MysteryGiftCards
};
set
{
MysteryGiftReceivedFlags = value.Flags;
MysteryGiftCards = value.Gifts;
}
}
public virtual bool BattleBoxLocked { get => false; set { } }
public virtual string JPEGTitle => null;
public virtual byte[] JPEGData => null;
public virtual int Country { get => -1; set { } }
public virtual int ConsoleRegion { get => -1; set { } }
public virtual int SubRegion { get => -1; set { } }
// Trainer Info
public virtual int Gender { get; set; }
public virtual int Language { get => -1; set { } }
public virtual int Game { get => -1; set { } }
public virtual int TID { get; set; }
public virtual int SID { get; set; }
public int TrainerID7 => (int)((uint)(TID | (SID << 16)) % 1000000);
public virtual string OT { get; set; } = "PKHeX";
public virtual int PlayedHours { get; set; }
public virtual int PlayedMinutes { get; set; }
public virtual int PlayedSeconds { get; set; }
public virtual int SecondsToStart { get; set; }
public virtual int SecondsToFame { get; set; }
public virtual uint Money { get; set; }
public abstract int BoxCount { get; }
public int SlotCount => BoxCount * BoxSlotCount;
public virtual int PartyCount { get; protected set; }
public virtual int MultiplayerSpriteID { get => 0; set { } }
public bool IsPartyAllEggs(params int[] except)
{
if (!HasParty)
return false;
var party = PartyData;
return party.Count == party.Where((t, i) => t.IsEgg || except.Contains(i)).Count();
}
// 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);
public virtual int GameSyncIDSize { get; } = 8;
public virtual string GameSyncID { get => null; set { } }
public virtual ulong? Secure1 { get => null; set { } }
public virtual ulong? Secure2 { get => null; set { } }
// Daycare
public int DaycareIndex = 0;
public virtual bool HasTwoDaycares => false;
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;
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) { }
// Storage
public virtual int BoxSlotCount => 30;
public virtual int BoxesUnlocked { get => -1; set { } }
public virtual byte[] BoxFlags { get => null; set { } }
public virtual int CurrentBox { get => 0; set { } }
protected int[] LockedSlots = new int[0];
protected int[] TeamSlots = new int[0];
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++;
}
Buffer.BlockCopy(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;
if (!IsBoxAbleToMove(box1) || !IsBoxAbleToMove(box2))
return false;
// Data
int b1o = GetBoxOffset(box1);
int b2o = GetBoxOffset(box2);
int len = BoxSlotCount*SIZE_STORED;
byte[] b1 = new byte[len];
Buffer.BlockCopy(Data, b1o, b1, 0, len);
Buffer.BlockCopy(Data, b2o, Data, b1o, len);
Buffer.BlockCopy(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;
}
private bool IsBoxAbleToMove(int box)
{
int min = BoxSlotCount * box;
int max = BoxSlotCount * box + BoxSlotCount;
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
return false;
return true;
}
protected virtual int GetBoxWallpaperOffset(int box) => -1;
public virtual int GetBoxWallpaper(int box)
{
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;
}
public virtual PKM GetPartySlot(int offset)
{
return GetPKM(DecryptPKM(GetData(offset, SIZE_PARTY)));
}
public virtual PKM GetStoredSlot(int offset)
{
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)
throw new ArgumentException($"PKM Format needs to be {PKMType} when setting to this Save File.");
if (trade ?? SetUpdatePKM)
SetPKM(pkm);
if (dex ?? SetUpdateDex)
SetDex(pkm);
SetPartyValues(pkm, isParty: true);
int i = GetPartyIndex(offset);
if (i <= -1)
throw new ArgumentException("Invalid Party offset provided; unable to resolve party slot index.");
// update party count
if (pkm.Species != 0)
{
if (PartyCount <= i)
PartyCount = i + 1;
}
else if (PartyCount > i)
PartyCount = i;
SetData(pkm.EncryptedPartyData, offset);
Edited = true;
}
private int GetPartyIndex(int offset)
{
for (int i = 0; i < 6; i++)
if (GetPartyOffset(i) == offset)
return i;
return -1;
}
public virtual void SetStoredSlot(PKM pkm, int offset, bool? trade = null, bool? dex = null)
{
if (pkm == null) return;
if (pkm.GetType() != PKMType)
throw new ArgumentException($"PKM Format needs to be {PKMType} when setting to this Save File.");
if (trade ?? SetUpdatePKM)
SetPKM(pkm);
if (dex ?? SetUpdateDex)
SetDex(pkm);
SetPartyValues(pkm, isParty: false);
SetData(pkm.EncryptedBoxData, offset);
Edited = true;
}
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;
}
public virtual bool IsSlotLocked(int box, int slot) => false;
public bool IsAnySlotLockedInBox(int BoxStart, int BoxEnd)
{
return LockedSlots.Any(slot => BoxStart*BoxSlotCount <= slot && slot < (BoxEnd + 1)*BoxSlotCount);
}
public virtual bool IsSlotInBattleTeam(int box, int slot) => false;
public void SortBoxes(int BoxStart = 0, int BoxEnd = -1, Func, IEnumerable> sortMethod = null, bool reverse = false)
{
var BD = BoxData;
int start = BoxSlotCount * BoxStart;
var Section = BD.Skip(start);
if (BoxEnd >= BoxStart)
Section = Section.Take(BoxSlotCount * (BoxEnd - BoxStart + 1));
var Sorted = (sortMethod ?? PKMSorting.OrderBySpecies)(Section);
if (reverse)
Sorted = Sorted.ReverseSort();
Sorted.CopyTo(BD, start);
BoxData = BD;
}
public void ClearBoxes(int BoxStart = 0, int BoxEnd = -1, Func deleteCriteria = null)
{
if (BoxEnd < 0)
BoxEnd = BoxCount - 1;
var blank = BlankPKM.EncryptedBoxData;
if (this is SAV3RSBox)
Array.Resize(ref blank, blank.Length + 4); // 00000 TID/SID at end
for (int i = BoxStart; i <= BoxEnd; i++)
{
int offset = GetBoxOffset(i);
for (int p = 0; p < BoxSlotCount; p++)
{
var ofs = offset + SIZE_STORED * p;
if (deleteCriteria != null)
{
var pk = GetStoredSlot(ofs);
if (!deleteCriteria(pk))
continue;
}
SetData(blank, ofs);
}
}
}
public void ModifyBoxes(Action action, int BoxStart = 0, int BoxEnd = -1)
{
if (BoxEnd < 0)
BoxEnd = BoxCount - 1;
var BD = BoxData;
for (int b = BoxStart; b <= BoxEnd; b++)
for (int s = 0; s < BoxSlotCount; s++)
{
if (IsSlotLocked(b, s))
continue;
var index = b * BoxSlotCount + s;
action(BD[index]);
}
BoxData = BD;
}
public byte[] PCBinary => BoxData.SelectMany(pk => pk.EncryptedBoxData).ToArray();
public byte[] GetBoxBinary(int box) => BoxData.Skip(box*BoxSlotCount).Take(BoxSlotCount).SelectMany(pk => pk.EncryptedBoxData).ToArray();
public bool SetPCBinary(byte[] data)
{
if (LockedSlots.Length != 0)
return false;
if (data.Length != PCBinary.Length)
return false;
var BD = BoxData;
var pkdata = PKX.GetPKMDataFromConcatenatedBinary(data, BlankPKM.EncryptedBoxData.Length);
pkdata.Select(z => GetPKM(DecryptPKM(z))).CopyTo(BD);
BoxData = BD;
return true;
}
public bool SetBoxBinary(byte[] data, int box)
{
int start = box * BoxSlotCount;
int end = start + BoxSlotCount;
if (LockedSlots.Any(slot => start <= slot && slot < end))
return false;
if (data.Length != GetBoxBinary(box).Length)
return false;
var BD = BoxData;
var pkdata = PKX.GetPKMDataFromConcatenatedBinary(data, BlankPKM.EncryptedBoxData.Length);
pkdata.Select(z => GetPKM(DecryptPKM(z))).CopyTo(BD, start);
BoxData = BD;
return true;
}
protected virtual void SetPartyValues(PKM pkm, bool isParty)
{
if (!isParty)
return;
if (pkm.Stat_HPMax != 0) // Stats already present
return;
pkm.SetStats(pkm.GetStats(pkm.PersonalInfo));
pkm.Stat_Level = pkm.CurrentLevel;
}
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) { }
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;
public byte[] GetData(int Offset, int Length)
{
byte[] data = new byte[Length];
Buffer.BlockCopy(Data, Offset, data, 0, Length);
return data;
}
public void SetData(byte[] input, int Offset)
{
input.CopyTo(Data, Offset);
Edited = true;
}
public bool IsRangeEmpty(int Offset, int Length)
{
for (int i = Offset; i < Offset + Length; i++)
if (Data[i] != 0)
return false;
return true;
}
public virtual bool IsPKMPresent(int Offset) => PKX.IsPKMPresent(Data, Offset);
public abstract string GetString(int Offset, int Length);
public abstract byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0);
public virtual string EBerryName => string.Empty;
public virtual bool IsEBerryIsEnigma => true;
}
}