mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-23 20:43:07 +00:00
Add 5th Gen save file object
new SAV5(data) => easy access to boxdata/party for export.
This commit is contained in:
parent
accbbd89eb
commit
572e1b9e32
3 changed files with 184 additions and 5 deletions
17
Misc/PK5.cs
17
Misc/PK5.cs
|
@ -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()
|
||||
{
|
||||
|
|
22
Misc/PKM.cs
22
Misc/PKM.cs
|
@ -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
150
Misc/SAV5.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue