From 4d702d261d7f4f629ffcc748c0c13624a074a4cd Mon Sep 17 00:00:00 2001 From: Kurt Date: Wed, 5 Mar 2025 22:45:41 -0600 Subject: [PATCH] Add ivs->channel seed recovery Still lack IVs/nature/shiny request for the BACD/M2 variants, maybe in the future those can be added. --- .../Templates/Gen3/Gifts/EncounterGift3.cs | 18 +++++ PKHeX.Core/Legality/RNG/Algorithms/XDRNG.cs | 76 ++++++++++++++++++- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3.cs index 7d0a5ac36..51602f87c 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3.cs @@ -269,6 +269,24 @@ private static uint GetMethod2(ref uint seed) private static uint SetPINGAChannel(PK3 pk, EncounterCriteria criteria) { + if (criteria.IsSpecifiedIVsAll()) + { + Span seeds = stackalloc uint[XDRNG.MaxCountSeedsChannel]; + var count = XDRNG.GetSeedsChannel(seeds, (uint)criteria.IV_HP, (uint)criteria.IV_ATK, (uint)criteria.IV_DEF, (uint)criteria.IV_SPA, (uint)criteria.IV_SPD, (uint)criteria.IV_SPE); + foreach (var seed in seeds[..count]) + { + if (!ChannelJirachi.IsPossible(seed)) + continue; + PIDGenerator.SetValuesFromSeedChannel(pk, seed); + var pid = pk.EncryptionConstant; + if (criteria.IsSpecifiedNature() && !criteria.IsSatisfiedNature((Nature)(pid % 25))) + continue; // try again + if (criteria.Shiny.IsShiny() != ShinyUtil.GetIsShiny(pk.ID32, pid, 8)) + continue; // try again + return seed; + } + } + while (true) { uint seed = Util.Rand32(); diff --git a/PKHeX.Core/Legality/RNG/Algorithms/XDRNG.cs b/PKHeX.Core/Legality/RNG/Algorithms/XDRNG.cs index b9e321d82..84c1e1265 100644 --- a/PKHeX.Core/Legality/RNG/Algorithms/XDRNG.cs +++ b/PKHeX.Core/Legality/RNG/Algorithms/XDRNG.cs @@ -105,6 +105,17 @@ public static uint Next16(ref uint seed) return seed >> 16; } + /// + /// Gets the upper 5 bits of the next RNG seed. + /// + /// Seed to advance one step. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Next5(ref uint seed) + { + seed = Next(seed); + return seed >> 27; + } + /// /// Gets the upper 0x7FFF bits of the next RNG seed. /// @@ -177,8 +188,7 @@ public static uint GetSequentialIV32(uint seed) uint result = 0; for (int i = 0; i < 6; i++) { - seed = Next(seed); - var value = seed >> 27; // extract top 5 bits + var value = Next5(ref seed); // extract top 5 bits result |= value << (i * 5); } return result; @@ -190,6 +200,7 @@ public static uint GetSequentialIV32(uint seed) // Instead of using yield and iterators, we calculate all results in a tight loop and return the count found. public const int MaxCountSeedsPID = 2; public const int MaxCountSeedsIV = 6; + public const int MaxCountSeedsChannel = 12; // Euclidean division constants private const uint Sub = Add - 0xFFFF; @@ -272,6 +283,67 @@ public static int GetSeedsIVs(Span result, uint first, uint second) return ctr; } + public static int GetSeedsChannel(Span result, uint hp, uint atk, uint def, uint spa, uint spd, uint spe) + { + // Recovers seeds from 6 successive calls to rand() that returns the top 5 bits of each. + // Mult(j) = Mult^j + // Add(j) = Add * (Mult^0 + Mult^1 + ... + Mult^(j-1)) + // Using j = 3 and XDRNG gives Mult = 0x45c82be5 and Add = 0xd2f65b55 + const uint mult = Mult3; // Modified mult (3 advances) + const uint sub = 0xcaf65b56; // Modified add - 0x7ffffff + const ulong b = 0x22e415e_ea37d41a; // (Modified mult + 1) * 0x7ffffff + + const ulong prime = 3; + const ulong add = 0x3_0000_0000; // prime * 0x1_0000_0000 + const uint rmax = 0x__1800_0000; // prime * 0x__0800_0000 + const ulong skip = 0x661D29; // prime * 2^32 % mult + + uint first = hp << 27; + uint t = (((spe << 27) - mult * first) - sub); + uint kmax = (uint)((b - t) >> 32); + ulong x = (t * prime) % mult; + + int ctr = 0; + for (ulong k = 0; k <= kmax;) + { + var r = (x + skip * k) % mult; + var m = r % prime; + if (m != 0) + { + m = m == 1 ? 2u : 1u; + r += m * skip; + k += (byte)m; + } + + var tmp = k << 32 | t; + while (r < rmax && k <= kmax) + { + uint seed = first | (uint)(tmp / mult); // hp + if (Next5(ref seed) == atk) + { + if (Next5(ref seed) == def) + { + _ = Next5(ref seed); // spe + if (Next5(ref seed) == spa) + { + if (Next5(ref seed) == spd) + result[ctr++] = Prev12(seed); // unroll to origin + } + } + } + + r += prime * skip; + k += prime; + tmp += add; + } + + // Rounding up without using floats + k += ((mult - r) + skip - 1) / skip; + } + + return ctr; + } + /// /// Multiplication constants for jumping 2^(index) frames forward. ///