Past gen fixes + improvements

Fixed crypt of past gen pkms (use chk not PID), add gen4 save, add save
version detection
This commit is contained in:
Kaphotics 2016-05-15 15:24:31 -07:00
parent 75b5cfd646
commit 58babfff71
6 changed files with 229 additions and 15 deletions

View file

@ -295,6 +295,8 @@ namespace PKHeX
}
// Complex Generated Attributes
public byte[] EncryptedPartyData => Encrypt().Take(SIZE_PARTY).ToArray();
public byte[] EncryptedBoxData => Encrypt().Take(SIZE_STORED).ToArray();
public int Characteristic
{
get
@ -325,6 +327,7 @@ namespace PKHeX
return ivTotal <= 150 ? 2 : 3;
}
}
public string FileName => $"{Species.ToString("000")}{(IsShiny ? " " : "")} - {Nickname} - {Checksum.ToString("X4")}{PID.ToString("X8")}.pkm";
// Methods
public void RefreshChecksum()
@ -385,6 +388,11 @@ namespace PKHeX
Move2 = Move2_PP = Move2_PPUps = 0;
}
}
public byte[] Encrypt()
{
Checksum = CalculateChecksum();
return PKM.encryptArray(Data);
}
public PK5 convertToPK5()
{

View file

@ -331,6 +331,7 @@ namespace PKHeX
return ivTotal <= 150 ? 2 : 3;
}
}
public string FileName => $"{Species.ToString("000")}{(IsShiny ? " " : "")} - {Nickname} - {Checksum.ToString("X4")}{PID.ToString("X8")}.pkm";
// Methods
public void RefreshChecksum()

View file

