From aa7974e158c364a4cf11e6f741c61b80f2bb5648 Mon Sep 17 00:00:00 2001 From: Kurt Date: Mon, 28 Dec 2015 19:00:10 -0800 Subject: [PATCH] Rearrange src, speed up encryption No need to shuffle 11*, just have the precomputed final shuffle values and redirect. Rearrangement removes PKX/PGT interaction (now only refers to past gen code base) --- Misc/PGT.cs | 79 ++++++++++++++++++++++--------------------- Misc/PKM.cs | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Misc/PKX.cs | 80 +++----------------------------------------- 3 files changed, 142 insertions(+), 113 deletions(-) diff --git a/Misc/PGT.cs b/Misc/PGT.cs index 935a0533c..d037105de 100644 --- a/Misc/PGT.cs +++ b/Misc/PGT.cs @@ -38,7 +38,7 @@ public PGT(byte[] data = null) byte[] ekdata = new byte[PK4.SIZE_PARTY]; Array.Copy(Data, 8, ekdata, 0, ekdata.Length); // Decrypt PK4 - PKM = new PK4(PKX.decryptG4Array(ekdata, BitConverter.ToUInt16(ekdata, 6))); + PK = new PK4(PKM.decryptArray(ekdata, BitConverter.ToUInt16(ekdata, 6))); Unknown = new byte[0x10]; Array.Copy(Data, 0xF4, Unknown, 0, 0x10); @@ -48,7 +48,7 @@ public PGT(byte[] data = null) // Unused 0x01 public byte Slot { get { return Data[2]; } set { Data[2] = value; } } public byte Detail { get { return Data[3]; } set { Data[3] = value; } } - public PK4 PKM; + public PK4 PK; public byte[] Unknown; public bool IsPokémon { get { return CardType == 1; } set { if (value) CardType = 1; } } @@ -61,37 +61,38 @@ public PK4 convertToPK4(SAV6 SAV) if (!PokémonGift) return null; + PK4 pk4 = new PK4(PK.Data); if (!IsPokémon && Detail == 0) { - PKM.OT_Name = "PKHeX"; - PKM.TID = 12345; - PKM.SID = 54321; - PKM.OT_Gender = (int)(Util.rnd32()%2); + pk4.OT_Name = "pk4HeX"; + pk4.TID = 12345; + pk4.SID = 54321; + pk4.OT_Gender = (int)(Util.rnd32()%2); } if (IsManaphyEgg) { // Since none of this data is populated, fill in default info. - PKM.Species = 490; + pk4.Species = 490; // Level 1 Moves - PKM.Move1 = 294; - PKM.Move2 = 145; - PKM.Move3 = 346; - PKM.FatefulEncounter = true; - PKM.Ball = 4; - PKM.Version = 10; // Diamond - PKM.Language = 2; // English - PKM.Nickname = "MANAPHY"; - PKM.Egg_Location = 1; // Ranger (will be +3000 later) + pk4.Move1 = 294; + pk4.Move2 = 145; + pk4.Move3 = 346; + pk4.FatefulEncounter = true; + pk4.Ball = 4; + pk4.Version = 10; // Diamond + pk4.Language = 2; // English + pk4.Nickname = "MANAPHY"; + pk4.Egg_Location = 1; // Ranger (will be +3000 later) } // Generate IV uint seed = Util.rnd32(); - if (PKM.PID == 1) // Create Nonshiny + if (pk4.PID == 1) // Create Nonshiny { - uint pid1 = PKX.LCRNG(ref seed) >> 16; - uint pid2 = PKX.LCRNG(ref seed) >> 16; + uint pid1 = PKM.LCRNG(ref seed) >> 16; + uint pid2 = PKM.LCRNG(ref seed) >> 16; - while ((pid1 ^ pid2 ^ PKM.TID ^ PKM.SID) < 8) + while ((pid1 ^ pid2 ^ pk4.TID ^ pk4.SID) < 8) { uint testPID = pid1 | (pid2 << 16); @@ -101,39 +102,41 @@ public PK4 convertToPK4(SAV6 SAV) pid1 = testPID & 0xFFFF; pid2 = testPID >> 16; } - PKM.PID = pid1 | (pid2 << 16); + pk4.PID = pid1 | (pid2 << 16); } // Generate IVs - if (PKM.IV32 == 0) + if (pk4.IV32 == 0) { - uint iv1 = PKX.LCRNG(ref seed) >> 16; - uint iv2 = PKX.LCRNG(ref seed) >> 16; - PKM.IV32 = (iv1 | (iv2 << 16)) & 0x3FFFFFFF; + uint iv1 = PKM.LCRNG(ref seed) >> 16; + uint iv2 = PKM.LCRNG(ref seed) >> 16; + pk4.IV32 = (iv1 | (iv2 << 16)) & 0x3FFFFFFF; } // Generate Met Info DateTime dt = DateTime.Now; if (IsPokémon) { - PKM.Met_Location = PKM.Egg_Location + 3000; - PKM.Egg_Location = 0; - PKM.Met_Day = dt.Day; - PKM.Met_Month = dt.Month; - PKM.Met_Year = dt.Year - 2000; - PKM.IsEgg = false; + pk4.Met_Location = pk4.Egg_Location + 3000; + pk4.Egg_Location = 0; + pk4.Met_Day = dt.Day; + pk4.Met_Month = dt.Month; + pk4.Met_Year = dt.Year - 2000; + pk4.IsEgg = false; } else { - PKM.Egg_Location = PKM.Egg_Location + 3000; - PKM.Egg_Day = dt.Day; - PKM.Egg_Month = dt.Month; - PKM.Egg_Year = dt.Year - 2000; - PKM.IsEgg = false; - // Met Location is modified when transferred to PK5; don't worry about it. + pk4.Egg_Location = pk4.Egg_Location + 3000; + pk4.Egg_Day = dt.Day; + pk4.Egg_Month = dt.Month; + pk4.Egg_Year = dt.Year - 2000; + pk4.IsEgg = false; + // Met Location is modified when transferred to pk5; don't worry about it. } + if (pk4.Species == 201) // Never will be true; Unown was never distributed. + pk4.AltForm = PKM.getUnownForm(pk4.PID); - return PKM; + return pk4; } } } diff --git a/Misc/PKM.cs b/Misc/PKM.cs index cb7256395..3ec56d894 100644 --- a/Misc/PKM.cs +++ b/Misc/PKM.cs @@ -5,6 +5,23 @@ namespace PKHeX { public class PKM // Past Gen { + internal static uint LCRNG(uint seed) + { + const uint a = 0x41C64E6D; + const uint c = 0x00006073; + + seed = (seed * a + c) & 0xFFFFFFFF; + return seed; + } + internal static uint LCRNG(ref uint seed) + { + const uint a = 0x41C64E6D; + const uint c = 0x00006073; + + seed = (seed * a + c) & 0xFFFFFFFF; + return seed; + } + internal static Converter Config = new Converter(); internal static string[] speclang_ja = Util.getStringList("Species", "ja"); internal static string[] speclang_en = Util.getStringList("Species", "en"); @@ -19,6 +36,85 @@ internal static string TrimFromFFFF(string input) return index < 0 ? input : input.Substring(0, index); } + // Past Gen Manipulation + internal static readonly byte[][] blockPosition = + { + new byte[] {0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 2, 3, 1, 1, 2, 3, 2, 3, 1, 1, 2, 3, 2, 3}, + new byte[] {1, 1, 2, 3, 2, 3, 0, 0, 0, 0, 0, 0, 2, 3, 1, 1, 3, 2, 2, 3, 1, 1, 3, 2}, + new byte[] {2, 3, 1, 1, 3, 2, 2, 3, 1, 1, 3, 2, 0, 0, 0, 0, 0, 0, 3, 2, 3, 2, 1, 1}, + new byte[] {3, 2, 3, 2, 1, 1, 3, 2, 3, 2, 1, 1, 3, 2, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0}, + }; + internal static readonly byte[] blockPositionInvert = + { + 0, 1, 2, 4, 3, 5, 6, 7, 12, 18, 13, 19, 8, 10, 14, 20, 16, 22, 9, 11, 15, 21, 17, 23 + }; + internal static byte[] shuffleArray(byte[] data, uint sv) + { + byte[] sdata = new byte[PK4.SIZE_PARTY]; + 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, 100); + + return sdata; + } + internal static byte[] decryptArray(byte[] ekm, uint seed = 0x10000) + { + byte[] pkm = (byte[])ekm.Clone(); + + uint pv = BitConverter.ToUInt32(pkm, 0); + uint sv = (((pv & 0x3E000) >> 0xD) % 24); + + seed = seed > 0xFFFF ? pv : seed; + + // Decrypt Blocks with RNG Seed + for (int i = 8; i < 136; i += 2) + BitConverter.GetBytes((ushort)(BitConverter.ToUInt16(pkm, i) ^ (LCRNG(ref seed) >> 16))).CopyTo(pkm, i); + + // Deshuffle + pkm = shuffleArray(pkm, sv); + + // Decrypt the Party Stats + seed = pv; + if (pkm.Length <= 136) return pkm; + for (int i = 136; i < 236; i += 2) + BitConverter.GetBytes((ushort)(BitConverter.ToUInt16(pkm, i) ^ (LCRNG(ref seed) >> 16))).CopyTo(pkm, i); + + return pkm; + } + internal static byte[] encryptArray(byte[] pkm, uint seed = 0x10000) + { + // Shuffle + uint pv = BitConverter.ToUInt32(pkm, 0); + uint sv = (((pv & 0x3E000) >> 0xD) % 24); + + byte[] ekm = (byte[])pkm.Clone(); + + ekm = shuffleArray(ekm, blockPositionInvert[sv]); + + seed = seed > 0xFFFF ? pv : seed; + + // Encrypt Blocks with RNG Seed + for (int i = 8; i < 136; i += 2) + BitConverter.GetBytes((ushort)(BitConverter.ToUInt16(ekm, i) ^ (LCRNG(ref seed) >> 16))).CopyTo(ekm, i); + + // If no party stats, return. + if (ekm.Length <= 136) return ekm; + + // Encrypt the Party Stats + seed = pv; + for (int i = 136; i < 236; i += 2) + BitConverter.GetBytes((ushort)(BitConverter.ToUInt16(ekm, i) ^ (LCRNG(ref seed) >> 16))).CopyTo(ekm, i); + + // Done + return ekm; + } + internal static int getUnownForm(uint PID) { byte[] data = BitConverter.GetBytes(PID); diff --git a/Misc/PKX.cs b/Misc/PKX.cs index fa3e85691..b5c74b025 100644 --- a/Misc/PKX.cs +++ b/Misc/PKX.cs @@ -450,6 +450,10 @@ internal static ushort[] getStats(PK6 pk6) new byte[] {2, 3, 1, 1, 3, 2, 2, 3, 1, 1, 3, 2, 0, 0, 0, 0, 0, 0, 3, 2, 3, 2, 1, 1}, new byte[] {3, 2, 3, 2, 1, 1, 3, 2, 3, 2, 1, 1, 3, 2, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0}, }; + internal static readonly byte[] blockPositionInvert = + { + 0, 1, 2, 4, 3, 5, 6, 7, 12, 18, 13, 19, 8, 10, 14, 20, 16, 22, 9, 11, 15, 21, 17, 23 + }; internal static byte[] shuffleArray(byte[] data, uint sv) { byte[] sdata = new byte[260]; @@ -497,10 +501,7 @@ internal static byte[] encryptArray(byte[] pkx) byte[] ekx = (byte[])pkx.Clone(); - // If I unshuffle 11 times, the 12th (decryption) will always decrypt to ABCD. - // 2 x 3 x 4 = 12 (possible unshuffle loops -> total iterations) - for (int i = 0; i < 11; i++) - ekx = shuffleArray(ekx, sv); + ekx = shuffleArray(ekx, blockPositionInvert[sv]); uint seed = pv; @@ -581,77 +582,6 @@ internal static uint getRandomPID(int species, int cg) return pid; } - // Past Gen Manipulation - internal static byte[] shuffleG4Array(byte[] data, uint sv) - { - byte[] sdata = new byte[PK4.SIZE_PARTY]; - 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, 100); - - return sdata; - } - internal static byte[] decryptG4Array(byte[] ekm, uint seed = 0x10000) - { - byte[] pkm = (byte[])ekm.Clone(); - - uint pv = BitConverter.ToUInt32(pkm, 0); - uint sv = (((pv & 0x3E000) >> 0xD) % 24); - - seed = seed > 0xFFFF ? pv : seed; - - // Decrypt Blocks with RNG Seed - for (int i = 8; i < 136; i += 2) - BitConverter.GetBytes((ushort)(BitConverter.ToUInt16(pkm, i) ^ (LCRNG(ref seed) >> 16))).CopyTo(pkm, i); - - // Deshuffle - pkm = shuffleG4Array(pkm, sv); - - // Decrypt the Party Stats - seed = pv; - if (pkm.Length <= 136) return pkm; - for (int i = 136; i < 236; i += 2) - BitConverter.GetBytes((ushort)(BitConverter.ToUInt16(pkm, i) ^ (LCRNG(ref seed) >> 16))).CopyTo(pkm, i); - - return pkm; - } - internal static byte[] encryptG4Array(byte[] pkm, uint seed = 0x10000) - { - // Shuffle - uint pv = BitConverter.ToUInt32(pkm, 0); - uint sv = (((pv & 0x3E000) >> 0xD) % 24); - - byte[] ekm = (byte[])pkm.Clone(); - - // If I unshuffle 11 times, the 12th (decryption) will always decrypt to ABCD. - // 2 x 3 x 4 = 12 (possible unshuffle loops -> total iterations) - for (int i = 0; i < 11; i++) - ekm = shuffleArray(ekm, sv); - - seed = seed > 0xFFFF ? pv : seed; - - // Encrypt Blocks with RNG Seed - for (int i = 8; i < 136; i += 2) - BitConverter.GetBytes((ushort)(BitConverter.ToUInt16(ekm, i) ^ (LCRNG(ref seed) >> 16))).CopyTo(ekm, i); - - // If no party stats, return. - if (ekm.Length <= 136) return ekm; - - // Encrypt the Party Stats - seed = pv; - for (int i = 136; i < 236; i += 2) - BitConverter.GetBytes((ushort)(BitConverter.ToUInt16(ekm, i) ^ (LCRNG(ref seed) >> 16))).CopyTo(ekm, i); - - // Done - return ekm; - } - // SAV Manipulation /// Calculates the CRC16-CCITT checksum over an input byte array. /// Input byte array