Split gen5-7 saves with inheritance (#2319)

refer to pull request comments for summary
This commit is contained in:
Kurt 2019-06-08 19:56:11 -07:00 committed by GitHub
parent 84d1354e2f
commit 1b028198ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
93 changed files with 4317 additions and 3511 deletions

View file

@ -15,6 +15,7 @@ namespace PKHeX.Core
else
sav.SetStoredSlot(pkm, slot.Offset);
}
public static IReadOnlyList<PKM> GetAllPKM(this SaveFile sav)
{
var pkms = new List<PKM>();
@ -139,8 +140,8 @@ namespace PKHeX.Core
if (!all)
return list;
for (int i = 0; i < SAV7.ResortCount; i++)
list.Add(new StorageSlotOffset { Type = StorageSlotType.Resort, Offset = sav.GetResortSlotOffset(i) });
for (int i = 0; i < ResortSave7.ResortCount; i++)
list.Add(new StorageSlotOffset { Type = StorageSlotType.Resort, Offset = sav.ResortSave.GetResortSlotOffset(i) });
return list;
}
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using static PKHeX.Core.MessageStrings;
@ -113,14 +112,11 @@ namespace PKHeX.Core
if (g is WC6 && g.CardID == 2048 && g.ItemID == 726) // Eon Ticket (OR/AS)
{
if (!SAV.ORAS)
if (!(SAV is SAV6AO))
{
message = MsgMysteryGiftSlotSpecialReject;
return false;
}
// Set the special recieved data
BitConverter.GetBytes(WC6.EonTicketConst).CopyTo(SAV.Data, ((SAV6)SAV).EonTicket);
}
message = null;

View file

@ -3,17 +3,35 @@ using System;
namespace PKHeX.Core
{
/// <summary>
/// Gen6+ Block Info
/// Gen6+ Block Info (inside BEEF chunk)
/// </summary>
public sealed class BlockInfo3DS : BlockInfo
public abstract class BlockInfo3DS : BlockInfo
{
public ushort Checksum;
private int BlockInfoOffset;
private readonly Func<byte[], int, int, ushort> CheckFunc;
public BlockInfo3DS() { }
private BlockInfo3DS(Func<byte[], int, int, ushort> func) => CheckFunc = func;
private int ChecksumOffset => BlockInfoOffset + 6 + ((int)ID * 8);
private ushort GetChecksum(byte[] data) => CheckFunc(data, Offset, Length);
private readonly int BlockInfoOffset;
// ==chunk def== @ BlockInfoOffset
// u64 timestamp1
// u64 timestamp2
// u8[4] BEEF magic
// n*{blockInfo}, where n varies per sav type
// ==block info def==
// u32 length
// u16 id
// u16 checksum
// when stored, each block size is rounded up to nearest 0x200, and the next chunk is immediately after
protected BlockInfo3DS(int bo, uint id, int ofs, int len)
{
BlockInfoOffset = bo;
ID = id;
Offset = ofs;
Length = len;
}
private int ChecksumOffset => BlockInfoOffset + 0x14 + ((int)ID * 8) + 6;
protected abstract ushort GetChecksum(byte[] data);
protected override bool ChecksumValid(byte[] data)
{
@ -27,68 +45,54 @@ namespace PKHeX.Core
ushort chk = GetChecksum(data);
BitConverter.GetBytes(chk).CopyTo(data, ChecksumOffset);
}
}
public sealed class BlockInfo6 : BlockInfo3DS
{
public BlockInfo6(int bo, uint id, int ofs, int len) : base(bo, id, ofs, len) { }
protected override ushort GetChecksum(byte[] data) => Checksums.CRC16_CCITT(data, Offset, Length);
}
public sealed class BlockInfo7 : BlockInfo3DS
{
public BlockInfo7(int bo, uint id, int ofs, int len) : base(bo, id, ofs, len) { }
protected override ushort GetChecksum(byte[] data) => Checksums.CRC16(data, Offset, Length);
}
public sealed class BlockInfo7b : BlockInfo3DS
{
public BlockInfo7b(int bo, uint id, int ofs, int len) : base(bo, id, ofs, len) { }
protected override ushort GetChecksum(byte[] data) => Checksums.CRC16NoInvert(data, Offset, Length);
}
public static class BlockInfoBEEFUtil
{
/// <summary>
/// Gets the <see cref="BlockInfo"/> table for the input <see cref="data"/>.
/// Gets the <see cref="BlockInfo"/> for the input <see cref="data"/>.
/// </summary>
/// <param name="data">Complete data array</param>
/// <param name="blockInfoOffset">Offset the <see cref="BlockInfo"/> starts at.</param>
/// <param name="CheckFunc">Checksum method for validating each block.</param>
public static BlockInfo[] GetBlockInfoData(byte[] data, out int blockInfoOffset, Func<byte[], int, int, ushort> CheckFunc)
public static void DumpComputedBlockInfo(byte[] data, int blockInfoOffset)
{
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 len = data.Length;
return GetBlockInfo(data, ref blockInfoOffset, CheckFunc, len);
}
blockInfoOffset += 0x14;
/// <summary>
/// Gets the <see cref="BlockInfo"/> table for the input <see cref="data"/>.
/// </summary>
/// <param name="data">Complete data array</param>
/// <param name="blockInfoOffset">Offset the <see cref="BlockInfo"/> starts at.</param>
/// <param name="CheckFunc">Checksum method for validating each block.</param>
/// <param name="dataLength"></param>
public static BlockInfo[] GetBlockInfoData(byte[] data, ref int blockInfoOffset, Func<byte[], int, int, ushort> CheckFunc, int dataLength)
{
if (BitConverter.ToUInt32(data, blockInfoOffset) != SaveUtil.BEEF)
blockInfoOffset -= 0x200; // No savegames have more than 0x3D blocks, maybe in the future?
return GetBlockInfo(data, ref blockInfoOffset, CheckFunc, dataLength);
}
private static BlockInfo[] GetBlockInfo(byte[] data, ref int blockInfoOffset, Func<byte[], int, int, ushort> CheckFunc, int dataLength)
{
int count = (dataLength - blockInfoOffset - 0x8) / 8;
blockInfoOffset += 4;
var Blocks = new BlockInfo[count];
int CurrentPosition = 0;
for (int i = 0; i < Blocks.Length; i++)
uint CurrentPosition = 0;
for (int i = 0;; i++)
{
int ofs = i * 8;
Blocks[i] = new BlockInfo3DS(CheckFunc)
{
Offset = CurrentPosition,
Length = BitConverter.ToInt32(data, blockInfoOffset + ofs + 0),
ID = BitConverter.ToUInt16(data, blockInfoOffset + ofs + 4),
Checksum = BitConverter.ToUInt16(data, blockInfoOffset + ofs + 6),
int ofs = blockInfoOffset + (i * 8);
BlockInfoOffset = blockInfoOffset
};
var Offset = CurrentPosition;
var Length = BitConverter.ToUInt32(data, ofs + 0);
if (Length == 0)
break;
var ID = BitConverter.ToUInt16(data, ofs + 4);
// var Checksum = BitConverter.ToUInt16(data, ofs + 6);
Console.WriteLine($"ID={ID}, Offset=0x{Offset:X5}, Length=0x{Length:X5}");
// Expand out to nearest 0x200
var remainder = Blocks[i].Length & 0x1FF;
CurrentPosition += remainder == 0 ? Blocks[i].Length : Blocks[i].Length + 0x200 - remainder;
if (Blocks[i].ID != 0 || i == 0)
continue;
count = i;
break;
var remainder = Length & 0x1FF;
CurrentPosition += remainder == 0 ? Length : Length + 0x200 - remainder;
}
// Fix Final Array Lengths
Array.Resize(ref Blocks, count);
return Blocks;
}
}
}

View file

@ -18,23 +18,30 @@ namespace PKHeX.Core
return 1 <= gen && gen <= 2;
}).ToArray();
public SAV1(byte[] data = null, GameVersion versionOverride = GameVersion.Any)
public SAV1(GameVersion versionOverride = GameVersion.RBY, bool japanese = false) : base(SaveUtil.SIZE_G1RAW)
{
Data = data ?? new byte[SaveUtil.SIZE_G1RAW];
BAK = (byte[])Data.Clone();
Exportable = !IsRangeEmpty(0, Data.Length);
Version = versionOverride;
Japanese = japanese;
Offsets = Japanese ? SAV1Offsets.JPN : SAV1Offsets.INT;
if (versionOverride != GameVersion.Any)
Version = versionOverride;
else if(data == null)
Version = GameVersion.RBY;
else Version = SaveUtil.GetIsG1SAV(Data);
Initialize(versionOverride);
ClearBoxes();
}
public SAV1(byte[] data, GameVersion versionOverride = GameVersion.Any) : base(data)
{
Version = versionOverride != GameVersion.Any ? versionOverride : SaveUtil.GetIsG1SAV(data);
if (Version == GameVersion.Invalid)
return;
Japanese = SaveUtil.GetIsG1SAVJ(Data);
Offsets = Japanese ? SAV1Offsets.JPN : SAV1Offsets.INT;
Initialize(versionOverride);
}
private void Initialize(GameVersion versionOverride)
{
// see if RBY can be differentiated
if (Starter != 0 && versionOverride != GameVersion.Any)
Version = Yellow ? GameVersion.YW : GameVersion.RB;
@ -102,9 +109,6 @@ namespace PKHeX.Core
// Enable Pokedex editing
PokeDex = 0;
if (!Exportable)
ClearBoxes();
}
private readonly SAV1Offsets Offsets;

View file

