From e814e7291f41271be3eca72b03638f87bc9c65f7 Mon Sep 17 00:00:00 2001 From: Kurt Date: Sat, 29 Apr 2017 20:04:54 -0700 Subject: [PATCH] Add chain shiny and shinyPGF pidiv detection delete incorrect legality message entry (used a few lines up for super training flags), is unused. fix for transferred 8<=xor<16 PIDs in which case the encryption constant is used (as it is the unmodified PID) --- PKHeX/Legality/Analysis.cs | 7 ++ PKHeX/Legality/LegalityCheckStrings.cs | 4 + PKHeX/Legality/RNG/MethodFinder.cs | 82 +++++++++++++++++-- PKHeX/Legality/RNG/PIDIV.cs | 2 + PKHeX/Legality/RNG/PIDType.cs | 4 +- .../text/en/LegalityCheckStrings_en.txt | 1 - Tests/PKHeX.Tests/PKM/PKMTests.cs | 20 ++++- 7 files changed, 109 insertions(+), 11 deletions(-) diff --git a/PKHeX/Legality/Analysis.cs b/PKHeX/Legality/Analysis.cs index 948cf520d..515715f37 100644 --- a/PKHeX/Legality/Analysis.cs +++ b/PKHeX/Legality/Analysis.cs @@ -364,6 +364,13 @@ private string getVerboseLegalityReport() lines.AddRange(br); lines.Add(string.Format(V195, EncounterName)); + var pidiv = MethodFinder.Analyze(pkm); + if (pidiv != null) + { + if (!pidiv.NoSeed) + lines.Add(string.Format(V248, pidiv.OriginSeed.ToString("X8"))); + lines.Add(string.Format(V249, pidiv.Type)); + } return getLegalityReport() + string.Join(Environment.NewLine, lines); } diff --git a/PKHeX/Legality/LegalityCheckStrings.cs b/PKHeX/Legality/LegalityCheckStrings.cs index 4426a3c66..044be09f8 100644 --- a/PKHeX/Legality/LegalityCheckStrings.cs +++ b/PKHeX/Legality/LegalityCheckStrings.cs @@ -24,6 +24,10 @@ public static class LegalityCheckStrings public static string V192 {get; set;} = "{0} Relearn Move {1}: {2}"; /// Format text for exporting the type of Encounter that was matched for the the public static string V195 {get; set;} = "Encounter Type: {0}"; + /// Format text for exporting the that was matched for the the + public static string V248 {get; set;} = "Origin Seed: {0}"; + /// Format text for exporting the that was matched for the the + public static string V249 {get; set;} = "PID Type: {0}"; /// Severity string for public static string V500 { get; set; } = "Indeterminate"; diff --git a/PKHeX/Legality/RNG/MethodFinder.cs b/PKHeX/Legality/RNG/MethodFinder.cs index 13c73433e..4ed0fdb52 100644 --- a/PKHeX/Legality/RNG/MethodFinder.cs +++ b/PKHeX/Legality/RNG/MethodFinder.cs @@ -17,7 +17,10 @@ public static PIDIV Analyze(PKM pk) { if (pk.Format < 3) return AnalyzeGB(pk); - var pid = pk.PID; + var pid = pk.Format >= 6 && pk.GenNumber >= 3 && pk.GenNumber < 6 + ? pk.EncryptionConstant // use unmodified PID, quicker than checking if bit was flipped + : pk.PID; // use actual PID + var top = pid >> 16; var bot = pid & 0xFFFF; @@ -37,10 +40,13 @@ public static PIDIV Analyze(PKM pk) return pidiv; if (getMG4Match(pid, IVs, out pidiv)) return pidiv; - if (getModifiedPID(pid, out pidiv)) + + if (getModifiedPID(pk, pid, out pidiv)) return pidiv; if (pid <= 0xFF && getCuteCharmMatch(pk, pid, out pidiv)) return pidiv; + if (pk.IsShiny && getChainShinyMatch(pk, pid, IVs, out pidiv)) + return pidiv; return pidiv; // no match } @@ -125,16 +131,26 @@ private static bool getMG4Match(uint pid, uint[] IVs, out PIDIV pidiv) if (!getIVs(C >> 16, D >> 16).SequenceEqual(IVs)) continue; - pidiv = new PIDIV {OriginSeed = seed, RNG = RNG.LCRNG, Type = PIDType.G4AntiShiny}; + pidiv = new PIDIV {OriginSeed = seed, RNG = RNG.LCRNG, Type = PIDType.G4MGAntiShiny}; return true; } pidiv = null; return false; } - private static bool getModifiedPID(uint pid, out PIDIV pidiv) + private static bool getModifiedPID(PKM pk, uint pid, out PIDIV pidiv) { + var low = pid & 0xFFFF; // generation 5 shiny PIDs - // todo + if (low <= 0xFF) + { + var high = pid >> 16; + if ((pk.TID ^ pk.SID ^ low) == high) + { + pidiv = new PIDIV {NoSeed = true, Type = PIDType.G5MGShiny}; + return true; + } + } + pidiv = null; return false; } @@ -153,7 +169,7 @@ private static bool getCuteCharmMatch(PKM pk, uint pid, out PIDIV pidiv) if (pk.PID >= gr + 25) break; - pidiv = new PIDIV { OriginSeed = 0, RNG = RNG.LCRNG, Type = PIDType.CuteCharm }; + pidiv = new PIDIV {NoSeed = true, RNG = RNG.LCRNG, Type = PIDType.CuteCharm}; return true; case 1: // female if (pk.PID >= 25) @@ -161,12 +177,57 @@ private static bool getCuteCharmMatch(PKM pk, uint pid, out PIDIV pidiv) if (254 <= pk.PersonalInfo.Gender) // no modification for PID break; - pidiv = new PIDIV { OriginSeed = 0, RNG = RNG.LCRNG, Type = PIDType.CuteCharm }; + pidiv = new PIDIV {NoSeed = true, RNG = RNG.LCRNG, Type = PIDType.CuteCharm}; return true; } pidiv = null; return false; } + private static bool getChainShinyMatch(PKM pk, uint pid, uint[] IVs, out PIDIV pidiv) + { + // 13 shiny bits + // PIDH & 7 + // PIDL & 7 + // IVs + var bot = getIVChunk(IVs, 0); + var top = getIVChunk(IVs, 3); + var reg = getSeedsFromIVs(RNG.LCRNG, top, bot); + foreach (var seed in reg) + { + // check the individual bits + var s = seed; + int i = 15; + while (true) + { + var bit = s >> 16 & 1; + if (bit != (pid >> i & 1)) + break; + s = RNG.LCRNG.Prev(s); + if (--i == 2) + break; + } + if (i != 2) // bit failed + break; + // Shiny Bits of PID validated + var upper = s; + if ((upper >> 16 & 7) != (pid >> 16 & 7)) + break; + var lower = RNG.LCRNG.Prev(upper); + if ((lower >> 16 & 7) != (pid & 7)) + break; + + var upid = ((pid & 0xFFFF) ^ pk.TID ^ pk.SID) & 0xFFF8 | (upper >> 16) & 0x7; + if (upid != pid >> 16) + break; + + s = RNG.LCRNG.Reverse(lower, 2); // unroll one final time to get the origin seed + pidiv = new PIDIV {OriginSeed = s, RNG = RNG.LCRNG, Type = PIDType.ChainShiny}; + return true; + } + + pidiv = null; + return false; + } private static PIDIV AnalyzeGB(PKM pk) { @@ -232,5 +293,12 @@ private static uint[] getIVs(RNG method, uint seed) } return ivs; } + private static uint getIVChunk(uint[] IVs, int start) + { + uint val = 0; + for (int i = 0; i < 3; i++) + val |= IVs[i+start] << (5*i); + return val; + } } } diff --git a/PKHeX/Legality/RNG/PIDIV.cs b/PKHeX/Legality/RNG/PIDIV.cs index a55eac525..ee7fe2843 100644 --- a/PKHeX/Legality/RNG/PIDIV.cs +++ b/PKHeX/Legality/RNG/PIDIV.cs @@ -8,6 +8,8 @@ public class PIDIV /// The RNG seed which immediately generates the PIDIV (starting with PID or IVs, whichever comes first). public uint OriginSeed; + public bool NoSeed; + /// Type of PIDIV correlation public PIDType Type; } diff --git a/PKHeX/Legality/RNG/PIDType.cs b/PKHeX/Legality/RNG/PIDType.cs index a24d56b3c..acf12f4ed 100644 --- a/PKHeX/Legality/RNG/PIDType.cs +++ b/PKHeX/Legality/RNG/PIDType.cs @@ -35,10 +35,10 @@ public enum PIDType Channel, // ARNG Based - G4AntiShiny, + G4MGAntiShiny, // Formulaic - G5AntiShiny, + G5MGShiny, // Specified Static, diff --git a/PKHeX/Resources/text/en/LegalityCheckStrings_en.txt b/PKHeX/Resources/text/en/LegalityCheckStrings_en.txt index 759da4af2..821975bed 100644 --- a/PKHeX/Resources/text/en/LegalityCheckStrings_en.txt +++ b/PKHeX/Resources/text/en/LegalityCheckStrings_en.txt @@ -162,7 +162,6 @@ V98 = Unused Super Training Flag is flagged. V95 = Can't receive Ribbon(s) as an egg. V96 = GBA Champion Ribbon V97 = Artist Ribbon -V98 = National Ribbon (Purified) V99 = Sinnoh Champion Ribbon V100 = Legend Ribbon V104 = Record Ribbon diff --git a/Tests/PKHeX.Tests/PKM/PKMTests.cs b/Tests/PKHeX.Tests/PKM/PKMTests.cs index 2cbe28435..2ff075bb9 100644 --- a/Tests/PKHeX.Tests/PKM/PKMTests.cs +++ b/Tests/PKHeX.Tests/PKM/PKMTests.cs @@ -180,7 +180,25 @@ public void PIDIVMatchingTest() PID = 0x07578CB7, // 0x5271E97E rerolled IVs = new[] {16, 13, 12, 02, 18, 03}, }; - Assert.AreEqual(PIDType.G4AntiShiny, MethodFinder.Analyze(pkASR)?.Type, "Unable to match PID to Antishiny4 spread"); + Assert.AreEqual(PIDType.G4MGAntiShiny, MethodFinder.Analyze(pkASR)?.Type, "Unable to match PID to Antishiny4 spread"); + + var pkCS = new PK4 + { + PID = 0xA9C1A9C6, + // TID = 0, + // SID = 0, // already default values, necessary for the forcing of a shiny PID + IVs = new[] {22, 14, 23, 24, 11, 04} + }; + Assert.AreEqual(PIDType.ChainShiny, MethodFinder.Analyze(pkCS)?.Type, "Unable to match PID to Chain Shiny spread"); + + var pkS5 = new PK5 + { + PID = 0xBEEF0037, + TID = 01337, + SID = 48097, + // IVs = new[] {22, 14, 23, 24, 11, 04} // unnecessary + }; + Assert.AreEqual(PIDType.G5MGShiny, MethodFinder.Analyze(pkS5)?.Type, "Unable to match PID to PGF Shiny spread"); } } }