pkm crypto Refactoring r2

condense repeated logic, reduce overall operations
removes 1 array alloc for pkm encryption (shuffle clones the array, so
don't clone->clone)
removes 1 array alloc for pkm decryption (mutate encrypted array), be
aware that no current use cases input an encrypted region and expect the
reference to stay encrypted (always was a temp array).
remove duplicate bounds checks (early return & for loop)
This commit is contained in:
Kurt
2017-12-29 15:29:31 -08:00
parent 0c2b16391f
commit ca20e78269
2 changed files with 76 additions and 147 deletions

View File

@@ -24,7 +24,7 @@ public BK4(byte[] decryptedData = null, string ident = null)
{
Data = decryptedData ?? new byte[SIZE_PARTY];
uint sv = ((PID & 0x3E000) >> 0xD) % 24;
Data = PKX.ShuffleArray45(Data, sv);
Data = PKX.ShuffleArray(Data, sv, PKX.SIZE_4BLOCK);
Identifier = ident;
if (Sanity != 0 && Species <= MaxSpeciesID && !ChecksumValid) // We can only hope
RefreshChecksum();
@@ -388,7 +388,7 @@ protected override ushort CalculateChecksum()
protected override byte[] Encrypt()
{
RefreshChecksum();
return PKX.ShuffleArray45(Data, PKX.blockPositionInvert[((PID & 0x3E000) >> 0xD)%24]);
return PKX.ShuffleArray(Data, PKX.blockPositionInvert[((PID & 0x3E000) >> 0xD)%24], PKX.SIZE_4BLOCK);
}
public PK4 ConvertToPK4()

View File

@@ -377,83 +377,108 @@ public static int GetGenderFromString(string s)
/// </summary>
/// <param name="data">Data to shuffle</param>
/// <param name="sv">Block Shuffle order</param>
/// <param name="blockSize">Size of shuffling chunks</param>
/// <returns>Shuffled byte array</returns>
public static byte[] ShuffleArray(byte[] data, uint sv)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte[] ShuffleArray(byte[] data, uint sv, int blockSize)
{
byte[] sdata = new byte[data.Length];
Array.Copy(data, sdata, 8); // Copy unshuffled bytes
// Shuffle Away!
byte[] sdata = (byte[])data.Clone();
for (int block = 0; block < 4; block++)
Array.Copy(data, 8 + 56*blockPosition[block][sv], sdata, 8 + 56*block, 56);
// Fill the Battle Stats back
if (data.Length > 232)
Array.Copy(data, 232, sdata, 232, 28);
Array.Copy(data, 8 + blockSize * blockPosition[block][sv], sdata, 8 + blockSize * block, blockSize);
return sdata;
}
/// <summary>
/// Decrypts a 232 byte + party stat byte array.
/// </summary>
/// <param name="ekx">Encrypted <see cref="PKM"/> data.</param>
/// <param name="ekm">Encrypted <see cref="PKM"/> data.</param>
/// <returns>Decrypted <see cref="PKM"/> data.</returns>
/// <returns>Encrypted <see cref="PKM"/> data.</returns>
public static byte[] DecryptArray(byte[] ekx)
public static byte[] DecryptArray(byte[] ekm)
{
byte[] pkx = (byte[])ekx.Clone();
uint pv = BitConverter.ToUInt32(pkx, 0);
uint pv = BitConverter.ToUInt32(ekm, 0);
uint sv = (pv >> 0xD & 0x1F) % 24;
uint seed = pv;
// Decrypt Blocks with RNG Seed
for (int i = 8; i < 232; i += 2)
Crypt(pkx, ref seed, i);
// Deshuffle
pkx = ShuffleArray(pkx, sv);
// Decrypt the Party Stats
seed = pv;
if (pkx.Length <= 232) return pkx;
for (int i = 232; i < 260; i += 2)
Crypt(pkx, ref seed, i);
return pkx;
CryptPKM(ekm, pv, SIZE_6BLOCK);
return ShuffleArray(ekm, sv, SIZE_6BLOCK);
}
/// <summary>
/// Encrypts a 232 byte + party stat byte array.
/// </summary>
/// <param name="pkx">Decrypted <see cref="PKM"/> data.</param>
public static byte[] EncryptArray(byte[] pkx)
/// <param name="pkm">Decrypted <see cref="PKM"/> data.</param>
public static byte[] EncryptArray(byte[] pkm)
{
// Shuffle
uint pv = BitConverter.ToUInt32(pkx, 0);
uint pv = BitConverter.ToUInt32(pkm, 0);
uint sv = (pv >> 0xD & 0x1F) % 24;
byte[] ekx = (byte[])pkx.Clone();
byte[] ekm = ShuffleArray(pkm, blockPositionInvert[sv], SIZE_6BLOCK);
CryptPKM(ekm, pv, SIZE_6BLOCK);
return ekm;
}
ekx = ShuffleArray(ekx, blockPositionInvert[sv]);
/// <summary>
/// Decrypts a 136 byte + party stat byte array.
/// </summary>
/// <param name="ekm">Encrypted <see cref="PKM"/> data.</param>
/// <returns>Decrypted <see cref="PKM"/> data.</returns>
public static byte[] DecryptArray45(byte[] ekm)
{
uint pv = BitConverter.ToUInt32(ekm, 0);
uint chk = BitConverter.ToUInt16(ekm, 6);
uint sv = (pv >> 0xD & 0x1F) % 24;
uint seed = pv;
CryptPKM45(ekm, pv, chk, SIZE_4BLOCK);
return ShuffleArray(ekm, sv, SIZE_4BLOCK);
}
// Encrypt Blocks with RNG Seed
for (int i = 8; i < 232; i += 2)
Crypt(ekx, ref seed, i);
/// <summary>
/// Encrypts a 136 byte + party stat byte array.
/// </summary>
/// <param name="pkm">Decrypted <see cref="PKM"/> data.</param>
/// <returns>Encrypted <see cref="PKM"/> data.</returns>
public static byte[] EncryptArray45(byte[] pkm)
{
uint pv = BitConverter.ToUInt32(pkm, 0);
uint chk = BitConverter.ToUInt16(pkm, 6);
uint sv = (pv >> 0xD & 0x1F) % 24;
// If no party stats, return.
if (ekx.Length <= 232) return ekx;
byte[] ekm = ShuffleArray(pkm, blockPositionInvert[sv], SIZE_4BLOCK);
CryptPKM45(ekm, pv, chk, SIZE_4BLOCK);
return ekm;
}
// Encrypt the Party Stats
seed = pv;
for (int i = 232; i < 260; i += 2)
Crypt(ekx, ref seed, i);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void CryptPKM(byte[] data, uint pv, int blockSize)
{
const int start = 8;
int end = 4 * blockSize + start;
CryptArray(data, pv, 8, end); // Blocks
CryptArray(data, pv, end, data.Length); // Party Stats
}
return ekx;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void CryptPKM45(byte[] data, uint pv, uint chk, int blockSize)
{
const int start = 8;
int end = 4 * blockSize + start;
CryptArray(data, chk, start, end); // Blocks
CryptArray(data, pv, end, data.Length); // Party Stats
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void CryptArray(byte[] data, uint seed, int start, int end)
{
for (int i = start; i < end; i += 2)
Crypt(data, ref seed, i);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Crypt(byte[] data, ref uint seed, int i)
{
seed = 0x41C64E6D * seed + 0x00006073;
data[i] ^= (byte)(seed >> 16);
data[i + 1] ^= (byte)(seed >> 24);
}
/// <summary>
@@ -611,102 +636,6 @@ public static int[] SetHPIVs(int type, int[] ivs)
{ 1, 1, 1, 1, 1, 1 }, // Dark
};
/// <summary>
/// Shuffles a 136 byte array containing <see cref="PKM"/> data.
/// </summary>
/// <param name="data">Data to shuffle</param>
/// <param name="sv">Block Shuffle order</param>
/// <returns>Shuffled byte array</returns>
public static byte[] ShuffleArray45(byte[] data, uint sv)
{
byte[] sdata = new byte[data.Length];
Array.Copy(data, sdata, 8); // Copy unshuffled bytes
// Shuffle Away!
for (int block = 0; block < 4; block++)
Array.Copy(data, 8 + 32 * blockPosition[block][sv], sdata, 8 + 32 * block, 32);
// Fill the Battle Stats back
if (data.Length > 136)
Array.Copy(data, 136, sdata, 136, data.Length - 136);
return sdata;
}
/// <summary>
/// Decrypts a 136 byte + party stat byte array.
/// </summary>
/// <param name="ekm">Encrypted <see cref="PKM"/> data.</param>
/// <returns>Decrypted <see cref="PKM"/> data.</returns>
public static byte[] DecryptArray45(byte[] ekm)
{
byte[] pkm = (byte[])ekm.Clone();
uint pv = BitConverter.ToUInt32(pkm, 0);
uint chk = BitConverter.ToUInt16(pkm, 6);
uint sv = ((pv & 0x3E000) >> 0xD) % 24;
uint seed = chk;
// Decrypt Blocks with RNG Seed
for (int i = 8; i < 136; i += 2)
Crypt(pkm, ref seed, i);
// Deshuffle
pkm = ShuffleArray45(pkm, sv);
// Decrypt the Party Stats
seed = pv;
if (pkm.Length <= 136) return pkm;
for (int i = 136; i < pkm.Length; i += 2)
Crypt(pkm, ref seed, i);
return pkm;
}
/// <summary>
/// Encrypts a 136 byte + party stat byte array.
/// </summary>
/// <param name="pkm">Decrypted <see cref="PKM"/> data.</param>
/// <returns>Encrypted <see cref="PKM"/> data.</returns>
public static byte[] EncryptArray45(byte[] pkm)
{
uint pv = BitConverter.ToUInt32(pkm, 0);
uint sv = ((pv & 0x3E000) >> 0xD) % 24;
uint chk = BitConverter.ToUInt16(pkm, 6);
byte[] ekm = (byte[])pkm.Clone();
ekm = ShuffleArray45(ekm, blockPositionInvert[sv]);
uint seed = chk;
// Encrypt Blocks with RNG Seed
for (int i = 8; i < 136; i += 2)
Crypt(ekm, ref seed, i);
// If no party stats, return.
if (ekm.Length <= 136) return ekm;
// Encrypt the Party Stats
seed = pv;
for (int i = 136; i < ekm.Length; i += 2)
Crypt(ekm, ref seed, i);
// Done
return ekm;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Crypt(byte[] data, ref uint seed, int i)
{
var val = data[i] | data[i + 1] << 8;
seed = 0x41C64E6D * seed + 0x00006073;
val ^= (ushort)(seed >> 16);
data[i] = (byte)val;
data[i + 1] = (byte)(val >> 8);
}
/// <summary>
/// Gets the Unown Forme ID from PID.
/// </summary>