Add new class

Performs SAV operations to match the division already done for PK6.
This commit is contained in:
Kurt 2015-10-22 00:16:44 -07:00
parent 5057be999f
commit 322fceb598
2 changed files with 435 additions and 0 deletions

434
Misc/SAV6.cs Normal file
View file

@ -0,0 +1,434 @@
using System;
using System.Linq;
using System.Text;
namespace PKHeX
{
public class BlockInfo
{
public int Offset;
public int Length;
public ushort ID;
public ushort Checksum;
}
public class SAV6 : PKX
{
// Save Data Attributes
public byte[] Data;
public bool Exportable;
public SAV6(byte[] data)
{
Exportable = !data.SequenceEqual(new byte[data.Length]);
Data = data;
// Load Info
getBlockInfo();
getSAVOffsets();
}
public void getSAVOffsets()
{
if (XY)
{
Box = 0x22600;
TrainerCard = 0x14000;
Party = 0x14200;
BattleBox = 0x04A00;
Daycare = 0x1B200;
GTS = 0x17800;
Fused = 0x16000;
SUBE = 0x1D890;
Puff = 0x00000;
Item = 0x00400;
Items = new Inventory(Item, 0);
Trainer1 = 0x1400;
Trainer2 = 0x4200;
PCLayout = 0x4400;
PCBackgrounds = PCLayout + 0x41E;
PCFlags = PCLayout + 0x43D;
WondercardFlags = 0x1BC00;
WondercardData = WondercardFlags + 0x100;
BerryField = 0x1B800;
OPower = 0x16A00;
EventConst = 0x14A00;
EventAsh = -1;
EventFlag = EventConst + 0x2FC;
PokeDex = 0x15000;
PokeDexLanguageFlags = PokeDex + 0x3C8;
Spinda = PokeDex + 0x648;
EncounterCount = -1;
HoF = 0x19400;
SuperTrain = 0x1F200;
JPEG = 0x57200;
MaisonStats = 0x1B1C0;
PSS = 0x05000;
PSSStats = 0x1E400;
Vivillon = 0x4250;
BoxWallpapers = 0x481E;
SecretBase = -1;
EonTicket = -1;
LastViewedBox = PCLayout + 0x43F;
}
else if (ORAS)
{
Box = 0x33000; // Confirmed
TrainerCard = 0x14000; // Confirmed
Party = 0x14200; // Confirmed
BattleBox = 0x04A00; // Confirmed
Daycare = 0x1BC00; // Confirmed (thanks Rei)
GTS = 0x18200; // Confirmed
Fused = 0x16A00; // Confirmed
SUBE = 0x1D890; // ****not in use, not updating?****
Puff = 0x00000; // Confirmed
Item = 0x00400; // Confirmed
Items = new Inventory(Item, 1);
Trainer1 = 0x01400; // Confirmed
Trainer2 = 0x04200; // Confirmed
PCLayout = 0x04400; // Confirmed
PCBackgrounds = PCLayout + 0x41E;
PCFlags = PCLayout + 0x43D;
WondercardFlags = 0x1CC00; // Confirmed
WondercardData = WondercardFlags + 0x100;
BerryField = 0x1C400; // ****changed****
OPower = 0x17400; // ****changed****
EventConst = 0x14A00;
EventAsh = EventConst + 0x78;
EventFlag = EventConst + 0x2FC;
PokeDex = 0x15000;
Spinda = PokeDex + 0x680;
EncounterCount = PokeDex + 0x686;
PokeDexLanguageFlags = PokeDex + 0x400;
HoF = 0x19E00; // Confirmed
SuperTrain = 0x20200;
JPEG = 0x67C00; // Confirmed
MaisonStats = 0x1BBC0;
PSS = 0x05000; // Confirmed (thanks Rei)
PSSStats = 0x1F400;
Vivillon = 0x4244;
BoxWallpapers = 0x481E;
SecretBase = 0x23A00;
EonTicket = 0x319B8;
LastViewedBox = PCLayout + 0x43F;
}
else
{
Box = TrainerCard = Party = BattleBox = GTS = Daycare =
Fused = SUBE = Puff = Item = Trainer1 = Trainer2 = SecretBase = EonTicket = LastViewedBox = BoxWallpapers =
PCLayout = PCBackgrounds = PCFlags = WondercardFlags = WondercardData = BerryField = OPower = SuperTrain = MaisonStats = PSSStats = Vivillon =
EventConst = EventAsh = EventFlag = PokeDex = PokeDexLanguageFlags = Spinda = EncounterCount = HoF = PSS = JPEG = 0;
Items = new Inventory(Item, -1);
}
DaycareSlot = new[] { Daycare, Daycare + 0x1F0 };
}
public class Inventory
{
public int HeldItem, KeyItem, Medicine, TMHM, Berry;
public Inventory(int Offset, int Game)
{
switch (Game)
{
case 1:
HeldItem = Offset + 0;
KeyItem = Offset + 0x640;
TMHM = Offset + 0x7C0;
Medicine = Offset + 0x968;
Berry = Offset + 0xA68;
break;
case 2:
HeldItem = Offset + 0;
KeyItem = Offset + 0x640;
TMHM = Offset + 0x7C0;
Medicine = Offset + 0x970;
Berry = Offset + 0xA70;
break;
default:
HeldItem = KeyItem = TMHM = Medicine = Berry = 0;
break;
}
}
}
public Inventory Items;
public int Box, Party, BattleBox, GTS, Daycare, EonTicket,
Fused, SUBE, Puff, Item, Trainer1, Trainer2, SuperTrain, PSSStats, MaisonStats, Vivillon, SecretBase, BoxWallpapers, LastViewedBox,
PCLayout, PCBackgrounds, PCFlags, WondercardFlags, WondercardData, BerryField, OPower, EventConst, EventFlag, EventAsh,
PokeDex, PokeDexLanguageFlags, Spinda, EncounterCount, HoF, PSS, JPEG;
public int TrainerCard = 0x14000;
public int[] DaycareSlot;
public int DaycareIndex = 0;
public enum GameVersion
{
X = 24, Y = 25,
AS = 26, OR = 27,
Z = 28,
Unknown = -1,
}
public GameVersion Version
{
get
{
switch (Game)
{
case 24: return GameVersion.X;
case 25: return GameVersion.Y;
case 26: return GameVersion.AS;
case 27: return GameVersion.OR;
}
return GameVersion.Unknown;
}
}
public bool ORAS { get { return ((Version == GameVersion.OR) || (Version == GameVersion.AS)); } }
public bool XY { get { return ((Version == GameVersion.X) || (Version == GameVersion.Y)); } }
// Save Information
private int BlockInfoOffset;
private BlockInfo[] Blocks;
private void getBlockInfo()
{
BlockInfoOffset = Data.Length - 0x200 + 0x10;
if (BitConverter.ToUInt32(Data, BlockInfoOffset) != 0x42454546) // BEEF, nice!
BlockInfoOffset -= 0x200; // No savegames have more than 0x3D blocks, maybe in the future?
int count = (Data.Length - BlockInfoOffset - 0x8) / 8;
BlockInfoOffset += 4;
Blocks = new BlockInfo[count];
int CurrentPosition = 0;
for (int i = 0; i < Blocks.Length; i++)
{
Blocks[i] = new BlockInfo
{
Offset = CurrentPosition,
Length = BitConverter.ToInt32(Data, BlockInfoOffset + 0 + 8*i),
ID = BitConverter.ToUInt16(Data, BlockInfoOffset + 4 + 8*i),
Checksum = BitConverter.ToUInt16(Data, BlockInfoOffset + 6 + 8*i)
};
// Expand out to nearest 0x200
CurrentPosition += (Blocks[i].Length % 0x200 == 0) ? Blocks[i].Length : (0x200 - Blocks[i].Length % 0x200 + Blocks[i].Length);
if ((Blocks[i].ID != 0) || i == 0) continue;
count = i;
break;
}
// Fix Final Array Lengths
Array.Resize(ref Blocks, count);
}
private void setChecksums()
{
// Check for invalid block lengths
if (Blocks.Length < 3) // arbitrary...
{
Console.WriteLine("Not enough blocks ({0}), aborting setChecksums", Blocks.Length);
return;
}
// Apply checksums
for (int i = 0; i < Blocks.Length; i++)
{
byte[] array = Data.Skip(Blocks[i].Offset).Take(Blocks[i].Length).ToArray();
Array.Copy(BitConverter.GetBytes(ccitt16(array)), 0, Data, BlockInfoOffset + 6 + i * 8, 2);
}
}
public byte[] Write()
{
setChecksums();
return Data;
}
public int CurrentBox { get { return Data[LastViewedBox] & 0x1F; } set { Data[LastViewedBox] = (byte)value; } }
// Player Information
public ushort TID { get { return BitConverter.ToUInt16(Data, TrainerCard + 0); } }
public ushort SID { get { return BitConverter.ToUInt16(Data, TrainerCard + 2); } }
public byte Game { get { return Data[TrainerCard + 4]; } }
public byte Gender { get { return Data[TrainerCard + 5]; } }
public byte SubRegion { get { return Data[TrainerCard + 0x26]; } }
public byte Country { get { return Data[TrainerCard + 0x27]; } }
public byte ConsoleRegion { get { return Data[TrainerCard + 0x2C]; } }
public byte Language { get { return Data[TrainerCard + 0x2D]; } }
public string OT { get { return Util.TrimFromZero(Encoding.Unicode.GetString(Data, TrainerCard + 0x48, 0x1A)); } }
// Misc Properties
public int PartyCount
{
get { return Data[Party + 6 * PK6.SIZE_PARTY]; }
set { Data[Party + 6 * PK6.SIZE_PARTY] = (byte)value; }
}
public bool BattleBoxLocked
{
get { return Data[BattleBox + 6 * PK6.SIZE_STORED] != 0; }
set { Data[BattleBox + 6 * PK6.SIZE_STORED] = (byte)(value ? 1 : 0); }
}
public ulong DaycareRNGSeed
{
get { return BitConverter.ToUInt64(Data, DaycareSlot[DaycareIndex] + 0x1E8); }
set { BitConverter.GetBytes(value).CopyTo(Data, DaycareSlot[DaycareIndex] + 0x1E8); }
}
public bool DaycareHasEgg
{
get { return Data[DaycareSlot[DaycareIndex] + 0x1E0] == 1; }
set { Data[DaycareSlot[DaycareIndex] + 0x1E0] = (byte)(value ? 1 : 0); }
}
public uint DaycareEXP1
{
get { return BitConverter.ToUInt32(Data, DaycareSlot[DaycareIndex] + (PK6.SIZE_STORED + 8)*0 + 4); }
set { BitConverter.GetBytes(value).CopyTo(Data, DaycareSlot[DaycareIndex] + (PK6.SIZE_STORED + 8)*0 + 4); }
}
public uint DaycareEXP2
{
get { return BitConverter.ToUInt32(Data, DaycareSlot[DaycareIndex] + (PK6.SIZE_STORED + 8)*1 + 4); }
set { BitConverter.GetBytes(value).CopyTo(Data, DaycareSlot[DaycareIndex] + (PK6.SIZE_STORED + 8)*1 + 4); }
}
public bool DaycareOccupied1
{
get { return Data[DaycareSlot[DaycareIndex] + (PK6.SIZE_STORED + 8)*0] != 0; }
set { Data[DaycareSlot[DaycareIndex] + (PK6.SIZE_STORED + 8)*0] = (byte) (value ? 1 : 0); }
}
public bool DaycareOccupied2
{
get { return Data[DaycareSlot[DaycareIndex] + (PK6.SIZE_STORED + 8) * 1] != 0; }
set { Data[DaycareSlot[DaycareIndex] + (PK6.SIZE_STORED + 8) * 1] = (byte)(value ? 1 : 0); }
}
// Data Accessing
public byte[] getData(int Offset, int Length)
{
return Data.Skip(Offset).Take(Length).ToArray();
}
public void setData(byte[] input, int Offset)
{
input.CopyTo(Data, Offset);
}
// Pokémon Requests
public PK6 getPK6Party(int offset)
{
return new PK6(decryptArray(getData(offset , PK6.SIZE_PARTY)));
}
public PK6 getPK6Stored(int offset)
{
return new PK6(decryptArray(getData(offset, PK6.SIZE_STORED)));
}
public void setPK6Party(PK6 pk6, int offset, bool trade = true)
{
if (trade)
{
// Apply to this Save File
// pk6.Trade();
// pk6.Write();
}
setPokeDex(pk6);
byte[] ek6 = encryptArray(pk6.Data);
setData(ek6, offset);
}
public void setPK6Stored(PK6 pk6, int offset, bool trade = true)
{
if (trade)
{
// Apply to this Save File
// pk6.Trade();
// pk6.Write();
}
setPokeDex(pk6);
byte[] ek6 = encryptArray(pk6.Data);
Array.Resize(ref ek6, PK6.SIZE_STORED);
setData(ek6, offset);
}
public void setEK6Stored(byte[] ek6, int offset, bool trade = true)
{
PK6 pk6 = new PK6(decryptArray(ek6));
if (trade)
{
// Apply to this Save File
// pk6.Trade();
// pk6.Write();
}
setPokeDex(pk6);
Array.Resize(ref ek6, PK6.SIZE_STORED);
setData(ek6, offset);
}
// Meta
public void setPokeDex(PK6 pk6)
{
if (pk6.Species == 0)
return;
int bit = pk6.Species - 1;
int lang = pk6.Language - 1; if (lang > 5) lang--; // 0-6 language vals
int origin = pk6.Version;
int gender = pk6.Gender;
int shiny = pk6.IsShiny ? 1 : 0;
int shiftoff = (shiny * 0x60 * 2) + (gender * 0x60) + 0x60;
// Set the [Species/Gender/Shiny] Owned Flag
Data[PokeDex + shiftoff + bit / 8 + 0x8] |= (byte)(1 << (bit % 8));
// Owned quality flag
if (origin < 0x18 && bit < 649 && !ORAS) // Species: 1-649 for X/Y, and not for ORAS; Set the Foreign Owned Flag
Data[PokeDex + 0x64C + bit / 8] |= (byte)(1 << (bit % 8));
else if (origin >= 0x18 || ORAS) // Set Native Owned Flag (should always happen)
Data[PokeDex + bit / 8 + 0x8] |= (byte)(1 << (bit % 8));
// Set the Display flag if none are set
bool[] chk =
{
// Flag Regions (base index 1 to reference Wiki and editor)
(Data[PokeDex + 0x60*(5) + bit/8 + 0x8] & (byte) (1 << (bit%8))) != 0,
(Data[PokeDex + 0x60*(6) + bit/8 + 0x8] & (byte) (1 << (bit%8))) != 0,
(Data[PokeDex + 0x60*(7) + bit/8 + 0x8] & (byte) (1 << (bit%8))) != 0,
(Data[PokeDex + 0x60*(8) + bit/8 + 0x8] & (byte) (1 << (bit%8))) != 0,
};
if (!chk.Contains(true)) // offset is already biased by 0x60, reuse shiftoff but for the display flags.
Data[PokeDex + shiftoff + 0x60 * (4) + bit / 8 + 0x8] |= (byte)(1 << (bit % 8));
// Set the Language
if (lang < 0) lang = 1;
Data[PokeDex + PokeDexLanguageFlags + (bit * 7 + lang) / 8] |= (byte)(1 << (((bit * 7) + lang) % 8));
}
public int getBoxWallpaper(int box)
{
return 1 + Data[BoxWallpapers + box];
}
public void setParty()
{
byte partymembers = 0; // start off with a ctr of 0
for (int i = 0; i < 6; i++)
{
// Gather all the species
byte[] data = new byte[PK6.SIZE_PARTY];
Array.Copy(Data, Party + i * PK6.SIZE_PARTY, data, 0, PK6.SIZE_PARTY);
byte[] decdata = decryptArray(data);
int species = BitConverter.ToInt16(decdata, 8);
if ((species != 0) && (species < 722))
Array.Copy(data, 0, Data, Party + (partymembers++) * PK6.SIZE_PARTY, PK6.SIZE_PARTY);
}
// Write in the current party count
PartyCount = partymembers;
// Zero out the party slots that are empty.
for (int i = 0; i < 6; i++)
if (i >= partymembers)
Array.Copy(encryptArray(new byte[PK6.SIZE_PARTY]), 0, Data, Party + (i * PK6.SIZE_PARTY), PK6.SIZE_PARTY);
// Repeat for Battle Box.
byte battlemem = 0;
for (int i = 0; i < 6; i++)
{
// Gather all the species
byte[] data = new byte[PK6.SIZE_PARTY];
Array.Copy(Data, BattleBox + i * PK6.SIZE_STORED, data, 0, PK6.SIZE_STORED);
byte[] decdata = decryptArray(data);
int species = BitConverter.ToInt16(decdata, 8);
if ((species != 0) && (species < 722))
Array.Copy(data, 0, Data, BattleBox + (battlemem++) * PK6.SIZE_STORED, PK6.SIZE_STORED);
}
// Zero out the party slots that are empty.
for (int i = 0; i < 6; i++)
if (i >= battlemem)
Array.Copy(encryptArray(new byte[PK6.SIZE_PARTY]), 0, Data, BattleBox + (i * PK6.SIZE_STORED), PK6.SIZE_STORED);
if (battlemem == 0)
BattleBoxLocked = false;
}
}
}

View file

@ -80,6 +80,7 @@
<Compile Include="Misc\QR.Designer.cs">
<DependentUpon>QR.cs</DependentUpon>
</Compile>
<Compile Include="Misc\SAV6.cs" />
<Compile Include="PKX\f2-Text.cs">
<SubType>Form</SubType>
</Compile>