@ -27,6 +27,14 @@ namespace PKHeX
internal static readonly string[] speclang_de = Util.getStringList("species", "de");
internal static readonly string[] speclang_es = Util.getStringList("species", "es");
internal static int getSAVGeneration(byte[] data)
{
if (SAV4.getIsG4SAV(data) != -1)
return 4;
if (SAV5.getIsG5SAV(data) != -1)
return 5;
return -1;
}
internal static string TrimFromFFFF(string input)
{
int index = input.IndexOf((char)0xFFFF);
@ -65,9 +73,10 @@ namespace PKHeX
byte[] pkm = (byte[])ekm.Clone();
uint pv = BitConverter.ToUInt32(pkm, 0);
uint chk = BitConverter.ToUInt16(pkm, 6);
uint sv = ((pv & 0x3E000) >> 0xD) % 24;
seed = seed > 0xFFFF ? pv : seed;
seed = seed > 0xFFFF ? chk : seed;
// Decrypt Blocks with RNG Seed
for (int i = 8; i < 136; i += 2)
@ -86,15 +95,15 @@ namespace PKHeX
}
internal static byte[] encryptArray(byte[] pkm, uint seed = 0x10000)
{
// Shuffle
uint pv = BitConverter.ToUInt32(pkm, 0);
uint sv = ((pv & 0x3E000) >> 0xD) % 24;
uint chk = BitConverter.ToUInt16(pkm, 6);
byte[] ekm = (byte[])pkm.Clone();
ekm = shuffleArray(ekm, blockPositionInvert[sv]);
seed = seed > 0xFFFF ? pv : seed;
seed = seed > 0xFFFF ? chk : seed;
// Encrypt Blocks with RNG Seed
for (int i = 8; i < 136; i += 2)
@ -1106,6 +1115,17 @@ namespace PKHeX
return new PK5(input).convertToPK6();
return new PK4(input).convertToPK5().convertToPK6(); // PK4
}
internal static PK5 ConvertPKMtoPK5(byte[] input)
{
// Detect Input Generation
if (input.Length == 100 || input.Length == 80) // PK3
return new PK3(input).convertToPK4().convertToPK5();
if (input.Length != 136 && input.Length != 236 && input.Length != 220) // Invalid
return null;
if ((BitConverter.ToUInt16(input, 0x80) >= 0x3333 || input[0x5F] >= 0x10) && BitConverter.ToUInt16(input, 0x46) == 0) // PK5
return new PK5(input);
return new PK4(input).convertToPK5(); // PK4
}
internal static void updateConfig(int SUBREGION, int COUNTRY, int _3DSREGION, string TRAINERNAME, int TRAINERGENDER)
{

178
Misc/SAV4.cs Normal file
View file

@ -0,0 +1,178 @@
using System;
using System.Linq;
namespace PKHeX
{
public class SAV4 : PKM
{
internal const int SIZERAW = 0x80000; // 512KB
internal static int getIsG4SAV(byte[] data)
{
int version = -1;
if (BitConverter.ToUInt16(data, 0xC0FE) == ccitt16(data.Take(0xC0EC).ToArray()))
version = 0; // DP
else if (BitConverter.ToUInt16(data, 0xCF2A) == ccitt16(data.Take(0xCF18).ToArray()))
version = 1; // PT
else if (BitConverter.ToUInt16(data, 0xF626) == ccitt16(data.Take(0xF618).ToArray()))
version = 2; // HGSS
return version;
}
// 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 SAV4(byte[] data = null)
{
Data = (byte[])(data ?? new byte[SIZERAW]).Clone();
BAK = (byte[])Data.Clone();
Exportable = !Data.SequenceEqual(new byte[Data.Length]);
// Get Version
int version = getIsG4SAV(Data);
getActiveBlock(version);
getSAVOffsets(version);
}
private int generalBlock = -1;
private int storageBlock = -1;
private void getActiveBlock(int version)
{
if (version < 0)
return;
int ofs = 0;
if (version == 0) ofs = 0xC0F0; // DP
else if (version == 1) ofs = 0xCF1C; // PT
else if (version == 2) ofs = 0xF626; // HGSS
generalBlock = BitConverter.ToUInt16(Data, ofs) >= BitConverter.ToUInt16(Data, ofs + 0x40000) ? 0 : 1;
if (version == 0) ofs = 0x1E2D0; // DP
else if (version == 1) ofs = 0x1F100; // PT
else if (version == 2) ofs = 0x21A00; // HGSS
storageBlock = BitConverter.ToUInt16(Data, ofs) >= BitConverter.ToUInt16(Data, ofs + 0x40000) ? 0 : 1;
}
private void getSAVOffsets(int version)
{
if (version < 0)
return;
switch (version)
{
case 0: // DP
Party = 0x98 + 0x40000 * generalBlock;
Box = 0xC104 + 0x40000 * storageBlock;
break;
case 1: // PT
Party = 0xA0 + 0x40000 * generalBlock;
Box = 0xCF34 + 0x40000 * storageBlock;
break;
case 2: // HGSS
Party = 0x98 + 0x40000 * generalBlock;
Box = 0xF704 + 0x40000 * storageBlock;
break;
}
}
private int Box, Party = -1;
public int PartyCount
{
get { return Data[Party - 4]; }
set { Data[Party - 4] = (byte)value; }
}
public int BoxCount
{
get { return Data[Box - 4]; }
set { Data[Box - 4] = (byte)value; }
}
public PK4[] BoxData
{
get
{
PK4[] data = new PK4[18 * 30];
for (int i = 0; i < data.Length; i++)
{
data[i] = getPK4Stored(Box + PK4.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 != 18 * 30)
throw new ArgumentException("Expected 540, got " + value.Length);
for (int i = 0; i < value.Length; i++)
setPK4Stored(value[i], Box + PK4.SIZE_STORED * i);
}
}
public PK4[] PartyData
{
get
{
PK4[] data = new PK4[PartyCount];
for (int i = 0; i < data.Length; i++)
data[i] = getPK4Party(Party + PK4.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);
PK4[] 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 PK4();
for (int i = 0; i < newParty.Length; i++)
setPK4Party(newParty[i], Party + PK4.SIZE_PARTY * i);
}
}
public PK4 getPK4Party(int offset)
{
return new PK4(decryptArray(getData(offset, PK4.SIZE_PARTY)));
}
public PK4 getPK4Stored(int offset)
{
return new PK4(decryptArray(getData(offset, PK4.SIZE_STORED)));
}
public void setPK4Party(PK4 pk4, int offset)
{
if (pk4 == null) return;
setData(pk4.EncryptedPartyData, offset);
Edited = true;
}
public void setPK4Stored(PK4 pk4, int offset)
{
if (pk4 == null) return;
setData(pk4.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;
}
}
}

View file

@ -9,6 +9,19 @@ namespace PKHeX
internal const int SIZE1 = 0x24000; // B/W
internal const int SIZE2 = 0x26000; // B2/W2
internal static int getIsG5SAV(byte[] data)
{
ushort chk1 = BitConverter.ToUInt16(data, SIZE1 - 0x100 + 0x94 + 0x10);
ushort actual1 = ccitt16(data.Skip(SIZE1 - 0x100).Take(0x94).ToArray());
if (chk1 == actual1)
return 0;
ushort chk2 = BitConverter.ToUInt16(data, SIZE2 - 0x100 + 0x94 + 0x10);
ushort actual2 = ccitt16(data.Skip(SIZE2 - 0x100).Take(0x94).ToArray());
if (chk2 == actual2)
return 1;
return -1;
}
// Global Settings
// Save Data Attributes
public readonly byte[] Data;
@ -23,19 +36,12 @@ namespace PKHeX
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)
Data = null; // Invalid/Not G5 Save File
int version = getIsG5SAV(Data);
if (version < 0) // Invalidate Data
Data = null;
// Different Offsets for different games.
BattleBox = B2W2 ? 0x20A00 : 0x20900;
BattleBox = version == 1 ? 0x20A00 : 0x20900;
}
private const int Box = 0x400;
@ -100,7 +106,7 @@ namespace PKHeX
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);
setPK5Party(newParty[i], Party + 8 + PK5.SIZE_PARTY * i);
}
}
public PK5[] BattleBoxData

View file

@ -85,6 +85,7 @@
<Compile Include="Misc\QR.Designer.cs">
<DependentUpon>QR.cs</DependentUpon>
</Compile>
<Compile Include="Misc\SAV4.cs" />
<Compile Include="Misc\SAV5.cs" />
<Compile Include="Misc\SAV6.cs" />
<Compile Include="Misc\WC6.cs" />