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
|
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);
|
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
protected virtual byte[] Write(bool DSV)
|
2016-06-20 04:22:43 +00:00
|
|
|
|
{
|
2017-06-18 01:37:19 +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;
|
|
|
|
|
}
|
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
|
|
|
|
|
2018-05-01 04:39:12 +00:00
|
|
|
|
public void 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);
|
2018-12-11 04:32:08 +00:00
|
|
|
|
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;
|
2016-06-20 04:22:43 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2018-05-01 00:29:13 +00:00
|
|
|
|
public void 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;
|
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);
|
2018-05-01 00:29:13 +00:00
|
|
|
|
if (deleteCriteria != null)
|
|
|
|
|
{
|
|
|
|
|
var pk = GetStoredSlot(ofs);
|
|
|
|
|
if (!deleteCriteria(pk))
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SetData(blank, ofs);
|
|
|
|
|
}
|
2016-06-20 04:22:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2018-04-22 19:43:18 +00:00
|
|
|
|
public void ModifyBoxes(Action<PKM> action, int BoxStart = 0, int BoxEnd = -1)
|
|
|
|
|
{
|
|
|
|
|
if (BoxEnd < 0)
|
2018-04-24 02:11:55 +00:00
|
|
|
|
BoxEnd = BoxCount - 1;
|
2018-04-22 19:43:18 +00:00
|
|
|
|
var BD = BoxData;
|
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;
|
|
|
|
|
var index = (b * BoxSlotCount) + s;
|
|
|
|
|
action(BD[index]);
|
|
|
|
|
}
|
2018-04-22 19:43:18 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2018-04-22 19:43:18 +00:00
|
|
|
|
BoxData = BD;
|
|
|
|
|
}
|
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
|
|
|
|
|
2018-03-18 23:22:21 +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
|
|
|
|
}
|
|
|
|
|
}
|