@ -20,34 +20,45 @@ namespace PKHeX.Core
return 1 <= gen && gen <= 2;
}).ToArray();
public SAV2(byte[] data = null, GameVersion versionOverride = GameVersion.Any)
public SAV2(GameVersion versionOverride = GameVersion.C, LanguageID lang = LanguageID.English) : base(SaveUtil.SIZE_G2RAW_J)
{
Data = data ?? new byte[SaveUtil.SIZE_G2RAW_U];
BAK = (byte[])Data.Clone();
Exportable = !IsRangeEmpty(0, Data.Length);
if (data == null)
Version = GameVersion.C;
else if (versionOverride != GameVersion.Any)
Version = versionOverride;
else
Version = SaveUtil.GetIsG2SAV(Data);
Version = versionOverride;
switch (lang)
{
case LanguageID.Japanese:
Japanese = true;
break;
case LanguageID.Korean:
Korean = true;
break;
// otherwise, both false
}
Offsets = new SAV2Offsets(this);
Initialize();
ClearBoxes();
}
public SAV2(byte[] data, GameVersion versionOverride = GameVersion.Any) : base(data)
{
Version = versionOverride != GameVersion.Any ? versionOverride : SaveUtil.GetIsG2SAV(Data);
if (Version == GameVersion.Invalid)
return;
Japanese = SaveUtil.GetIsG2SAVJ(Data) != GameVersion.Invalid;
if (!Japanese)
Korean = SaveUtil.GetIsG2SAVK(Data) != GameVersion.Invalid;
Offsets = new SAV2Offsets(this);
Initialize();
}
private void Initialize()
{
Box = Data.Length;
Array.Resize(ref Data, Data.Length + SIZE_RESERVED);
Party = GetPartyOffset(0);
Personal = Version == GameVersion.GS ? PersonalTable.GS : PersonalTable.C;
Offsets = new SAV2Offsets(this);
LegalItems = Legal.Pouch_Items_GSC;
LegalBalls = Legal.Pouch_Ball_GSC;
LegalKeyItems = Version == GameVersion.C ? Legal.Pouch_Key_C : Legal.Pouch_Key_GS;
@ -102,13 +113,17 @@ namespace PKHeX.Core
{
int offset = Offsets.Daycare;
DaycareFlags[0] = Data[offset]; offset++;
DaycareFlags[0] = Data[offset];
offset++;
var pk1 = ReadPKMFromOffset(offset); // parent 1
var daycare1 = new PokeList2(pk1);
offset += (StringLength * 2) + 0x20; // nick/ot/pkm
DaycareFlags[1] = Data[offset]; offset++;
byte steps = Data[offset]; offset++;
byte BreedMotherOrNonDitto = Data[offset]; offset++;
DaycareFlags[1] = Data[offset];
offset++;
byte steps = Data[offset];
offset++;
byte BreedMotherOrNonDitto = Data[offset];
offset++;
var pk2 = ReadPKMFromOffset(offset); // parent 2
var daycare2 = new PokeList2(pk2);
offset += (StringLength * 2) + PKX.SIZE_2STORED; // nick/ot/pkm
@ -126,9 +141,6 @@ namespace PKHeX.Core
PokeDex = 0;
EventFlag = Offsets.EventFlag;
EventConst = Offsets.EventConst;
if (!Exportable)
ClearBoxes();
}
private PK2 ReadPKMFromOffset(int offset)
@ -405,7 +417,10 @@ namespace PKHeX.Core
}
}
private readonly ushort[] LegalItems, LegalKeyItems, LegalBalls, LegalTMHMs;
private ushort[] LegalItems;
private ushort[] LegalKeyItems;
private ushort[] LegalBalls;
private ushort[] LegalTMHMs;
public override InventoryPouch[] Inventory
{

View file

@ -67,12 +67,111 @@ namespace PKHeX.Core
return chunkOffset;
}
public SAV3(byte[] data = null, GameVersion versionOverride = GameVersion.Any)
public SAV3(GameVersion version = GameVersion.FRLG, bool japanese = false) : base(SaveUtil.SIZE_G3RAW)
{
Data = data ?? new byte[SaveUtil.SIZE_G3RAW];
BAK = (byte[])Data.Clone();
Exportable = !IsRangeEmpty(0, Data.Length);
if (version == GameVersion.FR || version == GameVersion.LG)
Version = GameVersion.FRLG;
else if (version == GameVersion.R || version == GameVersion.LG)
Version = GameVersion.RS;
else
Version = version;
Personal = SaveUtil.GetG3Personal(Version) ?? PersonalTable.RS;
Japanese = japanese;
LoadBlocks();
// spoof block offsets
BlockOfs = Enumerable.Range(0, BLOCK_COUNT).ToArray();
Initialize();
ClearBoxes();
}
public SAV3(byte[] data, GameVersion versionOverride = GameVersion.Any) : base(data)
{
LoadBlocks();
Version = versionOverride != GameVersion.Any ? versionOverride : GetVersion(Data, BlockOfs[0]);
Personal = SaveUtil.GetG3Personal(Version) ?? PersonalTable.RS;
// Japanese games are limited to 5 character OT names; any unused characters are 0xFF.
// 5 for JP, 7 for INT. There's always 1 terminator, thus we can check 0x6-0x7 being 0xFFFF = INT
// OT name is stored at the top of the first block.
Japanese = BitConverter.ToInt16(Data, BlockOfs[0] + 0x6) == 0;
Initialize();
}
private void Initialize()
{
// Set up PC data buffer beyond end of save file.
Box = Data.Length;
Array.Resize(ref Data, Data.Length + SIZE_RESERVED); // More than enough empty space.
// Copy chunk to the allocated location
for (int i = 5; i < BLOCK_COUNT; i++)
{
int blockIndex = Array.IndexOf(BlockOrder, i);
if (blockIndex == -1) // block empty
continue;
Array.Copy(Data, (blockIndex * SIZE_BLOCK) + ABO, Data, Box + ((i - 5) * 0xF80), chunkLength[i]);
}
PokeDex = BlockOfs[0] + 0x18;
switch (Version)
{
case GameVersion.RS:
LegalKeyItems = Legal.Pouch_Key_RS;
OFS_PCItem = BlockOfs[1] + 0x0498;
OFS_PouchHeldItem = BlockOfs[1] + 0x0560;
OFS_PouchKeyItem = BlockOfs[1] + 0x05B0;
OFS_PouchBalls = BlockOfs[1] + 0x0600;
OFS_PouchTMHM = BlockOfs[1] + 0x0640;
OFS_PouchBerry = BlockOfs[1] + 0x0740;
SeenFlagOffsets = new[] {PokeDex + 0x44, BlockOfs[1] + 0x938, BlockOfs[4] + 0xC0C};
EventFlag = BlockOfs[2] + 0x2A0;
EventConst = EventFlag + (EventFlagMax / 8);
Daycare = BlockOfs[4] + 0x11C;
break;
case GameVersion.E:
LegalKeyItems = Legal.Pouch_Key_E;
OFS_PCItem = BlockOfs[1] + 0x0498;
OFS_PouchHeldItem = BlockOfs[1] + 0x0560;
OFS_PouchKeyItem = BlockOfs[1] + 0x05D8;
OFS_PouchBalls = BlockOfs[1] + 0x0650;
OFS_PouchTMHM = BlockOfs[1] + 0x0690;
OFS_PouchBerry = BlockOfs[1] + 0x0790;
SeenFlagOffsets = new[] {PokeDex + 0x44, BlockOfs[1] + 0x988, BlockOfs[4] + 0xCA4};
EventFlag = BlockOfs[2] + 0x2F0;
EventConst = EventFlag + (EventFlagMax / 8);
Daycare = BlockOfs[4] + 0x1B0;
break;
case GameVersion.FRLG:
LegalKeyItems = Legal.Pouch_Key_FRLG;
OFS_PCItem = BlockOfs[1] + 0x0298;
OFS_PouchHeldItem = BlockOfs[1] + 0x0310;
OFS_PouchKeyItem = BlockOfs[1] + 0x03B8;
OFS_PouchBalls = BlockOfs[1] + 0x0430;
OFS_PouchTMHM = BlockOfs[1] + 0x0464;
OFS_PouchBerry = BlockOfs[1] + 0x054C;
SeenFlagOffsets = new[] {PokeDex + 0x44, BlockOfs[1] + 0x5F8, BlockOfs[4] + 0xB98};
EventFlag = BlockOfs[1] + 0xEE0;
EventConst = BlockOfs[2] + 0x80;
Daycare = BlockOfs[4] + 0x100;
break;
}
LoadEReaderBerryData();
LegalItems = Legal.Pouch_Items_RS;
LegalBalls = Legal.Pouch_Ball_RS;
LegalTMHMs = Legal.Pouch_TMHM_RS;
LegalBerries = Legal.Pouch_Berries_RS;
HeldItems = Legal.HeldItems_RS;
// Sanity Check SeenFlagOffsets -- early saves may not have block 4 initialized yet
SeenFlagOffsets = SeenFlagOffsets?.Where(z => z >= 0).ToArray();
}
private void LoadBlocks()
{
int[] BlockOrder1 = GetBlockOrder(0);
if (Data.Length > SaveUtil.SIZE_G3RAWHALF)
{
@ -92,97 +191,6 @@ namespace PKHeX.Core
int index = Array.IndexOf(BlockOrder, i);
BlockOfs[i] = index < 0 ? int.MinValue : (index * SIZE_BLOCK) + ABO;
}
if (versionOverride != GameVersion.Any)
Version = versionOverride;
else if (data == null)
Version = GameVersion.FRLG;
else
Version = GetVersion(Data, BlockOfs[0]);
Personal = SaveUtil.GetG3Personal(Version);
if (data == null) // spoof block offsets
{
BlockOfs = Enumerable.Range(0, BLOCK_COUNT).ToArray();
if (Version == GameVersion.FR || Version == GameVersion.LG)
Version = GameVersion.FRLG;
else if (Version == GameVersion.R || Version == GameVersion.LG)
Version = GameVersion.RS;
}
// Set up PC data buffer beyond end of save file.
Box = Data.Length;
Array.Resize(ref Data, Data.Length + SIZE_RESERVED); // More than enough empty space.
// Copy chunk to the allocated location
for (int i = 5; i < BLOCK_COUNT; i++)
{
int blockIndex = Array.IndexOf(BlockOrder, i);
if (blockIndex == -1) // block empty
continue;
Array.Copy(Data, (blockIndex * SIZE_BLOCK) + ABO, Data, Box + ((i - 5) * 0xF80), chunkLength[i]);
}
// Japanese games are limited to 5 character OT names; any unused characters are 0xFF.
// 5 for JP, 7 for INT. There's always 1 terminator, thus we can check 0x6-0x7 being 0xFFFF = INT
// OT name is stored at the top of the first block.
Japanese = BitConverter.ToInt16(Data, BlockOfs[0] + 0x6) == 0 && Exportable;
PokeDex = BlockOfs[0] + 0x18;
switch (Version)
{
case GameVersion.RS:
LegalKeyItems = Legal.Pouch_Key_RS;
OFS_PCItem = BlockOfs[1] + 0x0498;
OFS_PouchHeldItem = BlockOfs[1] + 0x0560;
OFS_PouchKeyItem = BlockOfs[1] + 0x05B0;
OFS_PouchBalls = BlockOfs[1] + 0x0600;
OFS_PouchTMHM = BlockOfs[1] + 0x0640;
OFS_PouchBerry = BlockOfs[1] + 0x0740;
SeenFlagOffsets = new[] { PokeDex + 0x44, BlockOfs[1] + 0x938, BlockOfs[4] + 0xC0C };
EventFlag = BlockOfs[2] + 0x2A0;
EventConst = EventFlag + (EventFlagMax / 8);
Daycare = BlockOfs[4] + 0x11C;
break;
case GameVersion.E:
LegalKeyItems = Legal.Pouch_Key_E;
OFS_PCItem = BlockOfs[1] + 0x0498;
OFS_PouchHeldItem = BlockOfs[1] + 0x0560;
OFS_PouchKeyItem = BlockOfs[1] + 0x05D8;
OFS_PouchBalls = BlockOfs[1] + 0x0650;
OFS_PouchTMHM = BlockOfs[1] + 0x0690;
OFS_PouchBerry = BlockOfs[1] + 0x0790;
SeenFlagOffsets = new[] { PokeDex + 0x44, BlockOfs[1] + 0x988, BlockOfs[4] + 0xCA4 };
EventFlag = BlockOfs[2] + 0x2F0;
EventConst = EventFlag + (EventFlagMax / 8);
Daycare = BlockOfs[4] + 0x1B0;
break;
case GameVersion.FRLG:
LegalKeyItems = Legal.Pouch_Key_FRLG;
OFS_PCItem = BlockOfs[1] + 0x0298;
OFS_PouchHeldItem = BlockOfs[1] + 0x0310;
OFS_PouchKeyItem = BlockOfs[1] + 0x03B8;
OFS_PouchBalls = BlockOfs[1] + 0x0430;
OFS_PouchTMHM = BlockOfs[1] + 0x0464;
OFS_PouchBerry = BlockOfs[1] + 0x054C;
SeenFlagOffsets = new[] { PokeDex + 0x44, BlockOfs[1] + 0x5F8, BlockOfs[4] + 0xB98 };
EventFlag = BlockOfs[1] + 0xEE0;
EventConst = BlockOfs[2] + 0x80;
Daycare = BlockOfs[4] + 0x100;
break;
}
LoadEReaderBerryData();
LegalItems = Legal.Pouch_Items_RS;
LegalBalls = Legal.Pouch_Ball_RS;
LegalTMHMs = Legal.Pouch_TMHM_RS;
LegalBerries = Legal.Pouch_Berries_RS;
HeldItems = Legal.HeldItems_RS;
// Sanity Check SeenFlagOffsets -- early saves may not have block 4 initialized yet
SeenFlagOffsets = SeenFlagOffsets?.Where(z => z >= 0).ToArray();
if (!Exportable)
ClearBoxes();
}
private int[] GetBlockOrder(int ofs)
@ -244,10 +252,10 @@ namespace PKHeX.Core
return result;
}
private readonly int ActiveSAV;
private int ActiveSAV;
private int ABO => ActiveSAV*SIZE_BLOCK*0xE;
private readonly int[] BlockOrder;
private readonly int[] BlockOfs;
private int[] BlockOrder;
private int[] BlockOfs;
public int GetBlockOffset(int block) => BlockOfs[block];
// Configuration
@ -584,7 +592,11 @@ namespace PKHeX.Core
}
}
private readonly ushort[] LegalItems, LegalKeyItems, LegalBalls, LegalTMHMs, LegalBerries;
private ushort[] LegalItems;
private ushort[] LegalKeyItems;
private ushort[] LegalBalls;
private ushort[] LegalTMHMs;
private ushort[] LegalBerries;
public override InventoryPouch[] Inventory
{
@ -719,7 +731,7 @@ namespace PKHeX.Core
}
// Pokédex
private readonly int[] SeenFlagOffsets;
private int[] SeenFlagOffsets;
protected override void SetDex(PKM pkm)
{

View file

@ -29,25 +29,34 @@ namespace PKHeX.Core
private int SaveCount = -1;
private int SaveIndex = -1;
private readonly StrategyMemo StrategyMemo;
private StrategyMemo StrategyMemo;
public int MaxShadowID => 0x80; // 128
private readonly int Memo;
private readonly ushort[] LegalItems, LegalKeyItems, LegalBalls, LegalTMHMs, LegalBerries, LegalCologne;
private readonly int OFS_PouchCologne;
private int Memo;
private ushort[] LegalItems;
private ushort[] LegalKeyItems;
private ushort[] LegalBalls;
private ushort[] LegalTMHMs;
private ushort[] LegalBerries;
private ushort[] LegalCologne;
private int OFS_PouchCologne;
public SAV3Colosseum(byte[] data, SAV3GCMemoryCard MC) : this(data) { this.MC = MC; BAK = MC.Data; }
public SAV3Colosseum(byte[] data = null)
public SAV3Colosseum() : base(SaveUtil.SIZE_G3COLO)
{
Data = data ?? new byte[SaveUtil.SIZE_G3COLO];
BAK = (byte[])Data.Clone();
Exportable = !IsRangeEmpty(0, Data.Length);
Initialize();
ClearBoxes();
}
public SAV3Colosseum(byte[] data) : base(data)
{
InitializeData();
Initialize();
}
private void Initialize()
{
Personal = PersonalTable.RS;
HeldItems = Legal.HeldItems_COLO;
if (SaveUtil.GetIsG3COLOSAV(Data) == GameVersion.COLO)
InitializeData();
Trainer1 = 0x00078;
Party = 0x000A8;
OFS_PouchHeldItem = 0x007F8;
@ -60,7 +69,7 @@ namespace PKHeX.Core
Box = 0x00B90;
Daycare = 0x08170;
Memo = 0x082B0;
StrategyMemo = new StrategyMemo(Data, Memo, xd:false);
StrategyMemo = new StrategyMemo(Data, Memo, xd: false);
LegalItems = Legal.Pouch_Items_COLO;
LegalKeyItems = Legal.Pouch_Key_COLO;
@ -69,9 +78,6 @@ namespace PKHeX.Core
LegalBerries = Legal.Pouch_Berries_RS;
LegalCologne = Legal.Pouch_Cologne_COLO;
if (!Exportable)
ClearBoxes();
// Since PartyCount is not stored in the save file,
// Count up how many party slots are active.
for (int i = 0; i < 6; i++)

View file

@ -16,16 +16,28 @@ namespace PKHeX.Core
public SAV3RSBox(byte[] data, SAV3GCMemoryCard MC) : this(data) { this.MC = MC; BAK = MC.Data; }
public SAV3RSBox(byte[] data = null)
public SAV3RSBox() : base(SaveUtil.SIZE_G3BOX)
{
Data = data ?? new byte[SaveUtil.SIZE_G3BOX];
BAK = (byte[])Data.Clone();
Exportable = !IsRangeEmpty(0, Data.Length);
Box = 0;
ClearBoxes();
Initialize();
}
if (SaveUtil.GetIsG3BOXSAV(Data) != GameVersion.RSBOX)
return;
public SAV3RSBox(byte[] data) : base(data)
{
InitializeData();
Initialize();
}
Blocks = new BlockInfoRSBOX[2*BLOCK_COUNT];
private void Initialize()
{
Personal = PersonalTable.RS;
HeldItems = Legal.HeldItems_RS;
}
private void InitializeData()
{
Blocks = new BlockInfoRSBOX[2 * BLOCK_COUNT];
for (int i = 0; i < Blocks.Length; i++)
{
int offset = BLOCK_SIZE + (i * BLOCK_SIZE);
@ -33,10 +45,10 @@ namespace PKHeX.Core
}
// Detect active save
int[] SaveCounts = Blocks.Select(block => (int)block.SaveCount).ToArray();
int[] SaveCounts = Blocks.Select(block => (int) block.SaveCount).ToArray();
SaveCount = SaveCounts.Max();
int ActiveSAV = Array.IndexOf(SaveCounts, SaveCount) / BLOCK_COUNT;
Blocks = Blocks.Skip(ActiveSAV*BLOCK_COUNT).Take(BLOCK_COUNT).OrderBy(b => b.ID).ToArray();
Blocks = Blocks.Skip(ActiveSAV * BLOCK_COUNT).Take(BLOCK_COUNT).OrderBy(b => b.ID).ToArray();
// Set up PC data buffer beyond end of save file.
Box = Data.Length;
@ -45,17 +57,11 @@ namespace PKHeX.Core
// Copy block to the allocated location
const int copySize = BLOCK_SIZE - 0x10;
foreach (var b in Blocks)
Array.Copy(Data, b.Offset + 0xC, Data, (int)(Box + (b.ID*copySize)), copySize);
Personal = PersonalTable.RS;
HeldItems = Legal.HeldItems_RS;
if (!Exportable)
ClearBoxes();
Array.Copy(Data, b.Offset + 0xC, Data, (int) (Box + (b.ID * copySize)), copySize);
}
private readonly BlockInfoRSBOX[] Blocks;
private readonly int SaveCount;
private BlockInfoRSBOX[] Blocks;
private int SaveCount;
private const int BLOCK_COUNT = 23;
private const int BLOCK_SIZE = 0x2000;
private const int SIZE_RESERVED = BLOCK_COUNT * BLOCK_SIZE; // unpacked box data

View file

@ -18,26 +18,32 @@ namespace PKHeX.Core
private const int SLOT_START = 0x6000;
private const int SLOT_COUNT = 2;
private readonly int SaveCount = -1;
private readonly int SaveIndex = -1;
private readonly int Memo, Shadow;
private readonly StrategyMemo StrategyMemo;
private readonly ShadowInfoTableXD ShadowInfo;
private int SaveCount = -1;
private int SaveIndex = -1;
private int Memo;
private int Shadow;
private StrategyMemo StrategyMemo;
private ShadowInfoTableXD ShadowInfo;
public int MaxShadowID => ShadowInfo.Count;
private readonly ushort[] LegalItems, LegalKeyItems, LegalBalls, LegalTMHMs, LegalBerries, LegalCologne, LegalDisc;
private readonly int OFS_PouchCologne, OFS_PouchDisc;
private int OFS_PouchCologne;
private int OFS_PouchDisc;
private readonly int[] subOffsets = new int[16];
public SAV3XD(byte[] data, SAV3GCMemoryCard MC) : this(data) { this.MC = MC; BAK = MC.Data; }
public SAV3XD(byte[] data = null)
public SAV3XD() : base(SaveUtil.SIZE_G3XD)
{
Data = data ?? new byte[SaveUtil.SIZE_G3XD];
BAK = (byte[])Data.Clone();
Exportable = !IsRangeEmpty(0, Data.Length);
Initialize();
ClearBoxes();
}
if (SaveUtil.GetIsG3XDSAV(Data) != GameVersion.XD)
return;
public SAV3XD(byte[] data) : base(data)
{
InitializeData();
Initialize();
}
private void InitializeData()
{
// Scan all 3 save slots for the highest counter
for (int i = 0; i < SLOT_COUNT; i++)
{
@ -71,6 +77,7 @@ namespace PKHeX.Core
subLength[i] = BigEndian.ToUInt16(Data, 0x20 + (2 * i));
subOffsets[i] = BigEndian.ToUInt16(Data, 0x40 + (4 * i)) | BigEndian.ToUInt16(Data, 0x40 + (4 * i) + 2) << 16;
}
// Offsets are displaced by the 0xA8 savedata region
Trainer1 = subOffsets[1] + 0xA8;
Party = Trainer1 + 0x30;
@ -82,7 +89,10 @@ namespace PKHeX.Core
StrategyMemo = new StrategyMemo(Data, Memo, xd: true);
ShadowInfo = new ShadowInfoTableXD(Data.Skip(Shadow).Take(subLength[7]).ToArray());
}
private void Initialize()
{
OFS_PouchHeldItem = Trainer1 + 0x4C8;
OFS_PouchKeyItem = Trainer1 + 0x540;
OFS_PouchBalls = Trainer1 + 0x5EC;
@ -90,21 +100,9 @@ namespace PKHeX.Core
OFS_PouchBerry = Trainer1 + 0x72C;
OFS_PouchCologne = Trainer1 + 0x7E4;
OFS_PouchDisc = Trainer1 + 0x7F0;
LegalItems = Legal.Pouch_Items_XD;
LegalKeyItems = Legal.Pouch_Key_XD;
LegalBalls = Legal.Pouch_Ball_RS;
LegalTMHMs = Legal.Pouch_TM_RS; // not HMs
LegalBerries = Legal.Pouch_Berries_RS;
LegalCologne = Legal.Pouch_Cologne_XD;
LegalDisc = Legal.Pouch_Disc_XD;
Personal = PersonalTable.RS;
HeldItems = Legal.HeldItems_XD;
if (!Exportable)
ClearBoxes();
// Since PartyCount is not stored in the save file,
// Count up how many party slots are active.
for (int i = 0; i < 6; i++)
@ -329,13 +327,13 @@ namespace PKHeX.Core
{
InventoryPouch[] pouch =
{
new InventoryPouch3GC(InventoryType.Items, LegalItems, 999, OFS_PouchHeldItem, 30), // 20 COLO, 30 XD
new InventoryPouch3GC(InventoryType.KeyItems, LegalKeyItems, 1, OFS_PouchKeyItem, 43),
new InventoryPouch3GC(InventoryType.Balls, LegalBalls, 999, OFS_PouchBalls, 16),
new InventoryPouch3GC(InventoryType.TMHMs, LegalTMHMs, 999, OFS_PouchTMHM, 64),
new InventoryPouch3GC(InventoryType.Berries, LegalBerries, 999, OFS_PouchBerry, 46),
new InventoryPouch3GC(InventoryType.Medicine, LegalCologne, 999, OFS_PouchCologne, 3), // Cologne
new InventoryPouch3GC(InventoryType.BattleItems, LegalDisc, 999, OFS_PouchDisc, 60)
new InventoryPouch3GC(InventoryType.Items, Legal.Pouch_Items_XD, 999, OFS_PouchHeldItem, 30), // 20 COLO, 30 XD
new InventoryPouch3GC(InventoryType.KeyItems, Legal.Pouch_Key_XD, 1, OFS_PouchKeyItem, 43),
new InventoryPouch3GC(InventoryType.Balls, Legal.Pouch_Ball_RS, 999, OFS_PouchBalls, 16),
new InventoryPouch3GC(InventoryType.TMHMs, Legal.Pouch_TM_RS, 999, OFS_PouchTMHM, 64),
new InventoryPouch3GC(InventoryType.Berries, Legal.Pouch_Berries_RS, 999, OFS_PouchBerry, 46),
new InventoryPouch3GC(InventoryType.Medicine, Legal.Pouch_Cologne_XD, 999, OFS_PouchCologne, 3), // Cologne
new InventoryPouch3GC(InventoryType.BattleItems, Legal.Pouch_Disc_XD, 999, OFS_PouchDisc, 60)
};
return pouch.LoadAll(Data);
}

View file

@ -13,34 +13,41 @@ namespace PKHeX.Core
public override string Filter => (Footer.Length > 0 ? "DeSmuME DSV|*.dsv|" : string.Empty) + "SAV File|*.sav|All Files|*.*";
public override string Extension => ".sav";
public SAV4(byte[] data = null, GameVersion versionOverride = GameVersion.Any)
public SAV4(GameVersion versionOverride = GameVersion.HGSS) : base(SaveUtil.SIZE_G4RAW)
{
Data = data ?? new byte[SaveUtil.SIZE_G4RAW];
BAK = (byte[])Data.Clone();
Exportable = !IsRangeEmpty(0, Data.Length);
Version = versionOverride;
Initialize();
ClearBoxes();
}
public SAV4(byte[] data, GameVersion versionOverride = GameVersion.Any) : base(data)
{
// Get Version
if (data == null)
Version = GameVersion.HGSS;
else if (versionOverride != GameVersion.Any)
Version = versionOverride;
else Version = SaveUtil.GetIsG4SAV(Data);
Version = versionOverride != GameVersion.Any ? versionOverride : SaveUtil.GetIsG4SAV(Data);
if (Version == GameVersion.Invalid)
return;
Initialize();
}
private void Initialize()
{
generalBlock = GetActiveGeneralBlock();
storageBlock = GetActiveStorageBlock();
GetSAVOffsets();
switch (Version)
{
case GameVersion.DP: Personal = PersonalTable.DP; break;
case GameVersion.Pt: Personal = PersonalTable.Pt; break;
case GameVersion.HGSS: Personal = PersonalTable.HGSS; break;
case GameVersion.DP:
Personal = PersonalTable.DP;
break;
case GameVersion.Pt:
Personal = PersonalTable.Pt;
break;
case GameVersion.HGSS:
Personal = PersonalTable.HGSS;
break;
}
if (!Exportable)
ClearBoxes();
}
// Configuration
@ -133,8 +140,8 @@ namespace PKHeX.Core
}
// Blocks & Offsets
private readonly int generalBlock = -1; // Small Block
private readonly int storageBlock = -1; // Big Block
private int generalBlock = -1; // Small Block
private int storageBlock = -1; // Big Block
private int SBO => 0x40000 * storageBlock;
public int GBO => 0x40000 * generalBlock;

View file

@ -16,15 +16,26 @@ namespace PKHeX.Core
private const int SAVE_COUNT = 4;
public SAV4BR(byte[] data = null)
public SAV4BR() : base(SaveUtil.SIZE_G4BR)
{
Data = data ?? new byte[SaveUtil.SIZE_G4BR];
BAK = (byte[])Data.Clone();
Exportable = !IsRangeEmpty(0, Data.Length);
ClearBoxes();
Initialize();
}
if (SaveUtil.GetIsG4BRSAV(Data) != GameVersion.BATREV)
return;
public SAV4BR(byte[] data) : base(data)
{
InitializeData(data);
Initialize();
}
private void Initialize()
{
Personal = PersonalTable.DP;
HeldItems = Legal.HeldItems_DP;
}
private void InitializeData(byte[] data)
{
Data = DecryptPBRSaveData(data);
// Detect active save
@ -46,12 +57,6 @@ namespace PKHeX.Core
}
CurrentSlot = SaveSlots[0];
Personal = PersonalTable.DP;
HeldItems = Legal.HeldItems_DP;
if (!Exportable)
ClearBoxes();
}
private bool IsOTNamePresent(int i)
@ -59,7 +64,7 @@ namespace PKHeX.Core
return BitConverter.ToUInt16(Data, 0x390 + (0x6FF00 * i)) != 0;
}
private readonly uint SaveCount;
private uint SaveCount;
protected override byte[] GetFinalData()
{

View file

@ -1,111 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PKHeX.Core
{
/// <summary>
/// Generation 5 <see cref="SaveFile"/> object.
/// </summary>
public sealed class SAV5 : SaveFile
public abstract class SAV5 : SaveFile
{
// Save Data Attributes
protected override PKM GetPKM(byte[] data) => new PK5(data);
protected override byte[] DecryptPKM(byte[] data) => PKX.DecryptArray45(data);
protected override string BAKText => $"{OT} ({(GameVersion)Game}) - {PlayTimeString}";
public override string Filter => (Footer.Length != 0 ? "DeSmuME DSV|*.dsv|" : string.Empty) + "SAV File|*.sav|All Files|*.*";
public override string Extension => ".sav";
public SAV5(byte[] data = null, GameVersion versionOverride = GameVersion.Any)
{
Data = data ?? new byte[SaveUtil.SIZE_G5RAW];
BAK = (byte[])Data.Clone();
Exportable = !IsRangeEmpty(0, Data.Length);
// Get Version
if (data == null)
Version = GameVersion.B2W2;
else if (versionOverride != GameVersion.Any)
Version = versionOverride;
else Version = SaveUtil.GetIsG5SAV(Data);
if (Version == GameVersion.Invalid)
return;
// First blocks are always the same position/size
PCLayout = 0x0;
Box = 0x400;
Party = 0x18E00;
Trainer1 = 0x19400;
WondercardData = 0x1C800;
AdventureInfo = 0x1D900;
// Different Offsets for later blocks
switch (Version)
{
case GameVersion.BW:
BattleBox = 0x20A00;
Trainer2 = 0x21200;
EventConst = 0x20100;
EventFlag = EventConst + 0x27C;
Daycare = 0x20E00;
PokeDex = 0x21600;
PokeDexLanguageFlags = PokeDex + 0x320;
BattleSubway = 0x21D00;
CGearInfoOffset = 0x1C000;
CGearDataOffset = 0x52000;
EntreeForestOffset = 0x22C00;
// Inventory offsets are the same for each game.
OFS_PouchHeldItem = 0x18400; // 0x188D7
OFS_PouchKeyItem = 0x188D8; // 0x18A23
OFS_PouchTMHM = 0x18A24; // 0x18BD7
OFS_PouchMedicine = 0x18BD8; // 0x18C97
OFS_PouchBerry = 0x18C98; // 0x18DBF
LegalItems = Legal.Pouch_Items_BW;
LegalKeyItems = Legal.Pouch_Key_BW;
LegalTMHMs = Legal.Pouch_TMHM_BW;
LegalMedicine = Legal.Pouch_Medicine_BW;
LegalBerries = Legal.Pouch_Berries_BW;
Personal = PersonalTable.BW;
break;
case GameVersion.B2W2: // B2W2
BattleBox = 0x20900;
Trainer2 = 0x21100;
EventConst = 0x1FF00;
EventFlag = EventConst + 0x35E;
Daycare = 0x20D00;
PokeDex = 0x21400;
PokeDexLanguageFlags = PokeDex + 0x328; // forme flags size is + 8 from bw with new formes (therians)
BattleSubway = 0x21B00;
CGearInfoOffset = 0x1C000;
CGearDataOffset = 0x52800;
EntreeForestOffset = 0x22A00;
// Inventory offsets are the same for each game.
OFS_PouchHeldItem = 0x18400; // 0x188D7
OFS_PouchKeyItem = 0x188D8; // 0x18A23
OFS_PouchTMHM = 0x18A24; // 0x18BD7
OFS_PouchMedicine = 0x18BD8; // 0x18C97
OFS_PouchBerry = 0x18C98; // 0x18DBF
LegalItems = Legal.Pouch_Items_BW;
LegalKeyItems = Legal.Pouch_Key_B2W2;
LegalTMHMs = Legal.Pouch_TMHM_BW;
LegalMedicine = Legal.Pouch_Medicine_BW;
LegalBerries = Legal.Pouch_Berries_BW;
Personal = PersonalTable.B2W2;
break;
}
HeldItems = Legal.HeldItems_BW;
Blocks = Version == GameVersion.BW ? BlockInfoNDS.BlocksBW : BlockInfoNDS.BlocksB2W2;
if (!Exportable)
ClearBoxes();
}
// Configuration
public override SaveFile Clone() => new SAV5((byte[])Data.Clone(), Version) {Footer = (byte[])Footer.Clone()};
public override int SIZE_STORED => PKX.SIZE_5STORED;
protected override int SIZE_PARTY => PKX.SIZE_5PARTY;
public override PKM BlankPKM => new PK5();
@ -116,95 +25,79 @@ namespace PKHeX.Core
public override int Generation => 5;
public override int OTLength => 7;
public override int NickLength => 10;
protected override int EventConstMax => (Version == GameVersion.BW ? 0x27C : 0x35E) >> 1;
protected override int EventFlagMax => (Version == GameVersion.BW ? 0x16C : 0x17F) << 3;
protected override int GiftCountMax => 12;
public override int MaxMoveID => Legal.MaxMoveID_5;
public override int MaxSpeciesID => Legal.MaxSpeciesID_5;
public override int MaxItemID => Version == GameVersion.BW ? Legal.MaxItemID_5_BW : Legal.MaxItemID_5_B2W2;
public override int MaxAbilityID => Legal.MaxAbilityID_5;
public override int MaxBallID => Legal.MaxBallID_5;
public override int MaxGameID => Legal.MaxGameID_5; // B2
protected SAV5(int size) : base(size)
{
Initialize();
ClearBoxes();
}
protected SAV5(byte[] data) : base(data)
{
Initialize();
}
public override GameVersion Version
{
get => (GameVersion)Game;
protected set => Game = (int)value;
}
private void Initialize()
{
// First blocks are always the same position/size
PCLayout = 0x0;
Box = 0x400;
Party = 0x18E00;
Trainer1 = 0x19400;
WondercardData = 0x1C800;
AdventureInfo = 0x1D900;
HeldItems = Legal.HeldItems_BW;
BoxLayout = new BoxLayout5(this, PCLayout);
MysteryBlock = new MysteryBlock5(this, WondercardData);
PlayerData = new PlayerData5(this, Trainer1);
}
// Blocks & Offsets
public readonly IReadOnlyList<BlockInfoNDS> Blocks;
protected IReadOnlyList<BlockInfoNDS> Blocks;
protected override void SetChecksums() => Blocks.SetChecksums(Data);
public override bool ChecksumsValid => Blocks.GetChecksumsValid(Data);
public override string ChecksumInfo => Blocks.GetChecksumInfo(Data);
private const int wcSeed = 0x1D290;
protected MyItem Items { get; set; }
public Zukan Zukan { get; protected set; }
public Misc5 MiscBlock { get; protected set; }
private MysteryBlock5 MysteryBlock { get; set; }
protected Daycare5 DaycareBlock { get; set; }
public BoxLayout5 BoxLayout { get; private set; }
public PlayerData5 PlayerData { get; private set; }
public BattleSubway5 BattleSubwayBlock { get; protected set; }
public readonly int CGearInfoOffset, CGearDataOffset;
private readonly int EntreeForestOffset;
private readonly int Trainer2, AdventureInfo, BattleSubway;
public readonly int PokeDexLanguageFlags;
protected int CGearInfoOffset;
protected int CGearDataOffset;
protected int EntreeForestOffset;
protected int Trainer2;
private int AdventureInfo;
protected int BattleSubway;
public int PokeDexLanguageFlags;
// Daycare
public override int DaycareSeedSize => 16;
public override int GetDaycareSlotOffset(int loc, int slot)
{
return Daycare + 4 + (0xE4 * slot);
}
public override string GetDaycareRNGSeed(int loc)
{
if (Version != GameVersion.B2W2)
return null;
var data = Data.Skip(Daycare + 0x1CC).Take(DaycareSeedSize/2).Reverse().ToArray();
return BitConverter.ToString(data).Replace("-", string.Empty);
}
public override uint? GetDaycareEXP(int loc, int slot)
{
return BitConverter.ToUInt32(Data, Daycare + 4 + 0xDC + (slot * 0xE4));
}
public override bool? IsDaycareOccupied(int loc, int slot)
{
return BitConverter.ToUInt32(Data, Daycare + (0xE4 * slot)) == 1;
}
public override void SetDaycareEXP(int loc, int slot, uint EXP)
{
BitConverter.GetBytes(EXP).CopyTo(Data, Daycare + 4 + 0xDC + (slot * 0xE4));
}
public override void SetDaycareOccupied(int loc, int slot, bool occupied)
{
BitConverter.GetBytes((uint)(occupied ? 1 : 0)).CopyTo(Data, Daycare + 0x1CC);
}
public override void SetDaycareRNGSeed(int loc, string seed)
{
if (Version != GameVersion.B2W2)
return;
Enumerable.Range(0, seed.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(seed.Substring(x, 2), 16))
.Reverse().ToArray().CopyTo(Data, Daycare + 0x1CC);
}
// Inventory
private readonly ushort[] LegalItems, LegalKeyItems, LegalTMHMs, LegalMedicine, LegalBerries;
public override InventoryPouch[] Inventory
{
get
{
InventoryPouch[] pouch =
{
new InventoryPouch4(InventoryType.Items, LegalItems, 999, OFS_PouchHeldItem),
new InventoryPouch4(InventoryType.KeyItems, LegalKeyItems, 1, OFS_PouchKeyItem),
new InventoryPouch4(InventoryType.TMHMs, LegalTMHMs, 1, OFS_PouchTMHM),
new InventoryPouch4(InventoryType.Medicine, LegalMedicine, 999, OFS_PouchMedicine),
new InventoryPouch4(InventoryType.Berries, LegalBerries, 999, OFS_PouchBerry),
};
return pouch.LoadAll(Data);
}
set => value.SaveAll(Data);
}
public override int DaycareSeedSize => Daycare5.DaycareSeedSize;
public override bool? IsDaycareOccupied(int loc, int slot) => DaycareBlock.IsOccupied(slot);
public override int GetDaycareSlotOffset(int loc, int slot) => DaycareBlock.GetOffset(slot);
public override uint? GetDaycareEXP(int loc, int slot) => DaycareBlock.GetEXP(slot);
public override string GetDaycareRNGSeed(int loc) => DaycareBlock.GetSeed()?.ToString("X16");
public override void SetDaycareEXP(int loc, int slot, uint EXP) => DaycareBlock.SetEXP(slot, EXP);
public override void SetDaycareOccupied(int loc, int slot, bool occupied) => DaycareBlock.SetOccupied(slot, occupied);
public override void SetDaycareRNGSeed(int loc, string seed) => DaycareBlock.SetSeed(seed);
// Storage
public override int PartyCount
@ -213,59 +106,20 @@ namespace PKHeX.Core
protected set => Data[Party + 4] = (byte)value;
}
public override int GetBoxOffset(int box)
{
return Box + (SIZE_STORED * box * 30) + (box * 0x10);
}
public override int GetBoxOffset(int box) => Box + (SIZE_STORED * box * 30) + (box * 0x10);
public override int GetPartyOffset(int slot) => Party + 8 + (SIZE_PARTY * slot);
public override int GetPartyOffset(int slot)
{
return Party + 8 + (SIZE_PARTY * slot);
}
public override string GetBoxName(int box)
{
if (box >= BoxCount)
return string.Empty;
return Util.TrimFromFFFF(Encoding.Unicode.GetString(Data, GetBoxNameOffset(box), 0x28));
}
public override void SetBoxName(int box, string value)
{
if (value.Length > 38)
return;
value += '\uFFFF';
var data = Encoding.Unicode.GetBytes(value.PadRight(0x14, '\0'));
SetData(data, GetBoxNameOffset(box));
}
private int GetBoxNameOffset(int box) => PCLayout + (0x28 * box) + 4;
protected override int GetBoxWallpaperOffset(int box)
{
return PCLayout + 0x3C4 + box;
}
public override int CurrentBox
{
get => Data[PCLayout];
set => Data[PCLayout] = (byte)value;
}
protected override int GetBoxWallpaperOffset(int box) => BoxLayout.GetBoxWallpaperOffset(box);
public override int GetBoxWallpaper(int box) => BoxLayout.GetBoxWallpaper(box);
public override void SetBoxWallpaper(int box, int value) => BoxLayout.SetBoxWallpaper(box, value);
public override string GetBoxName(int box) => BoxLayout[box];
public override void SetBoxName(int box, string value) => BoxLayout[box] = value;
public override int CurrentBox { get => BoxLayout.CurrentBox; set => BoxLayout.CurrentBox = value; }
public override bool BattleBoxLocked
{
get => BattleBox >= 0 && Data[BattleBox + 0x358] != 0; // wifi/live
set { }
}
protected override PKM GetPKM(byte[] data)
{
return new PK5(data);
}
protected override byte[] DecryptPKM(byte[] data)
{
return PKX.DecryptArray45(data);
get => Data[BattleBox + 0x358] != 0; // wifi/live
set => Data[BattleBox + 0x358] = (byte)(value ? 1 : 0);
}
protected override void SetPKM(PKM pkm)
@ -277,268 +131,26 @@ namespace PKHeX.Core
pkm.RefreshChecksum();
}
// Mystery Gift
public override MysteryGiftAlbum GiftAlbum
{
get
{
uint seed = BitConverter.ToUInt32(Data, wcSeed);
MysteryGiftAlbum Info = new MysteryGiftAlbum { Seed = seed };
byte[] wcData = GetData(WondercardData, 0xA90); // Encrypted, Decrypt
PKX.CryptArray(wcData, seed);
Info.Flags = new bool[GiftFlagMax];
Info.Gifts = new MysteryGift[GiftCountMax];
// 0x100 Bytes for Used Flags
for (int i = 0; i < GiftFlagMax; i++)
Info.Flags[i] = (wcData[i/8] >> i%8 & 0x1) == 1;
// 12 PGFs
for (int i = 0; i < Info.Gifts.Length; i++)
Info.Gifts[i] = new PGF(wcData.Skip(0x100 + (i *PGF.Size)).Take(PGF.Size).ToArray());
return Info;
}
set
{
byte[] wcData = new byte[0xA90];
// Toss back into byte[]
for (int i = 0; i < value.Flags.Length; i++)
{
if (value.Flags[i])
wcData[i/8] |= (byte)(1 << (i & 7));
}
for (int i = 0; i < value.Gifts.Length; i++)
value.Gifts[i].Data.CopyTo(wcData, 0x100 + (i *PGF.Size));
// Decrypted, Encrypt
PKX.CryptArray(wcData, value.Seed);
// Write Back
wcData.CopyTo(Data, WondercardData);
BitConverter.GetBytes(value.Seed).CopyTo(Data, wcSeed);
}
}
protected override bool[] MysteryGiftReceivedFlags { get => Array.Empty<bool>(); set { } }
protected override MysteryGift[] MysteryGiftCards { get => Array.Empty<MysteryGift>(); set { } }
// Trainer Info
public override string OT
{
get => GetString(Trainer1 + 0x4, 16);
set => SetString(value, OTLength).CopyTo(Data, Trainer1 + 0x4);
}
public override int TID
{
get => BitConverter.ToUInt16(Data, Trainer1 + 0x14 + 0);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Trainer1 + 0x14 + 0);
}
public override int SID
{
get => BitConverter.ToUInt16(Data, Trainer1 + 0x14 + 2);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Trainer1 + 0x14 + 2);
}
public override uint Money
{
get => BitConverter.ToUInt32(Data, Trainer2);
set => BitConverter.GetBytes(value).CopyTo(Data, Trainer2);
}
public override int Gender
{
get => Data[Trainer1 + 0x21];
set => Data[Trainer1 + 0x21] = (byte)value;
}
public override int Language
{
get => Data[Trainer1 + 0x1E];
set => Data[Trainer1 + 0x1E] = (byte)value;
}
public override int Game
{
get => Data[Trainer1 + 0x1F];
set => Data[Trainer1 + 0x1F] = (byte)value;
}
public int Badges
{
get => Data[Trainer2 + 0x4];
set => Data[Trainer2 + 0x4] = (byte)value;
}
public int M
{
get => BitConverter.ToInt32(Data, Trainer1 + 0x180);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Trainer1 + 0x180);
}
public int X
{
get => BitConverter.ToUInt16(Data, Trainer1 + 0x186);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Trainer1 + 0x186);
}
public int Z
{
get => BitConverter.ToUInt16(Data, Trainer1 + 0x18A);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Trainer1 + 0x18A);
}
public int Y
{
get => BitConverter.ToUInt16(Data, Trainer1 + 0x18E);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Trainer1 + 0x18E);
}
public override int PlayedHours
{
get => BitConverter.ToUInt16(Data, Trainer1 + 0x24);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Trainer1 + 0x24);
}
public override int PlayedMinutes
{
get => Data[Trainer1 + 0x24 + 2];
set => Data[Trainer1 + 0x24 + 2] = (byte)value;
}
public override int PlayedSeconds
{
get => Data[Trainer1 + 0x24 + 3];
set => Data[Trainer1 + 0x24 + 3] = (byte)value;
}
// Player Data
public override string OT { get => PlayerData.OT; set => PlayerData.OT = value; }
public override int TID { get => PlayerData.TID; set => PlayerData.TID = value; }
public override int SID { get => PlayerData.SID; set => PlayerData.SID = value; }
public override int Language { get => PlayerData.Language; set => PlayerData.Language = value; }
public override int Game { get => PlayerData.Game; set => PlayerData.Game = value; }
public override int Gender { get => PlayerData.Gender; set => PlayerData.Gender = value; }
public override int PlayedHours { get => PlayerData.PlayedHours; set => PlayerData.PlayedHours = value; }
public override int PlayedMinutes { get => PlayerData.PlayedMinutes; set => PlayerData.PlayedMinutes = value; }
public override int PlayedSeconds { get => PlayerData.PlayedSeconds; set => PlayerData.PlayedSeconds = value; }
public override uint Money { get => MiscBlock.Money; set => MiscBlock.Money = value; }
public override uint SecondsToStart { get => BitConverter.ToUInt32(Data, AdventureInfo + 0x34); set => BitConverter.GetBytes(value).CopyTo(Data, AdventureInfo + 0x34); }
public override uint SecondsToFame { get => BitConverter.ToUInt32(Data, AdventureInfo + 0x3C); set => BitConverter.GetBytes(value).CopyTo(Data, AdventureInfo + 0x3C); }
public override MysteryGiftAlbum GiftAlbum { get => MysteryBlock.GiftAlbum; set => MysteryBlock.GiftAlbum = value; }
public override InventoryPouch[] Inventory { get => Items.Inventory; set => Items.Inventory = value; }
public int BP
{
get => BitConverter.ToUInt16(Data, BattleSubway);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, BattleSubway);
}
public ushort GetPWTRecord(int id) => GetPWTRecord((PWTRecordID) id);
public ushort GetPWTRecord(PWTRecordID id)
{
if (id < PWTRecordID.Normal || id > PWTRecordID.MixMaster)
throw new ArgumentException(nameof(id));
int ofs = 0x2375C + ((int)id * 2);
return BitConverter.ToUInt16(Data, ofs);
}
public void SetPWTRecord(int id, ushort value) => SetPWTRecord((PWTRecordID) id, value);
public void SetPWTRecord(PWTRecordID id, ushort value)
{
if (id < PWTRecordID.Normal || id > PWTRecordID.MixMaster)
throw new ArgumentException(nameof(id));
int ofs = 0x2375C + ((int)id * 2);
SetData(BitConverter.GetBytes(value), ofs);
}
protected override void SetDex(PKM pkm)
{
if (pkm.Species == 0)
return;
if (pkm.Species > MaxSpeciesID)
return;
if (Version == GameVersion.Invalid)
return;
if (PokeDex < 0)
return;
const int brSize = 0x54;
int bit = pkm.Species - 1;
int gender = pkm.Gender % 2; // genderless -> male
int shiny = pkm.IsShiny ? 1 : 0;
int shift = (shiny * 2) + gender + 1;
int shiftoff = (shiny * brSize * 2) + (gender * brSize) + brSize;
int ofs = PokeDex + 0x8 + (bit >> 3);
// Set the Species Owned Flag
Data[ofs + (brSize * 0)] |= (byte)(1 << (bit % 8));
// Set the [Species/Gender/Shiny] Seen Flag
Data[PokeDex + 0x8 + shiftoff + (bit / 8)] |= (byte)(1 << (bit&7));
// Set the Display flag if none are set
bool Displayed = false;
Displayed |= (Data[ofs + (brSize * 5)] & (byte)(1 << (bit&7))) != 0;
Displayed |= (Data[ofs + (brSize * 6)] & (byte)(1 << (bit&7))) != 0;
Displayed |= (Data[ofs + (brSize * 7)] & (byte)(1 << (bit&7))) != 0;
Displayed |= (Data[ofs + (brSize * 8)] & (byte)(1 << (bit&7))) != 0;
if (!Displayed) // offset is already biased by brSize, reuse shiftoff but for the display flags.
Data[ofs + (brSize *(shift + 4))] |= (byte)(1 << (bit&7));
// Set the Language
if (bit < 493) // shifted by 1, Gen5 species do not have international language bits
{
int lang = pkm.Language - 1; if (lang > 5) lang--; // 0-6 language vals
if (lang < 0) lang = 1;
Data[PokeDexLanguageFlags + (((bit * 7) + lang)>>3)] |= (byte)(1 << (((bit * 7) + lang) & 7));
}
// Formes
int fc = Personal[pkm.Species].FormeCount;
int f = B2W2 ? DexFormUtil.GetDexFormIndexB2W2(pkm.Species, fc) : DexFormUtil.GetDexFormIndexBW(pkm.Species, fc);
if (f < 0) return;
int FormLen = B2W2 ? 0xB : 0x9;
int FormDex = PokeDex + 0x8 + (brSize * 9);
bit = f + pkm.AltForm;
// Set Form Seen Flag
Data[FormDex + (FormLen * shiny) + (bit>>3)] |= (byte)(1 << (bit&7));
// Set Displayed Flag if necessary, check all flags
for (int i = 0; i < fc; i++)
{
bit = f + i;
if ((Data[FormDex + (FormLen * 2) + (bit>>3)] & (byte)(1 << (bit&7))) != 0) // Nonshiny
return; // already set
if ((Data[FormDex + (FormLen * 3) + (bit>>3)] & (byte)(1 << (bit&7))) != 0) // Shiny
return; // already set
}
bit = f + pkm.AltForm;
Data[FormDex + (FormLen * (2 + shiny)) + (bit>>3)] |= (byte)(1 << (bit&7));
}
public override bool GetCaught(int species)
{
int bit = species - 1;
int bd = bit >> 3; // div8
int bm = bit & 7; // mod8
int ofs = PokeDex // Raw Offset
+ 0x08; // Magic + Flags
return (1 << bm & Data[ofs + bd]) != 0;
}
public override bool GetSeen(int species)
{
const int brSize = 0x54;
int bit = species - 1;
int bd = bit >> 3; // div8
int bm = bit & 7; // mod8
int ofs = PokeDex // Raw Offset
+ 0x08; // Magic + Flags
for (int i = 1; i <= 4; i++)
{
if ((1 << bm & Data[ofs + bd + (i * brSize)]) != 0)
return true;
}
return false;
}
protected override void SetDex(PKM pkm) => Zukan.SetDex(pkm);
public override bool GetCaught(int species) => Zukan.GetCaught(species);
public override bool GetSeen(int species) => Zukan.GetSeen(species);
public override string GetString(byte[] data, int offset, int length) => StringConverter.GetString5(data, offset, length);
@ -562,7 +174,7 @@ namespace PKHeX.Core
{
get
{
byte[] data = new byte[0x2600];
byte[] data = new byte[CGearBackground.SIZE_CGB];
if (CGearSkinPresent)
Array.Copy(Data, CGearDataOffset, data, 0, data.Length);
return data;

View file

@ -0,0 +1,40 @@
namespace PKHeX.Core
{
public class SAV5B2W2 : SAV5
{
public SAV5B2W2() : base(SaveUtil.SIZE_G5RAW) => Initialize();
public SAV5B2W2(byte[] data) : base(data) => Initialize();
public override SaveFile Clone() => new SAV5B2W2((byte[])Data.Clone()) { Footer = (byte[])Footer.Clone() };
protected override int EventConstMax => 0x1AF; // this doesn't seem right?
protected override int EventFlagMax => 0xBF8;
public override int MaxItemID => Legal.MaxItemID_5_B2W2;
private void Initialize()
{
Blocks = BlockInfoNDS.BlocksB2W2;
Personal = PersonalTable.B2W2;
Items = new MyItem5B2W2(this, 0x18400);
BattleBox = 0x20900;
Trainer2 = 0x21100;
EventConst = 0x1FF00;
EventFlag = EventConst + 0x35E;
Daycare = 0x20D00;
PokeDex = 0x21400;
PokeDexLanguageFlags = PokeDex + 0x328; // forme flags size is + 8 from bw with new formes (therians)
BattleSubway = 0x21B00;
CGearInfoOffset = 0x1C000;
CGearDataOffset = 0x52800;
EntreeForestOffset = 0x22A00;
Zukan = new Zukan5(this, PokeDex, PokeDexLanguageFlags);
DaycareBlock = new Daycare5(this, Daycare);
MiscBlock = new Misc5(this, Trainer2);
PWTBlock = new PWTBlock5(this, 0x23700);
DaycareBlock = new Daycare5(this, Daycare);
BattleSubwayBlock = new BattleSubway5(this, BattleSubway);
}
public PWTBlock5 PWTBlock { get; private set; }
}
}

View file

@ -0,0 +1,37 @@
namespace PKHeX.Core
{
public class SAV5BW : SAV5
{
public SAV5BW() : base(SaveUtil.SIZE_G5RAW) => Initialize();
public SAV5BW(byte[] data) : base(data) => Initialize();
public override SaveFile Clone() => new SAV5BW((byte[])Data.Clone()) { Footer = (byte[])Footer.Clone() };
protected override int EventConstMax => 0x13E;
protected override int EventFlagMax => 0xB60;
public override int MaxItemID => Legal.MaxItemID_5_BW;
private void Initialize()
{
Blocks = BlockInfoNDS.BlocksBW;
Personal = PersonalTable.BW;
Items = new MyItem5BW(this, 0x18400);
BattleBox = 0x20A00;
Trainer2 = 0x21200;
EventConst = 0x20100;
EventFlag = EventConst + 0x27C;
Daycare = 0x20E00;
PokeDex = 0x21600;
PokeDexLanguageFlags = PokeDex + 0x320;
BattleSubway = 0x21D00;
CGearInfoOffset = 0x1C000;
CGearDataOffset = 0x52000;
EntreeForestOffset = 0x22C00;
MiscBlock = new Misc5(this, Trainer2);
Zukan = new Zukan5(this, PokeDex, PokeDexLanguageFlags);
DaycareBlock = new Daycare5(this, Daycare);
BattleSubwayBlock = new BattleSubway5(this, BattleSubway);
// Inventory offsets are the same for each game.
}
}
}

File diff suppressed because it is too large Load diff

351
PKHeX.Core/Saves/SAV6AO.cs Normal file
View file

@ -0,0 +1,351 @@
using System;
using System.Linq;
using System.Text;
namespace PKHeX.Core
{
/// <summary>
/// Generation 6 <see cref="SaveFile"/> object for <see cref="GameVersion.ORAS"/>.
/// </summary>
/// <inheritdoc cref="SAV6" />
public sealed class SAV6AO : SAV6, IPokePuff, IOPower, ILink
{
public SAV6AO(byte[] data) : base(data, BlocksAO, boAO) => Initialize();
public SAV6AO() : base(SaveUtil.SIZE_G6ORAS, BlocksAO, boAO) => Initialize();
public override SaveFile Clone() => new SAV6AO((byte[])Data.Clone());
public override int MaxMoveID => Legal.MaxMoveID_6_AO;
public override int MaxItemID => Legal.MaxItemID_6_AO;
public override int MaxAbilityID => Legal.MaxAbilityID_6_AO;
private const int boAO = SaveUtil.SIZE_G6ORAS - 0x200;
public static readonly BlockInfo[] BlocksAO =
{
new BlockInfo6 (boAO, 00, 0x00000, 0x002C8),
new BlockInfo6 (boAO, 01, 0x00400, 0x00B90),
new BlockInfo6 (boAO, 02, 0x01000, 0x0002C),
new BlockInfo6 (boAO, 03, 0x01200, 0x00038),
new BlockInfo6 (boAO, 04, 0x01400, 0x00150),
new BlockInfo6 (boAO, 05, 0x01600, 0x00004),
new BlockInfo6 (boAO, 06, 0x01800, 0x00008),
new BlockInfo6 (boAO, 07, 0x01A00, 0x001C0),
new BlockInfo6 (boAO, 08, 0x01C00, 0x000BE),
new BlockInfo6 (boAO, 09, 0x01E00, 0x00024),
new BlockInfo6 (boAO, 10, 0x02000, 0x02100),
new BlockInfo6 (boAO, 11, 0x04200, 0x00130),
new BlockInfo6 (boAO, 12, 0x04400, 0x00440),
new BlockInfo6 (boAO, 13, 0x04A00, 0x00574),
new BlockInfo6 (boAO, 14, 0x05000, 0x04E28),
new BlockInfo6 (boAO, 15, 0x0A000, 0x04E28),
new BlockInfo6 (boAO, 16, 0x0F000, 0x04E28),
new BlockInfo6 (boAO, 17, 0x14000, 0x00170),
new BlockInfo6 (boAO, 18, 0x14200, 0x0061C),
new BlockInfo6 (boAO, 19, 0x14A00, 0x00504),
new BlockInfo6 (boAO, 20, 0x15000, 0x011CC),
new BlockInfo6 (boAO, 21, 0x16200, 0x00644),
new BlockInfo6 (boAO, 22, 0x16A00, 0x00104),
new BlockInfo6 (boAO, 23, 0x16C00, 0x00004),
new BlockInfo6 (boAO, 24, 0x16E00, 0x00420),
new BlockInfo6 (boAO, 25, 0x17400, 0x00064),
new BlockInfo6 (boAO, 26, 0x17600, 0x003F0),
new BlockInfo6 (boAO, 27, 0x17A00, 0x0070C),
new BlockInfo6 (boAO, 28, 0x18200, 0x00180),
new BlockInfo6 (boAO, 29, 0x18400, 0x00004),
new BlockInfo6 (boAO, 30, 0x18600, 0x0000C),
new BlockInfo6 (boAO, 31, 0x18800, 0x00048),
new BlockInfo6 (boAO, 32, 0x18A00, 0x00054),
new BlockInfo6 (boAO, 33, 0x18C00, 0x00644),
new BlockInfo6 (boAO, 34, 0x19400, 0x005C8),
new BlockInfo6 (boAO, 35, 0x19A00, 0x002F8),
new BlockInfo6 (boAO, 36, 0x19E00, 0x01B40),
new BlockInfo6 (boAO, 37, 0x1BA00, 0x001F4),
new BlockInfo6 (boAO, 38, 0x1BC00, 0x003E0),
new BlockInfo6 (boAO, 39, 0x1C000, 0x00216),
new BlockInfo6 (boAO, 40, 0x1C400, 0x00640),
new BlockInfo6 (boAO, 41, 0x1CC00, 0x01A90),
new BlockInfo6 (boAO, 42, 0x1E800, 0x00400),
new BlockInfo6 (boAO, 43, 0x1EC00, 0x00618),
new BlockInfo6 (boAO, 44, 0x1F400, 0x0025C),
new BlockInfo6 (boAO, 45, 0x1F800, 0x00834),
new BlockInfo6 (boAO, 46, 0x20200, 0x00318),
new BlockInfo6 (boAO, 47, 0x20600, 0x007D0),
new BlockInfo6 (boAO, 48, 0x20E00, 0x00C48),
new BlockInfo6 (boAO, 49, 0x21C00, 0x00078),
new BlockInfo6 (boAO, 50, 0x21E00, 0x00200),
new BlockInfo6 (boAO, 51, 0x22000, 0x00C84),
new BlockInfo6 (boAO, 52, 0x22E00, 0x00628),
new BlockInfo6 (boAO, 53, 0x23600, 0x00400),
new BlockInfo6 (boAO, 54, 0x23A00, 0x07AD0),
new BlockInfo6 (boAO, 55, 0x2B600, 0x078B0),
new BlockInfo6 (boAO, 56, 0x33000, 0x34AD0),
new BlockInfo6 (boAO, 57, 0x67C00, 0x0E058),
};
private void Initialize()
{
/* 00: 00000-002C8, 002C8 */ // Puff = 0x00000;
/* 01: 00400-00F90, 00B90 */ // MyItem = 0x00400; // Bag
/* 02: 01000-0102C, 0002C */ // ItemInfo = 0x1000; // Select Bound Items
/* 03: 01200-01238, 00038 */ // GameTime = 0x01200;
/* 04: 01400-01550, 00150 */ Trainer1 = 0x01400; // Situation
/* 05: 01600-01604, 00004 */ // RandomGroup (rand seeds)
/* 06: 01800-01808, 00008 */ PlayTime = 0x1800; // PlayTime
/* 07: 01A00-01BC0, 001C0 */ Accessories = 0x1A00; // Fashion
/* 08: 01C00-01CBE, 000BE */ // amie minigame records
/* 09: 01E00-01E24, 00024 */ // temp variables (u32 id + 32 u8)
/* 10: 02000-04100, 02100 */ // FieldMoveModelSave
/* 11: 04200-04330, 00130 */ Trainer2 = 0x04200; // Misc
/* 12: 04400-04840, 00440 */ PCLayout = 0x04400; // BOX
/* 13: 04A00-04F74, 00574 */ BattleBox = 0x04A00; // BattleBox
/* 14: 05000-09E28, 04E28 */ PSS = 0x05000;
/* 15: 0A000-0EE28, 04E28 */ // PSS2
/* 16: 0F000-13E28, 04E28 */ // PSS3
/* 17: 14000-14170, 00170 */ // MyStatus
/* 18: 14200-1481C, 0061C */ Party = 0x14200; // PokePartySave
/* 19: 14A00-14F04, 00504 */ EventConst = 0x14A00; // EventWork
/* 20: 15000-161CC, 011CC */ PokeDex = 0x15000; // ZukanData
/* 21: 16200-16844, 00644 */ // hologram clips
/* 22: 16A00-16B04, 00104 */ Fused = 0x16A00; // UnionPokemon
/* 23: 16C00-16C04, 00004 */ // ConfigSave
/* 24: 16E00-17220, 00420 */ // Amie decoration stuff
/* 25: 17400-17464, 00064 */ // OPower = 0x17400;
/* 26: 17600-179F0, 003F0 */ // Strength Rock position (xyz float: 84 entries, 12bytes/entry)
/* 27: 17A00-1810C, 0070C */ // Trainer PR Video
/* 28: 18200-18380, 00180 */ GTS = 0x18200; // GtsData
/* 29: 18400-18404, 00004 */ // Packed Menu Bits
/* 30: 18600-1860C, 0000C */ // PSS Profile Q&A (6*questions, 6*answer)
/* 31: 18800-18848, 00048 */ // Repel Info, (Swarm?) and other overworld info (roamer)
/* 32: 18A00-18A54, 00054 */ // BOSS data fetch history (serial/mystery gift), 4byte intro & 20*4byte entries
/* 33: 18C00-19244, 00644 */ // Streetpass history
/* 34: 19400-199C8, 005C8 */ // LiveMatchData/BattleSpotData
/* 35: 19A00-19CF8, 002F8 */ // MAC Address & Network Connection Logging (0x98 per entry, 5 entries)
/* 36: 19E00-1B940, 01B40 */ HoF = 0x19E00; // Dendou
/* 37: 1BA00-1BBF4, 001F4 */ MaisonStats = 0x1BBC0; // BattleInstSave
/* 38: 1BC00-1BFE0, 003E0 */ Daycare = 0x1BC00; // Sodateya
/* 39: 1C000-1C216, 00216 */ // BattleInstSave
/* 40: 1C400-1CA40, 00640 */ BerryField = 0x1C400;
/* 41: 1CC00-1E690, 01A90 */ WondercardFlags = 0x1CC00; // MysteryGiftSave
/* 42: 1E800-1EC00, 00400 */ // Storyline Records
/* 43: 1EC00-1F218, 00618 */ SUBE = 0x1D890; // PokeDiarySave
/* 44: 1F400-1F65C, 0025C */ // Record = 0x1F400;
/* 45: 1F800-20034, 00834 */ // Friend Safari (0x15 per entry, 100 entries)
/* 46: 20200-20518, 00318 */ SuperTrain = 0x20200;
/* 47: 20600-20DD0, 007D0 */ // Unused (lmao)
/* 48: 20E00-21A48, 00C48 */ LinkInfo = 0x20E00;
/* 49: 21C00-21C78, 00078 */ // PSS usage info
/* 50: 21E00-22000, 00200 */ // GameSyncSave
/* 51: 22000-22C84, 00C84 */ // PSS Icon (bool32 data present, 40x40 u16 pic, unused)
/* 52: 22E00-23428, 00628 */ // ValidationSave (updatabale Public Key for legal check api calls)
/* 53: 23600-23A00, 00400 */ Contest = 0x23600;
/* 54: 23A00-2B4D0, 07AD0 */ SecretBase = 0x23A00;
/* 55: 2B600-32EB0, 078B0 */ EonTicket = 0x319B8;
/* 56: 33000-67AD0, 34AD0 */ Box = 0x33000;
/* 57: 67C00-75C58, 0E058 */ JPEG = 0x67C00;
Items = new MyItem6XY(this, 0x00400);
PuffBlock = new Puff6(this, 0x0000);
GameTime = new GameTime6(this, 0x01200);
Situation = new Situation6(this, 0x01400);
Played = new PlayTime6(this, 0x01800);
BoxLayout = new BoxLayout6(this, 0x04400);
Status = new MyStatus6(this, 0x14000);
Zukan = new Zukan6(this, 0x15000, 0x15000 + 0x400);
OPowerBlock = new OPower6(this, 0x17400);
MysteryBlock = new MysteryBlock6(this, 0x1CC00);
Records = new Record6(this, 0x1F400, Core.Records.MaxType_AO);
Sango = new SangoInfoBlock(this, 0x2B600);
EventFlag = EventConst + 0x2FC;
PokeDexLanguageFlags = PokeDex + 0x400;
Spinda = PokeDex + 0x680;
EncounterCount = PokeDex + 0x686;
WondercardData = WondercardFlags + 0x100;
Daycare2 = Daycare + 0x1F0;
HeldItems = Legal.HeldItem_XY;
Personal = PersonalTable.XY;
}
public int EonTicket { get; private set; } = int.MinValue;
public int Contest { get; private set; } = int.MinValue;
public Zukan6 Zukan { get; private set; }
public Puff6 PuffBlock { get; private set; }
public OPower6 OPowerBlock { get; private set; }
public BoxLayout6 BoxLayout { get; private set; }
public MysteryBlock6 MysteryBlock { get; private set; }
public SangoInfoBlock Sango { get; set; }
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)); }
public override GameVersion Version
{
get
{
switch (Game)
{
case (int)GameVersion.AS: return GameVersion.AS;
case (int)GameVersion.OR: return GameVersion.OR;
}
return GameVersion.Invalid;
}
}
public override bool GetCaught(int species) => Zukan.GetCaught(species);
public override bool GetSeen(int species) => Zukan.GetSeen(species);
public override void SetSeen(int species, bool seen) => Zukan.SetSeen(species, seen);
public override void SetCaught(int species, bool caught) => Zukan.SetCaught(species, caught);
protected override void SetDex(PKM pkm)
{
Zukan.SetDex(pkm);
int index = pkm.Species - 1;
if ((uint)index >= (uint)MaxSpeciesID)
return;
// Set DexNav count (only if not encountered previously)
if (GetEncounterCount(index) == 0)
SetEncounterCount(index, 1);
}
// Daycare
public override int DaycareSeedSize => 16;
public override bool HasTwoDaycares => true;
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? IsDaycareOccupied(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 string GetDaycareRNGSeed(int loc)
{
int ofs = loc == 0 ? Daycare : Daycare2;
if (ofs <= 0)
return null;
var data = Data.Skip(ofs + 0x1E8).Take(DaycareSeedSize / 2).Reverse().ToArray();
return BitConverter.ToString(data).Replace("-", string.Empty);
}
public override bool? IsDaycareHasEgg(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, string seed)
{
if (loc != 0)
return;
if (Daycare < 0)
return;
if (seed == null)
return;
if (seed.Length > DaycareSeedSize)
return;
Util.GetBytesFromHexString(seed).CopyTo(Data, Daycare + 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 override string JPEGTitle => HasJPPEGData ? string.Empty : Util.TrimFromZero(Encoding.Unicode.GetString(Data, JPEG, 0x1A));
public override byte[] JPEGData => HasJPPEGData ? Array.Empty<byte>() : GetData(JPEG + 0x54, 0xE004);
private bool HasJPPEGData => Data[JPEG + 0x54] == 0xFF;
protected override bool[] MysteryGiftReceivedFlags { get => MysteryBlock.MysteryGiftReceivedFlags; set => MysteryBlock.MysteryGiftReceivedFlags = value; }
protected override MysteryGift[] MysteryGiftCards { get => MysteryBlock.MysteryGiftCards; set => MysteryBlock.MysteryGiftCards = value; }
// Gym History
public ushort[][] GymTeams
{
get
{
if (SUBE < 0 || ORASDEMO)
return Array.Empty<ushort[]>(); // no gym data
const int teamsize = 2 * 6; // 2byte/species, 6species/team
const int size = teamsize * 8; // 8 gyms
int ofs = SUBE - size - 4;
var data = GetData(ofs, size);
ushort[][] teams = new ushort[8][];
for (int i = 0; i < teams.Length; i++)
Buffer.BlockCopy(data, teamsize * i, teams[i] = new ushort[6], 0, teamsize);
return teams;
}
set
{
if (SUBE < 0 || ORASDEMO)
return; // no gym data
const int teamsize = 2 * 6; // 2byte/species, 6species/team
const int size = teamsize * 8; // 8 gyms
int ofs = SUBE - size - 4;
byte[] data = new byte[size];
for (int i = 0; i < value.Length; i++)
Buffer.BlockCopy(value[i], 0, data, teamsize * i, teamsize);
SetData(data, ofs);
}
}
public byte[] LinkBlock
{
get => GetData(LinkInfo, 0xC48);
set
{
if (value.Length != 0xC48)
throw new ArgumentException(nameof(value));
SetData(value, LinkInfo);
}
}
public override int CurrentBox { get => BoxLayout.CurrentBox; set => BoxLayout.CurrentBox = value; }
protected override int GetBoxWallpaperOffset(int box) => BoxLayout.GetBoxWallpaperOffset(box);
public override int BoxesUnlocked { get => BoxLayout.BoxesUnlocked; set => BoxLayout.BoxesUnlocked = value; }
public override byte[] BoxFlags { get => BoxLayout.BoxFlags; set => BoxLayout.BoxFlags = value; }
}
}

View file

@ -0,0 +1,83 @@
namespace PKHeX.Core
{
/// <summary>
/// Generation 6 <see cref="SaveFile"/> object for <see cref="GameVersion.ORASDEMO"/>.
/// </summary>
/// <inheritdoc cref="SAV6" />
public sealed class SAV6AODemo : SAV6
{
public SAV6AODemo(byte[] data) : base(data, BlocksAODemo, boAOdemo) => Initialize();
public SAV6AODemo() : base(SaveUtil.SIZE_G6ORASDEMO, BlocksAODemo, boAOdemo) => Initialize();
public override SaveFile Clone() => new SAV6AODemo((byte[])Data.Clone());
public override int MaxMoveID => Legal.MaxMoveID_6_AO;
public override int MaxItemID => Legal.MaxItemID_6_AO;
public override int MaxAbilityID => Legal.MaxAbilityID_6_AO;
private const int boAOdemo = SaveUtil.SIZE_G6ORASDEMO - 0x200;
public static readonly BlockInfo[] BlocksAODemo =
{
new BlockInfo6 (boAOdemo, 00, 0x00000, 0x00B90),
new BlockInfo6 (boAOdemo, 01, 0x00C00, 0x0002C),
new BlockInfo6 (boAOdemo, 02, 0x00E00, 0x00038),
new BlockInfo6 (boAOdemo, 03, 0x01000, 0x00150),
new BlockInfo6 (boAOdemo, 04, 0x01200, 0x00004),
new BlockInfo6 (boAOdemo, 05, 0x01400, 0x00008),
new BlockInfo6 (boAOdemo, 06, 0x01600, 0x00024),
new BlockInfo6 (boAOdemo, 07, 0x01800, 0x02100),
new BlockInfo6 (boAOdemo, 08, 0x03A00, 0x00130),
new BlockInfo6 (boAOdemo, 09, 0x03C00, 0x00170),
new BlockInfo6 (boAOdemo, 10, 0x03E00, 0x0061C),
new BlockInfo6 (boAOdemo, 11, 0x04600, 0x00504),
new BlockInfo6 (boAOdemo, 12, 0x04C00, 0x00004),
new BlockInfo6 (boAOdemo, 13, 0x04E00, 0x00048),
new BlockInfo6 (boAOdemo, 14, 0x05000, 0x00400),
new BlockInfo6 (boAOdemo, 15, 0x05400, 0x0025C),
};
private void Initialize()
{
/* 00: */ // MyItem = 0x00000; // MyItem // Bag
/* 01: */ // ItemInfo = 0x00C00; // ItemInfo6
/* 02: */ // GameTime = 0x00E00; // GameTime
/* 03: */ // Trainer1 = 0x01000; // Situation
/* 04: */ // = 0x01200; // [00004] RandomGroup (rand seeds)
/* 05: */ // PlayTime = 0x01400; // PlayTime
/* 06: */ // = 0x01600; // [00024] temp variables (u32 id + 32 u8)
/* 07: */ // = 0x01800; // [02100] FieldMoveModelSave
/* 08: */ Trainer2 = 0x03A00; // Misc
/* 09: */ // = 0x03C00; // MyStatus
/* 10: */ Party = 0x03E00; // PokePartySave
/* 11: */ EventConst = 0x04600; // EventWork
/* 12: */ // = 0x04C00; // [00004] Packed Menu Bits
/* 13: */ // = 0x04E00; // [00048] Repel Info, (Swarm?) and other overworld info (roamer)
/* 14: */ SUBE = 0x05000; // PokeDiarySave
/* 15: */ // Record = 0x05400; // Record
Items = new MyItem6AO( this, 0x00000);
ItemInfo = new ItemInfo6( this, 0x00C00);
GameTime = new GameTime6( this, 0x00E00);
Situation = new Situation6(this, 0x01000);
Played = new PlayTime6( this, 0x01400);
Status = new MyStatus6( this, 0x03C00);
Records = new Record6(this, 0x05400, Core.Records.MaxType_AO);
EventFlag = EventConst + 0x2FC;
HeldItems = Legal.HeldItem_AO;
Personal = PersonalTable.AO;
}
public override GameVersion Version
{
get
{
switch (Game)
{
case (int)GameVersion.AS: return GameVersion.AS;
case (int)GameVersion.OR: return GameVersion.OR;
}
return GameVersion.Invalid;
}
}
}
}

305
PKHeX.Core/Saves/SAV6XY.cs Normal file
View file

@ -0,0 +1,305 @@
using System;
using System.Text;
namespace PKHeX.Core
{
/// <summary>
/// Generation 6 <see cref="SaveFile"/> object for <see cref="GameVersion.XY"/>.
/// </summary>
/// <inheritdoc cref="SAV6" />
public sealed class SAV6XY : SAV6, IPokePuff, IOPower, ILink
{
public SAV6XY(byte[] data) : base(data, BlocksXY, boXY) => Initialize();
public SAV6XY() : base(SaveUtil.SIZE_G6XY, BlocksXY, boXY) => Initialize();
public override SaveFile Clone() => new SAV6XY((byte[])Data.Clone());
public override int MaxMoveID => Legal.MaxMoveID_6_XY;
public override int MaxItemID => Legal.MaxItemID_6_XY;
public override int MaxAbilityID => Legal.MaxAbilityID_6_XY;
private const int boXY = SaveUtil.SIZE_G6XY - 0x200;
public static readonly BlockInfo[] BlocksXY =
{
new BlockInfo6(boXY, 00, 0x00000, 0x002C8),
new BlockInfo6(boXY, 01, 0x00400, 0x00B88),
new BlockInfo6(boXY, 02, 0x01000, 0x0002C),
new BlockInfo6(boXY, 03, 0x01200, 0x00038),
new BlockInfo6(boXY, 04, 0x01400, 0x00150),
new BlockInfo6(boXY, 05, 0x01600, 0x00004),
new BlockInfo6(boXY, 06, 0x01800, 0x00008),
new BlockInfo6(boXY, 07, 0x01A00, 0x001C0),
new BlockInfo6(boXY, 08, 0x01C00, 0x000BE),
new BlockInfo6(boXY, 09, 0x01E00, 0x00024),
new BlockInfo6(boXY, 10, 0x02000, 0x02100),
new BlockInfo6(boXY, 11, 0x04200, 0x00140),
new BlockInfo6(boXY, 12, 0x04400, 0x00440),
new BlockInfo6(boXY, 13, 0x04A00, 0x00574),
new BlockInfo6(boXY, 14, 0x05000, 0x04E28),
new BlockInfo6(boXY, 15, 0x0A000, 0x04E28),
new BlockInfo6(boXY, 16, 0x0F000, 0x04E28),
new BlockInfo6(boXY, 17, 0x14000, 0x00170),
new BlockInfo6(boXY, 18, 0x14200, 0x0061C),
new BlockInfo6(boXY, 19, 0x14A00, 0x00504),
new BlockInfo6(boXY, 20, 0x15000, 0x006A0),
new BlockInfo6(boXY, 21, 0x15800, 0x00644),
new BlockInfo6(boXY, 22, 0x16000, 0x00104),
new BlockInfo6(boXY, 23, 0x16200, 0x00004),
new BlockInfo6(boXY, 24, 0x16400, 0x00420),
new BlockInfo6(boXY, 25, 0x16A00, 0x00064),
new BlockInfo6(boXY, 26, 0x16C00, 0x003F0),
new BlockInfo6(boXY, 27, 0x17000, 0x0070C),
new BlockInfo6(boXY, 28, 0x17800, 0x00180),
new BlockInfo6(boXY, 29, 0x17A00, 0x00004),
new BlockInfo6(boXY, 30, 0x17C00, 0x0000C),
new BlockInfo6(boXY, 31, 0x17E00, 0x00048),
new BlockInfo6(boXY, 32, 0x18000, 0x00054),
new BlockInfo6(boXY, 33, 0x18200, 0x00644),
new BlockInfo6(boXY, 34, 0x18A00, 0x005C8),
new BlockInfo6(boXY, 35, 0x19000, 0x002F8),
new BlockInfo6(boXY, 36, 0x19400, 0x01B40),
new BlockInfo6(boXY, 37, 0x1B000, 0x001F4),
new BlockInfo6(boXY, 38, 0x1B200, 0x001F0),
new BlockInfo6(boXY, 39, 0x1B400, 0x00216),
new BlockInfo6(boXY, 40, 0x1B800, 0x00390),
new BlockInfo6(boXY, 41, 0x1BC00, 0x01A90),
new BlockInfo6(boXY, 42, 0x1D800, 0x00308),
new BlockInfo6(boXY, 43, 0x1DC00, 0x00618),
new BlockInfo6(boXY, 44, 0x1E400, 0x0025C),
new BlockInfo6(boXY, 45, 0x1E800, 0x00834),
new BlockInfo6(boXY, 46, 0x1F200, 0x00318),
new BlockInfo6(boXY, 47, 0x1F600, 0x007D0),
new BlockInfo6(boXY, 48, 0x1FE00, 0x00C48),
new BlockInfo6(boXY, 49, 0x20C00, 0x00078),
new BlockInfo6(boXY, 50, 0x20E00, 0x00200),
new BlockInfo6(boXY, 51, 0x21000, 0x00C84),
new BlockInfo6(boXY, 52, 0x21E00, 0x00628),
new BlockInfo6(boXY, 53, 0x22600, 0x34AD0),
new BlockInfo6(boXY, 54, 0x57200, 0x0E058),
};
private void Initialize()
{
/* 00: 00000-002C8, 002C8 */ // Puff = 0x00000;
/* 01: 00400-00F88, 00B88 */ // MyItem = 0x00400; // Bag
/* 02: 01000-0102C, 0002C */ // ItemInfo = 0x1000; // Select Bound Items
/* 03: 01200-01238, 00038 */ // GameTime = 0x01200;
/* 04: 01400-01550, 00150 */ Trainer1 = 0x1400; // Situation
/* 05: 01600-01604, 00004 */ // RandomGroup (rand seeds)
/* 06: 01800-01808, 00008 */ PlayTime = 0x1800; // PlayTime
/* 07: 01A00-01BC0, 001C0 */ Accessories = 0x1A00; // Fashion
/* 08: 01C00-01CBE, 000BE */ // amie minigame records
/* 09: 01E00-01E24, 00024 */ // temp variables (u32 id + 32 u8)
/* 10: 02000-04100, 02100 */ // FieldMoveModelSave
/* 11: 04200-04340, 00140 */ Trainer2 = 0x4200; // Misc
/* 12: 04400-04840, 00440 */ PCLayout = 0x4400; // BOX
/* 13: 04A00-04F74, 00574 */ BattleBox = 0x04A00; // BattleBox
/* 14: 05000-09E28, 04E28 */ PSS = 0x05000;
/* 15: 0A000-0EE28, 04E28 */ // PSS2
/* 16: 0F000-13E28, 04E28 */ // PSS3
/* 17: 14000-14170, 00170 */ // MyStatus
/* 18: 14200-1481C, 0061C */ Party = 0x14200; // PokePartySave
/* 19: 14A00-14F04, 00504 */ EventConst = 0x14A00; // EventWork
/* 20: 15000-156A0, 006A0 */ PokeDex = 0x15000; // ZukanData
/* 21: 15800-15E44, 00644 */ // hologram clips
/* 22: 16000-16104, 00104 */ Fused = 0x16000; // UnionPokemon
/* 23: 16200-16204, 00004 */ // ConfigSave
/* 24: 16400-16820, 00420 */ // Amie decoration stuff
/* 25: 16A00-16A64, 00064 */ // OPower = 0x16A00;
/* 26: 16C00-16FF0, 003F0 */ // Strength Rock position (xyz float: 84 entries, 12bytes/entry)
/* 27: 17000-1770C, 0070C */ // Trainer PR Video
/* 28: 17800-17980, 00180 */ GTS = 0x17800; // GtsData
/* 29: 17A00-17A04, 00004 */ // Packed Menu Bits
/* 30: 17C00-17C0C, 0000C */ // PSS Profile Q&A (6*questions, 6*answer)
/* 31: 17E00-17E48, 00048 */ // Repel Info, (Swarm?) and other overworld info (roamer)
/* 32: 18000-18054, 00054 */ // BOSS data fetch history (serial/mystery gift), 4byte intro & 20*4byte entries
/* 33: 18200-18844, 00644 */ // Streetpass history (4 byte intro, 20*4byte entries, 20*76 byte entries)
/* 34: 18A00-18FC8, 005C8 */ // LiveMatchData/BattleSpotData
/* 35: 19000-192F8, 002F8 */ // MAC Address & Network Connection Logging (0x98 per entry, 5 entries)
/* 36: 19400-1AF40, 01B40 */ HoF = 0x19400; // Dendou
/* 37: 1B000-1B1F4, 001F4 */ MaisonStats = 0x1B1C0; // BattleInstSave
/* 38: 1B200-1B3F0, 001F0 */ Daycare = 0x1B200; // Sodateya
/* 39: 1B400-1B616, 00216 */ // BattleInstSave
/* 40: 1B800-1BB90, 00390 */ BerryField = 0x1B800;
/* 41: 1BC00-1D690, 01A90 */ WondercardFlags = 0x1BC00; // MysteryGiftSave
/* 42: 1D800-1DB08, 00308 */ SUBE = 0x1D890; // PokeDiarySave
/* 43: 1DC00-1E218, 00618 */ // Storyline Records
/* 44: 1E400-1E65C, 0025C */ // Record = 0x1E400;
/* 45: 1E800-1F034, 00834 */ // Friend Safari (0x15 per entry, 100 entries)
/* 46: 1F200-1F518, 00318 */ SuperTrain = 0x1F200;
/* 47: 1F600-1FDD0, 007D0 */ // Unused (lmao)
/* 48: 1FE00-20A48, 00C48 */ LinkInfo = 0x1FE00;
/* 49: 20C00-20C78, 00078 */ // PSS usage info
/* 50: 20E00-21000, 00200 */ // GameSyncSave
/* 51: 21000-21C84, 00C84 */ // PSS Icon (bool32 data present, 40x40 u16 pic, unused)
/* 52: 21E00-22428, 00628 */ // ValidationSave (updatabale Public Key for legal check api calls)
/* 53: 22600-570D0, 34AD0 */ Box = 0x22600;
/* 54: 57200-65258, 0E058 */ JPEG = 0x57200;
Items = new MyItem6XY(this, 0x00400);
PuffBlock = new Puff6(this, 0x00000);
GameTime = new GameTime6(this, 0x01200);
Situation = new Situation6(this, 0x01400);
Played = new PlayTime6(this, 0x01800);
BoxLayout = new BoxLayout6(this, 0x4400);
Status = new MyStatus6XY(this, 0x14000);
Zukan = new Zukan6(this, 0x15000, 0x15000 + 0x3C8);
OPowerBlock = new OPower6(this, 0x16A00);
MysteryBlock = new MysteryBlock6(this, 0x1BC00);
Records = new Record6(this, 0x1E400, Core.Records.MaxType_XY);
EventFlag = EventConst + 0x2FC;
PokeDexLanguageFlags = PokeDex + 0x3C8;
Spinda = PokeDex + 0x648;
WondercardData = WondercardFlags + 0x100;
HeldItems = Legal.HeldItem_XY;
Personal = PersonalTable.XY;
}
public Zukan6 Zukan { get; private set; }
public Puff6 PuffBlock { get; private set; }
public OPower6 OPowerBlock { get; private set; }
public BoxLayout6 BoxLayout { get; private set; }
public MysteryBlock6 MysteryBlock { get; private set; }
protected override void SetDex(PKM pkm) => Zukan.SetDex(pkm);
// Daycare
public override int DaycareSeedSize => 16;
public override bool HasTwoDaycares => false;
public override bool? IsDaycareOccupied(int loc, int slot) => Data[Daycare + 0 + ((SIZE_STORED + 8) * slot)] == 1;
public override uint? GetDaycareEXP(int loc, int slot) => BitConverter.ToUInt32(Data, Daycare + 4 + ((SIZE_STORED + 8) * slot));
public override int GetDaycareSlotOffset(int loc, int slot) => Daycare + 8 + (slot * (SIZE_STORED + 8));
public override bool? IsDaycareHasEgg(int loc) => Data[Daycare + 0x1E0] == 1;
public override void SetDaycareHasEgg(int loc, bool hasEgg) => Data[Daycare + 0x1E0] = (byte)(hasEgg ? 1 : 0);
public override void SetDaycareOccupied(int loc, int slot, bool occupied) => Data[Daycare + ((SIZE_STORED + 8) * slot)] = (byte)(occupied ? 1 : 0);
public override void SetDaycareEXP(int loc, int slot, uint EXP) => BitConverter.GetBytes(EXP).CopyTo(Data, Daycare + 4 + ((SIZE_STORED + 8) * slot));
public override void SetDaycareRNGSeed(int loc, string seed)
{
if (loc != 0)
return;
if (Daycare < 0)
return;
if (seed == null)
return;
if (seed.Length > DaycareSeedSize)
return;
Util.GetBytesFromHexString(seed).CopyTo(Data, Daycare + 0x1E8);
}
public override string JPEGTitle => HasJPPEGData ? string.Empty : Util.TrimFromZero(Encoding.Unicode.GetString(Data, JPEG, 0x1A));
public override byte[] JPEGData => HasJPPEGData ? Array.Empty<byte>() : GetData(JPEG + 0x54, 0xE004);
private bool HasJPPEGData => Data[JPEG + 0x54] == 0xFF;
// Gym History
public ushort[][] GymTeams
{
get
{
if (SUBE < 0 || ORASDEMO)
return Array.Empty<ushort[]>(); // no gym data
const int teamsize = 2 * 6; // 2byte/species, 6species/team
const int size = teamsize * 8; // 8 gyms
int ofs = SUBE - size - 4;
var data = GetData(ofs, size);
ushort[][] teams = new ushort[8][];
for (int i = 0; i < teams.Length; i++)
Buffer.BlockCopy(data, teamsize * i, teams[i] = new ushort[6], 0, teamsize);
return teams;
}
set
{
if (SUBE < 0 || ORASDEMO)
return; // no gym data
const int teamsize = 2 * 6; // 2byte/species, 6species/team
const int size = teamsize * 8; // 8 gyms
int ofs = SUBE - size - 4;
byte[] data = new byte[size];
for (int i = 0; i < value.Length; i++)
Buffer.BlockCopy(value[i], 0, data, teamsize * i, teamsize);
SetData(data, ofs);
}
}
public void UnlockAllFriendSafariSlots()
{
if (!XY)
return;
// Unlock + reveal all safari slots if friend data is present
const int start = 0x1E7FF;
const int size = 0x15;
for (int i = 1; i < 101; i++)
{
int ofs = start + (i * size);
if (Data[ofs] != 0) // no friend data == 0x00
Data[ofs] = 0x3D;
}
Edited = true;
}
public void UnlockAllAccessories()
{
if (!XY)
return;
SetData(AllAccessories, Accessories);
}
private static readonly byte[] AllAccessories =
{
0xFE,0xFF,0xFF,0x7E,0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xEF,0xFF,0xFF,0xFF,0xF9,0xFF,0xFB,0xFF,0xF7,0xFF,0xFF,0x0F,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFF,
0xFF,0x7E,0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,
0xFF,0xFF,0xFF,0xF9,0xFF,0xFB,0xFF,0xF7,0xFF,0xFF,0x0F,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
public override GameVersion Version
{
get
{
switch (Game)
{
case (int)GameVersion.X: return GameVersion.X;
case (int)GameVersion.Y: return GameVersion.Y;
}
return GameVersion.Invalid;
}
}
protected override bool[] MysteryGiftReceivedFlags { get => MysteryBlock.MysteryGiftReceivedFlags; set => MysteryBlock.MysteryGiftReceivedFlags = value; }
protected override MysteryGift[] MysteryGiftCards { get => MysteryBlock.MysteryGiftCards; set => MysteryBlock.MysteryGiftCards = value; }
public byte[] LinkBlock
{
get => GetData(LinkInfo, 0xC48);
set
{
if (value.Length != 0xC48)
throw new ArgumentException(nameof(value));
value.CopyTo(Data, LinkInfo);
}
}
public override bool GetCaught(int species) => Zukan.GetCaught(species);
public override bool GetSeen(int species) => Zukan.GetSeen(species);
public override void SetSeen(int species, bool seen) => Zukan.SetSeen(species, seen);
public override void SetCaught(int species, bool caught) => Zukan.SetCaught(species, caught);
public override int CurrentBox { get => BoxLayout.CurrentBox; set => BoxLayout.CurrentBox = value; }
protected override int GetBoxWallpaperOffset(int box) => BoxLayout.GetBoxWallpaperOffset(box);
public override int BoxesUnlocked { get => BoxLayout.BoxesUnlocked; set => BoxLayout.BoxesUnlocked = value; }
public override byte[] BoxFlags { get => BoxLayout.BoxFlags; set => BoxLayout.BoxFlags = value; }
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,47 @@
namespace PKHeX.Core
{
public enum SAV7BlockIndex
{
MyItem,
Situation,
RandomGroup,
MyStatus,
PokePartySave,
EventWork,
ZukanData,
GtsData,
UnionPokemon,
Misc,
FieldMenu,
ConfigSave,
GameTime,
BOX,
BoxPokemon,
ResortSave,
PlayTime,
FieldMoveModelSave,
Fashion,
JoinFestaPersonalSave1,
JoinFestaPersonalSave2,
JoinFestaDataSave,
BerrySpot,
FishingSpot,
LiveMatchData,
BattleSpotData,
PokeFinderSave,
MysteryGiftSave,
Record,
ValidationSave,
GameSyncSave,
PokeDiarySave,
BattleInstSave,
Sodateya,
WeatherSave,
QRReaderSaveData,
TurtleSalmonSave,
// Ultra Sun/Ultra Moon only
BattleFesSave,
FinderStudioSave,
}
}

View file

@ -0,0 +1,68 @@
namespace PKHeX.Core
{
public class SAV7SM : SAV7
{
public SAV7SM(byte[] data) : base(data, BlocksSM, boSM) => Initialize();
public SAV7SM() : base(SaveUtil.SIZE_G7SM, BlocksSM, boSM) => Initialize();
public override SaveFile Clone() => new SAV7SM((byte[])Data.Clone());
private void Initialize()
{
Personal = PersonalTable.SM;
HeldItems = Legal.HeldItems_SM;
Items = new MyItem7SM(this, Bag);
Zukan = new Zukan7(this, PokeDex, PokeDexLanguageFlags);
Records = new Record6(this, Record, Core.Records.MaxType_SM);
}
protected override int EventFlagMax => 3968;
public override int MaxMoveID => Legal.MaxMoveID_7;
public override int MaxSpeciesID => Legal.MaxSpeciesID_7;
public override int MaxItemID => Legal.MaxItemID_7;
public override int MaxAbilityID => Legal.MaxAbilityID_7;
private const int boSM = SaveUtil.SIZE_G7SM - 0x200;
public static readonly BlockInfo[] BlocksSM =
{
new BlockInfo7 (boSM, 00, 0x00000, 0x00DE0),
new BlockInfo7 (boSM, 01, 0x00E00, 0x0007C),
new BlockInfo7 (boSM, 02, 0x01000, 0x00014),
new BlockInfo7 (boSM, 03, 0x01200, 0x000C0),
new BlockInfo7 (boSM, 04, 0x01400, 0x0061C),
new BlockInfo7 (boSM, 05, 0x01C00, 0x00E00),
new BlockInfo7 (boSM, 06, 0x02A00, 0x00F78),
new BlockInfo7 (boSM, 07, 0x03A00, 0x00228),
new BlockInfo7 (boSM, 08, 0x03E00, 0x00104),
new BlockInfo7 (boSM, 09, 0x04000, 0x00200),
new BlockInfo7 (boSM, 10, 0x04200, 0x00020),
new BlockInfo7 (boSM, 11, 0x04400, 0x00004),
new BlockInfo7 (boSM, 12, 0x04600, 0x00058),
new BlockInfo7 (boSM, 13, 0x04800, 0x005E6),
new BlockInfo7 (boSM, 14, 0x04E00, 0x36600),
new BlockInfo7 (boSM, 15, 0x3B400, 0x0572C),
new BlockInfo7 (boSM, 16, 0x40C00, 0x00008),
new BlockInfo7 (boSM, 17, 0x40E00, 0x01080),
new BlockInfo7 (boSM, 18, 0x42000, 0x01A08),
new BlockInfo7 (boSM, 19, 0x43C00, 0x06408),
new BlockInfo7 (boSM, 20, 0x4A200, 0x06408),
new BlockInfo7 (boSM, 21, 0x50800, 0x03998),
new BlockInfo7 (boSM, 22, 0x54200, 0x00100),
new BlockInfo7 (boSM, 23, 0x54400, 0x00100),
new BlockInfo7 (boSM, 24, 0x54600, 0x10528),
new BlockInfo7 (boSM, 25, 0x64C00, 0x00204),
new BlockInfo7 (boSM, 26, 0x65000, 0x00B60),
new BlockInfo7 (boSM, 27, 0x65C00, 0x03F50),
new BlockInfo7 (boSM, 28, 0x69C00, 0x00358),
new BlockInfo7 (boSM, 29, 0x6A000, 0x00728),
new BlockInfo7 (boSM, 30, 0x6A800, 0x00200),
new BlockInfo7 (boSM, 31, 0x6AA00, 0x00718),
new BlockInfo7 (boSM, 32, 0x6B200, 0x001FC),
new BlockInfo7 (boSM, 33, 0x6B400, 0x00200),
new BlockInfo7 (boSM, 34, 0x6B600, 0x00120),
new BlockInfo7 (boSM, 35, 0x6B800, 0x001C8),
new BlockInfo7 (boSM, 36, 0x6BA00, 0x00200),
};
}
}

View file

@ -0,0 +1,78 @@
namespace PKHeX.Core
{
public class SAV7USUM : SAV7
{
public SAV7USUM(byte[] data) : base(data, BlocksUSUM, boUU)
{
Initialize();
}
public SAV7USUM() : base(SaveUtil.SIZE_G7USUM, BlocksUSUM, boUU)
{
Initialize();
}
public override SaveFile Clone() => new SAV7USUM((byte[])Data.Clone());
private void Initialize()
{
Personal = PersonalTable.USUM;
HeldItems = Legal.HeldItems_USUM;
Items = new MyItem7USUM(this, Bag);
Zukan = new Zukan7(this, PokeDex, PokeDexLanguageFlags);
Records = new Record6(this, Record, Core.Records.MaxType_USUM);
}
protected override int EventFlagMax => 4928;
public override int MaxMoveID => Legal.MaxMoveID_7_USUM;
public override int MaxSpeciesID => Legal.MaxSpeciesID_7_USUM;
public override int MaxItemID => Legal.MaxItemID_7_USUM;
public override int MaxAbilityID => Legal.MaxAbilityID_7_USUM;
private const int boUU = SaveUtil.SIZE_G7USUM - 0x200;
public static readonly BlockInfo[] BlocksUSUM =
{
new BlockInfo7(boUU, 00, 0x00000, 0x00E28),
new BlockInfo7(boUU, 01, 0x01000, 0x0007C),
new BlockInfo7(boUU, 02, 0x01200, 0x00014),
new BlockInfo7(boUU, 03, 0x01400, 0x000C0),
new BlockInfo7(boUU, 04, 0x01600, 0x0061C),
new BlockInfo7(boUU, 05, 0x01E00, 0x00E00),
new BlockInfo7(boUU, 06, 0x02C00, 0x00F78),
new BlockInfo7(boUU, 07, 0x03C00, 0x00228),
new BlockInfo7(boUU, 08, 0x04000, 0x0030C),
new BlockInfo7(boUU, 09, 0x04400, 0x001FC),
new BlockInfo7(boUU, 10, 0x04600, 0x0004C),
new BlockInfo7(boUU, 11, 0x04800, 0x00004),
new BlockInfo7(boUU, 12, 0x04A00, 0x00058),
new BlockInfo7(boUU, 13, 0x04C00, 0x005E6),
new BlockInfo7(boUU, 14, 0x05200, 0x36600),
new BlockInfo7(boUU, 15, 0x3B800, 0x0572C),
new BlockInfo7(boUU, 16, 0x41000, 0x00008),
new BlockInfo7(boUU, 17, 0x41200, 0x01218),
new BlockInfo7(boUU, 18, 0x42600, 0x01A08),
new BlockInfo7(boUU, 19, 0x44200, 0x06408),
new BlockInfo7(boUU, 20, 0x4A800, 0x06408),
new BlockInfo7(boUU, 21, 0x50E00, 0x03998),
new BlockInfo7(boUU, 22, 0x54800, 0x00100),
new BlockInfo7(boUU, 23, 0x54A00, 0x00100),
new BlockInfo7(boUU, 24, 0x54C00, 0x10528),
new BlockInfo7(boUU, 25, 0x65200, 0x00204),
new BlockInfo7(boUU, 26, 0x65600, 0x00B60),
new BlockInfo7(boUU, 27, 0x66200, 0x03F50),
new BlockInfo7(boUU, 28, 0x6A200, 0x00358),
new BlockInfo7(boUU, 29, 0x6A600, 0x00728),
new BlockInfo7(boUU, 30, 0x6AE00, 0x00200),
new BlockInfo7(boUU, 31, 0x6B000, 0x00718),
new BlockInfo7(boUU, 32, 0x6B800, 0x001FC),
new BlockInfo7(boUU, 33, 0x6BA00, 0x00200),
new BlockInfo7(boUU, 34, 0x6BC00, 0x00120),
new BlockInfo7(boUU, 35, 0x6BE00, 0x001C8),
new BlockInfo7(boUU, 36, 0x6C000, 0x00200),
new BlockInfo7(boUU, 37, 0x6C200, 0x0039C),
new BlockInfo7(boUU, 38, 0x6C600, 0x00400),
};
}
}

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace PKHeX.Core
@ -8,7 +7,7 @@ namespace PKHeX.Core
/// <summary>
/// Generation 7 <see cref="SaveFile"/> object for <see cref="GameVersion.GG"/> games.
/// </summary>
public sealed class SAV7b : SaveFile, ISecureValueStorage
public sealed class SAV7b : SAV_BEEF
{
protected override string BAKText => $"{OT} ({Version}) - {Played.LastSavedTime}";
public override string Filter => "savedata|*.bin";
@ -22,18 +21,19 @@ namespace PKHeX.Core
public override SaveFile Clone() => new SAV7b((byte[])Data.Clone());
public SAV7b() : this(new byte[SaveUtil.SIZE_G7GG]) { }
public SAV7b(byte[] data)
public SAV7b() : base(SaveUtil.SIZE_G7GG, BlockInfoGG, 0xB8800)
{
Data = data;
BAK = (byte[])Data.Clone();
Exportable = !IsRangeEmpty(0, Data.Length);
Initialize();
ClearBoxes();
}
// Load Info
const int len = 0xB8800; // 1mb always allocated
BlockInfoOffset = len - 0x1F0;
Blocks = !Exportable ? BlockInfoGG : BlockInfo3DS.GetBlockInfoData(Data, ref BlockInfoOffset, Checksums.CRC16NoInvert, len);
public SAV7b(byte[] data) : base(data, BlockInfoGG, 0xB8800)
{
Initialize();
}
private void Initialize()
{
Personal = PersonalTable.GG;
Box = GetBlockOffset(BelugaBlockIndex.PokeListPokemon);
@ -53,23 +53,18 @@ namespace PKHeX.Core
WondercardData = GiftRecords.Offset;
HeldItems = Legal.HeldItems_GG;
if (Exportable)
CanReadChecksums();
else
ClearBoxes();
}
// Save Block accessors
public readonly MyItem Items;
public readonly Misc7b Misc;
public readonly Zukan7b Zukan;
public readonly MyStatus7b Status;
public readonly PlayTime7b Played;
public readonly ConfigSave7b Config;
public readonly EventWork7b EventWork;
public readonly PokeListHeader Storage;
public readonly WB7Records GiftRecords;
public MyItem Items { get; private set; }
public Misc7b Misc { get; private set; }
public Zukan7b Zukan { get; private set; }
public MyStatus7b Status { get; private set; }
public PlayTime7b Played { get; private set; }
public ConfigSave7b Config { get; private set; }
public EventWork7b EventWork { get; private set; }
public PokeListHeader Storage { get; private set; }
public WB7Records GiftRecords { get; private set; }
public override InventoryPouch[] Inventory { get => Items.Inventory; set => Items.Inventory = value; }
@ -98,54 +93,36 @@ namespace PKHeX.Core
public override int BoxSlotCount => 25;
public override int BoxCount => 40; // 1000/25
// Blocks & Offsets
private readonly int BlockInfoOffset;
public readonly BlockInfo[] Blocks;
public override bool ChecksumsValid => CanReadChecksums() && Blocks.GetChecksumsValid(Data);
public override string ChecksumInfo => CanReadChecksums() ? Blocks.GetChecksumInfo(Data) : string.Empty;
public BlockInfo GetBlock(BelugaBlockIndex index) => Blocks[(int)index >= Blocks.Length ? 0 : (int)index];
public BlockInfo GetBlock(BelugaBlockIndex index) => Blocks[(int)index];
public int GetBlockOffset(BelugaBlockIndex index) => GetBlock(index).Offset;
private const int boGG = 0xB8800; // nowhere near 1MB (savedata.bin size)
private static readonly BlockInfo[] BlockInfoGG =
{
new BlockInfo3DS {Offset = 0x00000, Length = 0x00D90},
new BlockInfo3DS {Offset = 0x00E00, Length = 0x00200},
new BlockInfo3DS {Offset = 0x01000, Length = 0x00168},
new BlockInfo3DS {Offset = 0x01200, Length = 0x01800},
new BlockInfo3DS {Offset = 0x02A00, Length = 0x020E8},
new BlockInfo3DS {Offset = 0x04C00, Length = 0x00930},
new BlockInfo3DS {Offset = 0x05600, Length = 0x00004},
new BlockInfo3DS {Offset = 0x05800, Length = 0x00130},
new BlockInfo3DS {Offset = 0x05A00, Length = 0x00012},
new BlockInfo3DS {Offset = 0x05C00, Length = 0x3F7A0},
new BlockInfo3DS {Offset = 0x45400, Length = 0x00008},
new BlockInfo3DS {Offset = 0x45600, Length = 0x00E90},
new BlockInfo3DS {Offset = 0x46600, Length = 0x010A4},
new BlockInfo3DS {Offset = 0x47800, Length = 0x000F0},
new BlockInfo3DS {Offset = 0x47A00, Length = 0x06010},
new BlockInfo3DS {Offset = 0x4DC00, Length = 0x00200},
new BlockInfo3DS {Offset = 0x4DE00, Length = 0x00098},
new BlockInfo3DS {Offset = 0x4E000, Length = 0x00068},
new BlockInfo3DS {Offset = 0x4E200, Length = 0x69780},
new BlockInfo3DS {Offset = 0xB7A00, Length = 0x000B0},
new BlockInfo3DS {Offset = 0xB7C00, Length = 0x00940},
new BlockInfo7b(boGG, 00, 0x00000, 0x00D90),
new BlockInfo7b(boGG, 01, 0x00E00, 0x00200),
new BlockInfo7b(boGG, 02, 0x01000, 0x00168),
new BlockInfo7b(boGG, 03, 0x01200, 0x01800),
new BlockInfo7b(boGG, 04, 0x02A00, 0x020E8),
new BlockInfo7b(boGG, 05, 0x04C00, 0x00930),
new BlockInfo7b(boGG, 06, 0x05600, 0x00004),
new BlockInfo7b(boGG, 07, 0x05800, 0x00130),
new BlockInfo7b(boGG, 08, 0x05A00, 0x00012),
new BlockInfo7b(boGG, 09, 0x05C00, 0x3F7A0),
new BlockInfo7b(boGG, 10, 0x45400, 0x00008),
new BlockInfo7b(boGG, 11, 0x45600, 0x00E90),
new BlockInfo7b(boGG, 12, 0x46600, 0x010A4),
new BlockInfo7b(boGG, 13, 0x47800, 0x000F0),
new BlockInfo7b(boGG, 14, 0x47A00, 0x06010),
new BlockInfo7b(boGG, 15, 0x4DC00, 0x00200),
new BlockInfo7b(boGG, 16, 0x4DE00, 0x00098),
new BlockInfo7b(boGG, 17, 0x4E000, 0x00068),
new BlockInfo7b(boGG, 18, 0x4E200, 0x69780),
new BlockInfo7b(boGG, 19, 0xB7A00, 0x000B0),
new BlockInfo7b(boGG, 20, 0xB7C00, 0x00940),
};
private bool CanReadChecksums()
{
if (Blocks.Length <= 3)
{ Debug.WriteLine($"Not enough blocks ({Blocks.Length}), aborting {nameof(CanReadChecksums)}"); return false; }
return true;
}
protected override void SetChecksums()
{
if (!CanReadChecksums())
return;
Blocks.SetChecksums(Data);
}
public bool FixPreWrite() => Storage.CompressStorage();
protected override void SetPKM(PKM pkm)
@ -202,18 +179,6 @@ namespace PKHeX.Core
return StringConverter.SetString7b(value, maxLength, Language, PadToSize, PadWith);
}
public ulong TimeStampCurrent
{
get => BitConverter.ToUInt64(Data, BlockInfoOffset - 0x14);
set => BitConverter.GetBytes(value).CopyTo(Data, BlockInfoOffset - 0x14);
}
public ulong TimeStampPrevious
{
get => BitConverter.ToUInt64(Data, BlockInfoOffset - 0xC);
set => BitConverter.GetBytes(value).CopyTo(Data, BlockInfoOffset - 0xC);
}
public override GameVersion Version
{
get

View file

@ -0,0 +1,40 @@
using System;
using System.Linq;
namespace PKHeX.Core
{
public abstract class SAV_BEEF : SaveFile, ISecureValueStorage
{
protected SAV_BEEF(byte[] data, BlockInfo[] blocks, int biOffset) : base(data)
{
Blocks = blocks;
BlockInfoOffset = biOffset;
}
protected SAV_BEEF(int size, BlockInfo[] blocks, int biOffset) : base(size)
{
Blocks = blocks;
BlockInfoOffset = biOffset;
}
protected override void SetChecksums() => Blocks.SetChecksums(Data);
public override bool ChecksumsValid => Blocks.GetChecksumsValid(Data);
public override string ChecksumInfo => Blocks.GetChecksumInfo(Data);
public override string MiscSaveInfo() => string.Join(Environment.NewLine, Blocks.Select(b => b.Summary));
protected readonly int BlockInfoOffset;
protected readonly BlockInfo[] Blocks;
public ulong TimeStampCurrent
{
get => BitConverter.ToUInt64(Data, BlockInfoOffset);
set => BitConverter.GetBytes(value).CopyTo(Data, BlockInfoOffset);
}
public ulong TimeStampPrevious
{
get => BitConverter.ToUInt64(Data, BlockInfoOffset);
set => BitConverter.GetBytes(value).CopyTo(Data, BlockInfoOffset);
}
}
}

View file

@ -47,6 +47,20 @@ namespace PKHeX.Core
public virtual int MaxIV => 31;
public ushort[] HeldItems { get; protected set; }
protected SaveFile(byte[] data)
{
Data = data;
BAK = (byte[])Data.Clone();
Exportable = true;
}
protected SaveFile(int size)
{
Data = new byte[size];
BAK = Data;
Exportable = false;
}
// General SAV Properties
public byte[] Write(ExportFlags flags = ExportFlags.None)
{
@ -105,13 +119,11 @@ namespace PKHeX.Core
public bool HasHoF => HoF > -1;
public bool HasSecretBase => SecretBase > -1;
public bool HasPSS => PSS > -1;
public bool HasOPower => OPower > -1;
public bool HasJPEG => JPEGData.Length > 0;
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;
@ -150,7 +162,6 @@ namespace PKHeX.Core
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

View file

@ -7,19 +7,15 @@ namespace PKHeX.Core
/// </summary>
public class BulkStorage : SaveFile
{
protected BulkStorage(byte[] data, Type t, int start, int slotsPerBox = 30)
protected BulkStorage(byte[] data, Type t, int start, int slotsPerBox = 30) : base(data)
{
Box = start;
Data = data;
SlotsPerBox = slotsPerBox;
blank = PKMConverter.GetBlank(t);
var slots = (Data.Length - Box) / blank.SIZE_STORED;
BoxCount = slots / SlotsPerBox;
Exportable = !IsRangeEmpty(0, Data.Length);
BAK = (byte[])Data.Clone();
GetIsPKMPresent = PKX.GetFuncIsPKMPresent(blank);
}

View file

@ -0,0 +1,15 @@
using System;
namespace PKHeX.Core
{
public class BattleSubway5 : SaveBlock
{
public BattleSubway5(SAV5 sav, int offset) : base(sav) => Offset = offset;
public int BP
{
get => BitConverter.ToUInt16(Data, Offset);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset);
}
}
}

View file

@ -0,0 +1,46 @@
namespace PKHeX.Core
{
public sealed class BoxLayout5 : SaveBlock
{
public BoxLayout5(SAV5 sav, int offset) : base(sav) => Offset = offset;
public int CurrentBox { get => Data[Offset]; set => Data[Offset] = (byte)value; }
public int GetBoxNameOffset(int box) => Offset + (0x28 * box) + 4;
public int GetBoxWallpaperOffset(int box) => Offset + 0x3C4 + box;
public int GetBoxWallpaper(int box)
{
if ((uint)box > SAV.BoxCount)
return 0;
return Data[GetBoxWallpaperOffset(box)];
}
public void SetBoxWallpaper(int box, int value)
{
if ((uint)box > SAV.BoxCount)
return;
Data[GetBoxWallpaperOffset(box)] = (byte)value;
}
public string GetBoxName(int box)
{
if (box >= SAV.BoxCount)
return string.Empty;
return SAV.GetString(GetBoxNameOffset(box), 0x14);
}
public void SetBoxName(int box, string value)
{
if (value.Length > 0x26 / 2)
return;
var data = SAV.SetString(value + '\uFFFF', 0x14, 0x14, 0);
SAV.SetData(data, GetBoxNameOffset(box));
}
public string this[int i]
{
get => GetBoxName(i);
set => SetBoxName(i, value);
}
}
}

View file

@ -0,0 +1,39 @@
using System;
namespace PKHeX.Core
{
public class Daycare5 : SaveBlock
{
private const int SlotSize = 4 + PKX.SIZE_5STORED + 4; // occupied u32 flag, pk5, exp
public const int DaycareSeedSize = 16; // 8 bytes, b2w2 only
public Daycare5(SaveFile sav, int offset) : base(sav) => Offset = offset;
public ulong? GetSeed()
{
if (SAV.Version != GameVersion.B2W2)
return null;
return BitConverter.ToUInt64(Data, Offset + 0x1CC);
}
public void SetSeed(string value)
{
if (value == null)
return;
var data = Util.GetBytesFromHexString(value);
SAV.SetData(data, Offset + 0x1CC);
}
private int SlotOffset(int slot) => Offset + (SlotSize * slot);
private int DaycareEXPOffset(int slot) => SlotOffset(slot) + 0xE0;
public bool? IsOccupied(int slot) => BitConverter.ToUInt32(Data, SlotOffset(slot)) == 1;
public void SetOccupied(int slot, bool occupied) => SAV.SetData(BitConverter.GetBytes((uint)(occupied ? 1 : 0)), SlotOffset(slot));
public int GetOffset(int slot) => SlotOffset(slot) + 4;
public uint? GetEXP(int slot) => BitConverter.ToUInt32(Data, DaycareEXPOffset(slot));
public void SetEXP(int slot, uint EXP) => SAV.SetData(BitConverter.GetBytes(EXP), SlotOffset(slot) + 0xE0);
}
}

View file

@ -0,0 +1,21 @@
using System;
namespace PKHeX.Core
{
public sealed class Misc5 : SaveBlock
{
public Misc5(SAV5 sav, int offset) : base(sav) => Offset = offset;
public uint Money
{
get => BitConverter.ToUInt32(Data, Offset);
set => BitConverter.GetBytes(value).CopyTo(Data, Offset);
}
public int Badges
{
get => Data[Offset + 0x4];
set => Data[Offset + 0x4] = (byte)value;
}
}
}

View file

@ -0,0 +1,37 @@
namespace PKHeX.Core
{
public sealed class MyItem5B2W2 : MyItem
{
// offsets/pouch sizes are the same for both BW and B2W2, but Key Item permissions are different
private const int HeldItem = 0x000; // 0
private const int KeyItem = 0x4D8; // 1
private const int TMHM = 0x624; // 2
private const int Medicine = 0x7D8; // 3
private const int Berry = 0x898; // 4
private static readonly ushort[] LegalItems = Legal.Pouch_Items_BW;
private static readonly ushort[] LegalKeyItems = Legal.Pouch_Key_B2W2;
private static readonly ushort[] LegalTMHMs = Legal.Pouch_TMHM_BW;
private static readonly ushort[] LegalMedicine = Legal.Pouch_Medicine_BW;
private static readonly ushort[] LegalBerries = Legal.Pouch_Berries_BW;
public MyItem5B2W2(SaveFile SAV, int offset) : base(SAV) => Offset = offset;
public override InventoryPouch[] Inventory
{
get
{
InventoryPouch[] pouch =
{
new InventoryPouch4(InventoryType.Items, LegalItems, 999, Offset + HeldItem),
new InventoryPouch4(InventoryType.KeyItems, LegalKeyItems, 1, Offset + KeyItem),
new InventoryPouch4(InventoryType.TMHMs, LegalTMHMs, 1, Offset + TMHM),
new InventoryPouch4(InventoryType.Medicine, LegalMedicine, 999, Offset + Medicine),
new InventoryPouch4(InventoryType.Berries, LegalBerries, 999, Offset + Berry)
};
return pouch.LoadAll(Data);
}
set => value.SaveAll(Data);
}
}
}

View file

@ -0,0 +1,37 @@
namespace PKHeX.Core
{
public sealed class MyItem5BW : MyItem
{
// offsets/pouch sizes are the same for both BW and B2W2, but Key Item permissions are different
private const int HeldItem = 0x000; // 0
private const int KeyItem = 0x4D8; // 1
private const int TMHM = 0x624; // 2
private const int Medicine = 0x7D8; // 3
private const int Berry = 0x898; // 4
private static readonly ushort[] LegalItems = Legal.Pouch_Items_BW;
private static readonly ushort[] LegalKeyItems = Legal.Pouch_Key_BW;
private static readonly ushort[] LegalTMHMs = Legal.Pouch_TMHM_BW;
private static readonly ushort[] LegalMedicine = Legal.Pouch_Medicine_BW;
private static readonly ushort[] LegalBerries = Legal.Pouch_Berries_BW;
public MyItem5BW(SaveFile SAV, int offset) : base(SAV) => Offset = offset;
public override InventoryPouch[] Inventory
{
get
{
InventoryPouch[] pouch =
{
new InventoryPouch4(InventoryType.Items, LegalItems, 999, Offset + HeldItem),
new InventoryPouch4(InventoryType.KeyItems, LegalKeyItems, 1, Offset + KeyItem),
new InventoryPouch4(InventoryType.TMHMs, LegalTMHMs, 1, Offset + TMHM),
new InventoryPouch4(InventoryType.Medicine, LegalMedicine, 999, Offset + Medicine),
new InventoryPouch4(InventoryType.Berries, LegalBerries, 999, Offset + Berry),
};
return pouch.LoadAll(Data);
}
set => value.SaveAll(Data);
}
}
}

View file

@ -0,0 +1,76 @@
using System;
namespace PKHeX.Core
{
public class MysteryBlock5 : SaveBlock
{
private const int FlagStart = 0;
private const int MaxReceivedFlag = 2048;
private const int MaxCardsPresent = 12;
private const int FlagRegionSize = (MaxReceivedFlag / 8); // 0x100
private const int CardStart = FlagStart + (MaxReceivedFlag / 8);
private const int DataSize = 0xA90;
private int SeedOffset => Offset + DataSize;
// Everything is stored encrypted, and only decrypted on demand. Only crypt on object fetch...
public MysteryBlock5(SAV5 sav, int offset) : base(sav) => Offset = offset;
public MysteryGiftAlbum GiftAlbum
{
get
{
uint seed = BitConverter.ToUInt32(Data, SeedOffset);
byte[] wcData = SAV.GetData(Offset + FlagStart, 0xA90); // Encrypted, Decrypt
return GetAlbum(seed, wcData);
}
set
{
var wcData = SetAlbum(value);
// Write Back
wcData.CopyTo(Data, Offset + FlagStart);
BitConverter.GetBytes(value.Seed).CopyTo(Data, SeedOffset);
}
}
private static MysteryGiftAlbum GetAlbum(uint seed, byte[] wcData)
{
MysteryGiftAlbum Info = new MysteryGiftAlbum { Seed = seed };
PKX.CryptArray(wcData, seed);
Info.Flags = new bool[MaxReceivedFlag];
Info.Gifts = new MysteryGift[MaxCardsPresent];
// 0x100 Bytes for Used Flags
for (int i = 0; i < Info.Flags.Length; i++)
Info.Flags[i] = (wcData[i / 8] >> i % 8 & 0x1) == 1;
// 12 PGFs
for (int i = 0; i < Info.Gifts.Length; i++)
{
var data = new byte[PGF.Size];
Array.Copy(wcData, FlagRegionSize + (i * PGF.Size), data, 0, PGF.Size);
Info.Gifts[i] = new PGF(data);
}
return Info;
}
private static byte[] SetAlbum(MysteryGiftAlbum value)
{
byte[] wcData = new byte[0xA90];
// Toss back into byte[]
for (int i = 0; i < value.Flags.Length; i++)
{
if (value.Flags[i])
wcData[i / 8] |= (byte) (1 << (i & 7));
}
for (int i = 0; i < value.Gifts.Length; i++)
value.Gifts[i].Data.CopyTo(wcData, 0x100 + (i * PGF.Size));
// Decrypted, Encrypt
PKX.CryptArray(wcData, value.Seed);
return wcData;
}
}
}

View file

@ -0,0 +1,29 @@
using System;
namespace PKHeX.Core
{
public class PWTBlock5 : SaveBlock
{
public PWTBlock5(SAV5B2W2 sav, int offset) : base(sav) => Offset = offset;
public ushort GetPWTRecord(int id) => GetPWTRecord((PWTRecordID)id);
public ushort GetPWTRecord(PWTRecordID id)
{
if (id < PWTRecordID.Normal || id > PWTRecordID.MixMaster)
throw new ArgumentException(nameof(id));
int ofs = Offset + 0x5C + ((int)id * 2);
return BitConverter.ToUInt16(Data, ofs);
}
public void SetPWTRecord(int id, ushort value) => SetPWTRecord((PWTRecordID)id, value);
public void SetPWTRecord(PWTRecordID id, ushort value)
{
if (id < PWTRecordID.Normal || id > PWTRecordID.MixMaster)
throw new ArgumentException(nameof(id));
int ofs = Offset + 0x5C + ((int)id * 2);
SAV.SetData(BitConverter.GetBytes(value), ofs);
}
}
}

View file

@ -0,0 +1,89 @@
using System;
namespace PKHeX.Core
{
public class PlayerData5 : SaveBlock
{
public PlayerData5(SAV5 sav, int offset) : base(sav) => Offset = offset;
public string OT
{
get => SAV.GetString(Offset + 0x4, 0x10);
set => SAV.SetString(value, SAV.OTLength).CopyTo(Data, Offset + 0x4);
}
public int TID
{
get => BitConverter.ToUInt16(Data, Offset + 0x14 + 0);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x14 + 0);
}
public int SID
{
get => BitConverter.ToUInt16(Data, Offset + 0x14 + 2);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x14 + 2);
}
public int Language
{
get => Data[Offset + 0x1E];
set => Data[Offset + 0x1E] = (byte)value;
}
public int Game
{
get => Data[Offset + 0x1F];
set => Data[Offset + 0x1F] = (byte)value;
}
public int Gender
{
get => Data[Offset + 0x21];
set => Data[Offset + 0x21] = (byte)value;
}
// 22,23 ??
public int PlayedHours
{
get => BitConverter.ToUInt16(Data, Offset + 0x24);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x24);
}
public int PlayedMinutes
{
get => Data[Offset + 0x24 + 2];
set => Data[Offset + 0x24 + 2] = (byte)value;
}
public int PlayedSeconds
{
get => Data[Offset + 0x24 + 3];
set => Data[Offset + 0x24 + 3] = (byte)value;
}
public int M
{
get => BitConverter.ToInt32(Data, Offset + 0x180);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x180);
}
public int X
{
get => BitConverter.ToUInt16(Data, Offset + 0x186);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x186);
}
public int Z
{
get => BitConverter.ToUInt16(Data, Offset + 0x18A);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x18A);
}
public int Y
{
get => BitConverter.ToUInt16(Data, Offset + 0x18E);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x18E);
}
}
}

View file

@ -0,0 +1,55 @@
namespace PKHeX.Core
{
public class BoxLayout6 : SaveBlock
{
private const int PCBackgrounds = 0x41E;
private const int PCFlags = 0x43D;
private const int LastViewedBoxOffset = 0x43F;
private const int strlen = SAV6.LongStringLength / 2;
public BoxLayout6(SAV6 sav, int offset) : base(sav) => Offset = offset;
public int GetBoxWallpaperOffset(int box) => Offset + PCBackgrounds + box;
public int GetBoxWallpaper(int box)
{
if ((uint)box > SAV.BoxCount)
return 0;
return Data[GetBoxWallpaperOffset(box)];
}
public void SetBoxWallpaper(int box, int value)
{
if ((uint)box > SAV.BoxCount)
return;
Data[GetBoxWallpaperOffset(box)] = (byte)value;
}
private int GetBoxNameOffset(int box) => Offset + (SAV6.LongStringLength * box);
public string GetBoxName(int box) => SAV.GetString(Data, GetBoxNameOffset(box), SAV6.LongStringLength);
public void SetBoxName(int box, string value)
{
var data = SAV.SetString(value, strlen, strlen, 0);
var offset = GetBoxNameOffset(box) + (SAV6.LongStringLength * box);
SAV.SetData(data, offset);
}
public byte[] BoxFlags
{
get => new[] { Data[Offset + PCFlags] }; // 7 bits for wallpaper unlocks, top bit to unlock final box (delta episode)
set
{
if (value.Length != 1)
return;
Data[Offset + PCFlags] = value[0];
}
}
public int BoxesUnlocked { get => Data[Offset + PCFlags + 1] - 1; set => Data[Offset + PCFlags + 1] = (byte)(value + 1); }
public int CurrentBox { get => Data[Offset + LastViewedBoxOffset]; set => Data[Offset + LastViewedBoxOffset] = (byte)value; }
}
}

