PKHeX/Saves/SAV6.cs
Kaphotics 8e62803897 Tweak savefile enumeration
Use one enum to denote Version across all code, also identifies if data
is invalid
Move some SAV code to virtual members in abstract class
(BAK/Exportable/Footer)

Few minor bugfixes here and there (hiding Characteristic label, loading
pk6 to sav3, sav3 version detection via gamecode)
2016-06-27 23:03:57 -07:00

847 lines
35 KiB
C#

using System;
using System.Linq;
using System.Text;
namespace PKHeX
{
public sealed class SAV6 : SaveFile
{
// Save Data Attributes
public override string BAKName => $"{FileName} [{OT} ({Version}) - {LastSavedTime}].bak";
public override string Filter => "Main SAV|*.*";
public override string Extension => "";
public SAV6(byte[] data = null)
{
Data = data == null ? new byte[SaveUtil.SIZE_G6ORAS] : (byte[])data.Clone();
BAK = (byte[])Data.Clone();
Exportable = !Data.SequenceEqual(new byte[Data.Length]);
// Load Info
getBlockInfo();
getSAVOffsets();
HeldItems = ORAS ? Legal.HeldItem_AO : Legal.HeldItem_XY;
Personal = ORAS ? Legal.PersonalAO : Legal.PersonalXY;
if (!Exportable)
resetBoxes();
}
// Configuration
public override SaveFile Clone() { return new SAV6(Data); }
public override int SIZE_STORED => PKX.SIZE_6STORED;
public override int SIZE_PARTY => PKX.SIZE_6PARTY;
public override PKM BlankPKM => new PK6();
protected override Type PKMType => typeof(PK6);
public override int BoxCount => 31;
public override int MaxEV => 252;
public override int Generation => 6;
protected override int GiftCountMax => 24;
protected override int GiftFlagMax => 0x100 * 8;
protected override int EventFlagMax => 8 * 0x180;
protected override int EventConstMax => (EventFlag - EventConst) / 2;
public override int OTLength => 12;
public override int NickLength => 12;
public override int MaxMoveID => XY ? 617 : 621;
public override int MaxSpeciesID => 721;
public override int MaxItemID => XY ? 717 : 775;
public override int MaxAbilityID => XY ? 188 : 191;
public override int MaxBallID => 0x19;
public override int MaxGameID => 27; // OR
// Feature Overrides
public override bool HasGeolocation => true;
// Blocks & Offsets
private int BlockInfoOffset;
private BlockInfo[] Blocks;
private void getBlockInfo()
{
BlockInfoOffset = Data.Length - 0x200 + 0x10;
if (BitConverter.ToUInt32(Data, BlockInfoOffset) != SaveUtil.BEEF)
BlockInfoOffset -= 0x200; // No savegames have more than 0x3D blocks, maybe in the future?
int count = (Data.Length - BlockInfoOffset - 0x8) / 8;
BlockInfoOffset += 4;
Blocks = new BlockInfo[count];
int CurrentPosition = 0;
for (int i = 0; i < Blocks.Length; i++)
{
Blocks[i] = new BlockInfo
{
Offset = CurrentPosition,
Length = BitConverter.ToInt32(Data, BlockInfoOffset + 0 + 8 * i),
ID = BitConverter.ToUInt16(Data, BlockInfoOffset + 4 + 8 * i),
Checksum = BitConverter.ToUInt16(Data, BlockInfoOffset + 6 + 8 * i)
};
// Expand out to nearest 0x200
CurrentPosition += Blocks[i].Length % 0x200 == 0 ? Blocks[i].Length : 0x200 - Blocks[i].Length % 0x200 + Blocks[i].Length;
if ((Blocks[i].ID != 0) || i == 0) continue;
count = i;
break;
}
// Fix Final Array Lengths
Array.Resize(ref Blocks, count);
}
protected override void setChecksums()
{
// Check for invalid block lengths
if (Blocks.Length < 3) // arbitrary...
{
Console.WriteLine("Not enough blocks ({0}), aborting setChecksums", Blocks.Length);
return;
}
// Apply checksums
for (int i = 0; i < Blocks.Length; i++)
{
byte[] array = Data.Skip(Blocks[i].Offset).Take(Blocks[i].Length).ToArray();
BitConverter.GetBytes(SaveUtil.ccitt16(array)).CopyTo(Data, BlockInfoOffset + 6 + i * 8);
}
}
public override bool ChecksumsValid => SaveUtil.verifyG6SAV(Data);
public override string ChecksumInfo => SaveUtil.verifyG6CHK(Data);
public ulong Secure1
{
get { return BitConverter.ToUInt64(Data, BlockInfoOffset - 0x14); }
set { BitConverter.GetBytes(value).CopyTo(Data, BlockInfoOffset - 0x14); }
}
public ulong Secure2
{
get { return BitConverter.ToUInt64(Data, BlockInfoOffset - 0xC); }
set { BitConverter.GetBytes(value).CopyTo(Data, BlockInfoOffset - 0xC); }
}
private void getSAVOffsets()
{
if (ORASDEMO)
{
/* 00: */ Item = 0x00000;
/* 01: */ ItemInfo = 0x00C00; // Select Bound Items
/* 02: */ AdventureInfo = 0x00E00;
/* 03: */ Trainer1 = 0x01000;
/* 04: */ // = 0x01200; [00004] // ???
/* 05: */ PlayTime = 0x01400;
/* 06: */ // = 0x01600; [00024] // FFFFFFFF
/* 07: */ // = 0x01800; [02100] // Overworld Data
/* 08: */ Trainer2 = 0x03A00;
/* 09: */ TrainerCard = 0x03C00;
/* 10: */ Party = 0x03E00;
/* 11: */ EventConst = 0x04600; EventFlag = EventConst + 0x2FC;
/* 12: */ // = 0x04C00; [00004] // 87B1A23F const
/* 13: */ // = 0x04E00; [00048] // Repel Info, (Swarm?) and other overworld info
/* 14: */ SUBE = 0x05000;
/* 15: */ PSSStats = 0x05400;
OFS_PouchHeldItem = Item + 0;
OFS_PouchKeyItem = Item + 0x640;
OFS_PouchTMHM = Item + 0x7C0;
OFS_PouchMedicine = Item + 0x970;
OFS_PouchBerry = Item + 0xA70;
}
else if (XY)
{
Puff = 0x00000;
Item = 0x00400;
ItemInfo = 0x1000;
AdventureInfo = 0x01200;
Trainer1 = 0x1400;
PlayTime = 0x1800;
Accessories = 0x1A00;
Trainer2 = 0x4200;
PCLayout = 0x4400;
BattleBox = 0x04A00;
PSS = 0x05000;
TrainerCard = 0x14000;
Party = 0x14200;
EventConst = 0x14A00;
PSSStats = 0x1E400;
PokeDex = 0x15000;
Fused = 0x16000;
OPower = 0x16A00;
GTS = 0x17800;
HoF = 0x19400;
MaisonStats = 0x1B1C0;
Daycare = 0x1B200;
BerryField = 0x1B800;
WondercardFlags = 0x1BC00;
SUBE = 0x1D890;
SuperTrain = 0x1F200;
Box = 0x22600;
JPEG = 0x57200;
PCBackgrounds = PCLayout + 0x41E;
PCFlags = PCLayout + 0x43D;
LastViewedBox = PCLayout + 0x43F;
EventFlag = EventConst + 0x2FC;
PokeDexLanguageFlags = PokeDex + 0x3C8;
Spinda = PokeDex + 0x648;
WondercardData = WondercardFlags + 0x100;
OFS_PouchHeldItem = Item + 0;
OFS_PouchKeyItem = Item + 0x640;
OFS_PouchTMHM = Item + 0x7C0;
OFS_PouchMedicine = Item + 0x968;
OFS_PouchBerry = Item + 0xA68;
}
else if (ORAS)
{
Puff = 0x00000;
Item = 0x00400;
ItemInfo = 0x1000;
AdventureInfo = 0x01200;
Trainer1 = 0x01400;
PlayTime = 0x1800;
Accessories = 0x1A00;
Trainer2 = 0x04200;
PCLayout = 0x04400;
BattleBox = 0x04A00;
PSS = 0x05000;
TrainerCard = 0x14000;
Party = 0x14200;
EventConst = 0x14A00;
PokeDex = 0x15000;
Fused = 0x16A00;
OPower = 0x17400;
GTS = 0x18200;
HoF = 0x19E00;
MaisonStats = 0x1BBC0;
Daycare = 0x1BC00;
BerryField = 0x1C400;
WondercardFlags = 0x1CC00;
SUBE = 0x1D890;
PSSStats = 0x1F400;
SuperTrain = 0x20200;
Contest = 0x23600;
SecretBase = 0x23A00;
EonTicket = 0x319B8;
Box = 0x33000;
JPEG = 0x67C00;
PCBackgrounds = PCLayout + 0x41E;
PCFlags = PCLayout + 0x43D;
LastViewedBox = PCLayout + 0x43F;
EventFlag = EventConst + 0x2FC;
PokeDexLanguageFlags = PokeDex + 0x400;
Spinda = PokeDex + 0x680;
EncounterCount = PokeDex + 0x686;
WondercardData = WondercardFlags + 0x100;
Daycare2 = Daycare + 0x1F0;
OFS_PouchHeldItem = Item + 0;
OFS_PouchKeyItem = Item + 0x640;
OFS_PouchTMHM = Item + 0x7C0;
OFS_PouchMedicine = Item + 0x970;
OFS_PouchBerry = Item + 0xA70;
}
else // Empty input
{
Party = 0x0;
Box = Party + SIZE_PARTY * 6 + 0x1000;
}
}
// Private Only
private int Item { get; set; } = int.MinValue;
private int AdventureInfo { get; set; } = int.MinValue;
private int Trainer2 { get; set; } = int.MinValue;
private int LastViewedBox { get; set; } = int.MinValue;
private int WondercardFlags { get; set; } = int.MinValue;
private int PlayTime { get; set; } = int.MinValue;
private int JPEG { get; set; } = int.MinValue;
private int ItemInfo { get; set; } = int.MinValue;
private int Daycare2 { get; set; } = int.MinValue;
// Accessible as SAV6
public int TrainerCard { get; private set; } = 0x14000;
public int PCFlags { get; private set; } = int.MinValue;
public int PSSStats { get; private set; } = int.MinValue;
public int MaisonStats { get; private set; } = int.MinValue;
public int EonTicket { get; private set; } = int.MinValue;
public int PCBackgrounds { get; private set; } = int.MinValue;
public int Contest { get; private set; } = int.MinValue;
public int Accessories { get; private set; } = int.MinValue;
public int PokeDexLanguageFlags { get; private set; } = int.MinValue;
public int Spinda { get; private set; } = int.MinValue;
public int EncounterCount { get; private set; } = int.MinValue;
public override GameVersion Version
{
get
{
switch (Game)
{
case 24: return GameVersion.X;
case 25: return GameVersion.Y;
case 26: return GameVersion.AS;
case 27: return GameVersion.OR;
}
return GameVersion.Unknown;
}
}
// Player Information
public override ushort TID
{
get { return BitConverter.ToUInt16(Data, TrainerCard + 0); }
set { BitConverter.GetBytes(value).CopyTo(Data, TrainerCard + 0); }
}
public override ushort SID
{
get { return BitConverter.ToUInt16(Data, TrainerCard + 2); }
set { BitConverter.GetBytes(value).CopyTo(Data, TrainerCard + 2); }
}
public override int Game
{
get { return Data[TrainerCard + 4]; }
set { Data[TrainerCard + 4] = (byte)value; }
}
public override int Gender
{
get { return Data[TrainerCard + 5]; }
set { Data[TrainerCard + 5] = (byte)value; }
}
public int Sprite
{
get { return Data[TrainerCard + 7]; }
set { Data[TrainerCard + 7] = (byte)value; }
}
public ulong GameSyncID
{
get { return BitConverter.ToUInt64(Data, TrainerCard + 8); }
set { BitConverter.GetBytes(value).CopyTo(Data, TrainerCard + 8); }
}
public override int SubRegion
{
get { return Data[TrainerCard + 0x26]; }
set { Data[TrainerCard + 0x26] = (byte)value; }
}
public override int Country
{
get { return Data[TrainerCard + 0x27]; }
set { Data[TrainerCard + 0x27] = (byte)value; }
}
public override int ConsoleRegion
{
get { return Data[TrainerCard + 0x2C]; }
set { Data[TrainerCard + 0x2C] = (byte)value; }
}
public override int Language
{
get { return Data[TrainerCard + 0x2D]; }
set { Data[TrainerCard + 0x2D] = (byte)value; }
}
public override string OT
{
get { return Util.TrimFromZero(Encoding.Unicode.GetString(Data, TrainerCard + 0x48, 0x1A)); }
set { Encoding.Unicode.GetBytes(value.PadRight(13, '\0')).CopyTo(Data, TrainerCard + 0x48); }
}
public string Saying1
{
get { return Util.TrimFromZero(Encoding.Unicode.GetString(Data, TrainerCard + 0x7C + 0x22 * 0, 0x22)); }
set { Encoding.Unicode.GetBytes(value.PadRight(value.Length + 1, '\0')).CopyTo(Data, TrainerCard + 0x7C + 0x22 * 0); }
}
public string Saying2
{
get { return Util.TrimFromZero(Encoding.Unicode.GetString(Data, TrainerCard + 0x7C + 0x22 * 1, 0x22)); }
set { Encoding.Unicode.GetBytes(value.PadRight(value.Length + 1, '\0')).CopyTo(Data, TrainerCard + 0x7C + 0x22 * 1); }
}
public string Saying3
{
get { return Util.TrimFromZero(Encoding.Unicode.GetString(Data, TrainerCard + 0x7C + 0x22 * 2, 0x22)); }
set { Encoding.Unicode.GetBytes(value.PadRight(value.Length + 1, '\0')).CopyTo(Data, TrainerCard + 0x7C + 0x22 * 2); }
}
public string Saying4
{
get { return Util.TrimFromZero(Encoding.Unicode.GetString(Data, TrainerCard + 0x7C + 0x22 * 3, 0x22)); }
set { Encoding.Unicode.GetBytes(value.PadRight(value.Length + 1, '\0')).CopyTo(Data, TrainerCard + 0x7C + 0x22 * 3); }
}
public string Saying5
{
get { return Util.TrimFromZero(Encoding.Unicode.GetString(Data, TrainerCard + 0x7C + 0x22 * 4, 0x22)); }
set { Encoding.Unicode.GetBytes(value.PadRight(value.Length + 1, '\0')).CopyTo(Data, TrainerCard + 0x7C + 0x22 * 4); }
}
public int M
{
get { return BitConverter.ToUInt16(Data, Trainer1 + 0x02); }
set { BitConverter.GetBytes((ushort)value).CopyTo(Data, Trainer1 + 0x02); }
}
public float X
{
get { return BitConverter.ToSingle(Data, Trainer1 + 0x10) / 18; }
set { BitConverter.GetBytes(value * 18).CopyTo(Data, Trainer1 + 0x10); }
}
public float Z
{
get { return BitConverter.ToSingle(Data, Trainer1 + 0x14); }
set { BitConverter.GetBytes(value).CopyTo(Data, Trainer1 + 0x14); }
}
public float Y
{
get { return BitConverter.ToSingle(Data, Trainer1 + 0x18) / 18; }
set { BitConverter.GetBytes(value * 18).CopyTo(Data, Trainer1 + 0x18); }
}
public int Style
{
get { return Data[Trainer1 + 0x14D]; }
set { Data[Trainer1 + 0x14D] = (byte)value; }
}
public override uint Money
{
get { return BitConverter.ToUInt32(Data, Trainer2 + 0x8); }
set { BitConverter.GetBytes(value).CopyTo(Data, Trainer2 + 0x8); }
}
public int Badges
{
get { return Data[Trainer2 + 0xC]; }
set { Data[Trainer2 + 0xC] = (byte)value; }
}
public int BP
{
get
{
int offset = Trainer2 + 0x3C;
if (ORAS) offset -= 0xC; // 0x30
return BitConverter.ToUInt16(Data, offset);
}
set
{
int offset = Trainer2 + 0x3C;
if (ORAS) offset -= 0xC; // 0x30
BitConverter.GetBytes((ushort)value).CopyTo(Data, offset);
}
}
public int Vivillon
{
get
{
int offset = Trainer2 + 0x50;
if (ORAS) offset -= 0xC; // 0x44
return Data[offset];
}
set
{
int offset = Trainer2 + 0x50;
if (ORAS) offset -= 0xC; // 0x44
Data[offset] = (byte)value;
}
}
public override int PlayedHours
{
get { return BitConverter.ToUInt16(Data, PlayTime); }
set { BitConverter.GetBytes((ushort)value).CopyTo(Data, PlayTime); }
}
public override int PlayedMinutes
{
get { return Data[PlayTime + 2]; }
set { Data[PlayTime + 2] = (byte)value; }
}
public override int PlayedSeconds
{
get { return Data[PlayTime + 3]; }
set { Data[PlayTime + 3] = (byte)value; }
}
public uint LastSaved { get { return BitConverter.ToUInt32(Data, PlayTime + 0x4); } set { BitConverter.GetBytes(value).CopyTo(Data, PlayTime + 0x4); } }
public int LastSavedYear { get { return (int)(LastSaved & 0xFFF); } set { LastSaved = LastSaved & 0xFFFFF000 | (uint)value; } }
public int LastSavedMonth { get { return (int)(LastSaved >> 12 & 0xF); } set { LastSaved = LastSaved & 0xFFFF0FFF | ((uint)value & 0xF) << 12; } }
public int LastSavedDay { get { return (int)(LastSaved >> 16 & 0x1F); } set { LastSaved = LastSaved & 0xFFE0FFFF | ((uint)value & 0x1F) << 16; } }
public int LastSavedHour { get { return (int)(LastSaved >> 21 & 0x1F); } set { LastSaved = LastSaved & 0xFC1FFFFF | ((uint)value & 0x1F) << 21; } }
public int LastSavedMinute { get { return (int)(LastSaved >> 26 & 0x3F); } set { LastSaved = LastSaved & 0x03FFFFFF | ((uint)value & 0x3F) << 26; } }
public string LastSavedTime => $"{LastSavedYear.ToString("0000")}{LastSavedMonth.ToString("00")}{LastSavedDay.ToString("00")}{LastSavedHour.ToString("00")}{LastSavedMinute.ToString("00")}";
public int ResumeYear { get { return BitConverter.ToInt32(Data, AdventureInfo + 0x4); } set { BitConverter.GetBytes(value).CopyTo(Data,AdventureInfo + 0x4); } }
public int ResumeMonth { get { return Data[AdventureInfo + 0x8]; } set { Data[AdventureInfo + 0x8] = (byte)value; } }
public int ResumeDay { get { return Data[AdventureInfo + 0x9]; } set { Data[AdventureInfo + 0x9] = (byte)value; } }
public int ResumeHour { get { return Data[AdventureInfo + 0xB]; } set { Data[AdventureInfo + 0xB] = (byte)value; } }
public int ResumeMinute { get { return Data[AdventureInfo + 0xC]; } set { Data[AdventureInfo + 0xC] = (byte)value; } }
public int ResumeSeconds { get { return Data[AdventureInfo + 0xD]; } set { Data[AdventureInfo + 0xD] = (byte)value; } }
public override int SecondsToStart { get { return BitConverter.ToInt32(Data, AdventureInfo + 0x18); } set { BitConverter.GetBytes(value).CopyTo(Data, AdventureInfo + 0x18); } }
public override int SecondsToFame { get { return BitConverter.ToInt32(Data, AdventureInfo + 0x20); } set { BitConverter.GetBytes(value).CopyTo(Data, AdventureInfo + 0x20); } }
public uint getPSSStat(int index) { return BitConverter.ToUInt32(Data, PSSStats + 4*index); }
public void setPSSStat(int index, uint value) { BitConverter.GetBytes(value).CopyTo(Data, PSSStats + 4*index); }
public ushort getMaisonStat(int index) { return BitConverter.ToUInt16(Data, MaisonStats + 2 * index); }
public void setMaisonStat(int index, ushort value) { BitConverter.GetBytes(value).CopyTo(Data, MaisonStats + 2*index); }
public uint getEncounterCount(int index) { return BitConverter.ToUInt16(Data, EncounterCount + 2*index); }
public void setEncounterCount(int index, ushort value) { BitConverter.GetBytes(value).CopyTo(Data, EncounterCount + 2*index); }
// Daycare
public override int DaycareSeedSize => 16;
public override int getDaycareSlotOffset(int loc, int slot)
{
int ofs = loc == 0 ? Daycare : Daycare2;
if (ofs < 0)
return -1;
return ofs + 8 + slot*(SIZE_STORED + 8);
}
public override uint? getDaycareEXP(int loc, int slot)
{
int ofs = loc == 0 ? Daycare : Daycare2;
if (ofs > -1)
return BitConverter.ToUInt32(Data, ofs + (SIZE_STORED + 8)*slot + 4);
return null;
}
public override bool? getDaycareOccupied(int loc, int slot)
{
int ofs = loc == 0 ? Daycare : Daycare2;
if (ofs > -1)
return Data[ofs + (SIZE_STORED + 8) * slot] == 1;
return null;
}
public override ulong? getDaycareRNGSeed(int loc)
{
int ofs = loc == 0 ? Daycare : Daycare2;
if (ofs > -1)
return BitConverter.ToUInt64(Data, ofs + 0x1E8);
return null;
}
public override bool? getDaycareHasEgg(int loc)
{
int ofs = loc == 0 ? Daycare : Daycare2;
if (ofs > -1)
return Data[ofs + 0x1E0] == 1;
return null;
}
public override void setDaycareEXP(int loc, int slot, uint EXP)
{
int ofs = loc == 0 ? Daycare : Daycare2;
if (ofs > -1)
BitConverter.GetBytes(EXP).CopyTo(Data, ofs + (SIZE_STORED + 8)*slot + 4);
}
public override void setDaycareOccupied(int loc, int slot, bool occupied)
{
int ofs = loc == 0 ? Daycare : Daycare2;
if (ofs > -1)
Data[ofs + (SIZE_STORED + 8)*slot] = (byte) (occupied ? 1 : 0);
}
public override void setDaycareRNGSeed(int loc, ulong seed)
{
int ofs = loc == 0 ? Daycare : Daycare2;
if (ofs > -1)
BitConverter.GetBytes(seed).CopyTo(Data, ofs + 0x1E8);
}
public override void setDaycareHasEgg(int loc, bool hasEgg)
{
int ofs = loc == 0 ? Daycare : Daycare2;
if (ofs > -1)
Data[ofs + 0x1E0] = (byte)(hasEgg ? 1 : 0);
}
public byte[] Puffs { get { return Data.Skip(Puff).Take(100).ToArray(); } set { value.CopyTo(Data, Puff); } }
public int PuffCount { get { return BitConverter.ToInt32(Data, Puff + 100); } set { BitConverter.GetBytes(value).CopyTo(Data, Puff + 100); } }
public int[] SelectItems
{
// UP,RIGHT,DOWN,LEFT
get
{
int[] list = new int[4];
for (int i = 0; i < list.Length; i++)
list[i] = BitConverter.ToUInt16(Data, ItemInfo + 10 + 2 * i);
return list;
}
set
{
if (value == null || value.Length > 4)
return;
for (int i = 0; i < value.Length; i++)
BitConverter.GetBytes((ushort)value[i]).CopyTo(Data, ItemInfo + 10 + 2 * i);
}
}
public int[] RecentItems
{
// Items recently interacted with (Give, Use)
get
{
int[] list = new int[12];
for (int i = 0; i < list.Length; i++)
list[i] = BitConverter.ToUInt16(Data, ItemInfo + 20 + 2 * i);
return list;
}
set
{
if (value == null || value.Length > 12)
return;
for (int i = 0; i < value.Length; i++)
BitConverter.GetBytes((ushort)value[i]).CopyTo(Data, ItemInfo + 20 + 2 * i);
}
}
public override string JPEGTitle => JPEG < 0 ? null : Util.TrimFromZero(Encoding.Unicode.GetString(Data, JPEG, 0x1A));
public override byte[] JPEGData => JPEG < 0 || Data[JPEG + 0x54] != 0xFF ? null : Data.Skip(JPEG + 0x54).Take(0xE004).ToArray();
// Inventory
public override InventoryPouch[] Inventory
{
get
{
ushort[] legalItems = ORAS ? Legal.Pouch_Items_AO : Legal.Pouch_Items_XY;
ushort[] legalKey = ORAS ? Legal.Pouch_Key_AO : Legal.Pouch_Key_XY;
ushort[] legalTMHM = ORAS ? Legal.Pouch_TMHM_AO : Legal.Pouch_TMHM_XY;
ushort[] legalMedicine = ORAS ? Legal.Pouch_Medicine_AO : Legal.Pouch_Medicine_XY;
InventoryPouch[] pouch =
{
new InventoryPouch(InventoryType.Items, legalItems, 995, OFS_PouchHeldItem),
new InventoryPouch(InventoryType.KeyItems, legalKey, 1, OFS_PouchKeyItem),
new InventoryPouch(InventoryType.TMHMs, legalTMHM, 1, OFS_PouchTMHM),
new InventoryPouch(InventoryType.Medicine, legalMedicine, 995, OFS_PouchMedicine),
new InventoryPouch(InventoryType.Berries, Legal.Pouch_Berry_XY, 995, OFS_PouchBerry),
};
foreach (var p in pouch)
p.getPouch(ref Data);
return pouch;
}
set
{
foreach (var p in value)
p.setPouch(ref Data);
}
}
// Storage
public override int CurrentBox { get { return Data[LastViewedBox]; } set { Data[LastViewedBox] = (byte)value; } }
public override int getPartyOffset(int slot)
{
return Party + SIZE_PARTY * slot;
}
public override int getBoxOffset(int box)
{
return Box + SIZE_STORED*box*30;
}
public override int getBoxWallpaper(int box)
{
return 1 + Data[PCBackgrounds + box];
}
public override string getBoxName(int box)
{
if (PCLayout < 0)
return "B" + (box + 1);
return Util.TrimFromZero(Encoding.Unicode.GetString(Data, PCLayout + 0x22*box, 0x22));
}
public override void setBoxName(int box, string val)
{
Encoding.Unicode.GetBytes(val.PadRight(0x11, '\0')).CopyTo(Data, PCLayout + 0x22*box);
Edited = true;
}
public override PKM getPKM(byte[] data)
{
return new PK6(data);
}
protected override void setPKM(PKM pkm)
{
PK6 pk6 = pkm as PK6;
// Apply to this Save File
int CT = pk6.CurrentHandler;
DateTime Date = DateTime.Now;
pk6.Trade(OT, TID, SID, Country, SubRegion, Gender, false, Date.Day, Date.Month, Date.Year);
if (CT != pk6.CurrentHandler) // Logic updated Friendship
{
// Copy over the Friendship Value only under certain circumstances
if (pk6.Moves.Contains(216)) // Return
pk6.CurrentFriendship = pk6.OppositeFriendship;
else if (pk6.Moves.Contains(218)) // Frustration
pkm.CurrentFriendship = pk6.OppositeFriendship;
else if (pk6.CurrentHandler == 1) // OT->HT, needs new Friendship/Affection
pk6.TradeFriendshipAffection(OT);
}
pkm.RefreshChecksum();
}
protected override void setDex(PKM pkm)
{
if (PokeDex < 0)
return;
if (pkm.Species == 0)
return;
if (pkm.Species > MaxSpeciesID)
return;
if (Version == GameVersion.Unknown)
return;
int bit = pkm.Species - 1;
int lang = pkm.Language - 1; if (lang > 5) lang--; // 0-6 language vals
int origin = pkm.Version;
int gender = pkm.Gender % 2; // genderless -> male
int shiny = pkm.IsShiny ? 1 : 0;
int shiftoff = shiny * 0x60 * 2 + gender * 0x60 + 0x60;
// Set the [Species/Gender/Shiny] Owned Flag
Data[PokeDex + shiftoff + bit / 8 + 0x8] |= (byte)(1 << (bit % 8));
// Owned quality flag
if (origin < 0x18 && bit < 649 && !ORAS) // Species: 1-649 for X/Y, and not for ORAS; Set the Foreign Owned Flag
Data[PokeDex + 0x64C + bit / 8] |= (byte)(1 << (bit % 8));
else if (origin >= 0x18 || ORAS) // Set Native Owned Flag (should always happen)
Data[PokeDex + bit / 8 + 0x8] |= (byte)(1 << (bit % 8));
// Set the Display flag if none are set
bool Displayed = false;
Displayed |= (Data[PokeDex + 0x60 * 5 + bit / 8 + 0x8] & (byte)(1 << (bit % 8))) != 0;
Displayed |= (Data[PokeDex + 0x60 * 6 + bit / 8 + 0x8] & (byte)(1 << (bit % 8))) != 0;
Displayed |= (Data[PokeDex + 0x60 * 7 + bit / 8 + 0x8] & (byte)(1 << (bit % 8))) != 0;
Displayed |= (Data[PokeDex + 0x60 * 8 + bit / 8 + 0x8] & (byte)(1 << (bit % 8))) != 0;
if (!Displayed) // offset is already biased by 0x60, reuse shiftoff but for the display flags.
Data[PokeDex + shiftoff + 0x60 * 4 + bit / 8 + 0x8] |= (byte)(1 << (bit % 8));
// Set the Language
if (lang < 0) lang = 1;
Data[PokeDexLanguageFlags + (bit * 7 + lang) / 8] |= (byte)(1 << ((bit * 7 + lang) % 8));
// Set Form flags
int fc = PKX.Personal[pkm.Species].FormeCount;
int f = ORAS ? SaveUtil.getDexFormIndexORAS(pkm.Species, fc) : SaveUtil.getDexFormIndexXY(pkm.Species, fc);
if (f >= 0)
{
int FormLen = ORAS ? 0x26 : 0x18;
int FormDex = PokeDex + 0x368;
bit = f + pkm.AltForm;
// Set Seen Flag
Data[FormDex + FormLen * shiny + bit / 8] |= (byte)(1 << (bit % 8));
// Set Displayed Flag if necessary, check all flags
bool FormDisplayed = false;
for (int i = 0; i < fc; i++)
{
bit = f + i;
FormDisplayed |= (Data[FormDex + FormLen * 2 + bit / 8] & (byte)(1 << (bit % 8))) != 0; // Nonshiny
FormDisplayed |= (Data[FormDex + FormLen * 3 + bit / 8] & (byte)(1 << (bit % 8))) != 0; // Shiny
}
if (!FormDisplayed)
{
bit = f + pkm.AltForm;
Data[FormDex + FormLen * (2 + shiny) + bit / 8] |= (byte)(1 << (bit % 8));
}
}
// Set DexNav count (only if not encountered previously)
if (ORAS && getEncounterCount(pkm.Species - 1) == 0)
setEncounterCount(pkm.Species - 1, 1);
}
public override byte[] decryptPKM(byte[] data)
{
return PKX.decryptArray(data);
}
public override int PartyCount
{
get { return Data[Party + 6 * SIZE_PARTY]; }
protected set { Data[Party + 6 * SIZE_PARTY] = (byte)value; }
}
public override bool BattleBoxLocked
{
get { return Data[BattleBox + 6 * SIZE_STORED] != 0; }
set { Data[BattleBox + 6 * SIZE_STORED] = (byte)(value ? 1 : 0); }
}
// Mystery Gift
protected override bool[] MysteryGiftReceivedFlags
{
get
{
if (WondercardData < 0 || WondercardFlags < 0)
return null;
bool[] r = new bool[(WondercardData-WondercardFlags)*8];
for (int i = 0; i < r.Length; i++)
r[i] = (Data[WondercardFlags + (i>>3)] >> (i&7) & 0x1) == 1;
return r;
}
set
{
if (WondercardData < 0 || WondercardFlags < 0)
return;
if ((WondercardData - WondercardFlags)*8 != value?.Length)
return;
byte[] data = new byte[value.Length/8];
for (int i = 0; i < value.Length; i++)
if (value[i])
data[i>>3] |= (byte)(1 << (i&7));
data.CopyTo(Data, WondercardFlags);
Edited = true;
}
}
protected override MysteryGift[] MysteryGiftCards
{
get
{
if (WondercardData < 0)
return null;
MysteryGift[] cards = new MysteryGift[GiftCountMax];
for (int i = 0; i < cards.Length; i++)
cards[i] = getWC6(i);
return cards;
}
set
{
if (value == null)
return;
if (value.Length > 24)
Array.Resize(ref value, 24);
for (int i = 0; i < value.Length; i++)
setWC6(value[i], i);
for (int i = value.Length; i < GiftCountMax; i++)
setWC6(new WC6(), i);
}
}
private WC6 getWC6(int index)
{
if (WondercardData < 0)
return null;
if (index < 0 || index > GiftCountMax)
return null;
return new WC6(Data.Skip(WondercardData + index * WC6.Size).Take(WC6.Size).ToArray());
}
private void setWC6(MysteryGift wc6, int index)
{
if (WondercardData < 0)
return;
if (index < 0 || index > GiftCountMax)
return;
wc6.Data.CopyTo(Data, WondercardData + index * WC6.Size);
for (int i = 0; i < GiftCountMax; i++)
if (BitConverter.ToUInt16(Data, WondercardData + i * WC6.Size) == 0)
for (int j = i + 1; j < GiftCountMax - i; j++) // Shift everything down
Array.Copy(Data, WondercardData + j * WC6.Size, Data, WondercardData + (j - 1) * WC6.Size, WC6.Size);
Edited = true;
}
// Writeback Validity
public override string MiscSaveChecks()
{
string r = "";
byte[] FFFF = Enumerable.Repeat((byte)0xFF, 0x200).ToArray();
for (int i = 0; i < Data.Length / 0x200; i++)
{
if (!FFFF.SequenceEqual(Data.Skip(i * 0x200).Take(0x200))) continue;
r = $"0x200 chunk @ 0x{(i*0x200).ToString("X5")} is FF'd."
+ Environment.NewLine + "Cyber will screw up (as of August 31st 2014)." + Environment.NewLine + Environment.NewLine;
// Check to see if it is in the Pokedex
if (i * 0x200 > PokeDex && i * 0x200 < PokeDex + 0x900)
{
r += "Problem lies in the Pokedex. ";
if (i * 0x200 == PokeDex + 0x400)
r += "Remove a language flag for a species < 585, ie Petilil";
}
break;
}
return r;
}
public override string MiscSaveInfo()
{
return Blocks.Aggregate("", (current, b) => current +
$"{b.ID.ToString("00")}: {b.Offset.ToString("X5")}-{(b.Offset + b.Length).ToString("X5")}, {b.Length.ToString("X5")}{Environment.NewLine}");
}
}
}