Add 5th Gen save file object

new SAV5(data) => easy access to boxdata/party for export.
This commit is contained in:
Kaphotics 2016-02-27 19:28:41 -08:00
parent accbbd89eb
commit 572e1b9e32
3 changed files with 184 additions and 5 deletions

View file

@ -4,7 +4,7 @@ using System.Text;
namespace PKHeX
{
public class PK5 // 5th Generation PKM File
public class PK5 : PKM // 5th Generation PKM File
{
internal const int SIZE_PARTY = 220;
internal const int SIZE_STORED = 136;
@ -168,7 +168,7 @@ namespace PKHeX
{
get
{
return PKM.TrimFromFFFF(Encoding.Unicode.GetString(Data, 0x48, 22))
return TrimFromFFFF(Encoding.Unicode.GetString(Data, 0x48, 22))
.Replace("\uE08F", "\u2640") // nidoran
.Replace("\uE08E", "\u2642") // nidoran
.Replace("\u2019", "\u0027"); // farfetch'd
@ -231,7 +231,7 @@ namespace PKHeX
{
get
{
return PKM.TrimFromFFFF(Encoding.Unicode.GetString(Data, 0x68, 16))
return TrimFromFFFF(Encoding.Unicode.GetString(Data, 0x68, 16))
.Replace("\uE08F", "\u2640") // Nidoran ♂
.Replace("\uE08E", "\u2642") // Nidoran ♀
.Replace("\u2019", "\u0027"); // farfetch'd
@ -297,6 +297,10 @@ namespace PKHeX
if (value.Length > 3) Move4 = value[3]; } }
// Complex Generated Attributes
public byte[] EncryptedPartyData => Encrypt().Take(SIZE_PARTY).ToArray();
public byte[] EncryptedBoxData => Encrypt().Take(SIZE_STORED).ToArray();
public byte[] DecryptedPartyData => Data.Take(SIZE_PARTY).ToArray();
public byte[] DecryptedBoxData => Data.Take(SIZE_STORED).ToArray();
public int Characteristic
{
get
@ -336,7 +340,7 @@ namespace PKHeX
public ushort CalculateChecksum()
{
ushort chk = 0;
for (int i = 8; i < SIZE_STORED; i += 2) // Loop through the entire PK6
for (int i = 8; i < SIZE_STORED; i += 2) // Loop through the entire PK5
chk += BitConverter.ToUInt16(Data, i);
return chk;
@ -385,6 +389,11 @@ namespace PKHeX
Move2 = Move2_PP = Move2_PPUps = 0;
}
}
public byte[] Encrypt()
{
Checksum = CalculateChecksum();
return encryptArray(Data);
}
public PK6 convertToPK6()
{

View file

@ -3,7 +3,7 @@ using System.Linq;
namespace PKHeX
{
public static class PKM // Past Gen
public class PKM // Past Gen
{
internal static uint LCRNG(uint seed)
{
@ -113,6 +113,26 @@ namespace PKHeX
return ekm;
}
/// <summary>Calculates the CRC16-CCITT checksum over an input byte array.</summary>
/// <param name="chunk">Input byte array</param>
/// <returns>Checksum</returns>
internal static ushort ccitt16(byte[] chunk)
{
ushort crc = 0xFFFF;
foreach (byte t in chunk)
{
crc ^= (ushort)(t << 8);
for (int j = 0; j < 8; j++)
{
if ((crc & 0x8000) > 0)
crc = (ushort)(crc << 1 ^ 0x1021);
else
crc <<= 1;
}
}
return crc;
}
internal static int getUnownForm(uint PID)
{
byte[] data = BitConverter.GetBytes(PID);

150
Misc/SAV5.cs Normal file
View file

@ -0,0 +1,150 @@
using System;
using System.Linq;
namespace PKHeX
{
public class SAV5 : PKM
{
internal const int SIZERAW = 0x80000; // 512KB
internal const int SIZE1 = 0x24000; // B/W
internal const int SIZE2 = 0x26000; // B2/W2
// Global Settings
// Save Data Attributes
public readonly byte[] Data;
public bool Edited;
public readonly bool Exportable;
public readonly byte[] BAK;
public string FileName, FilePath;
public SAV5(byte[] data = null)
{
Data = (byte[])(data ?? new byte[SIZERAW]).Clone();
BAK = (byte[])Data.Clone();
Exportable = !Data.SequenceEqual(new byte[Data.Length]);
// Get Version
// Validate Checksum over the Checksum Block to find BW or B2W2
ushort chk2 = BitConverter.ToUInt16(Data, SIZE2 - 0x100 + 0x94 + 0x10);
ushort actual2 = ccitt16(Data.Skip(SIZE2 - 0x100).Take(0x94).ToArray());
bool B2W2 = chk2 == actual2;
ushort chk1 = BitConverter.ToUInt16(Data, SIZE1 - 0x100 + 0x94 + 0x10);
ushort actual1 = ccitt16(Data.Skip(SIZE1 - 0x100).Take(0x94).ToArray());
bool BW = chk1 == actual1;
if (!BW && !B2W2)
Data = null; // Invalid/Not G5 Save File
// Different Offsets for different games.
BattleBox = B2W2 ? 0x20A00 : 0x20900;
}
private readonly int Box = 0x400;
private readonly int Party = 0x18E00;
private readonly int BattleBox;
public int PartyCount
{
get { return Data[Party]; }
set { Data[Party] = (byte)value; }
}
public PK5[] BoxData
{
get
{
PK5[] data = new PK5[24 * 30];
for (int i = 0; i < data.Length; i++)
{
data[i] = getPK5Stored(Box + PK5.SIZE_STORED * i);
data[i].Identifier = $"B{(i / 30 + 1).ToString("00")}:{(i % 30 + 1).ToString("00")}";
}
return data;
}
set
{
if (value == null)
throw new ArgumentNullException();
if (value.Length != 24 * 30)
throw new ArgumentException("Expected 720, got " + value.Length);
for (int i = 0; i < value.Length; i++)
setPK5Stored(value[i], Box + PK5.SIZE_STORED * i);
}
}
public PK5[] PartyData
{
get
{
PK5[] data = new PK5[PartyCount];
for (int i = 0; i < data.Length; i++)
data[i] = getPK5Party(Party + 8 + PK5.SIZE_PARTY * i);
return data;
}
set
{
if (value == null)
throw new ArgumentNullException();
if (value.Length == 0 || value.Length > 6)
throw new ArgumentException("Expected 1-6, got " + value.Length);
if (value[0].Species == 0)
throw new ArgumentException("Can't have an empty first slot." + value.Length);
PK5[] newParty = value.Where(pk => pk.Species != 0).ToArray();
PartyCount = newParty.Length;
Array.Resize(ref newParty, 6);
for (int i = PartyCount; i < newParty.Length; i++)
newParty[i] = new PK5();
for (int i = 0; i < newParty.Length; i++)
setPK5Party(newParty[i], Party + 8 + PK6.SIZE_PARTY * i);
}
}
public PK5[] BattleBoxData
{
get
{
PK5[] data = new PK5[6];
for (int i = 0; i < data.Length; i++)
{
data[i] = getPK5Stored(BattleBox + PK5.SIZE_STORED * i);
if (data[i].Species == 0)
return data.Take(i).ToArray();
}
return data;
}
}
public PK5 getPK5Party(int offset)
{
return new PK5(decryptArray(getData(offset, PK5.SIZE_PARTY)));
}
public PK5 getPK5Stored(int offset)
{
return new PK5(decryptArray(getData(offset, PK5.SIZE_STORED)));
}
public void setPK5Party(PK5 pk5, int offset)
{
if (pk5 == null) return;
setData(pk5.EncryptedPartyData, offset);
Edited = true;
}
public void setPK5Stored(PK5 pk5, int offset)
{
if (pk5 == null) return;
setData(pk5.EncryptedBoxData, offset);
Edited = true;
}
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);
Edited = true;
}
}
}