View file

@ -0,0 +1,17 @@
using System;
namespace PKHeX.Core
{
public sealed class GameTime6 : SaveBlock
{
public GameTime6(SaveFile sav, int offset) : base(sav) => Offset = offset;
public int ResumeYear { get => BitConverter.ToInt32(Data, Offset + 0x4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x4); }
public int ResumeMonth { get => Data[Offset + 0x8]; set => Data[Offset + 0x8] = (byte)value; }
public int ResumeDay { get => Data[Offset + 0x9]; set => Data[Offset + 0x9] = (byte)value; }
public int ResumeHour { get => Data[Offset + 0xB]; set => Data[Offset + 0xB] = (byte)value; }
public int ResumeMinute { get => Data[Offset + 0xC]; set => Data[Offset + 0xC] = (byte)value; }
public int ResumeSeconds { get => Data[Offset + 0xD]; set => Data[Offset + 0xD] = (byte)value; }
public uint SecondsToStart { get => BitConverter.ToUInt32(Data, Offset + 0x18); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x18); }
public uint SecondsToFame { get => BitConverter.ToUInt32(Data, Offset + 0x20); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x20); }
}
}

View file

@ -2,8 +2,15 @@ namespace PKHeX.Core
{
public interface IPokePuff
{
bool HasPuffData { get; }
byte[] Puffs { get; set; }
int PuffCount { get; set; }
Puff6 PuffBlock { get; }
}
public interface IOPower
{
OPower6 OPowerBlock { get; }
}
public interface ILink
{
byte[] LinkBlock { get; set; }
}
}

View file

@ -0,0 +1,47 @@
using System;
namespace PKHeX.Core
{
public sealed class ItemInfo6 : SaveBlock
{
public ItemInfo6(SaveFile sav, int offset) : base(sav) => Offset = offset;
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, Offset + 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, Offset + 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, Offset + 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, Offset + 20 + (2 * i));
}
}
}
}

View file

@ -0,0 +1,30 @@
namespace PKHeX.Core
{
public sealed class MyItem6AO : MyItem
{
private const int HeldItem = 0; // 0
private const int KeyItem = 0x640; // 1
private const int TMHM = 0x7C0; // 2
private const int Medicine = 0x968; // 3, +2 items shift because 2 HMs added
private const int Berry = 0xA70; // 4
public MyItem6AO(SaveFile SAV, int offset) : base(SAV) => Offset = offset;
public override InventoryPouch[] Inventory
{
get
{
InventoryPouch[] pouch =
{
new InventoryPouch4(InventoryType.Items, Legal.Pouch_Items_AO, 999, Offset + HeldItem),
new InventoryPouch4(InventoryType.KeyItems, Legal.Pouch_Key_AO, 1, Offset + KeyItem),
new InventoryPouch4(InventoryType.TMHMs, Legal.Pouch_TMHM_AO, 1, Offset + TMHM),
new InventoryPouch4(InventoryType.Medicine, Legal.Pouch_Medicine_AO, 999, Offset + Medicine),
new InventoryPouch4(InventoryType.Berries, Legal.Pouch_Berry_XY, 999, Offset + Berry),
};
return pouch.LoadAll(Data);
}
set => value.SaveAll(Data);
}
}
}

View file

@ -0,0 +1,29 @@
namespace PKHeX.Core
{
public sealed class MyItem6XY : MyItem
{
private const int HeldItem = 0; // 0
private const int KeyItem = 0x640; // 1
private const int TMHM = 0x7C0; // 2
private const int Medicine = 0x968; // 3
private const int Berry = 0xA68; // 4
public MyItem6XY(SaveFile SAV, int offset) : base(SAV) => Offset = offset;
public override InventoryPouch[] Inventory
{
get
{
InventoryPouch[] pouch =
{
new InventoryPouch4(InventoryType.Items, Legal.Pouch_Items_XY, 999, Offset + HeldItem),
new InventoryPouch4(InventoryType.KeyItems, Legal.Pouch_Key_XY, 1, Offset + KeyItem),
new InventoryPouch4(InventoryType.TMHMs, Legal.Pouch_TMHM_XY, 1, Offset + TMHM),
new InventoryPouch4(InventoryType.Medicine, Legal.Pouch_Medicine_XY, 999, Offset + Medicine),
new InventoryPouch4(InventoryType.Berries, Legal.Pouch_Berry_XY, 999, Offset + Berry),
};
return pouch.LoadAll(Data);
}
set => value.SaveAll(Data);
}
}
}

View file

@ -0,0 +1,115 @@
using System;
using System.Linq;
namespace PKHeX.Core
{
public class MyStatus6 : SaveBlock
{
public MyStatus6(SaveFile sav, int offset) : base(sav) => Offset = offset;
public int TID
{
get => BitConverter.ToUInt16(Data, Offset + 0);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0);
}
public int SID
{
get => BitConverter.ToUInt16(Data, Offset + 2);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 2);
}
public int Game
{
get => Data[Offset + 4];
set => Data[Offset + 4] = (byte)value;
}
public int Gender
{
get => Data[Offset + 5];
set => Data[Offset + 5] = (byte)value;
}
public int MultiplayerSpriteID
{
get => Data[Offset + 7];
set => Data[Offset + 7] = (byte)value;
}
public int GameSyncIDSize => 16; // 64 bits
public string GameSyncID
{
get
{
var data = Data.Skip(Offset + 8).Take(GameSyncIDSize / 2).Reverse().ToArray();
return BitConverter.ToString(data).Replace("-", string.Empty);
}
set
{
if (value == null)
return;
if (value.Length > GameSyncIDSize)
return;
Enumerable.Range(0, value.Length)
.Where(x => x % 2 == 0)
.Reverse()
.Select(x => Convert.ToByte(value.Substring(x, 2), 16))
.ToArray().CopyTo(Data, Offset + 8);
}
}
public int SubRegion
{
get => Data[Offset + 0x26];
set => Data[Offset + 0x26] = (byte)value;
}
public int Country
{
get => Data[Offset + 0x27];
set => Data[Offset + 0x27] = (byte)value;
}
public int ConsoleRegion
{
get => Data[Offset + 0x2C];
set => Data[Offset + 0x2C] = (byte)value;
}
public int Language
{
get => Data[Offset + 0x2D];
set => Data[Offset + 0x2D] = (byte)value;
}
public string OT
{
get => SAV.GetString(Offset + 0x48, 0x1A);
set => SAV.SetData(SAV.SetString(value, SAV.OTLength), Offset + 0x48);
}
private int GetSayingOffset(int say) => Offset + 0x7C + (SAV6.LongStringLength * say);
private string GetSaying(int say) => SAV.GetString(GetSayingOffset(say), SAV6.LongStringLength);
private void SetSaying(int say, string value) => SAV.SetData(SAV.SetString(value, SAV6.LongStringLength / 2), GetSayingOffset(say));
public string Saying1 { get => GetSaying(0); set => SetSaying(0, value); }
public string Saying2 { get => GetSaying(1); set => SetSaying(1, value); }
public string Saying3 { get => GetSaying(2); set => SetSaying(2, value); }
public string Saying4 { get => GetSaying(3); set => SetSaying(3, value); }
public string Saying5 { get => GetSaying(4); set => SetSaying(4, value); }
public bool IsMegaEvolutionUnlocked
{
get => (Data[Offset + 0x14A] & 0x01) != 0;
set => Data[Offset + 0x14A] = (byte)((Data[Offset + 0x14A] & 0xFE) | (value ? 1 : 0)); // in battle
}
public bool IsMegaRayquazaUnlocked
{
get => (Data[Offset + 0x14A] & 0x02) != 0;
set => Data[Offset + 0x14A] = (byte)((Data[Offset + 0x14A] & ~2) | (value ? 2 : 0)); // in battle
}
}
}

View file

@ -0,0 +1,27 @@
using System;
namespace PKHeX.Core
{
public sealed class MyStatus6XY : MyStatus6
{
public MyStatus6XY(SaveFile sav, int offset) : base(sav, offset) { }
public TrainerFashion6 Fashion
{
get => TrainerFashion6.GetFashion(SAV.Data, Offset + 0x30, SAV.Gender);
set => value.Write(Data, Offset + 0x30);
}
public string OT_Nick
{
get => SAV.GetString(Offset + 0x62, SAV6.ShortStringLength / 2);
set => SAV.SetData(SAV.SetString(value, SAV6.ShortStringLength / 2), Offset + 0x62);
}
public short EyeColor
{
get => BitConverter.ToInt16(Data, Offset + 0x148);
set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x148);
}
}
}

View file

@ -0,0 +1,78 @@
using System;
namespace PKHeX.Core
{
public class MysteryBlock6 : SaveBlock
{
private const int FlagStart = 0;
private const int MaxReceivedFlag = 2048;
private const int MaxCardsPresent = 24;
// private const int FlagRegionSize = (MaxReceivedFlag / 8); // 0x100
private const int CardStart = FlagStart + (MaxReceivedFlag / 8);
public MysteryBlock6(SAV6 sav, int offset) : base(sav) => Offset = offset;
public bool[] MysteryGiftReceivedFlags
{
get => Util.GitBitFlagArray(Data, Offset + FlagStart, MaxReceivedFlag);
set
{
if (value?.Length != MaxReceivedFlag)
return;
Util.SetBitFlagArray(Data, Offset + FlagStart, value);
SAV.Edited = true;
}
}
public MysteryGift[] MysteryGiftCards
{
get
{
var cards = new MysteryGift[MaxCardsPresent];
for (int i = 0; i < cards.Length; i++)
cards[i] = GetGift(i);
return cards;
}
set
{
int count = Math.Min(MaxCardsPresent, value.Length);
for (int i = 0; i < count; i++)
SetGift(value[i], i);
for (int i = value.Length; i < MaxCardsPresent; i++)
SetGift(new WC6(), i);
}
}
public MysteryGift GetGift(int index)
{
if ((uint)index > MaxCardsPresent)
throw new ArgumentOutOfRangeException(nameof(index));
var offset = GetGiftOffset(index);
var data = SAV.GetData(offset, WC6.Size);
return new WC6(data);
}
private int GetGiftOffset(int index) => Offset + CardStart + (index * WC6.Size);
public void SetGift(MysteryGift wc6, int index)
{
if ((uint)index > MaxCardsPresent)
throw new ArgumentOutOfRangeException(nameof(index));
if (wc6.Data.Length != WC6.Size)
throw new InvalidCastException(nameof(wc6));
if (wc6.CardID == 2048 && wc6.ItemID == 726) // Eon Ticket (OR/AS)
{
if (!(SAV is SAV6AO ao))
return;
// Set the special received data
var info = ao.Sango;
info.ReceiveEon();
info.EnableSendEon();
}
SAV.SetData(wc6.Data, GetGiftOffset(index));
}
}
}

View file

@ -0,0 +1,64 @@
using System;
namespace PKHeX.Core
{
public sealed class PlayTime6 : SaveBlock
{
public PlayTime6(SaveFile sav, int offset) : base(sav) => Offset = offset;
public int PlayedHours
{
get => BitConverter.ToUInt16(Data, Offset);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset);
}
public int PlayedMinutes
{
get => Data[Offset + 2];
set => Data[Offset + 2] = (byte)value;
}
public int PlayedSeconds
{
get => Data[Offset + 3];
set => Data[Offset + 3] = (byte)value;
}
private uint LastSaved { get => BitConverter.ToUInt32(Data, Offset + 0x4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x4); }
private int LastSavedYear { get => (int)(LastSaved & 0xFFF); set => LastSaved = (LastSaved & 0xFFFFF000) | (uint)value; }
private int LastSavedMonth { get => (int)(LastSaved >> 12 & 0xF); set => LastSaved = (LastSaved & 0xFFFF0FFF) | ((uint)value & 0xF) << 12; }
private int LastSavedDay { get => (int)(LastSaved >> 16 & 0x1F); set => LastSaved = (LastSaved & 0xFFE0FFFF) | ((uint)value & 0x1F) << 16; }
private int LastSavedHour { get => (int)(LastSaved >> 21 & 0x1F); set => LastSaved = (LastSaved & 0xFC1FFFFF) | ((uint)value & 0x1F) << 21; }
private int LastSavedMinute { get => (int)(LastSaved >> 26 & 0x3F); set => LastSaved = (LastSaved & 0x03FFFFFF) | ((uint)value & 0x3F) << 26; }
public string LastSavedTime => $"{LastSavedYear:0000}{LastSavedMonth:00}{LastSavedDay:00}{LastSavedHour:00}{LastSavedMinute:00}";
public DateTime? LastSavedDate
{
get => !Util.IsDateValid(LastSavedYear, LastSavedMonth, LastSavedDay)
? (DateTime?)null
: new DateTime(LastSavedYear, LastSavedMonth, LastSavedDay, LastSavedHour, LastSavedMinute, 0);
set
{
// Only update the properties if a value is provided.
if (value.HasValue)
{
var dt = value.Value;
LastSavedYear = dt.Year;
LastSavedMonth = dt.Month;
LastSavedDay = dt.Day;
LastSavedHour = dt.Hour;
LastSavedMinute = dt.Minute;
}
else // Clear the date.
{
// If code tries to access MetDate again, null will be returned.
LastSavedYear = 0;
LastSavedMonth = 0;
LastSavedDay = 0;
LastSavedHour = 0;
LastSavedMinute = 0;
}
}
}
}
}

View file

@ -0,0 +1,59 @@
using System;
namespace PKHeX.Core
{
public class Puff6 : SaveBlock
{
private const int MaxPuffID = 26; // Supreme Winter Poké Puff
private const int PuffSlots = 100;
public Puff6(SaveFile SAV, int offset) : base(SAV) => Offset = offset;
public byte[] Puffs
{
get => SAV.GetData(Offset, PuffSlots);
set => SAV.SetData(value, Offset);
}
public int PuffCount
{
get => BitConverter.ToInt32(Data, Offset + PuffSlots);
set => BitConverter.GetBytes(value).CopyTo(Data, Offset + PuffSlots);
}
public void Reset()
{
Array.Clear(Data, Offset, PuffSlots);
// Set the first few default Puffs
Data[Offset + 0] = 1;
Data[Offset + 1] = 2;
Data[Offset + 2] = 3;
Data[Offset + 3] = 4;
Data[Offset + 4] = 5;
PuffCount = 5;
}
public void MaxCheat(bool special = false)
{
if (special)
{
for (int i = 0; i < PuffSlots; i++)
Data[Offset + i] = (byte)(21 + Util.Rand.Next(2)); // Supreme Wish or Honor
}
else
{
for (int i = 0; i < PuffSlots; i++)
Data[Offset + i] = (byte)((i % MaxPuffID) + 1);
Util.Shuffle(Data, Offset, Offset + PuffSlots);
}
PuffCount = PuffSlots;
}
public void Sort(bool reverse = false)
{
Array.Sort(Data, Offset, PuffCount);
if (reverse)
Array.Reverse(Data, Offset, PuffCount);
}
}
}

View file

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace PKHeX.Core
{
public class Record6 : SaveBlock
{
public const int RecordCount = 200;
protected readonly IReadOnlyList<byte> RecordMax;
public Record6(SAV6 sav, int offset, IReadOnlyList<byte> maxes) : base(sav)
{
Offset = offset;
RecordMax = maxes;
}
public Record6(SAV7 sav, int offset, IReadOnlyList<byte> maxes) : base(sav)
{
Offset = offset;
RecordMax = maxes;
}
public int GetRecord(int recordID)
{
int ofs = Records.GetOffset(Offset, recordID);
if (recordID < 100)
return BitConverter.ToInt32(Data, ofs);
if (recordID < 200)
return BitConverter.ToInt16(Data, ofs);
Trace.Fail(nameof(recordID));
return 0;
}
public void SetRecord(int recordID, int value)
{
Debug.Assert(recordID < RecordCount);
int ofs = GetRecordOffset(recordID);
int max = GetRecordMax(recordID);
if (value > max)
value = max;
if (recordID < 100)
BitConverter.GetBytes(value).CopyTo(Data, ofs);
else if (recordID < 200)
BitConverter.GetBytes((ushort)value).CopyTo(Data, ofs);
else
Trace.Fail(nameof(recordID));
}
public int GetRecordMax(int recordID) => Records.GetMax(recordID, RecordMax);
public int GetRecordOffset(int recordID) => Records.GetOffset(Offset, recordID);
public void AddRecord(int recordID, int count = 1) => SetRecord(recordID, GetRecord(recordID) + count);
}
}

View file

@ -0,0 +1,32 @@
using System;
namespace PKHeX.Core
{
public class SangoInfoBlock : SaveBlock
{
public SangoInfoBlock(SaveFile SAV, int offset) : base(SAV) => Offset = offset;
private const uint EON_MAGIC = 0x225D73C2;
public uint EonTicketReceivedMagic // 0x319B8
{
get => BitConverter.ToUInt32(Data, Offset + 0x63B8);
set => SAV.SetData(BitConverter.GetBytes(value), Offset + 0x63B8);
}
public string SecretBaseQRText // 0x319BC -- 17*u16
{
get => SAV.GetString(Offset + 0x63BC, 0x10);
set => SAV.SetData(SAV.SetString(value, 0x10), Offset + 0x63BC);
}
public uint EonTicketSendMagic // 0x319DE
{
get => BitConverter.ToUInt32(Data, Offset + 0x63DE);
set => SAV.SetData(BitConverter.GetBytes(value), Offset + 0x63DE);
}
public void ReceiveEon() => EonTicketReceivedMagic = EON_MAGIC;
public void EnableSendEon() => EonTicketSendMagic = EON_MAGIC;
}
}

View file

@ -0,0 +1,60 @@
using System;
namespace PKHeX.Core
{
public class Situation6 : SaveBlock
{
public Situation6(SaveFile SAV, int offset) : base(SAV) => Offset = offset;
public int M
{
get => BitConverter.ToUInt16(Data, Offset + 0x02);
set
{
var val = BitConverter.GetBytes((ushort)value);
val.CopyTo(Data, Offset + 0x02);
val.CopyTo(Data, Offset + 0x02 + 0xF4);
}
}
public float X
{
get => BitConverter.ToSingle(Data, Offset + 0x10) / 18;
set
{
var val = BitConverter.GetBytes(value * 18);
val.CopyTo(Data, Offset + 0x10);
val.CopyTo(Data, Offset + 0x10 + 0xF4);
}
}
public float Z
{
get => BitConverter.ToSingle(Data, Offset + 0x14);
set
{
var val = BitConverter.GetBytes(value);
val.CopyTo(Data, Offset + 0x14);
val.CopyTo(Data, Offset + 0x14 + 0xF4);
}
}
public float Y
{
get => BitConverter.ToSingle(Data, Offset + 0x18) / 18;
set
{
var val = BitConverter.GetBytes(value * 18);
val.CopyTo(Data, Offset + 0x18);
val.CopyTo(Data, Offset + 0x18 + 0xF4);
}
}
// xy only
public int Style
{
get => Data[Offset + 0x14D];
set => Data[Offset + 0x14D] = (byte)value;
}
}
}

View file

@ -0,0 +1,40 @@
using System;
namespace PKHeX.Core
{
public class BattleTree7 : SaveBlock
{
public BattleTree7(SAV7 sav, int offset) : base(sav) => Offset = offset;
public int GetTreeStreak(int battletype, bool super, bool max)
{
if (battletype > 3)
throw new ArgumentException(nameof(battletype));
var offset = GetStreakOffset(battletype, super, max);
return BitConverter.ToUInt16(Data, Offset + offset);
}
public void SetTreeStreak(int value, int battletype, bool super, bool max)
{
if (battletype > 3)
throw new ArgumentException(nameof(battletype));
if (value > ushort.MaxValue)
value = ushort.MaxValue;
var offset = GetStreakOffset(battletype, super, max);
BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + offset);
}
private static int GetStreakOffset(int battletype, bool super, bool max)
{
int offset = 8 * battletype;
if (super)
offset += 2;
if (max)
offset += 4;
return offset;
}
}
}

View file

@ -0,0 +1,118 @@
using System;
namespace PKHeX.Core
{
public sealed class BoxLayout7 : SaveBlock
{
private const int BattleBoxFlags = 0x4C4;
private const int PCBackgrounds = 0x5C0;
private const int LastViewedBoxOffset = 0x5E3;
private const int PCFlags = 0x5E0;
private const int strlen = SAV6.LongStringLength / 2;
private const int TeamCount = 6;
private const int NONE_SELECTED = -1;
public readonly int[] TeamSlots = new int[TeamCount * 6];
public BoxLayout7(SAV7 sav, int offset) : base(sav) => Offset = offset;
public int GetBoxWallpaperOffset(int box) => Offset + PCBackgrounds + box;
public int GetBoxWallpaper(int box)
{
if ((uint)box > SAV.BoxCount)
return 0;
return Data[GetBoxWallpaperOffset(box)];
}
public void SetBoxWallpaper(int box, int value)
{
if ((uint)box > SAV.BoxCount)
return;
Data[GetBoxWallpaperOffset(box)] = (byte)value;
}
private int GetBoxNameOffset(int box) => Offset + (SAV6.LongStringLength * box);
public string GetBoxName(int box)
{
return SAV.GetString(Data, GetBoxNameOffset(box), SAV6.LongStringLength);
}
public void SetBoxName(int box, string value)
{
var data = SAV.SetString(value, strlen, strlen, 0);
var offset = GetBoxNameOffset(box) + (SAV6.LongStringLength * box);
SAV.SetData(data, offset);
}
public byte[] BoxFlags
{
get => new[] { Data[Offset + PCFlags] }; // bits for wallpaper unlocks
set
{
if (value.Length != 1)
return;
Data[Offset + PCFlags] = value[0];
}
}
public int BoxesUnlocked { get => Data[Offset + PCFlags + 1] - 1; set => Data[Offset + PCFlags + 1] = (byte)(value + 1); }
public int CurrentBox { get => Data[Offset + LastViewedBoxOffset]; set => Data[Offset + LastViewedBoxOffset] = (byte)value; }
public void LoadBattleTeams()
{
for (int i = 0; i < TeamCount * 6; i++)
{
short val = BitConverter.ToInt16(Data, Offset + BattleBoxFlags + (i * 2));
if (val < 0)
{
TeamSlots[i] = NONE_SELECTED;
continue;
}
int box = val >> 8;
int slot = val & 0xFF;
int index = (SAV.BoxSlotCount * box) + slot;
TeamSlots[i] = index & 0xFFFF;
}
}
public void ClearBattleTeams()
{
for (int i = 0; i < TeamSlots.Length; i++)
TeamSlots[i] = NONE_SELECTED;
for (int i = 0; i < TeamCount; i++)
SetIsTeamLocked(i, false);
}
public void SaveBattleTeams()
{
for (int i = 0; i < TeamCount * 6; i++)
{
int index = TeamSlots[i];
if (index < 0)
{
BitConverter.GetBytes((short)index).CopyTo(Data, Offset + BattleBoxFlags + (i * 2));
continue;
}
int box = index / SAV.BoxSlotCount;
int slot = index % SAV.BoxSlotCount;
int val = (box << 8) | slot;
BitConverter.GetBytes((short)val).CopyTo(Data, Offset + BattleBoxFlags + (i * 2));
}
}
public bool GetIsTeamLocked(int team) => Data[Offset + PCBackgrounds - TeamCount - team] == 1;
public void SetIsTeamLocked(int team, bool value) => Data[Offset + PCBackgrounds - TeamCount - team] = (byte)(value ? 1 : 0);
public string this[int i]
{
get => GetBoxName(i);
set => SetBoxName(i, value);
}
}
}

View file

@ -0,0 +1,86 @@
using System;
namespace PKHeX.Core
{
public sealed class ConfigSave7 : SaveBlock
{
/* ===32 bits===
* talkSpeed:2 0,1
* battleAnim:1 2
* battleStyle:1 3
* unknown:9 4..12
* buttonMode:2 13,14
* boxStatus:1 15
* everything else: unknown
*/
public ConfigSave7(SAV7 sav, int offset) : base(sav) => Offset = offset;
public int ConfigValue
{
get => BitConverter.ToInt32(Data, Offset);
set => BitConverter.GetBytes(value).CopyTo(Data, Offset);
}
public int TalkingSpeed
{
get => ConfigValue & 3;
set => ConfigValue = (ConfigValue & ~3) | (value & 3);
}
public int BattleAnimation
{
// Effects OFF = 1, Effects ON = 0
get => (ConfigValue >> 2) & 1;
set => ConfigValue = (ConfigValue & ~(1 << 2)) | (value << 2);
}
public int BattleStyle
{
// SET = 1, SWITCH = 0
get => (ConfigValue >> 3) & 1;
set => ConfigValue = (ConfigValue & ~(1 << 3)) | (value << 3);
}
// UNKNOWN?
public int ButtonMode
{
get => (ConfigValue >> 13) & 3;
set => ConfigValue = (ConfigValue & ~(1 << 13)) | (value << 13);
}
public int BoxStatus
{
// MANUAL = 1, AUTOMATIC = 0
get => (ConfigValue >> 15) & 1;
set => ConfigValue = (ConfigValue & ~(1 << 15)) | (value << 15);
}
// NOTE: BELOW COMES FROM LGPE. MAYBE THIS IS WHAT THEY USE THE FLAGS FOR?
/// <summary>
/// <see cref="LanguageID"/> for messages, stored with <see cref="LanguageID.UNUSED_6"/> skipped in the enumeration.
/// </summary>
public int Language
{
get => GetLanguageID((ConfigValue >> 4) & 0xF);
set => ConfigValue = ((ConfigValue & ~0xF0) | SetLanguageID(value) << 4);
}
private static int GetLanguageID(int i) => i >= (int)LanguageID.UNUSED_6 ? i + 1 : i; // sets langBank to LanguageID
private static int SetLanguageID(int i) => i > (int)LanguageID.UNUSED_6 ? i - 1 : i; // sets LanguageID to langBank
public enum BattleAnimationSetting
{
EffectsON,
EffectsOFF,
}
public enum BattleStyleSetting
{
SET,
SWITCH,
}
}
}

