From ca20e7826968028138f4e3be9649bc0bab687f7b Mon Sep 17 00:00:00 2001 From: Kurt Date: Fri, 29 Dec 2017 15:29:31 -0800 Subject: [PATCH] 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) --- PKHeX.Core/PKM/BK4.cs | 4 +- PKHeX.Core/PKM/PKX.cs | 219 ++++++++++++++---------------------------- 2 files changed, 76 insertions(+), 147 deletions(-) diff --git a/PKHeX.Core/PKM/BK4.cs b/PKHeX.Core/PKM/BK4.cs index 241fdc391..91fed0c53 100644 --- a/PKHeX.Core/PKM/BK4.cs +++ b/PKHeX.Core/PKM/BK4.cs @@ -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() diff --git a/PKHeX.Core/PKM/PKX.cs b/PKHeX.Core/PKM/PKX.cs index c58aa5f08..a951ff136 100644 --- a/PKHeX.Core/PKM/PKX.cs +++ b/PKHeX.Core/PKM/PKX.cs @@ -377,83 +377,108 @@ public static int GetGenderFromString(string s) /// /// Data to shuffle /// Block Shuffle order + /// Size of shuffling chunks /// Shuffled byte array - 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; } /// /// Decrypts a 232 byte + party stat byte array. /// - /// Encrypted data. + /// Encrypted data. /// Decrypted data. /// Encrypted data. - 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); } /// /// Encrypts a 232 byte + party stat byte array. /// - /// Decrypted data. - public static byte[] EncryptArray(byte[] pkx) + /// Decrypted data. + 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]); + /// + /// Decrypts a 136 byte + party stat byte array. + /// + /// Encrypted data. + /// Decrypted data. + 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); + /// + /// Encrypts a 136 byte + party stat byte array. + /// + /// Decrypted data. + /// Encrypted data. + 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); } /// @@ -611,102 +636,6 @@ public static int[] SetHPIVs(int type, int[] ivs) { 1, 1, 1, 1, 1, 1 }, // Dark }; - /// - /// Shuffles a 136 byte array containing data. - /// - /// Data to shuffle - /// Block Shuffle order - /// Shuffled byte array - 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; - } - - /// - /// Decrypts a 136 byte + party stat byte array. - /// - /// Encrypted data. - /// Decrypted data. - 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; - } - - /// - /// Encrypts a 136 byte + party stat byte array. - /// - /// Decrypted data. - /// Encrypted data. - 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); - } - /// /// Gets the Unown Forme ID from PID. ///