mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-23 12:33:06 +00:00
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:
parent
75b5cfd646
commit
58babfff71
6 changed files with 229 additions and 15 deletions
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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()
|
||||
|
|
26
Misc/PKM.cs
26
Misc/PKM.cs
|
@ -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
178
Misc/SAV4.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
30
Misc/SAV5.cs
30
Misc/SAV5.cs
|
@ -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
|
||||
|
|
|
@ -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" />
|
||||
|
|
Loading…
Reference in a new issue