View file

@ -0,0 +1,43 @@
namespace PKHeX.Core
{
public class Daycare7 : SaveBlock
{
public const int DaycareSeedSize = 32; // 128 bits
public Daycare7(SAV7 sav, int offset) : base(sav) => Offset = offset;
public bool GetIsOccupied(int slot)
{
return Data[Offset + ((PKX.SIZE_6STORED + 1) * slot)] != 0;
}
public void SetOccupied(int slot, bool occupied)
{
Data[Offset + ((PKX.SIZE_6STORED + 1) * slot)] = (byte)(occupied ? 1 : 0);
}
public int GetDaycareSlotOffset(int slot)
{
return Offset + 1 + (slot * (PKX.SIZE_6STORED + 1));
}
public bool HasEgg
{
get => Data[Offset + 0x1D8] == 1;
set => Data[Offset + 0x1D8] = (byte)(value ? 1 : 0);
}
public string RNGSeed
{
get => Util.GetHexStringFromBytes(Data, Offset + 0x1DC, DaycareSeedSize / 2);
set
{
if (value.Length != DaycareSeedSize)
return;
var data = Util.GetBytesFromHexString(value);
SAV.SetData(data, Offset + 0x1DC);
}
}
}
}

View file

@ -0,0 +1,35 @@
using System;
using System.Linq;
namespace PKHeX.Core
{
public sealed class FashionBlock7 : SaveBlock
{
public FashionBlock7(SAV7 sav, int offset) : base(sav) => Offset = offset;
private const int FashionLength = 0x1A08;
public sealed class FashionItem
{
public bool IsOwned { get; set; }
public bool IsNew { get; set; }
}
public FashionItem[] Wardrobe
{
get
{
var data = SAV.GetData(Offset, 0x5A8);
return data.Select(b => new FashionItem { IsOwned = (b & 1) != 0, IsNew = (b & 2) != 0 }).ToArray();
}
set
{
if (value.Length != 0x5A8)
throw new ArgumentOutOfRangeException($"Unexpected size: 0x{value.Length:X}");
SAV.SetData(value.Select(t => (byte)((t.IsOwned ? 1 : 0) | (t.IsNew ? 2 : 0))).ToArray(), Offset);
}
}
public void Clear() => Array.Clear(Data, Offset, FashionLength);
}
}

View file

@ -0,0 +1,14 @@
namespace PKHeX.Core
{
public sealed class FieldMenu7 : SaveBlock
{
public FieldMenu7(SAV7 sav, int offset) : base(sav) => Offset = offset;
// USUM ONLY
public string RotomOT
{
get => SAV.GetString(Offset + 0x30, 0x1A);
set => SAV.SetString(value, SAV.OTLength).CopyTo(Data, Offset + 0x30);
}
}
}

View file

@ -0,0 +1,15 @@
using System;
namespace PKHeX.Core
{
public class FieldMoveModelSave7 : SaveBlock
{
public FieldMoveModelSave7(SAV7 sav, int offset) : base(sav) => Offset = offset;
public int M { get => BitConverter.ToUInt16(Data, Offset + 0x00); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x00); }
public float X { get => BitConverter.ToSingle(Data, Offset + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x08); }
public float Z { get => BitConverter.ToSingle(Data, Offset + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x10); }
public float Y { get => (int)BitConverter.ToSingle(Data, Offset + 0x18); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x18); }
public float R { get => (int)BitConverter.ToSingle(Data, Offset + 0x20); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x20); }
}
}

View file

@ -0,0 +1,19 @@
using System;
namespace PKHeX.Core
{
public sealed class GameTime7 : SaveBlock
{
public GameTime7(SaveFile sav, int offset) : base(sav) => Offset = offset;
public int ResumeYear { get => BitConverter.ToInt32(Data, Offset + 0x4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x4); }
public int ResumeMonth { get => Data[Offset + 0x8]; set => Data[Offset + 0x8] = (byte)value; }
public int ResumeDay { get => Data[Offset + 0x9]; set => Data[Offset + 0x9] = (byte)value; }
public int ResumeHour { get => Data[Offset + 0xB]; set => Data[Offset + 0xB] = (byte)value; }
public int ResumeMinute { get => Data[Offset + 0xC]; set => Data[Offset + 0xC] = (byte)value; }
public int ResumeSeconds { get => Data[Offset + 0xD]; set => Data[Offset + 0xD] = (byte)value; }
public uint SecondsToStart { get => BitConverter.ToUInt32(Data, Offset + 0x18); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x28); }
public uint SecondsToFame { get => BitConverter.ToUInt32(Data, Offset + 0x20); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x30); }
public ulong AlolaTime { get => BitConverter.ToUInt64(Data, Offset + 0x48); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x48); }
}
}

View file

@ -0,0 +1,86 @@
using System;
using System.Text;
namespace PKHeX.Core
{
public sealed class JoinFesta7 : SaveBlock
{
public JoinFesta7(SAV7 sav, int offset) : base(sav) => Offset = offset;
public int FestaCoins
{
get => BitConverter.ToInt32(Data, Offset + 0x508);
set
{
if (value > 9999999)
value = 9999999;
BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x508);
TotalFestaCoins = ((SAV7)SAV).GetRecord(038) + value; // UsedFestaCoins
}
}
private int TotalFestaCoins
{
get => BitConverter.ToInt32(Data, Offset + 0x50C);
set
{
if (value > 9999999)
value = 9999999;
BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x50C);
}
}
public string FestivalPlazaName
{
get => Util.TrimFromZero(Encoding.Unicode.GetString(Data, Offset + 0x510, 0x2A));
set
{
const int max = 20;
if (value.Length > max)
value = value.Substring(0, max);
Encoding.Unicode.GetBytes(value.PadRight(value.Length + 1, '\0')).CopyTo(Data, Offset + 0x510);
}
}
public ushort FestaRank { get => BitConverter.ToUInt16(Data, Offset + 0x53A); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x53A); }
public ushort GetFestaMessage(int index) => BitConverter.ToUInt16(Data, Offset + (index * 2));
public void SetFestaMessage(int index, ushort value) => BitConverter.GetBytes(value).CopyTo(Data, Offset + (index * 2));
public bool GetFestaPhraseUnlocked(int index) => Data[Offset + 0x2A50 + index] != 0; //index: 0 to 105:commonPhrases, 106:Lv100!
public void SetFestaPhraseUnlocked(int index, bool value)
{
if (GetFestaPhraseUnlocked(index) != value)
Data[Offset + 0x2A50 + index] = (byte)(value ? 1 : 0);
}
public byte GetFestPrizeReceived(int index) => Data[Offset + 0x53C + index];
public void SetFestaPrizeReceived(int index, byte value) => Data[Offset + 0x53C + index] = value;
private int FestaYear { get => BitConverter.ToInt32(Data, Offset + 0x2F0); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x2F0); }
private int FestaMonth { get => BitConverter.ToInt32(Data, Offset + 0x2F4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x2F4); }
private int FestaDay { get => BitConverter.ToInt32(Data, Offset + 0x2F8); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x2F8); }
private int FestaHour { get => BitConverter.ToInt32(Data, Offset + 0x300); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x300); }
private int FestaMinute { get => BitConverter.ToInt32(Data, Offset + 0x304); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x304); }
private int FestaSecond { get => BitConverter.ToInt32(Data, Offset + 0x308); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x308); }
public DateTime? FestaDate
{
get => FestaYear >= 0 && FestaMonth > 0 && FestaDay > 0 && FestaHour >= 0 && FestaMinute >= 0 && FestaSecond >= 0 && Util.IsDateValid(FestaYear, FestaMonth, FestaDay)
? new DateTime(FestaYear, FestaMonth, FestaDay, FestaHour, FestaMinute, FestaSecond)
: (DateTime?)null;
set
{
if (value.HasValue)
{
DateTime dt = value.Value;
FestaYear = dt.Year;
FestaMonth = dt.Month;
FestaDay = dt.Day;
FestaHour = dt.Hour;
FestaMinute = dt.Minute;
FestaSecond = dt.Second;
}
}
}
}
}

View file

@ -0,0 +1,73 @@
using System;
namespace PKHeX.Core
{
public sealed class Misc7 : SaveBlock
{
public Misc7(SAV7 sav, int offset) : base(sav) => Offset = offset;
public uint Money
{
get => BitConverter.ToUInt32(Data, Offset + 0x4);
set
{
if (value > 9999999)
value = 9999999;
SAV.SetData(BitConverter.GetBytes(value), Offset + 0x4);
}
}
public uint Stamps
{
get => (BitConverter.ToUInt32(Data, Offset + 0x08) << 13) >> 17; // 15 stamps; discard top13, lowest4
set
{
uint flags = BitConverter.ToUInt32(Data, Offset + 0x08) & 0xFFF8000F;
flags |= (value & 0x7FFF) << 4;
SAV.SetData(BitConverter.GetBytes(flags), Offset + 0x08);
}
}
public uint BP
{
get => BitConverter.ToUInt32(Data, Offset + 0x11C);
set
{
if (value > 9999)
value = 9999;
SAV.SetData(BitConverter.GetBytes(value), Offset + 0x11C);
}
}
public int Vivillon
{
get => Data[Offset + 0x130] & 0x1F;
set => Data[Offset + 0x130] = (byte)((Data[Offset + 0x130] & ~0x1F) | (value & 0x1F));
}
public uint StarterEncryptionConstant
{
get => BitConverter.ToUInt32(Data, Offset + 0x148);
set => SAV.SetData(BitConverter.GetBytes(value), Offset + 0x148);
}
public int DaysFromRefreshed
{
get => Data[Offset + 0x123];
set => Data[Offset + 0x123] = (byte)value;
}
public int GetSurfScore(int recordID)
{
if (recordID < 0 || recordID > 4)
recordID = 0;
return BitConverter.ToInt32(Data, Offset + 0x138 + (4 * recordID));
}
public void SetSurfScore(int recordID, int score)
{
if (recordID < 0 || recordID > 4)
recordID = 0;
SAV.SetData(BitConverter.GetBytes(score), Offset + 0x138 + (4 * recordID));
}
}
}

View file

@ -4,11 +4,8 @@ namespace PKHeX.Core
{
public sealed class Misc7b : SaveBlock
{
private readonly SaveFile SAV;
public Misc7b(SaveFile sav) : base(sav)
{
SAV = sav;
Offset = ((SAV7b)sav).GetBlockOffset(BelugaBlockIndex.Misc);
}

View file

@ -0,0 +1,32 @@
namespace PKHeX.Core
{
public sealed class MyItem7SM : MyItem
{
private const int HeldItem = 0; // 430 (Case 0)
private const int KeyItem = HeldItem + (4 * 430); // 184 (Case 4)
private const int TMHM = KeyItem + (4 * 184); // 108 (Case 2)
private const int Medicine = TMHM + (4 * 108); // 64 (Case 1)
private const int Berry = Medicine + (4 * 64); // 72 (Case 3)
private const int ZCrystals = Berry + (4 * 72); // 30 (Case 5)
public MyItem7SM(SaveFile SAV, int offset) : base(SAV) => Offset = offset;
public override InventoryPouch[] Inventory
{
get
{
InventoryPouch[] pouch =
{
new InventoryPouch7(InventoryType.Medicine, Legal.Pouch_Medicine_SM, 999, Offset + Medicine),
new InventoryPouch7(InventoryType.Items, Legal.Pouch_Items_SM, 999, Offset + HeldItem),
new InventoryPouch7(InventoryType.TMHMs, Legal.Pouch_TMHM_SM, 1, Offset + TMHM),
new InventoryPouch7(InventoryType.Berries, Legal.Pouch_Berries_SM, 999, Offset + Berry),
new InventoryPouch7(InventoryType.KeyItems, Legal.Pouch_Key_SM, 1, Offset + KeyItem),
new InventoryPouch7(InventoryType.ZCrystals, Legal.Pouch_ZCrystal_SM, 1, Offset + ZCrystals),
};
return pouch.LoadAll(Data);
}
set => value.SaveAll(Data);
}
}
}

View file

@ -0,0 +1,34 @@
namespace PKHeX.Core
{
public sealed class MyItem7USUM : MyItem
{
private const int HeldItem = 0; // 427 (Case 0)
private const int KeyItem = HeldItem + (4 * 427); // 198 (Case 4)
private const int TMHM = KeyItem + (4 * 198); // 108 (Case 2)
private const int Medicine = TMHM + (4 * 108); // 60 (Case 1)
private const int Berry = Medicine + (4 * 60); // 67 (Case 3)
private const int ZCrystals = Berry + (4 * 67); // 35 (Case 5)
private const int BattleItems = ZCrystals + (4 * 35); // 11 (Case 6)
public MyItem7USUM(SaveFile SAV, int offset) : base(SAV) => Offset = offset;
public override InventoryPouch[] Inventory
{
get
{
InventoryPouch[] pouch =
{
new InventoryPouch7(InventoryType.Medicine, Legal.Pouch_Medicine_SM, 999, Medicine),
new InventoryPouch7(InventoryType.Items, Legal.Pouch_Items_SM, 999, HeldItem),
new InventoryPouch7(InventoryType.TMHMs, Legal.Pouch_TMHM_SM, 1, TMHM),
new InventoryPouch7(InventoryType.Berries, Legal.Pouch_Berries_SM, 999, Berry),
new InventoryPouch7(InventoryType.KeyItems, Legal.Pouch_Key_USUM, 1, KeyItem),
new InventoryPouch7(InventoryType.ZCrystals, Legal.Pouch_ZCrystal_USUM, 1, ZCrystals),
new InventoryPouch7(InventoryType.BattleItems, Legal.Pouch_Roto_USUM, 999, BattleItems),
};
return pouch.LoadAll(Data);
}
set => value.SaveAll(Data);
}
}
}

View file

@ -0,0 +1,140 @@
using System;
using System.Linq;
namespace PKHeX.Core
{
public class MyStatus7 : SaveBlock
{
public const int GameSyncIDSize = 16; // 64 bits
public const int NexUniqueIDSize = 32; // 128 bits
public MyStatus7(SAV7 sav, int offset) : base(sav) => Offset = offset;
public int TID
{
get => BitConverter.ToUInt16(Data, Offset + 0);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0);
}
public int SID
{
get => BitConverter.ToUInt16(Data, Offset + 2);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 2);
}
public int Game
{
get => Data[Offset + 4];
set => Data[Offset + 4] = (byte)value;
}
public int Gender
{
get => Data[Offset + 5];
set => Data[Offset + 5] = (byte)value;
}
public string GameSyncID
{
get => Util.GetHexStringFromBytes(Data, Offset + 0x10, GameSyncIDSize / 2);
set
{
if (value == null)
return;
if (value.Length != GameSyncIDSize)
return;
var data = Util.GetBytesFromHexString(value);
SAV.SetData(data, Offset + 0x10);
}
}
public string NexUniqueID
{
get => Util.GetHexStringFromBytes(Data, Offset + 0x18, NexUniqueIDSize / 2);
set
{
if (value == null)
return;
if (value.Length != NexUniqueIDSize)
return;
var data = Util.GetBytesFromHexString(value);
SAV.SetData(data, Offset + 0x18);
}
}
public byte[] FestaID // 12byte
{
get => Data.Skip(Offset + 0x28).Take(4).Concat(Data.Skip(Offset + 0x18).Take(8)).ToArray();
set
{
if (value?.Length != 12)
return;
Array.Copy(value, 0, Data, Offset + 0x28, 4);
Array.Copy(value, 4, Data, Offset + 0x18, 8);
}
}
public int SubRegion
{
get => Data[Offset + 0x2E];
set => Data[Offset + 0x2E] = (byte)value;
}
public int Country
{
get => Data[Offset + 0x2F];
set => Data[Offset + 0x2F] = (byte)value;
}
public int ConsoleRegion
{
get => Data[Offset + 0x34];
set => Data[Offset + 0x34] = (byte)value;
}
public int Language
{
get => Data[Offset + 0x35];
set => Data[Offset + 0x35] = (byte)value;
}
public string OT
{
get => SAV.GetString(Offset + 0x38, 0x1A);
set => SAV.SetString(value, SAV.OTLength).CopyTo(Data, Offset + 0x38);
}
public int DressUpSkinColor
{
get => (Data[Offset + 0x54] >> 2) & 7;
set => Data[Offset + 0x54] = (byte)((Data[Offset + 0x54] & ~(7 << 2)) | (value << 2));
}
public int MultiplayerSpriteID
{
get => Data[Offset + 0x58];
set => Data[Offset + 0x58] = (byte)value;
}
public bool MegaUnlocked
{
get => (Data[Offset + 0x78] & 0x01) != 0;
set => Data[Offset + 0x78] = (byte)((Data[Offset + 0x78] & 0xFE) | (value ? 1 : 0)); // in battle
}
public bool ZMoveUnlocked
{
get => (Data[Offset + 0x78] & 2) != 0;
set => Data[Offset + 0x78] = (byte)((Data[Offset + 0x78] & ~2) | (value ? 2 : 0)); // in battle
}
public int BallThrowType
{
get => Data[Offset + 0x7A];
set => Data[Offset + 0x7A] = (byte)(value > 8 ? 0 : value);
}
}
}

View file

@ -5,11 +5,8 @@ namespace PKHeX.Core
{
public sealed class MyStatus7b : SaveBlock
{
private readonly SaveFile SAV;
public MyStatus7b(SaveFile sav) : base(sav)
{
SAV = sav;
Offset = ((SAV7b)sav).GetBlockOffset(BelugaBlockIndex.MyStatus);
}

View file

@ -0,0 +1,69 @@
using System;
namespace PKHeX.Core
{
public sealed class MysteryBlock7 : SaveBlock
{
private const int FlagStart = 0;
private const int MaxReceivedFlag = 2048;
private const int MaxCardsPresent = 48;
// private const int FlagRegionSize = (MaxReceivedFlag / 8); // 0x100
private const int CardStart = FlagStart + (MaxReceivedFlag / 8);
public MysteryBlock7(SAV7 sav, int offset) : base(sav) => Offset = offset;
// Mystery Gift
public bool[] MysteryGiftReceivedFlags
{
get => Util.GitBitFlagArray(Data, Offset + FlagStart, MaxReceivedFlag);
set
{
if (value?.Length != MaxReceivedFlag)
return;
Util.SetBitFlagArray(Data, Offset + FlagStart, value);
SAV.Edited = true;
}
}
public MysteryGift[] MysteryGiftCards
{
get
{
var cards = new MysteryGift[MaxCardsPresent];
for (int i = 0; i < cards.Length; i++)
cards[i] = GetGift(i);
return cards;
}
set
{
int count = Math.Min(MaxCardsPresent, value.Length);
for (int i = 0; i < count; i++)
SetGift(value[i], i);
for (int i = value.Length; i < MaxCardsPresent; i++)
SetGift(new WC7(), i);
}
}
private MysteryGift GetGift(int index)
{
if ((uint)index > MaxCardsPresent)
throw new ArgumentOutOfRangeException(nameof(index));
var offset = GetGiftOffset(index);
var data = SAV.GetData(offset, WC6.Size);
return new WC7(data);
}
private int GetGiftOffset(int index) => Offset + CardStart + (index * WC7.Size);
private void SetGift(MysteryGift wc7, int index)
{
if ((uint)index > MaxCardsPresent)
throw new ArgumentOutOfRangeException(nameof(index));
if (wc7.Data.Length != WC7.Size)
throw new InvalidCastException(nameof(wc7));
SAV.SetData(wc7.Data, GetGiftOffset(index));
}
}
}

View file

@ -0,0 +1,58 @@
using System;
namespace PKHeX.Core
{
public class PokeFinder7 : SaveBlock
{
public PokeFinder7(SAV7 sav, int offset) : base(sav) => Offset = offset;
public ushort CameraVersion
{
get => BitConverter.ToUInt16(Data, Offset + 0x00);
set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x00);
}
public bool GyroFlag
{
get => BitConverter.ToUInt16(Data, Offset + 0x02) == 1;
set => BitConverter.GetBytes((ushort)(value ? 1 : 0)).CopyTo(Data, Offset + 0x02);
}
public uint SnapCount
{
get => BitConverter.ToUInt32(Data, Offset + 0x04);
set
{
if (value > 9999999) // Top bound is unchecked, check anyway
value = 9999999;
BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x04);
}
}
public uint ThumbsTotalValue
{
get => BitConverter.ToUInt32(Data, Offset + 0x0C);
set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x0C);
}
public uint ThumbsHighValue
{
get => BitConverter.ToUInt32(Data, Offset + 0x10);
set
{
if (value > 9_999_999)
value = 9_999_999;
BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x10);
if (value > ThumbsTotalValue)
ThumbsTotalValue = value;
}
}
public ushort TutorialFlags
{
get => BitConverter.ToUInt16(Data, Offset + 0x14);
set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x14);
}
}
}

View file

@ -16,8 +16,6 @@ namespace PKHeX.Core
/// </remarks>
public sealed class PokeListHeader : SaveBlock
{
private readonly SaveFile SAV;
/// <summary>
/// Raw representation of data, casted to ushort[].
/// </summary>
@ -30,7 +28,6 @@ namespace PKHeX.Core
public PokeListHeader(SaveFile sav) : base(sav)
{
SAV = sav;
Offset = ((SAV7b)sav).GetBlockOffset(BelugaBlockIndex.PokeListHeader);
PokeListInfo = LoadPointerData();
if (!sav.Exportable)

View file

@ -0,0 +1,52 @@
using System;
namespace PKHeX.Core
{
public sealed class ResortSave7 : SaveBlock
{
public ResortSave7(SAV7 sav, int offset) : base(sav) => Offset = offset;
public const int ResortCount = 93;
public int GetResortSlotOffset(int slot) => Offset + 0x16 + (slot * PKX.SIZE_6STORED);
public PKM[] ResortPKM
{
get
{
PKM[] data = new PKM[ResortCount];
for (int i = 0; i < data.Length; i++)
{
var bytes = SAV.GetData(GetResortSlotOffset(i), PKX.SIZE_6STORED);
data[i] = new PK7(bytes) { Identifier = $"Resort Slot {i}" };
}
return data;
}
set
{
if (value?.Length != ResortCount)
throw new ArgumentException(nameof(ResortCount));
for (int i = 0; i < value.Length; i++)
SAV.SetStoredSlot(value[i], GetResortSlotOffset(i));
}
}
public int GetPokebeanCount(int bean_id)
{
if ((uint)bean_id > 14)
throw new ArgumentException("Invalid bean id!");
return Data[Offset + 0x564C + bean_id];
}
public void SetPokebeanCount(int bean_id, int count)
{
if ((uint)bean_id > 14)
throw new ArgumentException("Invalid bean id!");
if (count < 0)
count = 0;
if (count > 255)
count = 255;
Data[Offset + 0x564C + bean_id] = (byte)count;
}
}
}

View file

@ -7,6 +7,7 @@ namespace PKHeX.Core
{
public int Offset { get; protected set; }
protected readonly byte[] Data;
protected SaveBlock(SaveFile SAV) => Data = SAV.Data;
protected readonly SaveFile SAV;
protected SaveBlock(SaveFile sav) => Data = (SAV = sav).Data;
}
}

View file

@ -0,0 +1,62 @@
using System;
namespace PKHeX.Core
{
public sealed class Situation7 : SaveBlock
{
public Situation7(SAV7 sav, int offset) : base(sav) => Offset = offset;
// "StartLocation"
public int M { get => BitConverter.ToUInt16(Data, Offset + 0x00); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x00); }
public float X { get => BitConverter.ToSingle(Data, Offset + 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x08); }
public float Z { get => BitConverter.ToSingle(Data, Offset + 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x10); }
public float Y { get => (int)BitConverter.ToSingle(Data, Offset + 0x18); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x18); }
public float R { get => (int)BitConverter.ToSingle(Data, Offset + 0x20); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x20); }
public void UpdateOverworldCoordinates()
{
var o = ((SAV7) SAV).OverworldBlock;
o.M = M;
o.X = X;
o.Z = Z;
o.Y = Y;
o.R = R;
}
public int SpecialLocation
{
get => Data[Offset + 0x24];
set => Data[Offset + 0x24] = (byte)value;
}
public int WarpContinueRequest
{
get => Data[Offset + 0x6E];
set => Data[Offset + 0x6E] = (byte)value;
}
public int StepCountEgg
{
get => BitConverter.ToInt32(Data, Offset + 0x70);
set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x70);
}
public int LastZoneID
{
get => BitConverter.ToUInt16(Data, Offset + 0x74);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x74);
}
public int StepCountFriendship
{
get => BitConverter.ToUInt16(Data, Offset + 0x76);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x76);
}
public int StepCountAffection // Kawaigari
{
get => BitConverter.ToUInt16(Data, Offset + 0x78);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0x78);
}
}
}

View file

@ -47,7 +47,7 @@ namespace PKHeX.Core
{
int[] beans = new int[Count];
for (int i = 0; i < beans.Length; i++)
beans[i] = SAV.GetPokebeanCount(i);
beans[i] = SAV.ResortSave.GetPokebeanCount(i);
return beans;
}
@ -56,13 +56,13 @@ namespace PKHeX.Core
if (beans.Count != Count)
return;
for (int i = 0; i < beans.Count; i++)
SAV.SetPokebeanCount(i, beans[i]);
SAV.ResortSave.SetPokebeanCount(i, beans[i]);
}
public void SetCountAll(int val)
{
for (int i = 0; i < Count; i++)
SAV.SetPokebeanCount(i, val);
SAV.ResortSave.SetPokebeanCount(i, val);
}
}
}

View file

@ -3,7 +3,7 @@ using static PKHeX.Core.OPower6Type;
namespace PKHeX.Core
{
public class OPower6
public class OPower6 : SaveBlock
{
private static readonly OPowerFlagSet[] Mapping =
{
@ -41,15 +41,7 @@ namespace PKHeX.Core
}
}
private readonly byte[] Data;
private readonly int Offset;
public OPower6(byte[] data, int offset)
{
Offset = offset;
Data = data;
}
public OPower6(SaveFile sav, int offset) : base(sav) => Offset = offset;
private OPowerFlagSet this[OPower6Type type] => Array.Find(Mapping, t => t.Identifier == type);
public int GetOPowerCount(OPower6Type type) => this[type].BaseCount;

View file

@ -73,7 +73,7 @@ namespace PKHeX.WinForms
{
int start = j;
int end = j;
if (SAV7.SanitizeFormsToIterate(spec, out int s, out int n, j, Parent.USUM))
if (Zukan7.SanitizeFormsToIterate(spec, out int s, out int n, j, Parent.USUM))
{
start = s;
end = n;

View file

@ -0,0 +1,164 @@
using System;
namespace PKHeX.Core
{
public class Zukan5 : Zukan
{
protected override int OFS_SEEN => OFS_CAUGHT + BitSeenSize;
protected override int OFS_CAUGHT => 0x8;
protected override int BitSeenSize => 0x54;
protected override int DexLangFlagByteCount => 7;
protected override int DexLangIDCount => 7;
public Zukan5(SaveFile sav, int dex, int langflag)
{
SAV = sav;
PokeDex = dex;
PokeDexLanguageFlags = langflag;
var wrap = SAV.BW ? DexFormUtil.GetDexFormIndexBW : (Func<int, int, int>)DexFormUtil.GetDexFormIndexB2W2;
DexFormIndexFetcher = (spec, form, _) => wrap(spec, form);
}
protected override int GetDexLangFlag(int lang)
{
lang--;
if (lang > 5)
lang--; // 0-6 language vals
if ((uint)lang > 5)
return -1;
return lang;
}
protected override bool GetSaneFormsToIterate(int species, out int formStart, out int formEnd, int formIn)
{
formStart = 0;
formEnd = 0;
return false;
}
protected override void SetSpindaDexData(PKM pkm, bool alreadySeen)
{
}
protected override void SetAllDexFlagsLanguage(int bit, int lang, bool value = true)
{
lang = GetDexLangFlag(lang);
if (lang < 0)
return;
// Set the Language
int lbit = (bit * DexLangIDCount) + lang;
if (bit < 493) // shifted by 1, Gen5 species do not have international language bits
SetFlag(PokeDexLanguageFlags, lbit, value);
}
protected override void SetAllDexSeenFlags(int baseBit, int altform, int gender, bool isShiny, bool value = true)
{
var shiny = isShiny ? 1 : 0;
SetDexFlags(baseBit, 0, gender, shiny);
SetFormFlags(baseBit + 1, altform, shiny, value);
}
public override void SetDex(PKM pkm)
{
if (pkm.Species == 0)
return;
if (pkm.Species > SAV.MaxSpeciesID)
return;
int bit = pkm.Species - 1;
SetCaughtFlag(bit);
// Set the [Species/Gender/Shiny] Seen Flag
SetAllDexSeenFlags(bit, pkm.AltForm, pkm.Gender, pkm.IsShiny);
SetAllDexFlagsLanguage(bit, pkm.Language);
SetFormFlags(pkm);
}
private void SetCaughtFlag(int bit) => SetFlag(OFS_CAUGHT, bit);
private int FormLen => SAV.B2W2 ? 0xB : 0x9;
private int FormDex => PokeDex + 0x8 + (BitSeenSize * 9);
private void SetFormFlags(PKM pkm)
{
int species = pkm.Species;
int form = pkm.AltForm;
var shiny = pkm.IsShiny ? 1 : 0;
SetFormFlags(species, form, shiny);
}
private void SetFormFlags(int species, int form, int shiny, bool value = true)
{
int fc = SAV.Personal[species].FormeCount;
int f = DexFormIndexFetcher(species, fc, SAV.MaxSpeciesID - 1);
if (f < 0)
return;
var bit = f + form;
// Set Form Seen Flag
SetFlag(FormDex + (FormLen * shiny), bit, value);
// Set Displayed Flag if necessary, check all flags
if (!value || !GetIsFormDisplayed(f, fc))
SetFlag(FormDex + (FormLen * (2 + shiny)), bit, value);
}
private bool GetIsFormDisplayed(int f, int fc)
{
for (int i = 0; i < fc; i++)
{
var bit2 = f + i;
if (GetFlag(FormDex + (FormLen * 2), bit2)) // Nonshiny
return true; // already set
if (GetFlag(FormDex + (FormLen * 3), bit2)) // Shiny
return true; // already set
}
return false;
}
public bool[] GetLanguageBitflags(int species)
{
var result = new bool[DexLangIDCount];
int bit = species - 1;
for (int i = 0; i < DexLangIDCount; i++)
{
int lbit = (bit * DexLangIDCount) + i;
result[i] = GetFlag(PokeDexLanguageFlags, lbit);
}
return result;
}
public void SetLanguageBitflags(int species, bool[] value)
{
int bit = species - 1;
for (int i = 0; i < DexLangIDCount; i++)
{
int lbit = (bit * DexLangIDCount) + i;
SetFlag(PokeDexLanguageFlags, lbit, value[i]);
}
}
public void ToggleLanguageFlagsAll(bool value)
{
var arr = GetBlankLanguageBits(value);
for (int i = 1; i <= SAV.MaxSpeciesID; i++)
SetLanguageBitflags(i, arr);
}
public void ToggleLanguageFlagsSingle(int species, bool value)
{
var arr = GetBlankLanguageBits(value);
SetLanguageBitflags(species, arr);
}
private bool[] GetBlankLanguageBits(bool value)
{
var result = new bool[DexLangIDCount];
for (int i = 0; i < DexLangIDCount; i++)
result[i] = value;
return result;
}
}
}

View file

@ -38,7 +38,6 @@ namespace PKHeX.Core
protected override void SetSpindaDexData(PKM pkm, bool alreadySeen)
{
}
protected override void SetAllDexFlagsLanguage(int bit, int lang, bool value = true)
@ -82,13 +81,13 @@ namespace PKHeX.Core
private void SetCaughtFlag(int bit, int origin)
{
// Owned quality flag
if (SAV.ORAS)
if (SAV is SAV6AO ao)
{
SetFlag(OFS_CAUGHT, bit);
// Set DexNav count (only if not encountered previously)
var count = ((SAV6)SAV).GetEncounterCount(bit);
var count = ao.GetEncounterCount(bit);
if (count == 0)
((SAV6)SAV).SetEncounterCount(bit, 1);
ao.SetEncounterCount(bit, 1);
}
else
{

View file

@ -73,6 +73,11 @@ namespace PKHeX.Core
}
protected override bool GetSaneFormsToIterate(int species, out int formStart, out int formEnd, int formIn)
{
return SanitizeFormsToIterate(species, out formStart, out formEnd, formIn, SAV.USUM);
}
public static bool SanitizeFormsToIterate(int species, out int formStart, out int formEnd, int formIn, bool USUM)
{
// 004AA370 in Moon
// Simplified in terms of usage -- only overrides to give all the battle forms for a pkm
@ -83,6 +88,12 @@ namespace PKHeX.Core
formEnd = 3;
return true;
case 421: // Cherrim
case 555: // Darmanitan
case 648: // Meloetta
case 746: // Wishiwashi
case 778: // Mimikyu
// Alolans
case 020: // Raticate
case 105: // Marowak
formStart = 0;
@ -100,22 +111,13 @@ namespace PKHeX.Core
case 744: // Rockruff
break;
case 421: // Cherrim
case 555: // Darmanitan
case 648: // Meloetta
case 746: // Wishiwashi
case 778: // Mimikyu
formStart = 0;
formEnd = 1;
return true;
case 774 when formIn <= 6: // Minior
break; // don't give meteor forms except the first
case 718 when formIn > 1:
break;
default:
int count = SAV.USUM ? DexFormUtil.GetDexFormCountUSUM(species) : DexFormUtil.GetDexFormCountSM(species);
int count = USUM ? DexFormUtil.GetDexFormCountUSUM(species) : DexFormUtil.GetDexFormCountSM(species);
formStart = formEnd = 0;
return count < formIn;
}

View file

@ -7,8 +7,8 @@ namespace PKHeX.Core
/// </summary>
public static class Records
{
private const byte LargeRecordCount = 100;
private const byte SmallRecordCount = 100;
private const byte LargeRecordCount = 100; // int32
private const byte SmallRecordCount = 100; // int16
private const byte Count = LargeRecordCount + SmallRecordCount;
/// <summary>
@ -27,9 +27,9 @@ namespace PKHeX.Core
public static int GetOffset(int baseOfs, int recordID)
{
if (recordID < LargeRecordCount)
return baseOfs + (recordID * 4);
return baseOfs + (recordID * sizeof(int));
if (recordID < Count)
return baseOfs + (recordID * 2) + 200; // first 100 are 4bytes, so bias the difference
return baseOfs + (recordID * sizeof(ushort)) + (LargeRecordCount * sizeof(int)); // first 100 are 4bytes, so bias the difference
return -1;
}

View file

@ -77,22 +77,23 @@ namespace PKHeX.Core
/// <summary>Determines the type of the provided save data.</summary>
/// <param name="data">Save data of which to determine the origins of</param>
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
private static GameVersion GetSAVGeneration(byte[] data)
private static GameVersion GetSAVType(byte[] data)
{
if (GetIsG1SAV(data) != GameVersion.Invalid)
return GameVersion.Gen1;
if (GetIsG2SAV(data) != GameVersion.Invalid)
return GameVersion.Gen2;
if (GetIsG3SAV(data) != GameVersion.Invalid)
return GameVersion.Gen3;
if (GetIsG4SAV(data) != GameVersion.Invalid)
return GameVersion.Gen4;
if (GetIsG5SAV(data) != GameVersion.Invalid)
return GameVersion.Gen5;
if (GetIsG6SAV(data) != GameVersion.Invalid)
return GameVersion.Gen6;
if (GetIsG7SAV(data) != GameVersion.Invalid)
return GameVersion.Gen7;
GameVersion ver;
if ((ver = GetIsG1SAV(data)) != GameVersion.Invalid)
return ver;
if ((ver = GetIsG2SAV(data)) != GameVersion.Invalid)
return ver;
if ((ver = GetIsG3SAV(data)) != GameVersion.Invalid)
return ver;
if ((ver = GetIsG4SAV(data)) != GameVersion.Invalid)
return ver;
if ((ver = GetIsG5SAV(data)) != GameVersion.Invalid)
return ver;
if ((ver = GetIsG6SAV(data)) != GameVersion.Invalid)
return ver;
if ((ver = GetIsG7SAV(data)) != GameVersion.Invalid)
return ver;
if (GetIsBelugaSAV(data) != GameVersion.Invalid)
return GameVersion.GG;
@ -106,13 +107,13 @@ namespace PKHeX.Core
return GameVersion.BATREV;
if (GetIsBank7(data)) // pokebank
return GameVersion.USUM;
return GameVersion.Gen7;
if (GetIsBank4(data)) // pokestock
return GameVersion.HGSS;
return GameVersion.Gen4;
if (GetIsBank3(data)) // pokestock
return GameVersion.RS;
return GameVersion.Gen3;
if (GetIsRanch4(data)) // ranch
return GameVersion.DP;
return GameVersion.DPPt;
return GameVersion.Invalid;
}
@ -464,16 +465,33 @@ namespace PKHeX.Core
private static SaveFile GetVariantSAVInternal(byte[] data)
{
switch (GetSAVGeneration(data))
switch (GetSAVType(data))
{
// Main Games
case GameVersion.Gen1: return new SAV1(data);
case GameVersion.Gen2: return new SAV2(data);
case GameVersion.Gen3: return new SAV3(data);
case GameVersion.Gen4: return new SAV4(data);
case GameVersion.Gen5: return new SAV5(data);
case GameVersion.Gen6: return new SAV6(data);
case GameVersion.Gen7: return new SAV7(data);
case GameVersion.RBY: return new SAV1(data);
case GameVersion.GS:
case GameVersion.C: return new SAV2(data);
case GameVersion.RS:
case GameVersion.E:
case GameVersion.FRLG: return new SAV3(data);
case GameVersion.DP:
case GameVersion.Pt:
case GameVersion.HGSS: return new SAV4(data);
case GameVersion.BW: return new SAV5BW(data);
case GameVersion.B2W2: return new SAV5B2W2(data);
case GameVersion.XY: return new SAV6XY(data);
case GameVersion.ORAS: return new SAV6AO(data);
case GameVersion.ORASDEMO: return new SAV6AODemo(data);
case GameVersion.SM:
return new SAV7SM(data);
case GameVersion.USUM:
return new SAV7USUM(data);
// Side Games
case GameVersion.COLO: return new SAV3Colosseum(data);
@ -483,10 +501,10 @@ namespace PKHeX.Core
case GameVersion.GG: return new SAV7b(data);
// Bulk Storage
case GameVersion.RS: return new Bank3(data);
case GameVersion.DP: return new SAV4Ranch(data);
case GameVersion.HGSS: return new Bank4(data);
case GameVersion.USUM: return Bank7.GetBank7(data);
case GameVersion.Gen3: return new Bank3(data);
case GameVersion.DPPt: return new SAV4Ranch(data);
case GameVersion.Gen4: return new Bank4(data);
case GameVersion.Gen7: return Bank7.GetBank7(data);
// No pattern matched
default: return null;
@ -559,13 +577,13 @@ namespace PKHeX.Core
return new SAV2();
case GameVersion.R: case GameVersion.S: case GameVersion.E: case GameVersion.FR: case GameVersion.LG:
return new SAV3(versionOverride: Game);
return new SAV3(version: Game);
case GameVersion.FRLG:
return new SAV3(versionOverride: GameVersion.FR);
return new SAV3(version: GameVersion.FR);
case GameVersion.RS:
return new SAV3(versionOverride: GameVersion.R);
return new SAV3(version: GameVersion.R);
case GameVersion.RSE:
return new SAV3(versionOverride: GameVersion.E);
return new SAV3(version: GameVersion.E);
case GameVersion.CXD:
case GameVersion.COLO:
@ -577,31 +595,31 @@ namespace PKHeX.Core
case GameVersion.D: case GameVersion.P: case GameVersion.DP:
case GameVersion.DPPt:
return new SAV4(new byte[SIZE_G4RAW], GameVersion.DP);
return new SAV4(GameVersion.DP);
case GameVersion.Pt:
return new SAV4(new byte[SIZE_G4RAW], GameVersion.Pt);
return new SAV4(GameVersion.Pt);
case GameVersion.HG: case GameVersion.SS: case GameVersion.HGSS:
return new SAV4(new byte[SIZE_G4RAW], GameVersion.HGSS);
return new SAV4(GameVersion.HGSS);
case GameVersion.B: case GameVersion.W: case GameVersion.BW:
return new SAV5(new byte[SIZE_G5RAW], GameVersion.BW);
return new SAV5BW();
case GameVersion.B2: case GameVersion.W2: case GameVersion.B2W2:
return new SAV5(new byte[SIZE_G5RAW], GameVersion.B2W2);
return new SAV5B2W2();
case GameVersion.X: case GameVersion.Y: case GameVersion.XY:
return new SAV6(new byte[SIZE_G6XY]);
return new SAV6XY();
case GameVersion.ORASDEMO:
return new SAV6(new byte[SIZE_G6ORASDEMO]);
return new SAV6AODemo();
case GameVersion.OR: case GameVersion.AS: case GameVersion.ORAS:
return new SAV6(new byte[SIZE_G6ORAS]);
return new SAV6AO();
case GameVersion.SN: case GameVersion.MN: case GameVersion.SM:
return new SAV7(new byte[SIZE_G7SM]);
return new SAV7SM();
case GameVersion.US: case GameVersion.UM: case GameVersion.USUM:
return new SAV7(new byte[SIZE_G7USUM]);
return new SAV7USUM();
case GameVersion.GO:
case GameVersion.GP: case GameVersion.GE: case GameVersion.GG:
return new SAV7b(new byte[SIZE_G7GG]);
return new SAV7b();
default:
return null;

View file

@ -17,12 +17,20 @@ namespace PKHeX.Core
/// </summary>
/// <typeparam name="T">Item type</typeparam>
/// <param name="items">Item collection</param>
public static void Shuffle<T>(IList<T> items)
public static void Shuffle<T>(IList<T> items) => Shuffle(items, 0, items.Count);
/// <summary>
/// Shuffles the order of items within a collection of items.
/// </summary>
/// <typeparam name="T">Item type</typeparam>
/// <param name="items">Item collection</param>
/// <param name="start">Starting position</param>
/// <param name="end">Ending position</param>
public static void Shuffle<T>(IList<T> items, int start, int end)
{
int n = items.Count;
for (int i = 0; i < n; i++)
for (int i = start; i < end; i++)
{
int index = i + Rand.Next(n-i);
int index = i + Rand.Next(end - i);
T t = items[index];
items[index] = items[i];
items[i] = t;

View file

@ -1,4 +1,5 @@
using System.Linq;
using System;
using System.Linq;
using System.Runtime.CompilerServices;
namespace PKHeX.Core
@ -90,6 +91,20 @@ namespace PKHeX.Core
return result;
}
public static byte[] GetBytesFromHexString(string seed)
{
return Enumerable.Range(0, seed.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(seed.Substring(x, 2), 16))
.Reverse().ToArray();
}
public static string GetHexStringFromBytes(byte[] data, int offset, int length)
{
data = data.Skip(offset).Take(length).Reverse().ToArray();
return BitConverter.ToString(data).Replace("-", string.Empty);
}
private static bool IsNum(char c) => c >= '0' && c <= '9';
private static bool IsHexUpper(char c) => c >= 'A' && c <= 'F';
private static bool IsHexLower(char c) => c >= 'a' && c <= 'f';
@ -128,5 +143,30 @@ namespace PKHeX.Core
int index = input.IndexOf(c);
return index < 0 ? input : input.Substring(0, index);
}
public static bool[] GitBitFlagArray(byte[] data, int offset, int count)
{
bool[] result = new bool[count];
for (int i = 0; i < result.Length; i++)
result[i] = (data[offset + (i >> 3)] >> (i & 7) & 0x1) == 1;
return result;
}
public static void SetBitFlagArray(byte[] data, int offset, bool[] value)
{
for (int i = 0; i < value.Length; i++)
{
if (value[i])
data[offset + (i >> 3)] |= (byte)(1 << (i & 7));
}
}
public static byte[] SetBitFlagArray(bool[] value)
{
byte[] data = new byte[value.Length / 8];
SetBitFlagArray(data, 0, value);
return data;
}
}
}

View file

@ -582,19 +582,18 @@ namespace PKHeX.WinForms.Controls
private void B_OpenOPowers_Click(object sender, EventArgs e)
{
if (SAV.Generation != 6)
return;
new SAV_OPower((SAV6)SAV).ShowDialog();
if (SAV is IOPower op)
new SAV_OPower(op).ShowDialog();
}
private void B_OpenFriendSafari_Click(object sender, EventArgs e)
{
if (!SAV.XY)
if (!(SAV is SAV6XY xy))
return;
var dr = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, MsgSaveGen6FriendSafari, MsgSaveGen6FriendSafariCheatDesc);
if (dr == DialogResult.Yes)
((SAV6)SAV).UnlockAllFriendSafariSlots();
xy.UnlockAllFriendSafariSlots();
}
private static Form GetPokeDexEditor(SaveFile sav)
@ -1006,13 +1005,13 @@ namespace PKHeX.WinForms.Controls
PAN_BattleBox.Visible = L_BattleBox.Visible = L_ReadOnlyPBB.Visible = sav.HasBattleBox;
GB_Daycare.Visible = sav.HasDaycare;
B_OpenSecretBase.Enabled = sav.HasSecretBase;
B_OpenPokepuffs.Enabled = sav is IPokePuff p && p.HasPuffData;
B_OpenPokepuffs.Enabled = sav is IPokePuff;
B_OUTPasserby.Enabled = sav.HasPSS;
B_OpenBoxLayout.Enabled = sav.HasNamableBoxes;
B_OpenWondercards.Enabled = sav.HasWondercards;
B_OpenSuperTraining.Enabled = sav.HasSuperTrain;
B_OpenHallofFame.Enabled = sav.HasHoF;
B_OpenOPowers.Enabled = sav.HasOPower;
B_OpenOPowers.Enabled = sav is IOPower;
B_OpenPokedex.Enabled = sav.HasPokeDex;
B_OpenBerryField.Enabled = sav.HasBerryField && sav.XY;
B_OpenFriendSafari.Enabled = sav.XY;

View file

@ -9,17 +9,17 @@ namespace PKHeX.WinForms
public partial class SAV_Link6 : Form
{
private readonly SaveFile Origin;
private readonly SAV6 SAV;
private readonly ILink SAV;
public SAV_Link6(SaveFile sav)
{
InitializeComponent();
WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage);
SAV = (SAV6)(Origin = sav).Clone();
SAV = (ILink)(Origin = sav).Clone();
foreach (var cb in TAB_Items.Controls.OfType<ComboBox>())
{
cb.InitializeBinding();
cb.DataSource = new BindingSource(GameInfo.ItemDataSource.Where(item => item.Value <= SAV.MaxItemID).ToArray(), null);
cb.DataSource = new BindingSource(GameInfo.ItemDataSource.Where(item => item.Value <= sav.MaxItemID).ToArray(), null);
}
byte[] data = SAV.LinkBlock;
if (data == null)
@ -44,7 +44,7 @@ namespace PKHeX.WinForms
BitConverter.GetBytes(ccitt).CopyTo(data, data.Length - 4);
SAV.LinkBlock = data;
Origin.SetData(SAV.Data, 0);
Origin.SetData(((SaveFile)SAV).Data, 0);
Close();
}

View file

@ -6,15 +6,17 @@ namespace PKHeX.WinForms
{
public partial class SAV_OPower : Form
{
private readonly SAV6 Origin;
private readonly SaveFile Origin;
private readonly SaveFile SAV;
private readonly OPower6 Data;
public SAV_OPower(SAV6 sav)
public SAV_OPower(IOPower sav)
{
InitializeComponent();
WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage);
Origin = sav;
Data = sav.OPowerData;
Origin = (SaveFile)sav;
SAV = Origin.Clone();
Data = ((IOPower)SAV).OPowerBlock;
Current = Types[0];
foreach (var z in Types)
@ -46,7 +48,8 @@ namespace PKHeX.WinForms
{
Data.MasterFlag = CHK_Master.Checked;
SaveCurrent();
Origin.OPowerData = Data;
Origin.Data = SAV.Data;
Origin.Edited = true;
}
private void LoadCurrent()

View file

@ -7,13 +7,13 @@ namespace PKHeX.WinForms
public partial class SAV_PokeBlockORAS : Form
{
private readonly SaveFile Origin;
private readonly SAV6 SAV;
private readonly SAV6AO SAV;
public SAV_PokeBlockORAS(SaveFile sav)
{
InitializeComponent();
WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage);
SAV = (SAV6)(Origin = sav).Clone();
SAV = (SAV6AO)(Origin = sav).Clone();
nup_spec = new[] { NUP_Red, NUP_Blue, NUP_Pink, NUP_Green, NUP_Yellow, NUP_Rainbow, NUP_RedPlus, NUP_BluePlus, NUP_PinkPlus, NUP_GreenPlus, NUP_YellowPlus, NUP_RainbowPlus };
Label[] lbl_spec = { L_Red, L_Blue, L_Pink, L_Green, L_Yellow, L_Rainbow, L_RedPlus, L_BluePlus, L_PinkPlus, L_GreenPlus, L_YellowPlus, L_RainbowPlus };

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using PKHeX.Core;
@ -14,9 +13,9 @@ namespace PKHeX.WinForms
{
InitializeComponent();
WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage);
SAV = (SAV6)sav;
SAV = (IPokePuff)sav;
var puffs = SAV.Puffs;
var puffs = SAV.PuffBlock.Puffs;
Setup(puffs.Length);
LoadPuffs(puffs);
@ -87,41 +86,20 @@ namespace PKHeX.WinForms
private void B_All_Click(object sender, EventArgs e)
{
int[] plus10 = {21, 22};
byte[] newpuffs = new byte[PuffCount];
if (ModifierKeys == Keys.Control)
{
for (int i = 0; i < PuffCount; i++)
newpuffs[i] = (byte)plus10[Util.Rand.Next(2)];
}
else
{
for (int i = 0; i < PuffCount; i++)
newpuffs[i] = (byte)((i % (pfa.Length - 1)) + 1);
Util.Shuffle(newpuffs);
}
LoadPuffs(newpuffs);
SAV.PuffBlock.MaxCheat(ModifierKeys == Keys.Control);
LoadPuffs(SAV.PuffBlock.Puffs);
}
private void B_None_Click(object sender, EventArgs e)
{
byte[] newpuffs = new byte[PuffCount];
newpuffs[0] = 1;
newpuffs[1] = 2;
newpuffs[2] = 3;
newpuffs[3] = 4;
newpuffs[4] = 5;
LoadPuffs(newpuffs);
SAV.PuffBlock.Reset();
LoadPuffs(SAV.PuffBlock.Puffs);
}
private void B_Sort_Click(object sender, EventArgs e)
{
bool reverse = ModifierKeys == Keys.Control;
var puffs = GetPuffs().GroupBy(z => z != 0);
var result = puffs.SelectMany(z => reverse ? z.OrderByDescending(x => x) : z.OrderBy(x => x)).ToArray();
LoadPuffs(result);
SAV.PuffBlock.Sort(ModifierKeys == Keys.Control);
LoadPuffs(SAV.PuffBlock.Puffs);
}
private byte[] GetPuffs()
@ -139,8 +117,8 @@ namespace PKHeX.WinForms
private void B_Save_Click(object sender, EventArgs e)
{
var puffs = GetPuffs();
SAV.Puffs = puffs;
SAV.PuffCount = puffs.Length;
SAV.PuffBlock.Puffs = puffs;
SAV.PuffBlock.PuffCount = puffs.Length;
Close();
}
}

View file

@ -25,8 +25,7 @@ namespace PKHeX.WinForms
CB_Ability.InitializeBinding();
LB_Favorite.SelectedIndex = 0;
// MT_Flags.Text = BitConverter.ToUInt16(sav, 0x24800 + 0x140).ToString(); PSS Stat transmitted
MT_Flags.Text = BitConverter.ToUInt32(SAV.Data, SAV.SecretBase + 0x62C).ToString(); // read counter
MT_Flags.Text = SAV.Records.GetRecord(080).ToString(); // read counter; also present in the Secret Base data block
B_SAV2FAV(null, EventArgs.Empty);
}
@ -215,7 +214,7 @@ namespace PKHeX.WinForms
private void B_Save_Click(object sender, EventArgs e)
{
uint flags = Util.ToUInt32(MT_Flags.Text);
Array.Copy(BitConverter.GetBytes(flags), 0, SAV.Data, SAV.Record + 0x140, 4); // write pss
SAV.Records.SetRecord(080, (int)flags);
Array.Copy(BitConverter.GetBytes(flags), 0, SAV.Data, SAV.SecretBase + 0x62C, 4); // write counter
Origin.SetData(SAV.Data, 0);
Close();

View file

@ -159,7 +159,7 @@ namespace PKHeX.WinForms
this.CHK_MegaUnlocked = new System.Windows.Forms.CheckBox();
this.Tab_Maison = new System.Windows.Forms.TabPage();
this.Tab_Appearance = new System.Windows.Forms.TabPage();
this.propertyGrid1 = new System.Windows.Forms.PropertyGrid();
this.PG_CurrentAppearance = new System.Windows.Forms.PropertyGrid();
this.L_TRNick = new System.Windows.Forms.Label();
this.TB_TRNick = new System.Windows.Forms.TextBox();
this.CHK_MegaRayquazaUnlocked = new System.Windows.Forms.CheckBox();
@ -1620,7 +1620,7 @@ namespace PKHeX.WinForms
//
// Tab_Appearance
//
this.Tab_Appearance.Controls.Add(this.propertyGrid1);
this.Tab_Appearance.Controls.Add(this.PG_CurrentAppearance);
this.Tab_Appearance.Controls.Add(this.L_TRNick);
this.Tab_Appearance.Controls.Add(this.TB_TRNick);
this.Tab_Appearance.Controls.Add(this.B_GiveAccessories);
@ -1633,15 +1633,15 @@ namespace PKHeX.WinForms
//
// propertyGrid1
//
this.propertyGrid1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
this.PG_CurrentAppearance.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.propertyGrid1.HelpVisible = false;
this.propertyGrid1.Location = new System.Drawing.Point(3, 3);
this.propertyGrid1.Name = "propertyGrid1";
this.propertyGrid1.PropertySort = System.Windows.Forms.PropertySort.Alphabetical;
this.propertyGrid1.Size = new System.Drawing.Size(281, 247);
this.propertyGrid1.TabIndex = 75;
this.propertyGrid1.ToolbarVisible = false;
this.PG_CurrentAppearance.HelpVisible = false;
this.PG_CurrentAppearance.Location = new System.Drawing.Point(3, 3);
this.PG_CurrentAppearance.Name = "PG_CurrentAppearance";
this.PG_CurrentAppearance.PropertySort = System.Windows.Forms.PropertySort.Alphabetical;
this.PG_CurrentAppearance.Size = new System.Drawing.Size(281, 247);
this.PG_CurrentAppearance.TabIndex = 75;
this.PG_CurrentAppearance.ToolbarVisible = false;
//
// L_TRNick
//
@ -1860,7 +1860,7 @@ namespace PKHeX.WinForms
private System.Windows.Forms.TextBox TB_TRNick;
private System.Windows.Forms.CheckBox CHK_MegaUnlocked;
private Subforms.Save_Editors.TrainerStat TrainerStats;
private System.Windows.Forms.PropertyGrid propertyGrid1;
private System.Windows.Forms.PropertyGrid PG_CurrentAppearance;
private System.Windows.Forms.CheckBox CHK_MegaRayquazaUnlocked;
}
}

View file

@ -63,8 +63,9 @@ namespace PKHeX.WinForms
GetBadges();
editing = false;
CHK_MegaUnlocked.Checked = SAV.IsMegaEvolutionUnlocked;
CHK_MegaRayquazaUnlocked.Checked = SAV.IsMegaRayquazaUnlocked;
var status = SAV.Status;
CHK_MegaUnlocked.Checked = status.IsMegaEvolutionUnlocked;
CHK_MegaRayquazaUnlocked.Checked = status.IsMegaRayquazaUnlocked;
}
private readonly bool editing = true;
@ -146,11 +147,12 @@ namespace PKHeX.WinForms
MT_SID.Text = SAV.SID.ToString("00000");
MT_Money.Text = SAV.Money.ToString();
TB_Saying1.Text = SAV.Saying1;
TB_Saying2.Text = SAV.Saying2;
TB_Saying3.Text = SAV.Saying3;
TB_Saying4.Text = SAV.Saying4;
TB_Saying5.Text = SAV.Saying5;
var status = SAV.Status;
TB_Saying1.Text = status.Saying1;
TB_Saying2.Text = status.Saying2;
TB_Saying3.Text = status.Saying3;
TB_Saying4.Text = status.Saying4;
TB_Saying5.Text = status.Saying5;
CB_Country.SelectedValue = SAV.Country;
CB_Region.SelectedValue = SAV.SubRegion;
@ -164,9 +166,10 @@ namespace PKHeX.WinForms
MaisonRecords[i].Text = SAV.GetMaisonStat(i).ToString();
}
NUD_M.Value = SAV.M;
var sit = SAV.Situation;
NUD_M.Value = sit.M;
// Sanity Check Map Coordinates
if (!GB_Map.Enabled || SAV.X%0.5 != 0 || SAV.Z%0.5 != 0 || SAV.Y%0.5 != 0)
if (!GB_Map.Enabled || sit.X%0.5 != 0 || sit.Z%0.5 != 0 || sit.Y%0.5 != 0)
{
GB_Map.Enabled = false;
}
@ -174,9 +177,9 @@ namespace PKHeX.WinForms
{
try
{
NUD_X.Value = (decimal)SAV.X;
NUD_Z.Value = (decimal)SAV.Z;
NUD_Y.Value = (decimal)SAV.Y;
NUD_X.Value = (decimal)sit.X;
NUD_Z.Value = (decimal)sit.Z;
NUD_Y.Value = (decimal)sit.Y;
}
catch { GB_Map.Enabled = false; }
}
@ -185,7 +188,7 @@ namespace PKHeX.WinForms
TB_BP.Text = SAV.BP.ToString();
TB_PM.Text = SAV.GetRecord(63).ToString();
TB_Style.Text = SAV.Style.ToString();
TB_Style.Text = sit.Style.ToString();
// Load Play Time
MT_Hours.Text = SAV.PlayedHours.ToString();
@ -196,19 +199,18 @@ namespace PKHeX.WinForms
CB_MultiplayerSprite.SelectedValue = SAV.MultiplayerSpriteID;
PB_Sprite.Image = SAV.Sprite();
if (SAV.XY)
if (SAV is SAV6XY xy)
{
// Load Clothing Data
propertyGrid1.SelectedObject = TrainerFashion6.GetFashion(SAV.Data, SAV.TrainerCard + 0x30, SAV.Gender);
TB_TRNick.Text = SAV.OT_Nick;
var xystat = ((MyStatus6XY) xy.Status);
PG_CurrentAppearance.SelectedObject = xystat.Fashion;
TB_TRNick.Text = xystat.OT_Nick;
}
CB_Vivillon.SelectedIndex = SAV.Vivillon;
if (SAV.LastSavedDate.HasValue)
if (SAV.Played.LastSavedDate.HasValue)
{
CAL_LastSavedDate.Value = SAV.LastSavedDate.Value;
CAL_LastSavedTime.Value = SAV.LastSavedDate.Value;
CAL_LastSavedDate.Value = SAV.Played.LastSavedDate.Value;
CAL_LastSavedTime.Value = SAV.Played.LastSavedDate.Value;
}
else
{
@ -237,11 +239,12 @@ namespace PKHeX.WinForms
SAV.OT = TB_OTName.Text;
SAV.Saying1 = TB_Saying1.Text;
SAV.Saying2 = TB_Saying2.Text;
SAV.Saying3 = TB_Saying3.Text;
SAV.Saying4 = TB_Saying4.Text;
SAV.Saying5 = TB_Saying5.Text;
var status = SAV.Status;
status.Saying1 = TB_Saying1.Text;
status.Saying2 = TB_Saying2.Text;
status.Saying3 = TB_Saying3.Text;
status.Saying4 = TB_Saying4.Text;
status.Saying5 = TB_Saying5.Text;
// Copy Maison Data in
if (SAV.MaisonStats > -1)
@ -251,12 +254,13 @@ namespace PKHeX.WinForms
}
// Copy Position
var sit = SAV.Situation;
if (GB_Map.Enabled && MapUpdated)
{
SAV.M = (int)NUD_M.Value;
SAV.X = (float)NUD_X.Value;
SAV.Z = (float)NUD_Z.Value;
SAV.Y = (float)NUD_Y.Value;
sit.M = (int)NUD_M.Value;
sit.X = (float)NUD_X.Value;
sit.Z = (float)NUD_Z.Value;
sit.Y = (float)NUD_Y.Value;
}
SAV.BP = ushort.Parse(TB_BP.Text);
@ -264,7 +268,7 @@ namespace PKHeX.WinForms
SAV.SetRecord(63, Util.ToInt32(TB_PM.Text));
// Set Max Obtained Pokémiles
SAV.SetRecord(64, Util.ToInt32(TB_PM.Text));
SAV.Style = byte.Parse(TB_Style.Text);
sit.Style = byte.Parse(TB_Style.Text);
// Copy Badges
int badgeval = 0;
@ -281,13 +285,11 @@ namespace PKHeX.WinForms
SAV.MultiplayerSpriteID = Convert.ToByte(CB_MultiplayerSprite.SelectedValue);
// Appearance
if (SAV.XY)
if (SAV is SAV6XY xy)
{
// Save Clothing Data
var obj = (TrainerFashion6)propertyGrid1.SelectedObject;
obj.Write(SAV.Data, SAV.TrainerCard + 0x30);
SAV.OT_Nick = TB_TRNick.Text;
var xystat = (MyStatus6XY)xy.Status;
xystat.Fashion = (TrainerFashion6)PG_CurrentAppearance.SelectedObject;
xystat.OT_Nick = TB_TRNick.Text;
}
// Vivillon
@ -303,11 +305,11 @@ namespace PKHeX.WinForms
fame += (uint)(CAL_HoFTime.Value - new DateTime(2000, 1, 1)).TotalSeconds;
SAV.SecondsToFame = fame;
if (SAV.LastSavedDate.HasValue)
SAV.LastSavedDate = new DateTime(CAL_LastSavedDate.Value.Year, CAL_LastSavedDate.Value.Month, CAL_LastSavedDate.Value.Day, CAL_LastSavedTime.Value.Hour, CAL_LastSavedTime.Value.Minute, 0);
if (SAV.Played.LastSavedDate.HasValue)
SAV.Played.LastSavedDate = new DateTime(CAL_LastSavedDate.Value.Year, CAL_LastSavedDate.Value.Month, CAL_LastSavedDate.Value.Day, CAL_LastSavedTime.Value.Hour, CAL_LastSavedTime.Value.Minute, 0);
SAV.IsMegaEvolutionUnlocked = CHK_MegaUnlocked.Checked;
SAV.IsMegaRayquazaUnlocked = CHK_MegaRayquazaUnlocked.Checked;
status.IsMegaEvolutionUnlocked = CHK_MegaUnlocked.Checked;
status.IsMegaRayquazaUnlocked = CHK_MegaRayquazaUnlocked.Checked;
}
private void ClickOT(object sender, MouseEventArgs e)
@ -364,7 +366,8 @@ namespace PKHeX.WinForms
private void GiveAllAccessories(object sender, EventArgs e)
{
SAV.UnlockAllAccessories();
if (SAV is SAV6XY xy)
xy.UnlockAllAccessories();
}
private void UpdateCountry(object sender, EventArgs e)

View file

@ -17,7 +17,7 @@ namespace PKHeX.WinForms
SAV = (SAV7)(Origin = sav).Clone();
editing = true;
typeMAX = SAV.USUM ? 0x7F : 0x7C;
TB_PlazaName.Text = SAV.FestivalPlazaName;
TB_PlazaName.Text = SAV.Festa.FestivalPlazaName;
if (SAV.USUM)
{
@ -36,8 +36,8 @@ namespace PKHeX.WinForms
catch (Exception e) { WinFormsUtil.Alert("Font loading failed...", e.ToString()); }
}
uint cc = SAV.FestaCoins;
uint cu = SAV.UsedFestaCoins;
var cc = SAV.Festa.FestaCoins;
var cu = SAV.GetRecord(038);
NUD_FC_Current.Value = Math.Min(cc, NUD_FC_Current.Maximum);
NUD_FC_Used.Value = Math.Min(cu, NUD_FC_Used.Maximum);
L_FC_CollectedV.Text = (cc + cu).ToString();
@ -72,18 +72,18 @@ namespace PKHeX.WinForms
break;
}
CLB_Phrases.Items.Clear();
CLB_Phrases.Items.Add(res.Last(), SAV.GetFestaPhraseUnlocked(106)); //add Lv100 before TentPhrases
CLB_Phrases.Items.Add(res.Last(), SAV.Festa.GetFestaPhraseUnlocked(106)); //add Lv100 before TentPhrases
for (int i = 0; i < res.Length - 1; i++)
CLB_Phrases.Items.Add(res[i], SAV.GetFestaPhraseUnlocked(i));
CLB_Phrases.Items.Add(res[i], SAV.Festa.GetFestaPhraseUnlocked(i));
DateTime dt = SAV.FestaDate ?? new DateTime(2000, 1, 1);
DateTime dt = SAV.Festa.FestaDate ?? new DateTime(2000, 1, 1);
CAL_FestaStartDate.Value = CAL_FestaStartTime.Value = dt;
string[] res2 = { "Rank 4: missions","Rank 8: facility","Rank 10: fashion","Rank 20: rename","Rank 30: special menu","Rank 40: BGM","Rank 50: theme Glitz","Rank 60: theme Fairy","Rank 70: theme Tone","Rank 100: phrase","Current Rank", };
CLB_Reward.Items.Clear();
CLB_Reward.Items.Add(res2.Last(), (CheckState)RewardState[SAV.GetFestPrizeReceived(10)]); //add CurrentRank before const-rewards
CLB_Reward.Items.Add(res2.Last(), (CheckState)RewardState[SAV.Festa.GetFestPrizeReceived(10)]); //add CurrentRank before const-rewards
for (int i = 0; i < res2.Length - 1; i++)
CLB_Reward.Items.Add(res2[i], (CheckState)RewardState[SAV.GetFestPrizeReceived(i)]);
CLB_Reward.Items.Add(res2[i], (CheckState)RewardState[SAV.Festa.GetFestPrizeReceived(i)]);
for (int i = 0; i < 7; i++)
f[i] = new FestaFacility(SAV, i);
@ -149,11 +149,11 @@ namespace PKHeX.WinForms
CB_LuckyResult.Items.Add($"{lv} {type}");
}
NUD_Rank.Value = SAV.FestaRank;
LoadRankLabel(SAV.FestaRank);
NUD_Rank.Value = SAV.Festa.FestaRank;
LoadRankLabel(SAV.Festa.FestaRank);
NUD_Messages = new[] { NUD_MyMessageMeet, NUD_MyMessagePart, NUD_MyMessageMoved, NUD_MyMessageDissapointed };
for (int i = 0; i < NUD_Messages.Length; i++)
NUD_Messages[i].Value = SAV.GetFestaMessage(i);
NUD_Messages[i].Value = SAV.Festa.GetFestaMessage(i);
LB_FacilityIndex.SelectedIndex = 0;
CB_FacilityMessage.SelectedIndex = 0;
@ -256,17 +256,17 @@ namespace PKHeX.WinForms
private void Save()
{
SAV.SetFestaPhraseUnlocked(106, CLB_Phrases.GetItemChecked(0));
SAV.Festa.SetFestaPhraseUnlocked(106, CLB_Phrases.GetItemChecked(0));
for (int i = 1; i < CLB_Phrases.Items.Count; i++)
SAV.SetFestaPhraseUnlocked(i - 1, CLB_Phrases.GetItemChecked(i));
SAV.Festa.SetFestaPhraseUnlocked(i - 1, CLB_Phrases.GetItemChecked(i));
SAV.UsedFestaCoins = (uint)NUD_FC_Used.Value;
SAV.FestaCoins = (uint)NUD_FC_Current.Value;
SAV.FestaDate = new DateTime(CAL_FestaStartDate.Value.Year, CAL_FestaStartDate.Value.Month, CAL_FestaStartDate.Value.Day, CAL_FestaStartTime.Value.Hour, CAL_FestaStartTime.Value.Minute, CAL_FestaStartTime.Value.Second);
SAV.SetRecord(038, (int)NUD_FC_Used.Value);
SAV.Festa.FestaCoins = (int)NUD_FC_Current.Value;
SAV.Festa.FestaDate = new DateTime(CAL_FestaStartDate.Value.Year, CAL_FestaStartDate.Value.Month, CAL_FestaStartDate.Value.Day, CAL_FestaStartTime.Value.Hour, CAL_FestaStartTime.Value.Minute, CAL_FestaStartTime.Value.Second);
SAV.SetFestaPrizeReceived(10, RewardState[(int)CLB_Reward.GetItemCheckState(0)]);
SAV.Festa.SetFestaPrizeReceived(10, RewardState[(int)CLB_Reward.GetItemCheckState(0)]);
for (int i = 1; i < CLB_Reward.Items.Count; i++)
SAV.SetFestaPrizeReceived(i - 1, RewardState[(int)CLB_Reward.GetItemCheckState(i)]);
SAV.Festa.SetFestaPrizeReceived(i - 1, RewardState[(int)CLB_Reward.GetItemCheckState(i)]);
SaveFacility();
foreach (FestaFacility facility in f)
@ -330,7 +330,7 @@ namespace PKHeX.WinForms
SAV.SetData(BitConverter.GetBytes((ushort)NUD_DefeatMon.Value), 0x6C558);
for (int i = 0; i < NUD_Trainers.Length; i++)
SAV.SetData(BitConverter.GetBytes((ushort)NUD_Trainers[i].Value), 0x6C56C + (0x14 * i));
SAV.FestivalPlazaName = TB_PlazaName.Text;
SAV.Festa.FestivalPlazaName = TB_PlazaName.Text;
}
private void NUD_FC_ValueChanged(object sender, EventArgs e)
@ -567,7 +567,7 @@ namespace PKHeX.WinForms
{
if (editing) return;
int rank = (int)NUD_Rank.Value;
SAV.FestaRank = (ushort)rank;
SAV.Festa.FestaRank = (ushort)rank;
LoadRankLabel(rank);
}
@ -578,7 +578,7 @@ namespace PKHeX.WinForms
if (editing) return;
int mmIndex = Array.IndexOf(NUD_Messages, (NumericUpDown)sender);
if (mmIndex < 0) return;
SAV.SetFestaMessage(mmIndex, (ushort)((NumericUpDown)sender).Value);
SAV.Festa.SetFestaMessage(mmIndex, (ushort)((NumericUpDown)sender).Value);
}
private void CHK_FacilityIntroduced_CheckedChanged(object sender, EventArgs e)

View file

@ -46,7 +46,7 @@ namespace PKHeX.WinForms
}
if (SAV.USUM)
TB_EC.Text = SAV.StarterEncryptionConstant.ToString("X8");
TB_EC.Text = SAV.MiscBlock.StarterEncryptionConstant.ToString("X8");
else
TB_EC.Visible = L_EC.Visible = false;
}
@ -72,7 +72,7 @@ namespace PKHeX.WinForms
}
if (SAV.USUM)
SAV.StarterEncryptionConstant = Util.GetHexValue(TB_EC.Text);
SAV.MiscBlock.StarterEncryptionConstant = Util.GetHexValue(TB_EC.Text);
Origin.SetData(SAV.Data, 0);
Close();

View file

@ -111,7 +111,7 @@ namespace PKHeX.WinForms
CB_Region.SelectedValue = SAV.SubRegion;
CB_3DSReg.SelectedValue = SAV.ConsoleRegion;
CB_Language.SelectedValue = SAV.Language;
var time = SAV.AlolaTime;
var time = SAV.GameTime.AlolaTime;
if (time == 0)
time = 24 * 60 * 60; // Patch up any bad times from previous program versions.
if (time == 9_999_999)
@ -121,14 +121,14 @@ namespace PKHeX.WinForms
if (CB_AlolaTime.SelectedValue == null)
CB_AlolaTime.Enabled = false;
NUD_M.Value = SAV.M;
NUD_M.Value = SAV.Situation.M;
// Sanity Check Map Coordinates
try
{
NUD_X.Value = (decimal)SAV.X;
NUD_Z.Value = (decimal)SAV.Z;
NUD_Y.Value = (decimal)SAV.Y;
NUD_R.Value = (decimal)SAV.R;
NUD_X.Value = (decimal)SAV.Situation.X;
NUD_Z.Value = (decimal)SAV.Situation.Z;
NUD_Y.Value = (decimal)SAV.Situation.Y;
NUD_R.Value = (decimal)SAV.Situation.R;
}
catch { GB_Map.Enabled = false; }
@ -137,10 +137,10 @@ namespace PKHeX.WinForms
MT_Minutes.Text = SAV.PlayedMinutes.ToString();
MT_Seconds.Text = SAV.PlayedSeconds.ToString();
if (SAV.LastSavedDate.HasValue)
if (SAV.Played.LastSavedDate.HasValue)
{
CAL_LastSavedDate.Value = SAV.LastSavedDate.Value;
CAL_LastSavedTime.Value = SAV.LastSavedDate.Value;
CAL_LastSavedDate.Value = SAV.Played.LastSavedDate.Value;
CAL_LastSavedTime.Value = SAV.Played.LastSavedDate.Value;
}
else
{
@ -153,47 +153,48 @@ namespace PKHeX.WinForms
CAL_HoFDate.Value = epoch.AddSeconds(SAV.SecondsToFame);
CAL_HoFTime.Value = epoch.AddSeconds(SAV.SecondsToFame % 86400);
NUD_BP.Value = Math.Min(NUD_BP.Maximum, SAV.BP);
NUD_FC.Value = Math.Min(NUD_FC.Maximum, SAV.FestaCoins);
NUD_BP.Value = Math.Min(NUD_BP.Maximum, SAV.MiscBlock.BP);
NUD_FC.Value = Math.Min(NUD_FC.Maximum, SAV.Festa.FestaCoins);
// Poké Finder
NUD_SnapCount.Value = Math.Min(NUD_SnapCount.Maximum, SAV.PokeFinderSnapCount);
NUD_ThumbsTotal.Value = Math.Min(NUD_ThumbsTotal.Maximum, SAV.PokeFinderThumbsTotalValue);
NUD_ThumbsRecord.Value = Math.Min(NUD_ThumbsRecord.Maximum, SAV.PokeFinderThumbsHighValue);
NUD_SnapCount.Value = Math.Min(NUD_SnapCount.Maximum, SAV.PokeFinder.SnapCount);
NUD_ThumbsTotal.Value = Math.Min(NUD_ThumbsTotal.Maximum, SAV.PokeFinder.ThumbsTotalValue);
NUD_ThumbsRecord.Value = Math.Min(NUD_ThumbsRecord.Maximum, SAV.PokeFinder.ThumbsHighValue);
CB_CameraVersion.SelectedIndex = Math.Min(CB_CameraVersion.Items.Count - 1, SAV.PokeFinderCameraVersion);
CHK_Gyro.Checked = SAV.PokeFinderGyroFlag;
CB_CameraVersion.SelectedIndex = Math.Min(CB_CameraVersion.Items.Count - 1, SAV.PokeFinder.CameraVersion);
CHK_Gyro.Checked = SAV.PokeFinder.GyroFlag;
// Battle Tree
NUD_RCStreak0.Value = Math.Min(NUD_RCStreak0.Maximum, SAV.GetTreeStreak(0, super: false, max: false));
NUD_RCStreak1.Value = Math.Min(NUD_RCStreak1.Maximum, SAV.GetTreeStreak(1, super: false, max: false));
NUD_RCStreak2.Value = Math.Min(NUD_RCStreak2.Maximum, SAV.GetTreeStreak(2, super: false, max: false));
NUD_RMStreak0.Value = Math.Min(NUD_RMStreak0.Maximum, SAV.GetTreeStreak(0, super: false, max: true));
NUD_RMStreak1.Value = Math.Min(NUD_RMStreak1.Maximum, SAV.GetTreeStreak(1, super: false, max: true));
NUD_RMStreak2.Value = Math.Min(NUD_RMStreak2.Maximum, SAV.GetTreeStreak(2, super: false, max: true));
var bt = SAV.BattleTreeBlock;
NUD_RCStreak0.Value = Math.Min(NUD_RCStreak0.Maximum, bt.GetTreeStreak(0, super: false, max: false));
NUD_RCStreak1.Value = Math.Min(NUD_RCStreak1.Maximum, bt.GetTreeStreak(1, super: false, max: false));
NUD_RCStreak2.Value = Math.Min(NUD_RCStreak2.Maximum, bt.GetTreeStreak(2, super: false, max: false));
NUD_RMStreak0.Value = Math.Min(NUD_RMStreak0.Maximum, bt.GetTreeStreak(0, super: false, max: true));
NUD_RMStreak1.Value = Math.Min(NUD_RMStreak1.Maximum, bt.GetTreeStreak(1, super: false, max: true));
NUD_RMStreak2.Value = Math.Min(NUD_RMStreak2.Maximum, bt.GetTreeStreak(2, super: false, max: true));
NUD_SCStreak0.Value = Math.Min(NUD_SCStreak0.Maximum, SAV.GetTreeStreak(0, super: true, max: false));
NUD_SCStreak1.Value = Math.Min(NUD_SCStreak1.Maximum, SAV.GetTreeStreak(1, super: true, max: false));
NUD_SCStreak2.Value = Math.Min(NUD_SCStreak2.Maximum, SAV.GetTreeStreak(2, super: true, max: false));
NUD_SMStreak0.Value = Math.Min(NUD_SMStreak0.Maximum, SAV.GetTreeStreak(0, super: true, max: true));
NUD_SMStreak1.Value = Math.Min(NUD_SMStreak1.Maximum, SAV.GetTreeStreak(1, super: true, max: true));
NUD_SMStreak2.Value = Math.Min(NUD_SMStreak2.Maximum, SAV.GetTreeStreak(2, super: true, max: true));
NUD_SCStreak0.Value = Math.Min(NUD_SCStreak0.Maximum, bt.GetTreeStreak(0, super: true, max: false));
NUD_SCStreak1.Value = Math.Min(NUD_SCStreak1.Maximum, bt.GetTreeStreak(1, super: true, max: false));
NUD_SCStreak2.Value = Math.Min(NUD_SCStreak2.Maximum, bt.GetTreeStreak(2, super: true, max: false));
NUD_SMStreak0.Value = Math.Min(NUD_SMStreak0.Maximum, bt.GetTreeStreak(0, super: true, max: true));
NUD_SMStreak1.Value = Math.Min(NUD_SMStreak1.Maximum, bt.GetTreeStreak(1, super: true, max: true));
NUD_SMStreak2.Value = Math.Min(NUD_SMStreak2.Maximum, bt.GetTreeStreak(2, super: true, max: true));
CB_SkinColor.SelectedIndex = SAV.DressUpSkinColor;
TB_PlazaName.Text = SAV.FestivalPlazaName;
CB_SkinColor.SelectedIndex = SAV.MyStatus.DressUpSkinColor;
TB_PlazaName.Text = SAV.Festa.FestivalPlazaName;
CB_Vivillon.SelectedIndex = (SAV.Vivillon < CB_Vivillon.Items.Count) ? SAV.Vivillon : -1;
NUD_DaysFromRefreshed.Value = Math.Min(NUD_DaysFromRefreshed.Maximum, SAV.DaysFromRefreshed);
CB_Vivillon.SelectedIndex = (SAV.MiscBlock.Vivillon < CB_Vivillon.Items.Count) ? SAV.MiscBlock.Vivillon : -1;
NUD_DaysFromRefreshed.Value = Math.Min(NUD_DaysFromRefreshed.Maximum, SAV.MiscBlock.DaysFromRefreshed);
if (SAV.BallThrowType >= 0 && SAV.BallThrowType < CB_BallThrowType.Items.Count)
CB_BallThrowType.SelectedIndex = SAV.BallThrowType;
if (SAV.MyStatus.BallThrowType >= 0 && SAV.MyStatus.BallThrowType < CB_BallThrowType.Items.Count)
CB_BallThrowType.SelectedIndex = SAV.MyStatus.BallThrowType;
if (SAV.SM)
LoadThrowTypeLists();
else
CB_BallThrowTypeListMode.Visible = LB_BallThrowTypeLearned.Visible = LB_BallThrowTypeUnlocked.Visible = false;
uint stampBits = SAV.Stamps;
uint stampBits = SAV.MiscBlock.Stamps;
for (int i = 0; i < LB_Stamps.Items.Count; i++)
LB_Stamps.SetSelected(i, (stampBits & (1 << i)) != 0);
@ -201,8 +202,8 @@ namespace PKHeX.WinForms
CHK_UnlockSuperDoubles.Checked = SAV.GetEventFlag(334);
CHK_UnlockSuperMulti.Checked = SAV.GetEventFlag(335);
CHK_UnlockMega.Checked = SAV.MegaUnlocked;
CHK_UnlockZMove.Checked = SAV.ZMoveUnlocked;
CHK_UnlockMega.Checked = SAV.MyStatus.MegaUnlocked;
CHK_UnlockZMove.Checked = SAV.MyStatus.ZMoveUnlocked;
LoadMapFlyToData();
}
@ -289,12 +290,12 @@ namespace PKHeX.WinForms
private void LoadUltraData()
{
NUD_Surf0.Value = SAV.GetSurfScore(0);
NUD_Surf1.Value = SAV.GetSurfScore(1);
NUD_Surf2.Value = SAV.GetSurfScore(2);
NUD_Surf3.Value = SAV.GetSurfScore(3);
NUD_Surf0.Value = SAV.MiscBlock.GetSurfScore(0);
NUD_Surf1.Value = SAV.MiscBlock.GetSurfScore(1);
NUD_Surf2.Value = SAV.MiscBlock.GetSurfScore(2);
NUD_Surf3.Value = SAV.MiscBlock.GetSurfScore(3);
TB_RotomOT.Font = TB_OTName.Font;
TB_RotomOT.Text = SAV.RotomOT;
TB_RotomOT.Text = SAV.FieldMenu.RotomOT;
}
private void Save()
@ -303,13 +304,13 @@ namespace PKHeX.WinForms
SavePokeFinder();
SaveBattleTree();
SaveTrainerAppearance();
SAV.DaysFromRefreshed = (byte)NUD_DaysFromRefreshed.Value;
SAV.MiscBlock.DaysFromRefreshed = (byte)NUD_DaysFromRefreshed.Value;
SaveThrowType();
SAV.FestivalPlazaName = TB_PlazaName.Text;
SAV.Festa.FestivalPlazaName = TB_PlazaName.Text;
// Vivillon
if (CB_Vivillon.SelectedIndex >= 0) SAV.Vivillon = CB_Vivillon.SelectedIndex;
if (CB_Vivillon.SelectedIndex >= 0) SAV.MiscBlock.Vivillon = CB_Vivillon.SelectedIndex;
SaveFlags();
@ -328,18 +329,19 @@ namespace PKHeX.WinForms
SAV.ConsoleRegion = WinFormsUtil.GetIndex(CB_3DSReg);
SAV.Language = WinFormsUtil.GetIndex(CB_Language);
if (CB_AlolaTime.Enabled)
SAV.AlolaTime = (ulong)WinFormsUtil.GetIndex(CB_AlolaTime);
SAV.GameTime.AlolaTime = (ulong)WinFormsUtil.GetIndex(CB_AlolaTime);
SAV.OT = TB_OTName.Text;
// Copy Position
if (GB_Map.Enabled && MapUpdated)
{
SAV.M = (int)NUD_M.Value;
SAV.X = (float)NUD_X.Value;
SAV.Z = (float)NUD_Z.Value;
SAV.Y = (float)NUD_Y.Value;
SAV.R = (float)NUD_R.Value;
SAV.Situation.M = (int)NUD_M.Value;
SAV.Situation.X = (float)NUD_X.Value;
SAV.Situation.Z = (float)NUD_Z.Value;
SAV.Situation.Y = (float)NUD_Y.Value;
SAV.Situation.R = (float)NUD_R.Value;
SAV.Situation.UpdateOverworldCoordinates();
}
// Save PlayTime
@ -358,38 +360,39 @@ namespace PKHeX.WinForms
fame += (uint)(CAL_HoFTime.Value - epoch).TotalSeconds;
SAV.SecondsToFame = fame;
if (SAV.LastSavedDate.HasValue)
SAV.LastSavedDate = new DateTime(CAL_LastSavedDate.Value.Year, CAL_LastSavedDate.Value.Month, CAL_LastSavedDate.Value.Day, CAL_LastSavedTime.Value.Hour, CAL_LastSavedTime.Value.Minute, 0);
if (SAV.Played.LastSavedDate.HasValue)
SAV.Played.LastSavedDate = new DateTime(CAL_LastSavedDate.Value.Year, CAL_LastSavedDate.Value.Month, CAL_LastSavedDate.Value.Day, CAL_LastSavedTime.Value.Hour, CAL_LastSavedTime.Value.Minute, 0);
SAV.BP = (uint)NUD_BP.Value;
SAV.FestaCoins = (uint)NUD_FC.Value;
SAV.MiscBlock.BP = (uint)NUD_BP.Value;
SAV.Festa.FestaCoins = (int)NUD_FC.Value;
}
private void SavePokeFinder()
{
SAV.PokeFinderSnapCount = (uint)NUD_SnapCount.Value;
SAV.PokeFinderThumbsTotalValue = (uint)NUD_ThumbsTotal.Value;
SAV.PokeFinderThumbsHighValue = (uint)NUD_ThumbsRecord.Value;
SAV.PokeFinder.SnapCount = (uint)NUD_SnapCount.Value;
SAV.PokeFinder.ThumbsTotalValue = (uint)NUD_ThumbsTotal.Value;
SAV.PokeFinder.ThumbsHighValue = (uint)NUD_ThumbsRecord.Value;
SAV.PokeFinderCameraVersion = (ushort)CB_CameraVersion.SelectedIndex;
SAV.PokeFinderGyroFlag = CHK_Gyro.Checked;
SAV.PokeFinder.CameraVersion = (ushort)CB_CameraVersion.SelectedIndex;
SAV.PokeFinder.GyroFlag = CHK_Gyro.Checked;
}
private void SaveBattleTree()
{
SAV.SetTreeStreak((int)NUD_RCStreak0.Value, 0, super:false, max:false);
SAV.SetTreeStreak((int)NUD_RCStreak1.Value, 1, super:false, max:false);
SAV.SetTreeStreak((int)NUD_RCStreak2.Value, 2, super:false, max:false);
SAV.SetTreeStreak((int)NUD_RMStreak0.Value, 0, super:false, max:true);
SAV.SetTreeStreak((int)NUD_RMStreak1.Value, 1, super:false, max:true);
SAV.SetTreeStreak((int)NUD_RMStreak2.Value, 2, super:false, max:true);
var bt = SAV.BattleTreeBlock;
bt.SetTreeStreak((int)NUD_RCStreak0.Value, 0, super:false, max:false);
bt.SetTreeStreak((int)NUD_RCStreak1.Value, 1, super:false, max:false);
bt.SetTreeStreak((int)NUD_RCStreak2.Value, 2, super:false, max:false);
bt.SetTreeStreak((int)NUD_RMStreak0.Value, 0, super:false, max:true);
bt.SetTreeStreak((int)NUD_RMStreak1.Value, 1, super:false, max:true);
bt.SetTreeStreak((int)NUD_RMStreak2.Value, 2, super:false, max:true);
SAV.SetTreeStreak((int)NUD_SCStreak0.Value, 0, super:true, max:false);
SAV.SetTreeStreak((int)NUD_SCStreak1.Value, 1, super:true, max:false);
SAV.SetTreeStreak((int)NUD_SCStreak2.Value, 2, super:true, max:false);
SAV.SetTreeStreak((int)NUD_SMStreak0.Value, 0, super:true, max:true);
SAV.SetTreeStreak((int)NUD_SMStreak1.Value, 1, super:true, max:true);
SAV.SetTreeStreak((int)NUD_SMStreak2.Value, 2, super:true, max:true);
bt.SetTreeStreak((int)NUD_SCStreak0.Value, 0, super:true, max:false);
bt.SetTreeStreak((int)NUD_SCStreak1.Value, 1, super:true, max:false);
bt.SetTreeStreak((int)NUD_SCStreak2.Value, 2, super:true, max:false);
bt.SetTreeStreak((int)NUD_SMStreak0.Value, 0, super:true, max:true);
bt.SetTreeStreak((int)NUD_SMStreak1.Value, 1, super:true, max:true);
bt.SetTreeStreak((int)NUD_SMStreak2.Value, 2, super:true, max:true);
}
private void SaveTrainerAppearance()
@ -400,17 +403,17 @@ namespace PKHeX.WinForms
string gStr = CB_Gender.Items[gender].ToString();
string sStr = CB_Gender.Items[skin].ToString();
if (SAV.DressUpSkinColor == CB_SkinColor.SelectedIndex)
if (SAV.MyStatus.DressUpSkinColor == CB_SkinColor.SelectedIndex)
return;
if (SAV.Gender == skin || DialogResult.Yes == WinFormsUtil.Prompt(MessageBoxButtons.YesNo, $"Gender-Skin mismatch:{Environment.NewLine}Gender: {gStr}, Skin: {sStr}", "Save selected Skin Color?"))
SAV.DressUpSkinColor = CB_SkinColor.SelectedIndex;
SAV.MyStatus.DressUpSkinColor = CB_SkinColor.SelectedIndex;
}
private void SaveThrowType()
{
if (CB_BallThrowType.SelectedIndex >= 0)
SAV.BallThrowType = CB_BallThrowType.SelectedIndex;
SAV.MyStatus.BallThrowType = CB_BallThrowType.SelectedIndex;
if (!SAV.SM) // unlock flags are in flag editor instead
return;
@ -425,14 +428,14 @@ namespace PKHeX.WinForms
private void SaveFlags()
{
SAV.Stamps = GetBits(LB_Stamps);
SAV.MiscBlock.Stamps = GetBits(LB_Stamps);
SAV.SetEventFlag(333, CHK_UnlockSuperSingles.Checked);
SAV.SetEventFlag(334, CHK_UnlockSuperDoubles.Checked);
SAV.SetEventFlag(335, CHK_UnlockSuperMulti.Checked);
SAV.MegaUnlocked = CHK_UnlockMega.Checked;
SAV.ZMoveUnlocked = CHK_UnlockZMove.Checked;
SAV.MyStatus.MegaUnlocked = CHK_UnlockMega.Checked;
SAV.MyStatus.ZMoveUnlocked = CHK_UnlockZMove.Checked;
for (int i = 0; i < CLB_FlyDest.Items.Count; i++)
SAV.SetEventFlag(SkipFlag + FlyDestFlagOfs[i], CLB_FlyDest.GetItemChecked(i));
@ -442,21 +445,21 @@ namespace PKHeX.WinForms
private void SaveUltraData()
{
SAV.SetSurfScore(0, (int)NUD_Surf0.Value);
SAV.SetSurfScore(1, (int)NUD_Surf1.Value);
SAV.SetSurfScore(2, (int)NUD_Surf2.Value);
SAV.SetSurfScore(3, (int)NUD_Surf3.Value);
SAV.MiscBlock.SetSurfScore(0, (int)NUD_Surf0.Value);
SAV.MiscBlock.SetSurfScore(1, (int)NUD_Surf1.Value);
SAV.MiscBlock.SetSurfScore(2, (int)NUD_Surf2.Value);
SAV.MiscBlock.SetSurfScore(3, (int)NUD_Surf3.Value);
if (TB_RotomOT.Text != TB_OTName.Text // different Rotom name from OT
&& TB_OTName.Text != SAV.OT // manually changed
&& DialogResult.Yes == // wants to update
WinFormsUtil.Prompt(MessageBoxButtons.YesNo, "Rotom OT does not match OT name. Update Rotom OT name with OT name?"))
{
SAV.RotomOT = TB_OTName.Text;
SAV.FieldMenu.RotomOT = TB_OTName.Text;
}
else
{
SAV.RotomOT = TB_RotomOT.Text;
SAV.FieldMenu.RotomOT = TB_RotomOT.Text;
}
}
@ -529,7 +532,7 @@ namespace PKHeX.WinForms
return;
// Clear Block
new byte[SAV.FashionLength].CopyTo(SAV.Data, SAV.Fashion);
SAV.FashionBlock.Clear();
// Write Payload
// Every fashion item is 2 bits, New Flag (high) & Owned Flag (low)

View file

@ -135,14 +135,14 @@ namespace PKHeX.WinForms
control.Visible = true;
}
L_Coins.Text = "BP"; // no translation boo
MT_Coins.Text = s.BP.ToString();
MT_Coins.Text = s.BattleSubwayBlock.BP.ToString();
NUD_M.Value = s.M;
NUD_X.Value = s.X;
NUD_Z.Value = s.Z;
NUD_Y.Value = s.Y;
badgeval = s.Badges;
var pd = s.PlayerData;
NUD_M.Value = pd.M;
NUD_X.Value = pd.X;
NUD_Z.Value = pd.Z;
NUD_Y.Value = pd.Y;
badgeval = s.MiscBlock.Badges;
}
for (int i = 0; i < cba.Length; i++)
@ -242,13 +242,14 @@ namespace PKHeX.WinForms
{
if (MapUpdated)
{
s.M = (int)NUD_M.Value;
s.X = (int)NUD_X.Value;
s.Z = (int)NUD_Z.Value;
s.Y = (int)NUD_Y.Value;
var pd = s.PlayerData;
pd.M = (int)NUD_M.Value;
pd.X = (int)NUD_X.Value;
pd.Z = (int)NUD_Z.Value;
pd.Y = (int)NUD_Y.Value;
}
s.Badges = badgeval & 0xFF;
s.BP = (ushort)Math.Min(Util.ToUInt32(MT_Coins.Text), SAV.MaxCoins);
s.MiscBlock.Badges = badgeval & 0xFF;
s.BattleSubwayBlock.BP = (ushort)Math.Min(Util.ToUInt32(MT_Coins.Text), SAV.MaxCoins);
}
SAV.SecondsToStart = GetSeconds(CAL_AdventureStartDate, CAL_AdventureStartTime);
@ -265,9 +266,10 @@ namespace PKHeX.WinForms
private static uint GetSeconds(DateTimePicker date, DateTimePicker time)
{
uint val = (uint)(date.Value - new DateTime(2000, 1, 1)).TotalSeconds;
var epoch = new DateTime(2000, 1, 1);
uint val = (uint)(date.Value - epoch).TotalSeconds;
val -= val % 86400;
val += (uint)(time.Value - new DateTime(2000, 1, 1)).TotalSeconds;
val += (uint)(time.Value - epoch).TotalSeconds;
return val;
}

View file

@ -11,7 +11,7 @@ namespace PKHeX.Tests.Saves
{
var folder = TestUtil.GetRepoPath();
var path = Path.Combine(folder, "TestData", "SM Project 802.main");
return new SAV7(File.ReadAllBytes(path));
return new SAV7SM(File.ReadAllBytes(path));
}
[Fact]
@ -25,7 +25,7 @@ namespace PKHeX.Tests.Saves
{
var save = GetSave();
var originalChecksumInfo = save.ChecksumInfo;
var newSave = new SAV7(save.Write());
var newSave = new SAV7SM(save.Write());
save.ChecksumInfo.Should().BeEquivalentTo(originalChecksumInfo, "because the checksum should have been modified");
save.ChecksumsValid.Should().BeTrue("because the checksum should be valid after write");