mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-10 06:34:19 +00:00
Refactor RNG advance/reverse methods (#3579)
The new LCRNG/GCRNG/ARNG classes are static, rather than singletons. Allows them to be inlined much better.
This commit is contained in:
parent
d9ad0052a1
commit
92a50264cc
22 changed files with 974 additions and 639 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
|
@ -46,12 +47,9 @@ public static class EncounterGenerator3
|
||||||
{
|
{
|
||||||
if (z is EncounterSlot3PokeSpot w)
|
if (z is EncounterSlot3PokeSpot w)
|
||||||
{
|
{
|
||||||
var seeds = MethodFinder.GetPokeSpotSeeds(pk, w.SlotNumber);
|
var pidiv = MethodFinder.GetPokeSpotSeedFirst(pk, w.SlotNumber);
|
||||||
foreach (var s in seeds)
|
if (pidiv.Type == PIDType.PokeSpot)
|
||||||
{
|
info.PIDIV = pidiv;
|
||||||
info.PIDIV = s;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (z is EncounterStaticShadow s)
|
else if (z is EncounterStaticShadow s)
|
||||||
{
|
{
|
||||||
|
@ -229,12 +227,15 @@ public static class EncounterGenerator3
|
||||||
return LockFinder.IsAllShadowLockValid(s, info.PIDIV, pk);
|
return LockFinder.IsAllShadowLockValid(s, info.PIDIV, pk);
|
||||||
|
|
||||||
// E-Reader have fixed IVs, and aren't recognized as CXD (no PID-IV correlation).
|
// E-Reader have fixed IVs, and aren't recognized as CXD (no PID-IV correlation).
|
||||||
var possible = MethodFinder.GetColoEReaderMatches(pk.EncryptionConstant);
|
Span<uint> seeds = stackalloc uint[4];
|
||||||
foreach (var poss in possible)
|
var count = XDRNG.GetSeeds(seeds, pk.EncryptionConstant);
|
||||||
|
var xdc = seeds[..count];
|
||||||
|
foreach (var seed in xdc)
|
||||||
{
|
{
|
||||||
if (!LockFinder.IsAllShadowLockValid(s, poss, pk))
|
var pidiv = new PIDIV(PIDType.CXD, XDRNG.Next4(seed));
|
||||||
|
if (!LockFinder.IsAllShadowLockValid(s, pidiv, pk))
|
||||||
continue;
|
continue;
|
||||||
info.PIDIV = poss;
|
info.PIDIV = pidiv;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
66
PKHeX.Core/Legality/RNG/Algorithms/ARNG.cs
Normal file
66
PKHeX.Core/Legality/RNG/Algorithms/ARNG.cs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 32 Bit Linear Congruential Random Number Generator
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Frame advancement for forward and reverse.
|
||||||
|
/// <br>
|
||||||
|
/// https://en.wikipedia.org/wiki/Linear_congruential_generator
|
||||||
|
/// </br>
|
||||||
|
/// <br>
|
||||||
|
/// seed_n+1 = seed_n * <see cref="Mult"/> + <see cref="Add"/>
|
||||||
|
/// </br>
|
||||||
|
/// </remarks>
|
||||||
|
public static class ARNG
|
||||||
|
{
|
||||||
|
// Forward and reverse constants
|
||||||
|
public const uint Mult = 0x6C078965;
|
||||||
|
public const uint Add = 0x00000001;
|
||||||
|
public const uint rMult = 0x9638806D;
|
||||||
|
public const uint rAdd = 0x69C77F93;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Advances the RNG seed to the next state value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="seed">Current seed</param>
|
||||||
|
/// <returns>Seed advanced a single time.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static uint Next(uint seed) => (seed * Mult) + Add;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reverses the RNG seed to the previous state value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="seed">Current seed</param>
|
||||||
|
/// <returns>Seed reversed a single time.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static uint Prev(uint seed) => (seed * rMult) + rAdd;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Advances the RNG seed to the next state value a specified amount of times.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="seed">Current seed</param>
|
||||||
|
/// <param name="frames">Amount of times to advance.</param>
|
||||||
|
/// <returns>Seed advanced the specified amount of times.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static uint Advance(uint seed, int frames)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < frames; i++)
|
||||||
|
seed = Next(seed);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reverses the RNG seed to the previous state value a specified amount of times.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="seed">Current seed</param>
|
||||||
|
/// <param name="frames">Amount of times to reverse.</param>
|
||||||
|
/// <returns>Seed reversed the specified amount of times.</returns>
|
||||||
|
public static uint Reverse(uint seed, int frames)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < frames; i++)
|
||||||
|
seed = Prev(seed);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace PKHeX.Core;
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
|
@ -13,39 +13,76 @@ namespace PKHeX.Core;
|
||||||
/// seed_n+1 = seed_n * <see cref="Mult"/> + <see cref="Add"/>
|
/// seed_n+1 = seed_n * <see cref="Mult"/> + <see cref="Add"/>
|
||||||
/// </br>
|
/// </br>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class LCRNG
|
public static class LCRNG
|
||||||
{
|
{
|
||||||
// Forward
|
// Forward and reverse constants
|
||||||
protected readonly uint Mult;
|
public const uint Mult = 0x41C64E6D;
|
||||||
private readonly uint Add;
|
public const uint Add = 0x00006073;
|
||||||
|
public const uint rMult = 0xEEB9EB65;
|
||||||
|
public const uint rAdd = 0x0A3561A1;
|
||||||
|
|
||||||
// Reverse
|
private const uint Mult2 = unchecked(Mult * Mult); // 0xC2A29A69
|
||||||
private readonly uint rMult;
|
private const uint rMult2 = unchecked(rMult * rMult); // 0xDC6C95D9
|
||||||
private readonly uint rAdd;
|
private const uint Add2 = unchecked(Add * (Mult + 1)); // 0xE97E7B6A
|
||||||
|
private const uint rAdd2 = unchecked(rAdd * (rMult + 1)); // 0x4D3CB126
|
||||||
|
|
||||||
public LCRNG(uint f_mult, uint f_add, uint r_mult, uint r_add)
|
private const uint Mult3 = unchecked(Mult2 * Mult); // 0x807DBCB5
|
||||||
{
|
private const uint rMult3 = unchecked(rMult2 * rMult); // 0xAC36519D
|
||||||
Mult = f_mult;
|
private const uint Add3 = unchecked((Add2 * Mult) + Add); // 0x52713895
|
||||||
Add = f_add;
|
private const uint rAdd3 = unchecked((rAdd2 * rMult) + rAdd);// 0x923B279F
|
||||||
rMult = r_mult;
|
|
||||||
rAdd = r_add;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
private const uint Mult4 = unchecked(Mult3 * Mult); // 0xEE067F11
|
||||||
/// Advances the RNG seed to the next state value.
|
private const uint rMult4 = unchecked(rMult3 * rMult); // 0xBECE51F1
|
||||||
/// </summary>
|
private const uint Add4 = unchecked((Add3 * Mult) + Add); // 0x31B0DDE4
|
||||||
/// <param name="seed">Current seed</param>
|
private const uint rAdd4 = unchecked((rAdd3 * rMult) + rAdd);// 0x7CD1F85C
|
||||||
/// <returns>Seed advanced a single time.</returns>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public uint Next(uint seed) => (seed * Mult) + Add;
|
|
||||||
|
|
||||||
/// <summary>
|
private const uint Mult5 = unchecked(Mult4 * Mult); // 0xEBA1483D
|
||||||
/// Reverses the RNG seed to the previous state value.
|
private const uint rMult5 = unchecked(rMult4 * rMult); // 0xF1C78F15
|
||||||
/// </summary>
|
private const uint Add5 = unchecked((Add4 * Mult) + Add); // 0x8E425287
|
||||||
/// <param name="seed">Current seed</param>
|
private const uint rAdd5 = unchecked((rAdd4 * rMult) + rAdd);// 0x0A84D1ED
|
||||||
/// <returns>Seed reversed a single time.</returns>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
private const uint Mult6 = unchecked(Mult5 * Mult); // 0xD3DC57F9
|
||||||
public uint Prev(uint seed) => (seed * rMult) + rAdd;
|
private const uint rMult6 = unchecked(rMult5 * rMult); // 0x8040BA49
|
||||||
|
private const uint Add6 = unchecked((Add5 * Mult) + Add); // 0xE2CCA5EE
|
||||||
|
private const uint rAdd6 = unchecked((rAdd5 * rMult) + rAdd);// 0x2795C322
|
||||||
|
|
||||||
|
private const uint Mult7 = unchecked(Mult6 * Mult); // 0x9B355305
|
||||||
|
private const uint rMult7 = unchecked(rMult6 * rMult); // 0x814B81CD
|
||||||
|
private const uint Add7 = unchecked((Add6 * Mult) + Add); // 0xAFC58AC9
|
||||||
|
private const uint rAdd7 = unchecked((rAdd6 * rMult) + rAdd);// 0xC1FD940B
|
||||||
|
|
||||||
|
private const uint Mult8 = unchecked(Mult7 * Mult); // 0xCFDDDF21
|
||||||
|
private const uint rMult8 = unchecked(rMult7 * rMult); // 0xB61664E1
|
||||||
|
private const uint Add8 = unchecked((Add7 * Mult) + Add); // 0x67DBB608
|
||||||
|
private const uint rAdd8 = unchecked((rAdd7 * rMult) + rAdd);// 0x9019E2F8
|
||||||
|
|
||||||
|
private const uint Mult9 = unchecked(Mult8 * Mult); // 0xFFA0F0DU
|
||||||
|
private const uint rMult9 = unchecked(rMult8 * rMult); // 0x7A0957C5
|
||||||
|
private const uint Add9 = unchecked((Add8 * Mult) + Add); // 0xFC3351DB
|
||||||
|
private const uint rAdd9 = unchecked((rAdd8 * rMult) + rAdd);// 0x3CFD9579
|
||||||
|
|
||||||
|
public const int MaxCountSeedsPID = 3;
|
||||||
|
public const int MaxCountSeedsIV = 6;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next (uint seed) => (seed * Mult ) + Add ;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next2(uint seed) => (seed * Mult2) + Add2;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next3(uint seed) => (seed * Mult3) + Add3;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next4(uint seed) => (seed * Mult4) + Add5;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next5(uint seed) => (seed * Mult5) + Add5;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next6(uint seed) => (seed * Mult6) + Add6;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next7(uint seed) => (seed * Mult7) + Add7;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next8(uint seed) => (seed * Mult8) + Add8;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next9(uint seed) => (seed * Mult9) + Add9;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev (uint seed) => (seed * rMult ) + rAdd ;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev2(uint seed) => (seed * rMult2) + rAdd2;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev3(uint seed) => (seed * rMult3) + rAdd3;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev4(uint seed) => (seed * rMult4) + rAdd4;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev5(uint seed) => (seed * rMult5) + rAdd5;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev6(uint seed) => (seed * rMult6) + rAdd6;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev7(uint seed) => (seed * rMult7) + rAdd7;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev8(uint seed) => (seed * rMult8) + rAdd8;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev9(uint seed) => (seed * rMult9) + rAdd9;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Advances the RNG seed to the next state value a specified amount of times.
|
/// Advances the RNG seed to the next state value a specified amount of times.
|
||||||
|
@ -54,7 +91,7 @@ public class LCRNG
|
||||||
/// <param name="frames">Amount of times to advance.</param>
|
/// <param name="frames">Amount of times to advance.</param>
|
||||||
/// <returns>Seed advanced the specified amount of times.</returns>
|
/// <returns>Seed advanced the specified amount of times.</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public uint Advance(uint seed, int frames)
|
public static uint Advance(uint seed, int frames)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < frames; i++)
|
for (int i = 0; i < frames; i++)
|
||||||
seed = Next(seed);
|
seed = Next(seed);
|
||||||
|
@ -67,8 +104,7 @@ public class LCRNG
|
||||||
/// <param name="seed">Current seed</param>
|
/// <param name="seed">Current seed</param>
|
||||||
/// <param name="frames">Amount of times to reverse.</param>
|
/// <param name="frames">Amount of times to reverse.</param>
|
||||||
/// <returns>Seed reversed the specified amount of times.</returns>
|
/// <returns>Seed reversed the specified amount of times.</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
public static uint Reverse(uint seed, int frames)
|
||||||
public uint Reverse(uint seed, int frames)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < frames; i++)
|
for (int i = 0; i < frames; i++)
|
||||||
seed = Prev(seed);
|
seed = Prev(seed);
|
||||||
|
|
148
PKHeX.Core/Legality/RNG/Algorithms/LCRNGReversal.cs
Normal file
148
PKHeX.Core/Legality/RNG/Algorithms/LCRNGReversal.cs
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Seed reversal logic for the <see cref="LCRNG"/> algorithm.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Use a meet-in-the-middle attack to reduce the search space to 2^8 instead of 2^16
|
||||||
|
/// flag/2^8 tables are precomputed and constant (unrelated to rand pairs)
|
||||||
|
/// https://crypto.stackexchange.com/a/10609
|
||||||
|
/// </remarks>
|
||||||
|
public static class LCRNGReversal
|
||||||
|
{
|
||||||
|
// Bruteforce cache for searching seeds
|
||||||
|
private const int cacheSize = 1 << 16;
|
||||||
|
private static readonly byte[] low8 = new byte[cacheSize];
|
||||||
|
private static readonly bool[] flags = new bool[cacheSize];
|
||||||
|
private const uint Mult = LCRNG.Mult;
|
||||||
|
private const uint Add = LCRNG.Add;
|
||||||
|
private const uint k2 = Mult << 8;
|
||||||
|
|
||||||
|
static LCRNGReversal()
|
||||||
|
{
|
||||||
|
// Populate Meet Middle Arrays
|
||||||
|
var f = flags;
|
||||||
|
var b = low8;
|
||||||
|
for (uint i = 0; i <= byte.MaxValue; i++)
|
||||||
|
{
|
||||||
|
// the second rand() also has 16 bits that aren't known. It is a 16 bit value added to either side.
|
||||||
|
// to consider these bits and their impact, they can at most increment/decrement the result by 1.
|
||||||
|
// with the current calc setup, the search loop's calculated value may be -1 (loop does subtraction)
|
||||||
|
// since LCGs are linear (hence the name), there's no values in adjacent cells. (no collisions)
|
||||||
|
// if we mark the prior adjacent cell, we eliminate the need to check flags twice on each loop.
|
||||||
|
uint right = (Mult * i) + Add;
|
||||||
|
ushort val = (ushort)(right >> 16);
|
||||||
|
|
||||||
|
f[val] = true; b[val] = (byte)i;
|
||||||
|
--val;
|
||||||
|
f[val] = true; b[val] = (byte)i;
|
||||||
|
// now the search only has to access the flags array once per loop.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all seeds that can generate the <see cref="pid"/> by two successive rand() calls.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">Result storage array, to be populated starting at index 0.</param>
|
||||||
|
/// <param name="pid">PID to be reversed into seeds that generate it.</param>
|
||||||
|
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||||
|
public static int GetSeeds(Span<uint> result, uint pid)
|
||||||
|
{
|
||||||
|
uint first = pid << 16;
|
||||||
|
uint second = pid & 0xFFFF_0000;
|
||||||
|
return GetSeeds(result, first, second);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all seeds that can generate the IVs by two successive rand() calls.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">Result storage array, to be populated starting at index 0.</param>
|
||||||
|
/// <param name="hp" >Entity IV for HP</param>
|
||||||
|
/// <param name="atk">Entity IV for Attack</param>
|
||||||
|
/// <param name="def">Entity IV for Defense</param>
|
||||||
|
/// <param name="spa">Entity IV for Special Attack</param>
|
||||||
|
/// <param name="spd">Entity IV for Special Defense</param>
|
||||||
|
/// <param name="spe">Entity IV for Speed</param>
|
||||||
|
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||||
|
public static int GetSeedsIVs(Span<uint> result, uint hp, uint atk, uint def, uint spa, uint spd, uint spe)
|
||||||
|
{
|
||||||
|
uint first = (hp | (atk << 5) | (def << 10)) << 16;
|
||||||
|
uint second = (spe | (spa << 5) | (spd << 10)) << 16;
|
||||||
|
return GetSeedsIVs(result, first, second);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all the origin seeds for two 16 bit rand() calls
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">Result storage array, to be populated starting at index 0.</param>
|
||||||
|
/// <param name="first">First rand() call, 16 bits, already shifted left 16 bits.</param>
|
||||||
|
/// <param name="second">Second rand() call, 16 bits, already shifted left 16 bits.</param>
|
||||||
|
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||||
|
public static int GetSeeds(Span<uint> result, uint first, uint second)
|
||||||
|
{
|
||||||
|
int ctr = 0;
|
||||||
|
uint k1 = second - (first * Mult);
|
||||||
|
for (uint i = 0, k3 = k1; i <= 255; ++i, k3 -= k2)
|
||||||
|
{
|
||||||
|
ushort val = (ushort)(k3 >> 16);
|
||||||
|
if (!flags[val])
|
||||||
|
continue;
|
||||||
|
// Verify PID calls line up
|
||||||
|
var seed = first | (i << 8) | low8[val];
|
||||||
|
var next = LCRNG.Next(seed);
|
||||||
|
if ((next & 0xFFFF0000) == second)
|
||||||
|
result[ctr++] = LCRNG.Prev(seed);
|
||||||
|
}
|
||||||
|
return ctr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all the origin seeds for two 15 bit rand() calls
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">Result storage array, to be populated starting at index 0.</param>
|
||||||
|
/// <param name="first">First rand() call, 15 bits, already shifted left 16 bits.</param>
|
||||||
|
/// <param name="second">Second rand() call, 15 bits, already shifted left 16 bits.</param>
|
||||||
|
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||||
|
public static int GetSeedsIVs(Span<uint> result, uint first, uint second)
|
||||||
|
{
|
||||||
|
int ctr = 0;
|
||||||
|
// Check with the top bit of the first call both
|
||||||
|
// flipped and unflipped to account for only knowing 15 bits
|
||||||
|
uint search1 = second - (first * Mult);
|
||||||
|
uint search2 = second - ((first ^ 0x80000000) * Mult);
|
||||||
|
|
||||||
|
for (uint i = 0; i <= 255; i++, search1 -= k2, search2 -= k2)
|
||||||
|
{
|
||||||
|
uint val = (ushort)(search1 >> 16);
|
||||||
|
if (flags[val])
|
||||||
|
{
|
||||||
|
// Verify PID calls line up
|
||||||
|
var seed = first | (i << 8) | low8[val];
|
||||||
|
var next = LCRNG.Next(seed);
|
||||||
|
if ((next & 0x7FFF0000) == second)
|
||||||
|
{
|
||||||
|
var origin = LCRNG.Prev(seed);
|
||||||
|
result[ctr++] = origin;
|
||||||
|
result[ctr++] = origin ^ 0x80000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val = (ushort)(search2 >> 16);
|
||||||
|
if (flags[val])
|
||||||
|
{
|
||||||
|
// Verify PID calls line up
|
||||||
|
var seed = first | (i << 8) | low8[val];
|
||||||
|
var next = LCRNG.Next(seed);
|
||||||
|
if ((next & 0x7FFF0000) == second)
|
||||||
|
{
|
||||||
|
var origin = LCRNG.Prev(seed);
|
||||||
|
result[ctr++] = origin;
|
||||||
|
result[ctr++] = origin ^ 0x80000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctr;
|
||||||
|
}
|
||||||
|
}
|
147
PKHeX.Core/Legality/RNG/Algorithms/LCRNGReversalSkip.cs
Normal file
147
PKHeX.Core/Legality/RNG/Algorithms/LCRNGReversalSkip.cs
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Seed reversal logic for the <see cref="LCRNG"/> algorithm, with a gap in between the two observed rand() results.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Use a meet-in-the-middle attack to reduce the search space to 2^8 instead of 2^16
|
||||||
|
/// flag/2^8 tables are precomputed and constant (unrelated to rand pairs)
|
||||||
|
/// https://crypto.stackexchange.com/a/10609
|
||||||
|
/// </remarks>
|
||||||
|
public static class LCRNGReversalSkip
|
||||||
|
{
|
||||||
|
// Bruteforce cache for searching seeds
|
||||||
|
private const int cacheSize = 1 << 16;
|
||||||
|
private static readonly byte[] low8 = new byte[cacheSize];
|
||||||
|
private static readonly bool[] flags = new bool[cacheSize];
|
||||||
|
private const uint Mult = unchecked(LCRNG.Mult * LCRNG.Mult); // 0xC2A29A69
|
||||||
|
private const uint Add = unchecked(LCRNG.Add * (LCRNG.Mult + 1)); // 0xE97E7B6A
|
||||||
|
private const uint k2 = Mult << 8;
|
||||||
|
|
||||||
|
static LCRNGReversalSkip()
|
||||||
|
{
|
||||||
|
// Populate Meet Middle Arrays
|
||||||
|
var f = flags;
|
||||||
|
var b = low8;
|
||||||
|
for (uint i = 0; i <= byte.MaxValue; i++)
|
||||||
|
{
|
||||||
|
// the second rand() also has 16 bits that aren't known. It is a 16 bit value added to either side.
|
||||||
|
// to consider these bits and their impact, they can at most increment/decrement the result by 1.
|
||||||
|
// with the current calc setup, the search loop's calculated value may be -1 (loop does subtraction)
|
||||||
|
// since LCGs are linear (hence the name), there's no values in adjacent cells. (no collisions)
|
||||||
|
// if we mark the prior adjacent cell, we eliminate the need to check flags twice on each loop.
|
||||||
|
uint right = (Mult * i) + Add;
|
||||||
|
ushort val = (ushort)(right >> 16);
|
||||||
|
|
||||||
|
f[val] = true; b[val] = (byte)i;
|
||||||
|
--val;
|
||||||
|
f[val] = true; b[val] = (byte)i;
|
||||||
|
// now the search only has to access the flags array once per loop.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all seeds that can generate the <see cref="pid"/> with a discarded rand() between the two halves.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">Result storage array, to be populated starting at index 0.</param>
|
||||||
|
/// <param name="pid">PID to be reversed into seeds that generate it.</param>
|
||||||
|
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||||
|
public static int GetSeeds(Span<uint> result, uint pid)
|
||||||
|
{
|
||||||
|
uint first = pid << 16;
|
||||||
|
uint second = pid & 0xFFFF_0000;
|
||||||
|
return GetSeeds(result, first, second);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all seeds that can generate the IVs, with a vblank skip between the two IV rand() calls.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">Result storage array, to be populated starting at index 0.</param>
|
||||||
|
/// <param name="hp" >Entity IV for HP</param>
|
||||||
|
/// <param name="atk">Entity IV for Attack</param>
|
||||||
|
/// <param name="def">Entity IV for Defense</param>
|
||||||
|
/// <param name="spa">Entity IV for Special Attack</param>
|
||||||
|
/// <param name="spd">Entity IV for Special Defense</param>
|
||||||
|
/// <param name="spe">Entity IV for Speed</param>
|
||||||
|
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||||
|
public static int GetSeedsIVs(Span<uint> result, uint hp, uint atk, uint def, uint spa, uint spd, uint spe)
|
||||||
|
{
|
||||||
|
uint first = (hp | (atk << 5) | (def << 10)) << 16;
|
||||||
|
uint second = (spe | (spa << 5) | (spd << 10)) << 16;
|
||||||
|
return GetSeedsIVs(result, first, second);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all the origin seeds for two 16 bit rand() calls (ignoring a rand() in between)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">Result storage array, to be populated starting at index 0.</param>
|
||||||
|
/// <param name="first">First rand() call, 16 bits, already shifted left 16 bits.</param>
|
||||||
|
/// <param name="third">Third rand() call, 16 bits, already shifted left 16 bits.</param>
|
||||||
|
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||||
|
public static int GetSeeds(Span<uint> result, uint first, uint third)
|
||||||
|
{
|
||||||
|
int ctr = 0;
|
||||||
|
uint search = third - (first * Mult);
|
||||||
|
for (uint i = 0; i <= 255; ++i, search -= k2)
|
||||||
|
{
|
||||||
|
ushort val = (ushort)(search >> 16);
|
||||||
|
if (flags[val])
|
||||||
|
{
|
||||||
|
// Verify PID calls line up
|
||||||
|
var seed = first | (i << 8) | low8[val];
|
||||||
|
var next = LCRNG.Next2(seed);
|
||||||
|
if ((next & 0xFFFF0000) == third)
|
||||||
|
result[ctr++] = LCRNG.Prev(seed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all the origin seeds for two 15 bit rand() calls (ignoring a rand() in between)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">Result storage array, to be populated starting at index 0.</param>
|
||||||
|
/// <param name="first">First rand() call, 15 bits, already shifted left 16 bits.</param>
|
||||||
|
/// <param name="third">Third rand() call, 15 bits, already shifted left 16 bits.</param>
|
||||||
|
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||||
|
public static int GetSeedsIVs(Span<uint> result, uint first, uint third)
|
||||||
|
{
|
||||||
|
int ctr = 0;
|
||||||
|
uint search1 = third - (first * Mult);
|
||||||
|
uint search3 = third - ((first ^ 0x80000000) * Mult);
|
||||||
|
|
||||||
|
for (uint i = 0; i <= 255; i++, search1 -= k2, search3 -= k2)
|
||||||
|
{
|
||||||
|
uint val = (ushort)(search1 >> 16);
|
||||||
|
if (flags[val])
|
||||||
|
{
|
||||||
|
// Verify PID calls line up
|
||||||
|
var seed = first | (i << 8) | low8[val];
|
||||||
|
var next = LCRNG.Next2(seed);
|
||||||
|
if ((next & 0x7FFF0000) == third)
|
||||||
|
{
|
||||||
|
var origin = LCRNG.Prev(seed);
|
||||||
|
result[ctr++] = origin;
|
||||||
|
result[ctr++] = origin ^ 0x80000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val = (ushort)(search3 >> 16);
|
||||||
|
if (flags[val])
|
||||||
|
{
|
||||||
|
// Verify PID calls line up
|
||||||
|
var seed = first | (i << 8) | low8[val];
|
||||||
|
var next = LCRNG.Next2(seed);
|
||||||
|
if ((next & 0x7FFF0000) == third)
|
||||||
|
{
|
||||||
|
var origin = LCRNG.Prev(seed);
|
||||||
|
result[ctr++] = origin;
|
||||||
|
result[ctr++] = origin ^ 0x80000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctr;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,187 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace PKHeX.Core;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <inheritdoc cref="Core.LCRNG"/>
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <inheritdoc cref="Core.LCRNG"/>
|
|
||||||
/// <br>
|
|
||||||
/// Provides common RNG algorithms used by Generation 3 & 4.
|
|
||||||
/// This class has extra logic (tuned for performance) that can be used to find the original state(s) based on a limited amount of observed results.
|
|
||||||
/// Refer to the documentation for those methods.
|
|
||||||
/// </br>
|
|
||||||
/// </remarks>
|
|
||||||
public sealed class RNG : LCRNG
|
|
||||||
{
|
|
||||||
/// <summary> LCRNG used for Encryption and mainline game RNG calls. </summary>
|
|
||||||
public static readonly RNG LCRNG = new(0x41C64E6D, 0x00006073, 0xEEB9EB65, 0x0A3561A1);
|
|
||||||
|
|
||||||
/// <summary> LCRNG used by Colosseum & XD for game RNG calls. </summary>
|
|
||||||
public static readonly RNG XDRNG = new(0x000343FD, 0x00269EC3, 0xB9B33155, 0xA170F641);
|
|
||||||
|
|
||||||
/// <summary> Alternate LCRNG used by mainline game RNG calls to disassociate the seed from the <see cref="LCRNG"/>, for anti-shiny and other purposes. </summary>
|
|
||||||
public static readonly LCRNG ARNG = new(0x6C078965, 0x00000001, 0x9638806D, 0x69C77F93);
|
|
||||||
|
|
||||||
#region Seed Reversal Logic
|
|
||||||
|
|
||||||
// Bruteforce cache for searching seeds
|
|
||||||
private const int cacheSize = 1 << 16;
|
|
||||||
// 1,2 (no gap)
|
|
||||||
private readonly uint k2; // Mult<<8
|
|
||||||
private readonly byte[] low8 = new byte[cacheSize];
|
|
||||||
private readonly bool[] flags = new bool[cacheSize];
|
|
||||||
// 1,3 (single gap)
|
|
||||||
private readonly uint k0g; // Mult*Mult
|
|
||||||
private readonly uint k2s; // Mult*Mult<<8
|
|
||||||
private readonly byte[] g_low8 = new byte[cacheSize];
|
|
||||||
private readonly bool[] g_flags = new bool[cacheSize];
|
|
||||||
|
|
||||||
// Euclidean division approach
|
|
||||||
private readonly long t0; // Add - 0xFFFF
|
|
||||||
private readonly long t1; // 0xFFFF * ((long)Mult + 1)
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private RNG(uint f_mult, uint f_add, uint r_mult, uint r_add) : base(f_mult, f_add, r_mult, r_add)
|
|
||||||
{
|
|
||||||
// Set up bruteforce utility
|
|
||||||
k2 = f_mult << 8;
|
|
||||||
k0g = f_mult * f_mult;
|
|
||||||
k2s = k0g << 8;
|
|
||||||
|
|
||||||
// Populate Meet Middle Arrays
|
|
||||||
uint k4g = f_add * (f_mult + 1); // 1,3's multiplier
|
|
||||||
for (uint i = 0; i <= byte.MaxValue; i++)
|
|
||||||
{
|
|
||||||
SetFlagData(i, f_mult, f_add, flags, low8); // 1,2
|
|
||||||
SetFlagData(i, k0g, k4g, g_flags, g_low8); // 1,3
|
|
||||||
}
|
|
||||||
|
|
||||||
t0 = f_add - 0xFFFFU;
|
|
||||||
t1 = 0xFFFFL * ((long) f_mult + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Initialization
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static void SetFlagData(uint i, uint mult, uint add, bool[] f, byte[] v)
|
|
||||||
{
|
|
||||||
// the second rand() also has 16 bits that aren't known. It is a 16 bit value added to either side.
|
|
||||||
// to consider these bits and their impact, they can at most increment/decrement the result by 1.
|
|
||||||
// with the current calc setup, the search loop's calculated value may be -1 (loop does subtraction)
|
|
||||||
// since LCGs are linear (hence the name), there's no values in adjacent cells. (no collisions)
|
|
||||||
// if we mark the prior adjacent cell, we eliminate the need to check flags twice on each loop.
|
|
||||||
uint right = (mult * i) + add;
|
|
||||||
ushort val = (ushort) (right >> 16);
|
|
||||||
|
|
||||||
f[val] = true; v[val] = (byte)i;
|
|
||||||
--val;
|
|
||||||
f[val] = true; v[val] = (byte)i;
|
|
||||||
// now the search only has to access the flags array once per loop.
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the origin seeds for two successive 16 bit rand() calls using a meet-in-the-middle approach.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="first">First rand() call, 16 bits, already shifted left 16 bits.</param>
|
|
||||||
/// <param name="second">Second rand() call, 16 bits, already shifted left 16 bits.</param>
|
|
||||||
/// <remarks>
|
|
||||||
/// Use a meet-in-the-middle attack to reduce the search space to 2^8 instead of 2^16
|
|
||||||
/// flag/2^8 tables are precomputed and constant (unrelated to rand pairs)
|
|
||||||
/// https://crypto.stackexchange.com/a/10609
|
|
||||||
/// </remarks>
|
|
||||||
/// <returns>Possible origin seeds that generate the 2 random numbers</returns>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
internal IEnumerable<uint> RecoverLower16Bits(uint first, uint second)
|
|
||||||
{
|
|
||||||
uint k1 = second - (first * Mult);
|
|
||||||
for (uint i = 0, k3 = k1; i <= 255; ++i, k3 -= k2)
|
|
||||||
{
|
|
||||||
ushort val = (ushort)(k3 >> 16);
|
|
||||||
if (flags[val])
|
|
||||||
yield return Prev(first | (i << 8) | low8[val]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the origin seeds for two 16 bit rand() calls (ignoring a rand() in between) using a meet-in-the-middle approach.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="first">First rand() call, 16 bits, already shifted left 16 bits.</param>
|
|
||||||
/// <param name="third">Third rand() call, 16 bits, already shifted left 16 bits.</param>
|
|
||||||
/// <remarks>
|
|
||||||
/// Use a meet-in-the-middle attack to reduce the search space to 2^8 instead of 2^16
|
|
||||||
/// flag/2^8 tables are precomputed and constant (unrelated to rand pairs)
|
|
||||||
/// https://crypto.stackexchange.com/a/10609
|
|
||||||
/// </remarks>
|
|
||||||
/// <returns>Possible origin seeds that generate the 2 random numbers</returns>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
internal IEnumerable<uint> RecoverLower16BitsGap(uint first, uint third)
|
|
||||||
{
|
|
||||||
uint k1 = third - (first * k0g);
|
|
||||||
for (uint i = 0, k3 = k1; i <= 255; ++i, k3 -= k2s)
|
|
||||||
{
|
|
||||||
ushort val = (ushort)(k3 >> 16);
|
|
||||||
if (g_flags[val])
|
|
||||||
yield return Prev(first | (i << 8) | g_low8[val]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the origin seeds for two successive 16 bit rand() calls using a Euclidean division approach.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="first">First rand() call, 16 bits, already shifted left 16 bits.</param>
|
|
||||||
/// <param name="second">Second rand() call, 16 bits, already shifted left 16 bits.</param>
|
|
||||||
/// <remarks>
|
|
||||||
/// For favorable multiplier values, this k_max gives a search space less than 2^8 (meet-in-the-middle)
|
|
||||||
/// For the programmed methods in this program, it is only advantageous to use this with <see cref="XDRNG"/>.
|
|
||||||
/// https://crypto.stackexchange.com/a/10629
|
|
||||||
/// </remarks>
|
|
||||||
/// <returns>Possible origin seeds that generate the 2 random numbers</returns>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
internal IEnumerable<uint> RecoverLower16BitsEuclid16(uint first, uint second)
|
|
||||||
{
|
|
||||||
const int bitshift = 32;
|
|
||||||
const long inc = 1L << bitshift;
|
|
||||||
return GetPossibleSeedsEuclid(first, second, bitshift, inc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the origin seeds for two successive 15 bit rand() calls using a Euclidean division approach.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="first">First rand() call, 15 bits, already shifted left 16 bits.</param>
|
|
||||||
/// <param name="second">Second rand() call, 15 bits, already shifted left 16 bits.</param>
|
|
||||||
/// <remarks>
|
|
||||||
/// Calculate the quotient of the Euclidean division (k_max) attack to reduce the search space.
|
|
||||||
/// For favorable multiplier values, this k_max gives a search space less than 2^8 (meet-in-the-middle)
|
|
||||||
/// For the programmed methods in this program, it is only advantageous to use this with <see cref="XDRNG"/>.
|
|
||||||
/// https://crypto.stackexchange.com/a/10629
|
|
||||||
/// </remarks>
|
|
||||||
/// <returns>Possible origin seeds that generate the 2 random numbers</returns>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
internal IEnumerable<uint> RecoverLower16BitsEuclid15(uint first, uint second)
|
|
||||||
{
|
|
||||||
const int bitshift = 31;
|
|
||||||
const long inc = 1L << bitshift;
|
|
||||||
return GetPossibleSeedsEuclid(first, second, bitshift, inc);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private IEnumerable<uint> GetPossibleSeedsEuclid(uint first, uint second, int bitshift, long inc)
|
|
||||||
{
|
|
||||||
long t = second - (Mult * first) - t0;
|
|
||||||
long kmax = (((t1 - t) >> bitshift) << bitshift) + t;
|
|
||||||
for (long k = t; k <= kmax; k += inc)
|
|
||||||
{
|
|
||||||
// compute modulo in steps for reuse in yielded value (x % Mult)
|
|
||||||
long fix = k / Mult;
|
|
||||||
long remainder = k - (Mult * fix);
|
|
||||||
if (remainder >> 16 == 0)
|
|
||||||
yield return Prev(first | (uint) fix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace PKHeX.Core;
|
|
||||||
|
|
||||||
public static class RNGUtil
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Generates an IV for each RNG call using the top 5 bits of frame seeds.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="rng">RNG to use</param>
|
|
||||||
/// <param name="seed">RNG seed</param>
|
|
||||||
/// <param name="IVs">Expected IVs</param>
|
|
||||||
/// <returns>True if all match.</returns>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
internal static bool GetSequentialIVsUInt32(this LCRNG rng, uint seed, ReadOnlySpan<uint> IVs)
|
|
||||||
{
|
|
||||||
foreach (var iv in IVs)
|
|
||||||
{
|
|
||||||
seed = rng.Next(seed);
|
|
||||||
var IV = seed >> 27;
|
|
||||||
if (IV != iv)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generates an IV for each RNG call using the top 5 bits of frame seeds.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="rng">RNG to use</param>
|
|
||||||
/// <param name="seed">RNG seed</param>
|
|
||||||
/// <param name="ivs">Buffer to store generated values</param>
|
|
||||||
/// <returns>Array of 6 IVs as <see cref="int"/>.</returns>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
internal static void GetSequentialIVsInt32(this LCRNG rng, uint seed, Span<int> ivs)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < ivs.Length; i++)
|
|
||||||
{
|
|
||||||
seed = rng.Next(seed);
|
|
||||||
ivs[i] = (int)(seed >> 27);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
235
PKHeX.Core/Legality/RNG/Algorithms/XDRNG.cs
Normal file
235
PKHeX.Core/Legality/RNG/Algorithms/XDRNG.cs
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 32 Bit Linear Congruential Random Number Generator
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Frame advancement for forward and reverse.
|
||||||
|
/// <br>
|
||||||
|
/// https://en.wikipedia.org/wiki/Linear_congruential_generator
|
||||||
|
/// </br>
|
||||||
|
/// <br>
|
||||||
|
/// seed_n+1 = seed_n * <see cref="Mult"/> + <see cref="Add"/>
|
||||||
|
/// </br>
|
||||||
|
/// </remarks>
|
||||||
|
public static class XDRNG
|
||||||
|
{
|
||||||
|
// Forward and reverse constants
|
||||||
|
public const uint Mult = 0x000343FD;
|
||||||
|
public const uint Add = 0x00269EC3;
|
||||||
|
public const uint rMult = 0xB9B33155;
|
||||||
|
public const uint rAdd = 0xA170F641;
|
||||||
|
|
||||||
|
private const uint Mult2 = unchecked(Mult * Mult); // 0xA9FC6809
|
||||||
|
private const uint rMult2 = unchecked(rMult * rMult); // 0xE05FA639
|
||||||
|
private const uint Add2 = unchecked(Add * (Mult + 1)); // 0x1E278E7A
|
||||||
|
private const uint rAdd2 = unchecked(rAdd * (rMult + 1)); // 0x03882AD6
|
||||||
|
|
||||||
|
private const uint Mult3 = unchecked(Mult2 * Mult); // 0x45C82BE5
|
||||||
|
private const uint rMult3 = unchecked(rMult2 * rMult); // 0x396E19ED
|
||||||
|
private const uint Add3 = unchecked((Add2 * Mult) + Add); // 0xD2F65B55
|
||||||
|
private const uint rAdd3 = unchecked((rAdd2 * rMult) + rAdd);// 0x777C254F
|
||||||
|
|
||||||
|
private const uint Mult4 = unchecked(Mult3 * Mult); // 0xDDFF5051
|
||||||
|
private const uint rMult4 = unchecked(rMult3 * rMult); // 0x8A3BF8B1
|
||||||
|
private const uint Add4 = unchecked((Add3 * Mult) + Add); // 0x098520C4
|
||||||
|
private const uint rAdd4 = unchecked((rAdd3 * rMult) + rAdd);// 0x3E0A787C
|
||||||
|
|
||||||
|
private const uint Mult5 = unchecked(Mult4 * Mult); // 0x284A930D
|
||||||
|
private const uint rMult5 = unchecked(rMult4 * rMult); // 0x2D4673C5
|
||||||
|
private const uint Add5 = unchecked((Add4 * Mult) + Add); // 0xA2974C77
|
||||||
|
private const uint rAdd5 = unchecked((rAdd4 * rMult) + rAdd);// 0x16AEB36D
|
||||||
|
|
||||||
|
private const uint Mult6 = unchecked(Mult5 * Mult); // 0x0F56BAD9
|
||||||
|
private const uint rMult6 = unchecked(rMult5 * rMult); // 0xD44C2569
|
||||||
|
private const uint Add6 = unchecked((Add5 * Mult) + Add); // 0x2E15555E
|
||||||
|
private const uint rAdd6 = unchecked((rAdd5 * rMult) + rAdd);// 0xD4016672
|
||||||
|
|
||||||
|
private const uint Mult7 = unchecked(Mult6 * Mult); // 0x0C287375
|
||||||
|
private const uint rMult7 = unchecked(rMult6 * rMult); // 0x19DC84DD
|
||||||
|
private const uint Add7 = unchecked((Add6 * Mult) + Add); // 0x20AD96A9
|
||||||
|
private const uint rAdd7 = unchecked((rAdd6 * rMult) + rAdd);// 0x4E39CC1B
|
||||||
|
|
||||||
|
private const uint Mult8 = unchecked(Mult7 * Mult); // 0xF490B9A1
|
||||||
|
private const uint rMult8 = unchecked(rMult7 * rMult); // 0x672D6A61
|
||||||
|
private const uint Add8 = unchecked((Add7 * Mult) + Add); // 0x7E1DBEC8
|
||||||
|
private const uint rAdd8 = unchecked((rAdd7 * rMult) + rAdd);// 0xE493E638
|
||||||
|
|
||||||
|
private const uint Mult9 = unchecked(Mult8 * Mult); // 0xC07F971D
|
||||||
|
private const uint rMult9 = unchecked(rMult8 * rMult); // 0x6E43E335
|
||||||
|
private const uint Add9 = unchecked((Add8 * Mult) + Add); // 0xA8D2826B
|
||||||
|
private const uint rAdd9 = unchecked((rAdd8 * rMult) + rAdd);// 0x46C51ED9
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next (uint seed) => (seed * Mult ) + Add ;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next2(uint seed) => (seed * Mult2) + Add2;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next3(uint seed) => (seed * Mult3) + Add3;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next4(uint seed) => (seed * Mult4) + Add4;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next5(uint seed) => (seed * Mult5) + Add5;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next6(uint seed) => (seed * Mult6) + Add6;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next7(uint seed) => (seed * Mult7) + Add7;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next8(uint seed) => (seed * Mult8) + Add8;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next9(uint seed) => (seed * Mult9) + Add9;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev (uint seed) => (seed * rMult ) + rAdd ;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev2(uint seed) => (seed * rMult2) + rAdd2;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev3(uint seed) => (seed * rMult3) + rAdd3;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev4(uint seed) => (seed * rMult4) + rAdd4;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev5(uint seed) => (seed * rMult5) + rAdd5;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev6(uint seed) => (seed * rMult6) + rAdd6;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev7(uint seed) => (seed * rMult7) + rAdd7;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev8(uint seed) => (seed * rMult8) + rAdd8;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev9(uint seed) => (seed * rMult9) + rAdd9;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Advances the RNG seed to the next state value a specified amount of times.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="seed">Current seed</param>
|
||||||
|
/// <param name="frames">Amount of times to advance.</param>
|
||||||
|
/// <returns>Seed advanced the specified amount of times.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static uint Advance(uint seed, int frames)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < frames; i++)
|
||||||
|
seed = Next(seed);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reverses the RNG seed to the previous state value a specified amount of times.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="seed">Current seed</param>
|
||||||
|
/// <param name="frames">Amount of times to reverse.</param>
|
||||||
|
/// <returns>Seed reversed the specified amount of times.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static uint Reverse(uint seed, int frames)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < frames; i++)
|
||||||
|
seed = Prev(seed);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates an IV for each RNG call using the top 5 bits of frame seeds.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="seed">RNG seed</param>
|
||||||
|
/// <param name="IVs">Expected IVs</param>
|
||||||
|
/// <returns>True if all match.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static bool GetSequentialIVsUInt32(uint seed, ReadOnlySpan<uint> IVs)
|
||||||
|
{
|
||||||
|
foreach (var iv in IVs)
|
||||||
|
{
|
||||||
|
seed = Next(seed);
|
||||||
|
var expect = seed >> 27;
|
||||||
|
if (iv != expect)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates an IV for each RNG call using the top 5 bits of frame seeds.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="seed">RNG seed</param>
|
||||||
|
/// <param name="ivs">Buffer to store generated values</param>
|
||||||
|
/// <returns>Array of 6 IVs as <see cref="int"/>.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static void GetSequentialIVsInt32(uint seed, Span<int> ivs)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < ivs.Length; i++)
|
||||||
|
{
|
||||||
|
seed = Next(seed);
|
||||||
|
ivs[i] = (int)(seed >> 27);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// By abusing the innate properties of a LCG, we can calculate the seed from a known result.
|
||||||
|
// https://crypto.stackexchange.com/questions/10608/how-to-attack-a-fixed-lcg-with-partial-output/10629#10629
|
||||||
|
// Unlike our LCRNG implementation, `k` is small enough (max = 7).
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// Euclidean division constants
|
||||||
|
private const uint Sub = Add - 0xFFFF;
|
||||||
|
private const ulong Base = (Mult + 1ul) * 0xFFFF;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all seeds that can generate the <see cref="pid"/> by two successive rand() calls.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">Result storage array, to be populated starting at index 0.</param>
|
||||||
|
/// <param name="pid">PID to be reversed into seeds that generate it.</param>
|
||||||
|
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||||
|
public static int GetSeeds(Span<uint> result, uint pid)
|
||||||
|
{
|
||||||
|
uint first = pid & 0xFFFF_0000;
|
||||||
|
uint second = pid << 16;
|
||||||
|
return GetSeeds(result, first, second);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all seeds that can generate the IVs by two successive rand() calls.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">Result storage array, to be populated starting at index 0.</param>
|
||||||
|
/// <param name="hp" >Entity IV for HP</param>
|
||||||
|
/// <param name="atk">Entity IV for Attack</param>
|
||||||
|
/// <param name="def">Entity IV for Defense</param>
|
||||||
|
/// <param name="spa">Entity IV for Special Attack</param>
|
||||||
|
/// <param name="spd">Entity IV for Special Defense</param>
|
||||||
|
/// <param name="spe">Entity IV for Speed</param>
|
||||||
|
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||||
|
public static int GetSeeds(Span<uint> result, uint hp, uint atk, uint def, uint spa, uint spd, uint spe)
|
||||||
|
{
|
||||||
|
var first = (hp | (atk << 5) | (def << 10)) << 16;
|
||||||
|
var second = (spe | (spa << 5) | (spd << 10)) << 16;
|
||||||
|
return GetSeedsIVs(result, first, second);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all the origin seeds for two 16 bit rand() calls
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">Result storage array, to be populated starting at index 0.</param>
|
||||||
|
/// <param name="first">First rand() call, 16 bits, already shifted left 16 bits.</param>
|
||||||
|
/// <param name="second">Second rand() call, 16 bits, already shifted left 16 bits.</param>
|
||||||
|
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||||
|
public static int GetSeeds(Span<uint> result, uint first, uint second)
|
||||||
|
{
|
||||||
|
ulong t = second - (first * Mult) - Sub;
|
||||||
|
ulong kmax = (Base - t) >> 32;
|
||||||
|
|
||||||
|
int ctr = 0;
|
||||||
|
for (ulong k = 0; k <= kmax; k++, t += 0x1_0000_0000) // at most 4 iterations
|
||||||
|
{
|
||||||
|
if (t % Mult < 0x1_0000)
|
||||||
|
result[ctr++] = Prev(first | (ushort)(t / Mult));
|
||||||
|
}
|
||||||
|
return ctr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all the origin seeds for two 15 bit rand() calls
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">Result storage array, to be populated starting at index 0.</param>
|
||||||
|
/// <param name="first">First rand() call, 15 bits, already shifted left 16 bits.</param>
|
||||||
|
/// <param name="second">Second rand() call, 15 bits, already shifted left 16 bits.</param>
|
||||||
|
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||||
|
public static int GetSeedsIVs(Span<uint> result, uint first, uint second)
|
||||||
|
{
|
||||||
|
ulong t = (second - (first * Mult) - Sub) & 0x7FFF_FFFF;
|
||||||
|
ulong kmax = (Base - t) >> 31;
|
||||||
|
|
||||||
|
int ctr = 0;
|
||||||
|
for (ulong k = 0; k <= kmax; k++, t += 0x8000_0000) // at most 7 iterations
|
||||||
|
{
|
||||||
|
if (t % Mult < 0x1_0000)
|
||||||
|
{
|
||||||
|
var s = Prev(first | (ushort)(t / Mult));
|
||||||
|
result[ctr++] = s;
|
||||||
|
result[ctr++] = s ^ 0x8000_0000; // top bit flip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctr;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace PKHeX.Core;
|
namespace PKHeX.Core;
|
||||||
|
@ -21,7 +21,7 @@ public ref struct XorShift128
|
||||||
[FieldOffset(0x8)] private readonly ulong s1;
|
[FieldOffset(0x8)] private readonly ulong s1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uses the <see cref="RNG.ARNG"/> to advance the seed for each 32-bit input.
|
/// Uses the <see cref="ARNG"/> to advance the seed for each 32-bit input.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="seed">32 bit seed</param>
|
/// <param name="seed">32 bit seed</param>
|
||||||
/// <remarks>sub_E0F5E0 in v1.1.3</remarks>
|
/// <remarks>sub_E0F5E0 in v1.1.3</remarks>
|
||||||
|
|
|
@ -1,34 +1,28 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace PKHeX.Core;
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Frame List used to cache <see cref="RNG"/> results.
|
/// Frame List used to cache <see cref="XDRNG"/> results, lazily reversing backwards and keeping the previous results.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class FrameCache
|
public sealed class FrameCache
|
||||||
{
|
{
|
||||||
private const int DefaultSize = 32;
|
private const int DefaultSize = 32;
|
||||||
private readonly List<uint> Seeds = new(DefaultSize);
|
private readonly List<uint> Seeds = new(DefaultSize);
|
||||||
private readonly List<uint> Values = new(DefaultSize);
|
private readonly List<uint> Values = new(DefaultSize);
|
||||||
private readonly Func<uint, uint> Advance;
|
private uint Last;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of a <see cref="FrameCache"/>.
|
/// Creates a new instance of a <see cref="FrameCache"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="origin">Seed at frame 0.</param>
|
/// <param name="origin">Seed at frame 0.</param>
|
||||||
/// <param name="advance"><see cref="RNG"/> method used to get the next seed. Can use <see cref="RNG.Next"/> or <see cref="RNG.Prev"/>.</param>
|
public FrameCache(uint origin) => Add(origin);
|
||||||
public FrameCache(uint origin, Func<uint, uint> advance)
|
|
||||||
{
|
|
||||||
Advance = advance;
|
|
||||||
Add(origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void Add(uint seed)
|
private void Add(uint seed)
|
||||||
{
|
{
|
||||||
Seeds.Add(seed);
|
Seeds.Add(Last = seed);
|
||||||
Values.Add(seed >> 16);
|
Values.Add(seed >> 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +35,7 @@ public sealed class FrameCache
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
while (index >= Seeds.Count)
|
while (index >= Seeds.Count)
|
||||||
Add(Advance(Seeds[^1]));
|
Add(XDRNG.Prev(Last));
|
||||||
return Values[index];
|
return Values[index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +48,7 @@ public sealed class FrameCache
|
||||||
public uint GetSeed(int index)
|
public uint GetSeed(int index)
|
||||||
{
|
{
|
||||||
while (index >= Seeds.Count)
|
while (index >= Seeds.Count)
|
||||||
Add(Advance(Seeds[^1]));
|
Add(XDRNG.Prev(Last));
|
||||||
return Seeds[index];
|
return Seeds[index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,11 +67,11 @@ public static class FrameFinder
|
||||||
if (noLead)
|
if (noLead)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var prev = info.RNG.Prev(f.Seed); // ESV
|
var prev = LCRNG.Prev(f.Seed); // ESV
|
||||||
var rand = prev >> 16;
|
var rand = prev >> 16;
|
||||||
f.RandESV = rand;
|
f.RandESV = rand;
|
||||||
f.RandLevel = f.Seed >> 16;
|
f.RandLevel = f.Seed >> 16;
|
||||||
f.OriginSeed = info.RNG.Prev(prev);
|
f.OriginSeed = LCRNG.Prev(prev);
|
||||||
if (f.Lead != LeadRequired.CuteCharm) // needs proc checking
|
if (f.Lead != LeadRequired.CuteCharm) // needs proc checking
|
||||||
yield return f;
|
yield return f;
|
||||||
|
|
||||||
|
@ -94,9 +94,9 @@ public static class FrameFinder
|
||||||
// 3 different rand places
|
// 3 different rand places
|
||||||
LeadRequired lead;
|
LeadRequired lead;
|
||||||
var prev0 = f.Seed; // 0
|
var prev0 = f.Seed; // 0
|
||||||
var prev1 = info.RNG.Prev(f.Seed); // -1
|
var prev1 = LCRNG.Prev(f.Seed); // -1
|
||||||
var prev2 = info.RNG.Prev(prev1); // -2
|
var prev2 = LCRNG.Prev(prev1); // -2
|
||||||
var prev3 = info.RNG.Prev(prev2); // -3
|
var prev3 = LCRNG.Prev(prev2); // -3
|
||||||
|
|
||||||
// Rand call raw values
|
// Rand call raw values
|
||||||
var p0 = prev0 >> 16;
|
var p0 = prev0 >> 16;
|
||||||
|
@ -167,12 +167,12 @@ public static class FrameFinder
|
||||||
var rand = f.Seed >> 16;
|
var rand = f.Seed >> 16;
|
||||||
f.RandESV = rand;
|
f.RandESV = rand;
|
||||||
f.RandLevel = rand; // unused
|
f.RandLevel = rand; // unused
|
||||||
f.OriginSeed = info.RNG.Prev(f.Seed);
|
f.OriginSeed = LCRNG.Prev(f.Seed);
|
||||||
yield return f;
|
yield return f;
|
||||||
|
|
||||||
// Create a copy for level; shift ESV and origin back
|
// Create a copy for level; shift ESV and origin back
|
||||||
var esv = f.OriginSeed >> 16;
|
var esv = f.OriginSeed >> 16;
|
||||||
var origin = info.RNG.Prev(f.OriginSeed);
|
var origin = LCRNG.Prev(f.OriginSeed);
|
||||||
var withLevel = info.GetFrame(f.Seed, f.Lead | LeadRequired.UsesLevelCall, esv, f.RandLevel, origin);
|
var withLevel = info.GetFrame(f.Seed, f.Lead | LeadRequired.UsesLevelCall, esv, f.RandLevel, origin);
|
||||||
yield return withLevel;
|
yield return withLevel;
|
||||||
|
|
||||||
|
@ -194,9 +194,9 @@ public static class FrameFinder
|
||||||
{
|
{
|
||||||
LeadRequired lead;
|
LeadRequired lead;
|
||||||
var prev0 = f.Seed; // 0
|
var prev0 = f.Seed; // 0
|
||||||
var prev1 = info.RNG.Prev(f.Seed); // -1
|
var prev1 = LCRNG.Prev(f.Seed); // -1
|
||||||
var prev2 = info.RNG.Prev(prev1); // -2
|
var prev2 = LCRNG.Prev(prev1); // -2
|
||||||
var prev3 = info.RNG.Prev(prev2); // -3
|
var prev3 = LCRNG.Prev(prev2); // -3
|
||||||
|
|
||||||
// Rand call raw values
|
// Rand call raw values
|
||||||
var p0 = prev0 >> 16;
|
var p0 = prev0 >> 16;
|
||||||
|
@ -290,12 +290,12 @@ public static class FrameFinder
|
||||||
if (!sync && !reg) // doesn't generate nature frame
|
if (!sync && !reg) // doesn't generate nature frame
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
uint prev = RNG.LCRNG.Prev(s);
|
uint prev = LCRNG.Prev(s);
|
||||||
if (info.AllowLeads && reg) // check for failed sync
|
if (info.AllowLeads && reg) // check for failed sync
|
||||||
{
|
{
|
||||||
var failsync = (info.DPPt ? prev >> 31 : (prev >> 16) & 1) != 1;
|
var failsync = (info.DPPt ? prev >> 31 : (prev >> 16) & 1) != 1;
|
||||||
if (failsync)
|
if (failsync)
|
||||||
yield return info.GetFrame(RNG.LCRNG.Prev(prev), LeadRequired.SynchronizeFail);
|
yield return info.GetFrame(LCRNG.Prev(prev), LeadRequired.SynchronizeFail);
|
||||||
}
|
}
|
||||||
if (sync)
|
if (sync)
|
||||||
yield return info.GetFrame(prev, LeadRequired.Synchronize);
|
yield return info.GetFrame(prev, LeadRequired.Synchronize);
|
||||||
|
@ -308,7 +308,7 @@ public static class FrameFinder
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (info.Safari3)
|
if (info.Safari3)
|
||||||
prev = RNG.LCRNG.Prev(prev); // wasted RNG call
|
prev = LCRNG.Prev(prev); // wasted RNG call
|
||||||
yield return info.GetFrame(prev, LeadRequired.None);
|
yield return info.GetFrame(prev, LeadRequired.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,10 +328,10 @@ public static class FrameFinder
|
||||||
for (uint i = 0; i < 25; i++)
|
for (uint i = 0; i < 25; i++)
|
||||||
{
|
{
|
||||||
for (uint j = 1 + i; j < 25; j++)
|
for (uint j = 1 + i; j < 25; j++)
|
||||||
stack.Push(seed = RNG.LCRNG.Prev(seed));
|
stack.Push(seed = LCRNG.Prev(seed));
|
||||||
}
|
}
|
||||||
|
|
||||||
natureOrigin = RNG.LCRNG.Prev(stack.Peek());
|
natureOrigin = LCRNG.Prev(stack.Peek());
|
||||||
if (natureOrigin >> (16 % 100) >= 80) // failed proc
|
if (natureOrigin >> (16 % 100) >= 80) // failed proc
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -368,7 +368,7 @@ public static class FrameFinder
|
||||||
return false; // current nature is chosen instead, fail!
|
return false; // current nature is chosen instead, fail!
|
||||||
}
|
}
|
||||||
// unroll once more to hit the level calc (origin with respect for beginning the nature calcs)
|
// unroll once more to hit the level calc (origin with respect for beginning the nature calcs)
|
||||||
natureOrigin = RNG.LCRNG.Prev(natureOrigin);
|
natureOrigin = LCRNG.Prev(natureOrigin);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,13 +389,13 @@ public static class FrameFinder
|
||||||
if (nature != info.Nature)
|
if (nature != info.Nature)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var prev = RNG.LCRNG.Prev(s);
|
var prev = LCRNG.Prev(s);
|
||||||
var proc = prev >> 16;
|
var proc = prev >> 16;
|
||||||
bool charmProc = (info.DPPt ? proc / 0x5556 : proc % 3) != 0; // 2/3 odds
|
bool charmProc = (info.DPPt ? proc / 0x5556 : proc % 3) != 0; // 2/3 odds
|
||||||
if (!charmProc)
|
if (!charmProc)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
yield return info.GetFrame(RNG.LCRNG.Prev(prev), LeadRequired.CuteCharm);
|
yield return info.GetFrame(LCRNG.Prev(prev), LeadRequired.CuteCharm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using static PKHeX.Core.GameVersion;
|
using static PKHeX.Core.GameVersion;
|
||||||
|
|
||||||
namespace PKHeX.Core;
|
namespace PKHeX.Core;
|
||||||
|
@ -15,7 +15,6 @@ public sealed class FrameGenerator
|
||||||
public readonly bool DPPt;
|
public readonly bool DPPt;
|
||||||
public readonly bool AllowLeads;
|
public readonly bool AllowLeads;
|
||||||
public readonly FrameType FrameType;
|
public readonly FrameType FrameType;
|
||||||
public readonly RNG RNG = RNG.LCRNG;
|
|
||||||
public readonly bool Safari3;
|
public readonly bool Safari3;
|
||||||
|
|
||||||
public Frame GetFrame(uint seed, LeadRequired lead) => new(seed, FrameType, lead);
|
public Frame GetFrame(uint seed, LeadRequired lead) => new(seed, FrameType, lead);
|
||||||
|
|
|
@ -19,7 +19,7 @@ public readonly record struct SeedInfo(uint Seed, bool Charm3 = false)
|
||||||
yield return new SeedInfo(seed);
|
yield return new SeedInfo(seed);
|
||||||
|
|
||||||
var s1 = seed;
|
var s1 = seed;
|
||||||
var s2 = RNG.LCRNG.Prev(s1);
|
var s2 = LCRNG.Prev(s1);
|
||||||
bool charm3 = false;
|
bool charm3 = false;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
@ -38,8 +38,8 @@ public readonly record struct SeedInfo(uint Seed, bool Charm3 = false)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
s1 = RNG.LCRNG.Prev(s2);
|
s1 = LCRNG.Prev(s2);
|
||||||
s2 = RNG.LCRNG.Prev(s1);
|
s2 = LCRNG.Prev(s1);
|
||||||
|
|
||||||
yield return new SeedInfo(s1, charm3);
|
yield return new SeedInfo(s1, charm3);
|
||||||
}
|
}
|
||||||
|
@ -55,9 +55,11 @@ public readonly record struct SeedInfo(uint Seed, bool Charm3 = false)
|
||||||
// We cannot rely on a PID-IV origin seed. Since IVs are 2^30, they are not strong enough to assume a single seed was the source.
|
// We cannot rely on a PID-IV origin seed. Since IVs are 2^30, they are not strong enough to assume a single seed was the source.
|
||||||
// We must reverse the IVs to find all seeds that could generate this.
|
// We must reverse the IVs to find all seeds that could generate this.
|
||||||
// ESV,Proc,Nature,IV1,IV2; these do not do the nature loop for Method J/K so each seed originates a single seed frame.
|
// ESV,Proc,Nature,IV1,IV2; these do not do the nature loop for Method J/K so each seed originates a single seed frame.
|
||||||
var seeds = MethodFinder.GetCuteCharmSeeds(pk);
|
|
||||||
foreach (var seed in seeds)
|
var seeds = new uint[LCRNG.MaxCountSeedsIV];
|
||||||
yield return new SeedInfo(seed);
|
int ctr = LCRNGReversal.GetSeedsIVs(seeds, (uint)pk.IV_HP, (uint)pk.IV_ATK, (uint)pk.IV_DEF, (uint)pk.IV_SPA, (uint)pk.IV_SPD, (uint)pk.IV_SPE);
|
||||||
|
for (int i = 0; i < ctr; i++)
|
||||||
|
yield return new SeedInfo(seeds[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -73,7 +75,7 @@ public readonly record struct SeedInfo(uint Seed, bool Charm3 = false)
|
||||||
yield return new SeedInfo(seed);
|
yield return new SeedInfo(seed);
|
||||||
|
|
||||||
var s1 = seed;
|
var s1 = seed;
|
||||||
var s2 = RNG.LCRNG.Prev(s1);
|
var s2 = LCRNG.Prev(s1);
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
var a = s2 >> 16;
|
var a = s2 >> 16;
|
||||||
|
@ -91,8 +93,8 @@ public readonly record struct SeedInfo(uint Seed, bool Charm3 = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s1 = RNG.LCRNG.Prev(s2);
|
s1 = LCRNG.Prev(s2);
|
||||||
s2 = RNG.LCRNG.Prev(s1);
|
s2 = LCRNG.Prev(s1);
|
||||||
|
|
||||||
yield return new SeedInfo(s1);
|
yield return new SeedInfo(s1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,8 +53,8 @@ public static class LockFinder
|
||||||
public static bool IsXDStarterValid(uint seed, int TID, int SID)
|
public static bool IsXDStarterValid(uint seed, int TID, int SID)
|
||||||
{
|
{
|
||||||
// pidiv reversed 2x yields SID, 3x yields TID. shift by 7 if another PKM is generated prior
|
// pidiv reversed 2x yields SID, 3x yields TID. shift by 7 if another PKM is generated prior
|
||||||
var SIDf = RNG.XDRNG.Reverse(seed, 2);
|
var SIDf = XDRNG.Prev2(seed);
|
||||||
var TIDf = RNG.XDRNG.Prev(SIDf);
|
var TIDf = XDRNG.Prev(SIDf);
|
||||||
return SIDf >> 16 == SID && TIDf >> 16 == TID;
|
return SIDf >> 16 == SID && TIDf >> 16 == TID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,16 +71,14 @@ public static class LockFinder
|
||||||
public static bool IsColoStarterValid(ushort species, ref uint seed, int TID, int SID, uint pkPID, uint IV1, uint IV2)
|
public static bool IsColoStarterValid(ushort species, ref uint seed, int TID, int SID, uint pkPID, uint IV1, uint IV2)
|
||||||
{
|
{
|
||||||
// reverse the seed the bare minimum
|
// reverse the seed the bare minimum
|
||||||
int rev = 2;
|
uint SIDf = species == (int)Species.Espeon
|
||||||
if (species == (int)Species.Espeon)
|
? XDRNG.Prev9(seed)
|
||||||
rev += 7;
|
: XDRNG.Prev2(seed);
|
||||||
|
|
||||||
// reverse until we find the TID/SID, then run the generation forward to see if it matches our inputs.
|
// reverse until we find the TID/SID, then run the generation forward to see if it matches our inputs.
|
||||||
var rng = RNG.XDRNG;
|
|
||||||
var SIDf = rng.Reverse(seed, rev);
|
|
||||||
int ctr = 0;
|
int ctr = 0;
|
||||||
uint temp;
|
uint temp;
|
||||||
while ((temp = rng.Prev(SIDf)) >> 16 != TID || SIDf >> 16 != SID)
|
while ((temp = XDRNG.Prev(SIDf)) >> 16 != TID || SIDf >> 16 != SID)
|
||||||
{
|
{
|
||||||
SIDf = temp;
|
SIDf = temp;
|
||||||
if (ctr > 32) // arbitrary
|
if (ctr > 32) // arbitrary
|
||||||
|
@ -88,7 +86,7 @@ public static class LockFinder
|
||||||
ctr++;
|
ctr++;
|
||||||
}
|
}
|
||||||
|
|
||||||
var next = rng.Next(SIDf);
|
var next = XDRNG.Next(SIDf);
|
||||||
|
|
||||||
// generate Umbreon
|
// generate Umbreon
|
||||||
var PIDIV = GenerateValidColoStarterPID(ref next, TID, SID);
|
var PIDIV = GenerateValidColoStarterPID(ref next, TID, SID);
|
||||||
|
@ -97,7 +95,7 @@ public static class LockFinder
|
||||||
|
|
||||||
if (!PIDIV.Equals(pkPID, IV1, IV2))
|
if (!PIDIV.Equals(pkPID, IV1, IV2))
|
||||||
return false;
|
return false;
|
||||||
seed = rng.Reverse(SIDf, 2);
|
seed = XDRNG.Prev2(SIDf);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,17 +106,15 @@ public static class LockFinder
|
||||||
|
|
||||||
private static PIDIVGroup GenerateValidColoStarterPID(ref uint uSeed, int TID, int SID)
|
private static PIDIVGroup GenerateValidColoStarterPID(ref uint uSeed, int TID, int SID)
|
||||||
{
|
{
|
||||||
var rng = RNG.XDRNG;
|
uSeed = XDRNG.Next2(uSeed); // skip fakePID
|
||||||
|
|
||||||
uSeed = rng.Advance(uSeed, 2); // skip fakePID
|
|
||||||
var IV1 = (uSeed >> 16) & 0x7FFF;
|
var IV1 = (uSeed >> 16) & 0x7FFF;
|
||||||
uSeed = rng.Next(uSeed);
|
uSeed = XDRNG.Next(uSeed);
|
||||||
var IV2 = (uSeed >> 16) & 0x7FFF;
|
var IV2 = (uSeed >> 16) & 0x7FFF;
|
||||||
uSeed = rng.Next(uSeed);
|
uSeed = XDRNG.Next(uSeed);
|
||||||
uSeed = rng.Advance(uSeed, 1); // skip ability call
|
uSeed = XDRNG.Next(uSeed); // skip ability call
|
||||||
var PID = GenerateStarterPID(ref uSeed, TID, SID);
|
var PID = GenerateStarterPID(ref uSeed, TID, SID);
|
||||||
|
|
||||||
uSeed = rng.Advance(uSeed, 2); // PID calls consumed
|
uSeed = XDRNG.Next2(uSeed); // PID calls consumed
|
||||||
|
|
||||||
return new PIDIVGroup(PID, IV1, IV2);
|
return new PIDIVGroup(PID, IV1, IV2);
|
||||||
}
|
}
|
||||||
|
@ -131,11 +127,11 @@ public static class LockFinder
|
||||||
const byte ratio = 0x1F; // 12.5% F (can't be female)
|
const byte ratio = 0x1F; // 12.5% F (can't be female)
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
var next = RNG.XDRNG.Next(uSeed);
|
var next = XDRNG.Next(uSeed);
|
||||||
PID = (uSeed & 0xFFFF0000) | (next >> 16);
|
PID = (uSeed & 0xFFFF0000) | (next >> 16);
|
||||||
if ((PID & 0xFF) >= ratio && !IsShiny(TID, SID, PID))
|
if ((PID & 0xFF) >= ratio && !IsShiny(TID, SID, PID))
|
||||||
break;
|
break;
|
||||||
uSeed = RNG.XDRNG.Next(next);
|
uSeed = XDRNG.Next(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PID;
|
return PID;
|
||||||
|
|
|
@ -62,7 +62,7 @@ public sealed class TeamLockResult
|
||||||
{
|
{
|
||||||
Locks = new Stack<NPCLock>((Specifications = teamSpec).Locks);
|
Locks = new Stack<NPCLock>((Specifications = teamSpec).Locks);
|
||||||
Team = new Stack<SeedFrame>(Locks.Count);
|
Team = new Stack<SeedFrame>(Locks.Count);
|
||||||
Cache = new FrameCache(RNG.XDRNG.Reverse(originSeed, 2), RNG.XDRNG.Prev);
|
Cache = new FrameCache(XDRNG.Prev2(originSeed));
|
||||||
TSV = tsv;
|
TSV = tsv;
|
||||||
|
|
||||||
Valid = FindLockSeed();
|
Valid = FindLockSeed();
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using static PKHeX.Core.PIDType;
|
using static PKHeX.Core.PIDType;
|
||||||
|
|
||||||
|
@ -22,44 +20,49 @@ public static class MethodFinder
|
||||||
return AnalyzeGB(pk);
|
return AnalyzeGB(pk);
|
||||||
var pid = pk.EncryptionConstant;
|
var pid = pk.EncryptionConstant;
|
||||||
|
|
||||||
var top = pid >> 16;
|
var top = pid & 0xFFFF0000;
|
||||||
var bot = pid & 0xFFFF;
|
var bot = pid << 16;
|
||||||
|
|
||||||
Span<uint> temp = stackalloc uint[6];
|
Span<uint> temp = stackalloc uint[6];
|
||||||
for (int i = 0; i < 6; i++)
|
for (int i = 0; i < 6; i++)
|
||||||
temp[i] = (uint)pk.GetIV(i);
|
temp[i] = (uint)pk.GetIV(i);
|
||||||
ReadOnlySpan<uint> IVs = temp;
|
ReadOnlySpan<uint> IVs = temp;
|
||||||
|
|
||||||
if (GetLCRNGMatch(top, bot, IVs, out PIDIV pidiv))
|
// Between XDRNG and LCRNG, the LCRNG will have the most results.
|
||||||
|
// Reuse our temp buffer across all methods.
|
||||||
|
const int maxResults = LCRNG.MaxCountSeedsIV;
|
||||||
|
Span<uint> seeds = stackalloc uint[maxResults];
|
||||||
|
|
||||||
|
if (GetLCRNGMatch(seeds, top, bot, IVs, out PIDIV pidiv))
|
||||||
return pidiv;
|
return pidiv;
|
||||||
if (pk.Species == (int)Species.Unown && GetLCRNGUnownMatch(top, bot, IVs, out pidiv)) // frlg only
|
if (pk.Species == (int)Species.Unown && GetLCRNGUnownMatch(seeds, top, bot, IVs, out pidiv)) // frlg only
|
||||||
return pidiv;
|
return pidiv;
|
||||||
if (GetColoStarterMatch(pk, top, bot, IVs, out pidiv))
|
if (GetColoStarterMatch(seeds, pk, top, bot, IVs, out pidiv))
|
||||||
return pidiv;
|
return pidiv;
|
||||||
if (GetXDRNGMatch(pk, top, bot, IVs, out pidiv))
|
if (GetXDRNGMatch(seeds, pk, top, bot, IVs, out pidiv))
|
||||||
return pidiv;
|
return pidiv;
|
||||||
|
|
||||||
// Special cases
|
// Special cases
|
||||||
if (GetLCRNGRoamerMatch(top, bot, IVs, out pidiv))
|
if (GetLCRNGRoamerMatch(seeds, top, bot, IVs, out pidiv))
|
||||||
return pidiv;
|
return pidiv;
|
||||||
if (GetChannelMatch(top, bot, IVs, out pidiv, pk))
|
if (GetChannelMatch(seeds, top, bot, IVs, out pidiv, pk))
|
||||||
return pidiv;
|
return pidiv;
|
||||||
if (GetMG4Match(pid, IVs, out pidiv))
|
if (GetMG4Match(seeds, pid, IVs, out pidiv))
|
||||||
return pidiv;
|
return pidiv;
|
||||||
|
|
||||||
if (GetBACDMatch(pk, pid, IVs, out pidiv))
|
if (GetBACDMatch(seeds, pk, pid, IVs, out pidiv))
|
||||||
return pidiv;
|
return pidiv;
|
||||||
if (GetModifiedPIDMatch(pk, pid, IVs, out pidiv))
|
if (GetModifiedPIDMatch(seeds, pk, pid, IVs, out pidiv))
|
||||||
return pidiv;
|
return pidiv;
|
||||||
|
|
||||||
return PIDIV.None; // no match
|
return PIDIV.None; // no match
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetModifiedPIDMatch(PKM pk, uint pid, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
private static bool GetModifiedPIDMatch(Span<uint> seeds, PKM pk, uint pid, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
||||||
{
|
{
|
||||||
if (pk.IsShiny)
|
if (pk.IsShiny)
|
||||||
{
|
{
|
||||||
if (GetChainShinyMatch(pk, pid, IVs, out pidiv))
|
if (GetChainShinyMatch(seeds, pk, pid, IVs, out pidiv))
|
||||||
return true;
|
return true;
|
||||||
if (GetModified8BitMatch(pk, pid, out pidiv))
|
if (GetModified8BitMatch(pk, pid, out pidiv))
|
||||||
return true;
|
return true;
|
||||||
|
@ -80,22 +83,23 @@ public static class MethodFinder
|
||||||
: GetG5MGShinyMatch(pk, pid, out pidiv) || (pid <= 0xFF && GetCuteCharmMatch(pk, pid, out pidiv));
|
: GetG5MGShinyMatch(pk, pid, out pidiv) || (pid <= 0xFF && GetCuteCharmMatch(pk, pid, out pidiv));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetLCRNGMatch(uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
private static bool GetLCRNGMatch(Span<uint> seeds, uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
||||||
{
|
{
|
||||||
var reg = GetSeedsFromPID(RNG.LCRNG, top, bot);
|
var count = LCRNGReversal.GetSeeds(seeds, bot, top);
|
||||||
|
var reg = seeds[..count];
|
||||||
var iv1 = GetIVChunk(IVs, 0);
|
var iv1 = GetIVChunk(IVs, 0);
|
||||||
var iv2 = GetIVChunk(IVs, 3);
|
var iv2 = GetIVChunk(IVs, 3);
|
||||||
foreach (var seed in reg)
|
foreach (var seed in reg)
|
||||||
{
|
{
|
||||||
// A and B are already used by PID
|
// A and B are already used by PID
|
||||||
var B = RNG.LCRNG.Advance(seed, 2);
|
var B = LCRNG.Next2(seed);
|
||||||
|
|
||||||
// Method 1/2/4 can use 3 different RNG frames
|
// Method 1/2/4 can use 3 different RNG frames
|
||||||
var C = RNG.LCRNG.Next(B);
|
var C = LCRNG.Next(B);
|
||||||
var ivC = C >> 16 & 0x7FFF;
|
var ivC = C >> 16 & 0x7FFF;
|
||||||
if (iv1 == ivC)
|
if (iv1 == ivC)
|
||||||
{
|
{
|
||||||
var D = RNG.LCRNG.Next(C);
|
var D = LCRNG.Next(C);
|
||||||
var ivD = D >> 16 & 0x7FFF;
|
var ivD = D >> 16 & 0x7FFF;
|
||||||
if (iv2 == ivD) // ABCD
|
if (iv2 == ivD) // ABCD
|
||||||
{
|
{
|
||||||
|
@ -103,7 +107,7 @@ public static class MethodFinder
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var E = RNG.LCRNG.Next(D);
|
var E = LCRNG.Next(D);
|
||||||
var ivE = E >> 16 & 0x7FFF;
|
var ivE = E >> 16 & 0x7FFF;
|
||||||
if (iv2 == ivE) // ABCE
|
if (iv2 == ivE) // ABCE
|
||||||
{
|
{
|
||||||
|
@ -113,12 +117,12 @@ public static class MethodFinder
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var D = RNG.LCRNG.Next(C);
|
var D = LCRNG.Next(C);
|
||||||
var ivD = D >> 16 & 0x7FFF;
|
var ivD = D >> 16 & 0x7FFF;
|
||||||
if (iv1 != ivD)
|
if (iv1 != ivD)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var E = RNG.LCRNG.Next(D);
|
var E = LCRNG.Next(D);
|
||||||
var ivE = E >> 16 & 0x7FFF;
|
var ivE = E >> 16 & 0x7FFF;
|
||||||
if (iv2 == ivE) // ABDE
|
if (iv2 == ivE) // ABDE
|
||||||
{
|
{
|
||||||
|
@ -127,18 +131,19 @@ public static class MethodFinder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reg = GetSeedsFromPIDSkip(RNG.LCRNG, top, bot);
|
count = LCRNGReversalSkip.GetSeeds(seeds, bot, top);
|
||||||
|
reg = seeds[..count];
|
||||||
foreach (var seed in reg)
|
foreach (var seed in reg)
|
||||||
{
|
{
|
||||||
// A and B are already used by PID
|
// A and B are already used by PID
|
||||||
var C = RNG.LCRNG.Advance(seed, 3);
|
var C = LCRNG.Next3(seed);
|
||||||
|
|
||||||
// Method 3
|
// Method 3
|
||||||
var D = RNG.LCRNG.Next(C);
|
var D = LCRNG.Next(C);
|
||||||
var ivD = D >> 16 & 0x7FFF;
|
var ivD = D >> 16 & 0x7FFF;
|
||||||
if (iv1 != ivD)
|
if (iv1 != ivD)
|
||||||
continue;
|
continue;
|
||||||
var E = RNG.LCRNG.Next(D);
|
var E = LCRNG.Next(D);
|
||||||
var ivE = E >> 16 & 0x7FFF;
|
var ivE = E >> 16 & 0x7FFF;
|
||||||
if (iv2 != ivE)
|
if (iv2 != ivE)
|
||||||
continue;
|
continue;
|
||||||
|
@ -148,23 +153,24 @@ public static class MethodFinder
|
||||||
return GetNonMatch(out pidiv);
|
return GetNonMatch(out pidiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetLCRNGUnownMatch(uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
private static bool GetLCRNGUnownMatch(Span<uint> seeds, uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
||||||
{
|
{
|
||||||
// this is an exact copy of LCRNG 1,2,4 matching, except the PID has its halves switched (BACD, BADE, BACE)
|
// this is an exact copy of LCRNG 1,2,4 matching, except the PID has its halves switched (BACD, BADE, BACE)
|
||||||
var reg = GetSeedsFromPID(RNG.LCRNG, bot, top); // reversed!
|
var count = LCRNGReversal.GetSeeds(seeds, top, bot); // reversed!
|
||||||
|
var reg = seeds[..count];
|
||||||
var iv1 = GetIVChunk(IVs, 0);
|
var iv1 = GetIVChunk(IVs, 0);
|
||||||
var iv2 = GetIVChunk(IVs, 3);
|
var iv2 = GetIVChunk(IVs, 3);
|
||||||
foreach (var seed in reg)
|
foreach (var seed in reg)
|
||||||
{
|
{
|
||||||
// A and B are already used by PID
|
// A and B are already used by PID
|
||||||
var B = RNG.LCRNG.Advance(seed, 2);
|
var B = LCRNG.Next2(seed);
|
||||||
|
|
||||||
// Method 1/2/4 can use 3 different RNG frames
|
// Method 1/2/4 can use 3 different RNG frames
|
||||||
var C = RNG.LCRNG.Next(B);
|
var C = LCRNG.Next(B);
|
||||||
var ivC = C >> 16 & 0x7FFF;
|
var ivC = C >> 16 & 0x7FFF;
|
||||||
if (iv1 == ivC)
|
if (iv1 == ivC)
|
||||||
{
|
{
|
||||||
var D = RNG.LCRNG.Next(C);
|
var D = LCRNG.Next(C);
|
||||||
var ivD = D >> 16 & 0x7FFF;
|
var ivD = D >> 16 & 0x7FFF;
|
||||||
if (iv2 == ivD) // BACD
|
if (iv2 == ivD) // BACD
|
||||||
{
|
{
|
||||||
|
@ -172,7 +178,7 @@ public static class MethodFinder
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var E = RNG.LCRNG.Next(D);
|
var E = LCRNG.Next(D);
|
||||||
var ivE = E >> 16 & 0x7FFF;
|
var ivE = E >> 16 & 0x7FFF;
|
||||||
if (iv2 == ivE) // BACE
|
if (iv2 == ivE) // BACE
|
||||||
{
|
{
|
||||||
|
@ -182,12 +188,12 @@ public static class MethodFinder
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var D = RNG.LCRNG.Next(C);
|
var D = LCRNG.Next(C);
|
||||||
var ivD = D >> 16 & 0x7FFF;
|
var ivD = D >> 16 & 0x7FFF;
|
||||||
if (iv1 != ivD)
|
if (iv1 != ivD)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var E = RNG.LCRNG.Next(D);
|
var E = LCRNG.Next(D);
|
||||||
var ivE = E >> 16 & 0x7FFF;
|
var ivE = E >> 16 & 0x7FFF;
|
||||||
if (iv2 == ivE) // BADE
|
if (iv2 == ivE) // BADE
|
||||||
{
|
{
|
||||||
|
@ -196,18 +202,19 @@ public static class MethodFinder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reg = GetSeedsFromPIDSkip(RNG.LCRNG, bot, top); // reversed!
|
count = LCRNGReversalSkip.GetSeeds(seeds, top, bot); // reversed!
|
||||||
|
reg = seeds[..count];
|
||||||
foreach (var seed in reg)
|
foreach (var seed in reg)
|
||||||
{
|
{
|
||||||
// A and B are already used by PID
|
// A and B are already used by PID
|
||||||
var C = RNG.LCRNG.Advance(seed, 3);
|
var C = LCRNG.Next3(seed);
|
||||||
|
|
||||||
// Method 3
|
// Method 3
|
||||||
var D = RNG.LCRNG.Next(C);
|
var D = LCRNG.Next(C);
|
||||||
var ivD = D >> 16 & 0x7FFF;
|
var ivD = D >> 16 & 0x7FFF;
|
||||||
if (iv1 != ivD)
|
if (iv1 != ivD)
|
||||||
continue;
|
continue;
|
||||||
var E = RNG.LCRNG.Next(D);
|
var E = LCRNG.Next(D);
|
||||||
var ivE = E >> 16 & 0x7FFF;
|
var ivE = E >> 16 & 0x7FFF;
|
||||||
if (iv2 != ivE)
|
if (iv2 != ivE)
|
||||||
continue;
|
continue;
|
||||||
|
@ -217,16 +224,18 @@ public static class MethodFinder
|
||||||
return GetNonMatch(out pidiv);
|
return GetNonMatch(out pidiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetLCRNGRoamerMatch(uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
private static bool GetLCRNGRoamerMatch(Span<uint> seeds, uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
||||||
{
|
{
|
||||||
if (IVs[2] != 0 || IVs[3] != 0 || IVs[4] != 0 || IVs[5] != 0 || IVs[1] > 7)
|
if (IVs[2] != 0 || IVs[3] != 0 || IVs[4] != 0 || IVs[5] != 0 || IVs[1] > 7)
|
||||||
return GetNonMatch(out pidiv);
|
return GetNonMatch(out pidiv);
|
||||||
|
|
||||||
var iv1 = GetIVChunk(IVs, 0);
|
var iv1 = GetIVChunk(IVs, 0);
|
||||||
var reg = GetSeedsFromPID(RNG.LCRNG, top, bot);
|
var count = LCRNGReversal.GetSeeds(seeds, bot, top);
|
||||||
|
var reg = seeds[..count];
|
||||||
foreach (var seed in reg)
|
foreach (var seed in reg)
|
||||||
{
|
{
|
||||||
// Only the first 8 bits are kept
|
// Only the first 8 bits are kept
|
||||||
var ivC = RNG.LCRNG.Advance(seed, 3) >> 16 & 0x00FF;
|
var ivC = LCRNG.Next3(seed) >> 16 & 0x00FF;
|
||||||
if (iv1 != ivC)
|
if (iv1 != ivC)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -236,19 +245,20 @@ public static class MethodFinder
|
||||||
return GetNonMatch(out pidiv);
|
return GetNonMatch(out pidiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetXDRNGMatch(PKM pk, uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
private static bool GetXDRNGMatch(Span<uint> seeds, PKM pk, uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
||||||
{
|
{
|
||||||
var xdc = GetSeedsFromPIDEuclid(RNG.XDRNG, top, bot);
|
var count = XDRNG.GetSeeds(seeds, top, bot);
|
||||||
|
var xdc = seeds[..count];
|
||||||
foreach (var seed in xdc)
|
foreach (var seed in xdc)
|
||||||
{
|
{
|
||||||
var B = RNG.XDRNG.Prev(seed);
|
var B = XDRNG.Prev(seed);
|
||||||
var A = RNG.XDRNG.Prev(B);
|
var A = XDRNG.Prev(B);
|
||||||
|
|
||||||
var hi = A >> 16;
|
var hi = A >> 16;
|
||||||
var lo = B >> 16;
|
var lo = B >> 16;
|
||||||
if (IVsMatch(hi, lo, IVs))
|
if (IVsMatch(hi, lo, IVs))
|
||||||
{
|
{
|
||||||
pidiv = new PIDIV(CXD, RNG.XDRNG.Prev(A));
|
pidiv = new PIDIV(CXD, XDRNG.Prev(A));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,18 +276,18 @@ public static class MethodFinder
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
B = RNG.XDRNG.Prev(A);
|
B = XDRNG.Prev(A);
|
||||||
A = RNG.XDRNG.Prev(B);
|
A = XDRNG.Prev(B);
|
||||||
hi = A >> 16;
|
hi = A >> 16;
|
||||||
lo = B >> 16;
|
lo = B >> 16;
|
||||||
if (IVsMatch(hi, lo, IVs))
|
if (IVsMatch(hi, lo, IVs))
|
||||||
{
|
{
|
||||||
pidiv = new PIDIV(CXDAnti, RNG.XDRNG.Prev(A));
|
pidiv = new PIDIV(CXDAnti, XDRNG.Prev(A));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
p2 = RNG.XDRNG.Prev(p1);
|
p2 = XDRNG.Prev(p1);
|
||||||
p1 = RNG.XDRNG.Prev(p2);
|
p1 = XDRNG.Prev(p2);
|
||||||
psv = (p2 ^ p1) >> 19;
|
psv = (p2 ^ p1) >> 19;
|
||||||
}
|
}
|
||||||
while (psv == tsv);
|
while (psv == tsv);
|
||||||
|
@ -285,50 +295,54 @@ public static class MethodFinder
|
||||||
return GetNonMatch(out pidiv);
|
return GetNonMatch(out pidiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetChannelMatch(uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv, PKM pk)
|
private static bool GetChannelMatch(Span<uint> seeds, uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv, PKM pk)
|
||||||
{
|
{
|
||||||
var ver = pk.Version;
|
var ver = pk.Version;
|
||||||
if (ver is not ((int)GameVersion.R or (int)GameVersion.S))
|
if (ver is not ((int)GameVersion.R or (int)GameVersion.S))
|
||||||
return GetNonMatch(out pidiv);
|
return GetNonMatch(out pidiv);
|
||||||
|
|
||||||
var undo = top ^ 0x8000;
|
var undo = (top >> 16) ^ 0x8000;
|
||||||
if ((undo > 7 ? 0 : 1) != (bot ^ pk.SID ^ 40122))
|
if ((undo > 7 ? 0 : 1) != ((bot >> 16) ^ pk.SID ^ 40122))
|
||||||
top = undo;
|
top = (undo << 16);
|
||||||
var channel = GetSeedsFromPIDEuclid(RNG.XDRNG, top, bot);
|
|
||||||
|
var count = XDRNG.GetSeeds(seeds, top, bot);
|
||||||
|
var channel = seeds[..count];
|
||||||
foreach (var seed in channel)
|
foreach (var seed in channel)
|
||||||
{
|
{
|
||||||
var C = RNG.XDRNG.Advance(seed, 3); // held item
|
var C = XDRNG.Next3(seed); // held item
|
||||||
// no checks, held item can be swapped
|
// no checks, held item can be swapped
|
||||||
|
|
||||||
var D = RNG.XDRNG.Next(C); // Version
|
var D = XDRNG.Next(C); // Version
|
||||||
if ((D >> 31) + 1 != ver) // (0-Sapphire, 1-Ruby)
|
if ((D >> 31) + 1 != ver) // (0-Sapphire, 1-Ruby)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var E = RNG.XDRNG.Next(D); // OT Gender
|
var E = XDRNG.Next(D); // OT Gender
|
||||||
if (E >> 31 != pk.OT_Gender)
|
if (E >> 31 != pk.OT_Gender)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!RNG.XDRNG.GetSequentialIVsUInt32(E, IVs))
|
if (!XDRNG.GetSequentialIVsUInt32(E, IVs))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (seed >> 16 != pk.SID)
|
if (seed >> 16 != pk.SID)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
pidiv = new PIDIV(Channel, RNG.XDRNG.Prev(seed));
|
pidiv = new PIDIV(Channel, XDRNG.Prev(seed));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return GetNonMatch(out pidiv);
|
return GetNonMatch(out pidiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetMG4Match(uint pid, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
private static bool GetMG4Match(Span<uint> seeds, uint pid, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
||||||
{
|
{
|
||||||
uint mg4Rev = RNG.ARNG.Prev(pid);
|
uint mg4Rev = ARNG.Prev(pid);
|
||||||
var mg4 = GetSeedsFromPID(RNG.LCRNG, mg4Rev >> 16, mg4Rev & 0xFFFF);
|
|
||||||
|
var count = LCRNGReversal.GetSeeds(seeds, mg4Rev << 16, mg4Rev & 0xFFFF0000);
|
||||||
|
var mg4 = seeds[..count];
|
||||||
foreach (var seed in mg4)
|
foreach (var seed in mg4)
|
||||||
{
|
{
|
||||||
var B = RNG.LCRNG.Advance(seed, 2);
|
var B = LCRNG.Next2(seed);
|
||||||
var C = RNG.LCRNG.Next(B);
|
var C = LCRNG.Next(B);
|
||||||
var D = RNG.LCRNG.Next(C);
|
var D = LCRNG.Next(C);
|
||||||
if (!IVsMatch(C >> 16, D >> 16, IVs))
|
if (!IVsMatch(C >> 16, D >> 16, IVs))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -361,10 +375,13 @@ public static class MethodFinder
|
||||||
return GetNonMatch(out pidiv);
|
return GetNonMatch(out pidiv);
|
||||||
|
|
||||||
(var species, int genderValue) = GetCuteCharmGenderSpecies(pk, pid, pk.Species);
|
(var species, int genderValue) = GetCuteCharmGenderSpecies(pk, pid, pk.Species);
|
||||||
if ((uint)species > Legal.MaxSpeciesID_4)
|
static int getRatio(ushort species)
|
||||||
return GetNonMatch(out pidiv);
|
{
|
||||||
|
return species <= Legal.MaxSpeciesID_4
|
||||||
|
? PersonalTable.HGSS[species].Gender
|
||||||
|
: PKX.Personal[species].Gender;
|
||||||
|
}
|
||||||
|
|
||||||
static int getRatio(ushort species) => PersonalTable.HGSS[species].Gender;
|
|
||||||
switch (genderValue)
|
switch (genderValue)
|
||||||
{
|
{
|
||||||
case 2: break; // can't cute charm a genderless pk
|
case 2: break; // can't cute charm a genderless pk
|
||||||
|
@ -391,15 +408,17 @@ public static class MethodFinder
|
||||||
return GetNonMatch(out pidiv);
|
return GetNonMatch(out pidiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetChainShinyMatch(PKM pk, uint pid, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
private static bool GetChainShinyMatch(Span<uint> seeds, PKM pk, uint pid, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
||||||
{
|
{
|
||||||
// 13 shiny bits
|
// 13 shiny bits
|
||||||
// PIDH & 7
|
// PIDH & 7
|
||||||
// PIDL & 7
|
// PIDL & 7
|
||||||
// IVs
|
// IVs
|
||||||
var bot = GetIVChunk(IVs, 0);
|
var bot = GetIVChunk(IVs, 0) << 16;
|
||||||
var top = GetIVChunk(IVs, 3);
|
var top = GetIVChunk(IVs, 3) << 16;
|
||||||
var reg = GetSeedsFromIVs(RNG.LCRNG, top, bot);
|
|
||||||
|
var count = LCRNGReversal.GetSeedsIVs(seeds, bot, top);
|
||||||
|
var reg = seeds[..count];
|
||||||
foreach (var seed in reg)
|
foreach (var seed in reg)
|
||||||
{
|
{
|
||||||
// check the individual bits
|
// check the individual bits
|
||||||
|
@ -410,7 +429,7 @@ public static class MethodFinder
|
||||||
var bit = s >> 16 & 1;
|
var bit = s >> 16 & 1;
|
||||||
if (bit != (pid >> i & 1))
|
if (bit != (pid >> i & 1))
|
||||||
break;
|
break;
|
||||||
s = RNG.LCRNG.Prev(s);
|
s = LCRNG.Prev(s);
|
||||||
}
|
}
|
||||||
while (--i != 2);
|
while (--i != 2);
|
||||||
if (i != 2) // bit failed
|
if (i != 2) // bit failed
|
||||||
|
@ -419,7 +438,7 @@ public static class MethodFinder
|
||||||
var upper = s;
|
var upper = s;
|
||||||
if ((upper >> 16 & 7) != (pid >> 16 & 7))
|
if ((upper >> 16 & 7) != (pid >> 16 & 7))
|
||||||
continue;
|
continue;
|
||||||
var lower = RNG.LCRNG.Prev(upper);
|
var lower = LCRNG.Prev(upper);
|
||||||
if ((lower >> 16 & 7) != (pid & 7))
|
if ((lower >> 16 & 7) != (pid & 7))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -427,34 +446,25 @@ public static class MethodFinder
|
||||||
if (upid != pid >> 16)
|
if (upid != pid >> 16)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
s = RNG.LCRNG.Reverse(lower, 2); // unroll one final time to get the origin seed
|
s = LCRNG.Prev2(lower); // unroll one final time to get the origin seed
|
||||||
pidiv = new PIDIV(ChainShiny, s);
|
pidiv = new PIDIV(ChainShiny, s);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return GetNonMatch(out pidiv);
|
return GetNonMatch(out pidiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<uint> GetCuteCharmSeeds(PKM pk)
|
private static bool GetBACDMatch(Span<uint> seeds, PKM pk, uint pid, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
||||||
{
|
{
|
||||||
Span<uint> IVs = stackalloc uint[6];
|
var bot = GetIVChunk(IVs, 0) << 16;
|
||||||
for (int i = 0; i < 6; i++)
|
var top = GetIVChunk(IVs, 3) << 16;
|
||||||
IVs[i] = (uint)pk.GetIV(i);
|
|
||||||
var bot = GetIVChunk(IVs, 0);
|
|
||||||
var top = GetIVChunk(IVs, 3);
|
|
||||||
|
|
||||||
return GetSeedsFromIVs(RNG.LCRNG, top, bot);
|
var count = LCRNGReversal.GetSeedsIVs(seeds, bot, top);
|
||||||
}
|
var reg = seeds[..count];
|
||||||
|
|
||||||
private static bool GetBACDMatch(PKM pk, uint pid, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
|
||||||
{
|
|
||||||
var bot = GetIVChunk(IVs, 0);
|
|
||||||
var top = GetIVChunk(IVs, 3);
|
|
||||||
var reg = GetSeedsFromIVs(RNG.LCRNG, top, bot);
|
|
||||||
PIDType type = BACD_U;
|
PIDType type = BACD_U;
|
||||||
foreach (var seed in reg)
|
foreach (var seed in reg)
|
||||||
{
|
{
|
||||||
var B = seed;
|
var B = seed;
|
||||||
var A = RNG.LCRNG.Prev(B);
|
var A = LCRNG.Prev(B);
|
||||||
var low = B >> 16;
|
var low = B >> 16;
|
||||||
|
|
||||||
var PID = (A & 0xFFFF0000) | low;
|
var PID = (A & 0xFFFF0000) | low;
|
||||||
|
@ -482,11 +492,11 @@ public static class MethodFinder
|
||||||
type = BACD_U_A;
|
type = BACD_U_A;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var s = RNG.LCRNG.Prev(A);
|
var s = LCRNG.Prev(A);
|
||||||
|
|
||||||
// Check for prior Restricted seed
|
// Check for prior Restricted seed
|
||||||
var sn = s;
|
var sn = s;
|
||||||
for (int i = 0; i < 3; i++, sn = RNG.LCRNG.Prev(sn))
|
for (int i = 0; i < 3; i++, sn = LCRNG.Prev(sn))
|
||||||
{
|
{
|
||||||
if ((sn & 0xFFFF0000) != 0)
|
if ((sn & 0xFFFF0000) != 0)
|
||||||
continue;
|
continue;
|
||||||
|
@ -540,7 +550,7 @@ public static class MethodFinder
|
||||||
return pid == oldpid;
|
return pid == oldpid;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetColoStarterMatch(PKM pk, uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
private static bool GetColoStarterMatch(Span<uint> seeds, PKM pk, uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
||||||
{
|
{
|
||||||
bool starter = pk.Version == (int)GameVersion.CXD && pk.Species switch
|
bool starter = pk.Version == (int)GameVersion.CXD && pk.Species switch
|
||||||
{
|
{
|
||||||
|
@ -553,7 +563,9 @@ public static class MethodFinder
|
||||||
|
|
||||||
var iv1 = GetIVChunk(IVs, 0);
|
var iv1 = GetIVChunk(IVs, 0);
|
||||||
var iv2 = GetIVChunk(IVs, 3);
|
var iv2 = GetIVChunk(IVs, 3);
|
||||||
var xdc = GetSeedsFromPIDEuclid(RNG.XDRNG, top, bot);
|
|
||||||
|
var count = XDRNG.GetSeeds(seeds, top, bot);
|
||||||
|
var xdc = seeds[..count];
|
||||||
foreach (var seed in xdc)
|
foreach (var seed in xdc)
|
||||||
{
|
{
|
||||||
uint origin = seed;
|
uint origin = seed;
|
||||||
|
@ -597,7 +609,7 @@ public static class MethodFinder
|
||||||
// 3-FORCEBITS
|
// 3-FORCEBITS
|
||||||
// PID = PIDH << 16 | (SID ^ TID ^ PIDH)
|
// PID = PIDH << 16 | (SID ^ TID ^ PIDH)
|
||||||
|
|
||||||
var X = RNG.LCRNG.Prev(A); // unroll once as there's 3 calls instead of 2
|
var X = LCRNG.Prev(A); // unroll once as there's 3 calls instead of 2
|
||||||
uint PID = (X & 0xFFFF0000) | (idxor ^ X >> 16);
|
uint PID = (X & 0xFFFF0000) | (idxor ^ X >> 16);
|
||||||
PID &= 0xFFFFFFF8;
|
PID &= 0xFFFFFFF8;
|
||||||
PID |= low & 0x7; // lowest 3 bits
|
PID |= low & 0x7; // lowest 3 bits
|
||||||
|
@ -644,75 +656,6 @@ public static class MethodFinder
|
||||||
return PIDIV.None;
|
return PIDIV.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<uint> GetSeedsFromPID(RNG method, uint a, uint b)
|
|
||||||
{
|
|
||||||
Debug.Assert(a >> 16 == 0);
|
|
||||||
Debug.Assert(b >> 16 == 0);
|
|
||||||
uint second = a << 16;
|
|
||||||
uint first = b << 16;
|
|
||||||
return method.RecoverLower16Bits(first, second);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<uint> GetSeedsFromPIDSkip(RNG method, uint a, uint b)
|
|
||||||
{
|
|
||||||
Debug.Assert(a >> 16 == 0);
|
|
||||||
Debug.Assert(b >> 16 == 0);
|
|
||||||
uint third = a << 16;
|
|
||||||
uint first = b << 16;
|
|
||||||
return method.RecoverLower16BitsGap(first, third);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<uint> GetSeedsFromIVs(RNG method, uint a, uint b)
|
|
||||||
{
|
|
||||||
Debug.Assert(a >> 15 == 0);
|
|
||||||
Debug.Assert(b >> 15 == 0);
|
|
||||||
uint second = a << 16;
|
|
||||||
uint first = b << 16;
|
|
||||||
|
|
||||||
var attempt1 = method.RecoverLower16Bits(first, second);
|
|
||||||
foreach (var z in attempt1)
|
|
||||||
{
|
|
||||||
yield return z;
|
|
||||||
yield return z ^ 0x80000000; // sister bitflip
|
|
||||||
}
|
|
||||||
var attempt2 = method.RecoverLower16Bits(first, second ^ 0x80000000);
|
|
||||||
foreach (var z in attempt2)
|
|
||||||
{
|
|
||||||
yield return z;
|
|
||||||
yield return z ^ 0x80000000; // sister bitflip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<uint> GetSeedsFromIVsSkip(RNG method, uint rand1, uint rand3)
|
|
||||||
{
|
|
||||||
Debug.Assert(rand1 >> 15 == 0);
|
|
||||||
Debug.Assert(rand3 >> 15 == 0);
|
|
||||||
rand1 <<= 16;
|
|
||||||
rand3 <<= 16;
|
|
||||||
var attempt1 = method.RecoverLower16BitsGap(rand1, rand3);
|
|
||||||
foreach (var z in attempt1)
|
|
||||||
{
|
|
||||||
yield return z;
|
|
||||||
yield return z ^ 0x80000000; // sister bitflip
|
|
||||||
}
|
|
||||||
var attempt2 = method.RecoverLower16BitsGap(rand1, rand3 ^ 0x80000000);
|
|
||||||
foreach (var z in attempt2)
|
|
||||||
{
|
|
||||||
yield return z;
|
|
||||||
yield return z ^ 0x80000000; // sister bitflip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<uint> GetSeedsFromPIDEuclid(RNG method, uint rand1, uint rand2)
|
|
||||||
{
|
|
||||||
return method.RecoverLower16BitsEuclid16(rand1 << 16, rand2 << 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<uint> GetSeedsFromIVsEuclid(RNG method, uint rand1, uint rand2)
|
|
||||||
{
|
|
||||||
return method.RecoverLower16BitsEuclid15(rand1 << 16, rand2 << 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates IVs from 2 RNG calls using 15 bits of each to generate 6 IVs (5bits each).
|
/// Generates IVs from 2 RNG calls using 15 bits of each to generate 6 IVs (5bits each).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -762,41 +705,7 @@ public static class MethodFinder
|
||||||
val |= IVs[i+start] << (5*i);
|
val |= IVs[i+start] << (5*i);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<PIDIV> GetColoEReaderMatches(uint PID)
|
|
||||||
{
|
|
||||||
var top = PID >> 16;
|
|
||||||
var bot = (ushort)PID;
|
|
||||||
var xdc = GetSeedsFromPIDEuclid(RNG.XDRNG, top, bot);
|
|
||||||
foreach (var seed in xdc)
|
|
||||||
{
|
|
||||||
var B = RNG.XDRNG.Prev(seed);
|
|
||||||
var A = RNG.XDRNG.Prev(B);
|
|
||||||
|
|
||||||
var C = RNG.XDRNG.Advance(A, 7);
|
|
||||||
|
|
||||||
yield return new PIDIV(CXD, RNG.XDRNG.Prev(C));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<PIDIV> GetPokeSpotSeeds(PKM pk, int slot)
|
|
||||||
{
|
|
||||||
// Activate (rand % 3)
|
|
||||||
// Munchlax / Bonsly (10%/30%)
|
|
||||||
// Encounter Slot Value (ESV) = 50%/35%/15% rarity (0-49, 50-84, 85-99)
|
|
||||||
var pid = pk.PID;
|
|
||||||
var top = pid >> 16;
|
|
||||||
var bot = pid & 0xFFFF;
|
|
||||||
var seeds = GetSeedsFromPIDEuclid(RNG.XDRNG, top, bot);
|
|
||||||
foreach (var seed in seeds)
|
|
||||||
{
|
|
||||||
// check for valid encounter slot info
|
|
||||||
if (!IsPokeSpotActivation(slot, seed, out uint s))
|
|
||||||
continue;
|
|
||||||
yield return new PIDIV(PokeSpot, s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsPokeSpotActivation(int slot, uint seed, out uint s)
|
public static bool IsPokeSpotActivation(int slot, uint seed, out uint s)
|
||||||
{
|
{
|
||||||
s = seed;
|
s = seed;
|
||||||
|
@ -806,14 +715,14 @@ public static class MethodFinder
|
||||||
// todo
|
// todo
|
||||||
}
|
}
|
||||||
// check for valid activation
|
// check for valid activation
|
||||||
s = RNG.XDRNG.Prev(seed);
|
s = XDRNG.Prev(seed);
|
||||||
if ((s >> 16) % 3 != 0)
|
if ((s >> 16) % 3 != 0)
|
||||||
{
|
{
|
||||||
if ((s >> 16) % 100 < 10) // can't fail a munchlax/bonsly encounter check
|
if ((s >> 16) % 100 < 10) // can't fail a munchlax/bonsly encounter check
|
||||||
{
|
{
|
||||||
// todo
|
// todo
|
||||||
}
|
}
|
||||||
s = RNG.XDRNG.Prev(s);
|
s = XDRNG.Prev(s);
|
||||||
if ((s >> 16) % 3 != 0) // can't activate even if generous
|
if ((s >> 16) % 3 != 0) // can't activate even if generous
|
||||||
{
|
{
|
||||||
// todo
|
// todo
|
||||||
|
@ -913,7 +822,7 @@ public static class MethodFinder
|
||||||
|
|
||||||
bool IsAntiShinyARNG()
|
bool IsAntiShinyARNG()
|
||||||
{
|
{
|
||||||
var shinyPID = RNG.ARNG.Prev(pk.PID);
|
var shinyPID = ARNG.Prev(pk.PID);
|
||||||
return (pk.TID ^ pk.SID ^ (shinyPID & 0xFFFF) ^ (shinyPID >> 16)) < 8; // shiny proc
|
return (pk.TID ^ pk.SID ^ (shinyPID & 0xFFFF) ^ (shinyPID >> 16)) < 8; // shiny proc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -954,7 +863,27 @@ public static class MethodFinder
|
||||||
|
|
||||||
// Future evolutions
|
// Future evolutions
|
||||||
(int)Species.Sylveon => ((int)Species.Eevee, pk.Gender),
|
(int)Species.Sylveon => ((int)Species.Eevee, pk.Gender),
|
||||||
|
(int)Species.MrRime => ((int)Species.MimeJr, pk.Gender),
|
||||||
|
(int)Species.Kleavor => ((int)Species.Scyther, pk.Gender),
|
||||||
|
|
||||||
_ => (currentSpecies, pk.Gender),
|
_ => (currentSpecies, pk.Gender),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static PIDIV GetPokeSpotSeedFirst(PKM pk, byte slot)
|
||||||
|
{
|
||||||
|
// Activate (rand % 3)
|
||||||
|
// Munchlax / Bonsly (10%/30%)
|
||||||
|
// Encounter Slot Value (ESV) = 50%/35%/15% rarity (0-49, 50-84, 85-99)
|
||||||
|
|
||||||
|
Span<uint> seeds = stackalloc uint[XDRNG.MaxCountSeedsPID];
|
||||||
|
int count = XDRNG.GetSeeds(seeds, pk.EncryptionConstant);
|
||||||
|
var reg = seeds[..count];
|
||||||
|
foreach (var seed in reg)
|
||||||
|
{
|
||||||
|
// check for valid encounter slot info
|
||||||
|
if (IsPokeSpotActivation(slot, seed, out uint s))
|
||||||
|
return new PIDIV(PokeSpot, s);
|
||||||
|
}
|
||||||
|
return default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace PKHeX.Core;
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
|
@ -9,12 +9,11 @@ public static class PIDGenerator
|
||||||
{
|
{
|
||||||
private static void SetValuesFromSeedLCRNG(PKM pk, PIDType type, uint seed)
|
private static void SetValuesFromSeedLCRNG(PKM pk, PIDType type, uint seed)
|
||||||
{
|
{
|
||||||
var rng = RNG.LCRNG;
|
var A = LCRNG.Next(seed);
|
||||||
var A = rng.Next(seed);
|
var B = LCRNG.Next(A);
|
||||||
var B = rng.Next(A);
|
|
||||||
var skipBetweenPID = type is PIDType.Method_3 or PIDType.Method_3_Unown;
|
var skipBetweenPID = type is PIDType.Method_3 or PIDType.Method_3_Unown;
|
||||||
if (skipBetweenPID) // VBlank skip between PID rand() [RARE]
|
if (skipBetweenPID) // VBlank skip between PID rand() [RARE]
|
||||||
B = rng.Next(B);
|
B = LCRNG.Next(B);
|
||||||
|
|
||||||
var swappedPIDHalves = type is >= PIDType.Method_1_Unown and <= PIDType.Method_4_Unown;
|
var swappedPIDHalves = type is >= PIDType.Method_1_Unown and <= PIDType.Method_4_Unown;
|
||||||
if (swappedPIDHalves) // switched order of PID halves, "BA.."
|
if (swappedPIDHalves) // switched order of PID halves, "BA.."
|
||||||
|
@ -22,15 +21,15 @@ public static class PIDGenerator
|
||||||
else
|
else
|
||||||
pk.PID = (B & 0xFFFF0000) | (A >> 16);
|
pk.PID = (B & 0xFFFF0000) | (A >> 16);
|
||||||
|
|
||||||
var C = rng.Next(B);
|
var C = LCRNG.Next(B);
|
||||||
var skipIV1Frame = type is PIDType.Method_2 or PIDType.Method_2_Unown;
|
var skipIV1Frame = type is PIDType.Method_2 or PIDType.Method_2_Unown;
|
||||||
if (skipIV1Frame) // VBlank skip after PID
|
if (skipIV1Frame) // VBlank skip after PID
|
||||||
C = rng.Next(C);
|
C = LCRNG.Next(C);
|
||||||
|
|
||||||
var D = rng.Next(C);
|
var D = LCRNG.Next(C);
|
||||||
var skipIV2Frame = type is PIDType.Method_4 or PIDType.Method_4_Unown;
|
var skipIV2Frame = type is PIDType.Method_4 or PIDType.Method_4_Unown;
|
||||||
if (skipIV2Frame) // VBlank skip between IVs
|
if (skipIV2Frame) // VBlank skip between IVs
|
||||||
D = rng.Next(D);
|
D = LCRNG.Next(D);
|
||||||
|
|
||||||
Span<int> IVs = stackalloc int[6];
|
Span<int> IVs = stackalloc int[6];
|
||||||
MethodFinder.GetIVsInt32(IVs, C >> 16, D >> 16);
|
MethodFinder.GetIVsInt32(IVs, C >> 16, D >> 16);
|
||||||
|
@ -46,13 +45,12 @@ public static class PIDGenerator
|
||||||
|
|
||||||
private static void SetValuesFromSeedBACD(PKM pk, PIDType type, uint seed)
|
private static void SetValuesFromSeedBACD(PKM pk, PIDType type, uint seed)
|
||||||
{
|
{
|
||||||
var rng = RNG.LCRNG;
|
|
||||||
bool shiny = type is PIDType.BACD_R_S or PIDType.BACD_U_S;
|
bool shiny = type is PIDType.BACD_R_S or PIDType.BACD_U_S;
|
||||||
uint X = shiny ? rng.Next(seed) : seed;
|
uint X = shiny ? LCRNG.Next(seed) : seed;
|
||||||
var A = rng.Next(X);
|
var A = LCRNG.Next(X);
|
||||||
var B = rng.Next(A);
|
var B = LCRNG.Next(A);
|
||||||
var C = rng.Next(B);
|
var C = LCRNG.Next(B);
|
||||||
var D = rng.Next(C);
|
var D = LCRNG.Next(C);
|
||||||
|
|
||||||
if (shiny)
|
if (shiny)
|
||||||
{
|
{
|
||||||
|
@ -83,25 +81,24 @@ public static class PIDGenerator
|
||||||
|
|
||||||
private static void SetValuesFromSeedXDRNG(PKM pk, uint seed)
|
private static void SetValuesFromSeedXDRNG(PKM pk, uint seed)
|
||||||
{
|
{
|
||||||
var rng = RNG.XDRNG;
|
|
||||||
switch (pk.Species)
|
switch (pk.Species)
|
||||||
{
|
{
|
||||||
case (int)Species.Umbreon or (int)Species.Eevee: // Colo Umbreon, XD Eevee
|
case (int)Species.Umbreon or (int)Species.Eevee: // Colo Umbreon, XD Eevee
|
||||||
pk.TID = (int)((seed = rng.Next(seed)) >> 16);
|
pk.TID = (int)((seed = XDRNG.Next(seed)) >> 16);
|
||||||
pk.SID = (int)((seed = rng.Next(seed)) >> 16);
|
pk.SID = (int)((seed = XDRNG.Next(seed)) >> 16);
|
||||||
seed = rng.Advance(seed, 2); // PID calls consumed
|
seed = XDRNG.Next2(seed); // PID calls consumed
|
||||||
break;
|
break;
|
||||||
case (int)Species.Espeon: // Colo Espeon
|
case (int)Species.Espeon: // Colo Espeon
|
||||||
pk.TID = (int)((seed = rng.Next(seed)) >> 16);
|
pk.TID = (int)((seed = XDRNG.Next(seed)) >> 16);
|
||||||
pk.SID = (int)((seed = rng.Next(seed)) >> 16);
|
pk.SID = (int)((seed = XDRNG.Next(seed)) >> 16);
|
||||||
seed = rng.Advance(seed, 9); // PID calls consumed, skip over Umbreon
|
seed = XDRNG.Next9(seed); // PID calls consumed, skip over Umbreon
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
var A = rng.Next(seed); // IV1
|
var A = XDRNG.Next(seed); // IV1
|
||||||
var B = rng.Next(A); // IV2
|
var B = XDRNG.Next(A); // IV2
|
||||||
var C = rng.Next(B); // Ability?
|
var C = XDRNG.Next(B); // Ability?
|
||||||
var D = rng.Next(C); // PID
|
var D = XDRNG.Next(C); // PID
|
||||||
var E = rng.Next(D); // PID
|
var E = XDRNG.Next(D); // PID
|
||||||
|
|
||||||
pk.PID = (D & 0xFFFF0000) | (E >> 16);
|
pk.PID = (D & 0xFFFF0000) | (E >> 16);
|
||||||
Span<int> IVs = stackalloc int[6];
|
Span<int> IVs = stackalloc int[6];
|
||||||
|
@ -111,23 +108,20 @@ public static class PIDGenerator
|
||||||
|
|
||||||
public static void SetValuesFromSeedXDRNG_EReader(PKM pk, uint seed)
|
public static void SetValuesFromSeedXDRNG_EReader(PKM pk, uint seed)
|
||||||
{
|
{
|
||||||
var rng = RNG.XDRNG;
|
var D = XDRNG.Prev3(seed); // PID
|
||||||
var A = rng.Reverse(seed, 4);
|
var E = XDRNG.Next(D); // PID
|
||||||
var D = rng.Next(A); // PID
|
|
||||||
var E = rng.Next(D); // PID
|
|
||||||
|
|
||||||
pk.PID = (D & 0xFFFF0000) | (E >> 16);
|
pk.PID = (D & 0xFFFF0000) | (E >> 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetValuesFromSeedChannel(PKM pk, uint seed)
|
private static void SetValuesFromSeedChannel(PKM pk, uint seed)
|
||||||
{
|
{
|
||||||
var rng = RNG.XDRNG;
|
var O = XDRNG.Next(seed); // SID
|
||||||
var O = rng.Next(seed); // SID
|
var A = XDRNG.Next(O); // PID
|
||||||
var A = rng.Next(O); // PID
|
var B = XDRNG.Next(A); // PID
|
||||||
var B = rng.Next(A); // PID
|
var C = XDRNG.Next(B); // Held Item
|
||||||
var C = rng.Next(B); // Held Item
|
var D = XDRNG.Next(C); // Version
|
||||||
var D = rng.Next(C); // Version
|
var E = XDRNG.Next(D); // OT Gender
|
||||||
var E = rng.Next(D); // OT Gender
|
|
||||||
|
|
||||||
const int TID = 40122;
|
const int TID = 40122;
|
||||||
var SID = (int)(O >> 16);
|
var SID = (int)(O >> 16);
|
||||||
|
@ -143,7 +137,7 @@ public static class PIDGenerator
|
||||||
pk.Version = (int)(D >> 31) + 1; // 0-Sapphire, 1-Ruby
|
pk.Version = (int)(D >> 31) + 1; // 0-Sapphire, 1-Ruby
|
||||||
pk.OT_Gender = (int)(E >> 31);
|
pk.OT_Gender = (int)(E >> 31);
|
||||||
Span<int> ivs = stackalloc int[6];
|
Span<int> ivs = stackalloc int[6];
|
||||||
rng.GetSequentialIVsInt32(E, ivs);
|
XDRNG.GetSequentialIVsInt32(E, ivs);
|
||||||
pk.SetIVs(ivs);
|
pk.SetIVs(ivs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +198,7 @@ public static class PIDGenerator
|
||||||
// 1 3-bit for upper
|
// 1 3-bit for upper
|
||||||
// 1 3-bit for lower
|
// 1 3-bit for lower
|
||||||
|
|
||||||
uint Next() => (seed = RNG.LCRNG.Next(seed)) >> 16;
|
uint Next() => (seed = LCRNG.Next(seed)) >> 16;
|
||||||
uint lower = Next() & 7;
|
uint lower = Next() & 7;
|
||||||
uint upper = Next() & 7;
|
uint upper = Next() & 7;
|
||||||
for (int i = 0; i < 13; i++)
|
for (int i = 0; i < 13; i++)
|
||||||
|
@ -226,9 +220,8 @@ public static class PIDGenerator
|
||||||
if (!MethodFinder.IsPokeSpotActivation(slot, seed, out _))
|
if (!MethodFinder.IsPokeSpotActivation(slot, seed, out _))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var rng = RNG.XDRNG;
|
var D = XDRNG.Next(seed); // PID
|
||||||
var D = rng.Next(seed); // PID
|
var E = XDRNG.Next(D); // PID
|
||||||
var E = rng.Next(D); // PID
|
|
||||||
|
|
||||||
pk.PID = (D & 0xFFFF0000) | (E >> 16);
|
pk.PID = (D & 0xFFFF0000) | (E >> 16);
|
||||||
if (!IsValidCriteria4(pk, nature, ability, gender))
|
if (!IsValidCriteria4(pk, nature, ability, gender))
|
||||||
|
|
|
@ -12,99 +12,99 @@ public enum PIDType
|
||||||
#region LCRNG
|
#region LCRNG
|
||||||
|
|
||||||
/// <summary> Method 1 Variants (H1/J/K) </summary>
|
/// <summary> Method 1 Variants (H1/J/K) </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
/// <remarks><see cref="LCRNG"/></remarks>
|
||||||
Method_1,
|
Method_1,
|
||||||
|
|
||||||
/// <summary> Method H2 </summary>
|
/// <summary> Method H2 </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
/// <remarks><see cref="LCRNG"/></remarks>
|
||||||
Method_2,
|
Method_2,
|
||||||
|
|
||||||
/// <summary> Method H3 </summary>
|
/// <summary> Method H3 </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
/// <remarks><see cref="LCRNG"/></remarks>
|
||||||
Method_3,
|
Method_3,
|
||||||
|
|
||||||
/// <summary> Method H4 </summary>
|
/// <summary> Method H4 </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
/// <remarks><see cref="LCRNG"/></remarks>
|
||||||
Method_4,
|
Method_4,
|
||||||
|
|
||||||
/// <summary> Method H1_Unown (FRLG) </summary>
|
/// <summary> Method H1_Unown (FRLG) </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
/// <remarks><see cref="LCRNG"/></remarks>
|
||||||
Method_1_Unown,
|
Method_1_Unown,
|
||||||
|
|
||||||
/// <summary> Method H2_Unown (FRLG) </summary>
|
/// <summary> Method H2_Unown (FRLG) </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
/// <remarks><see cref="LCRNG"/></remarks>
|
||||||
Method_2_Unown,
|
Method_2_Unown,
|
||||||
|
|
||||||
/// <summary> Method H3_Unown (FRLG) </summary>
|
/// <summary> Method H3_Unown (FRLG) </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
/// <remarks><see cref="LCRNG"/></remarks>
|
||||||
Method_3_Unown,
|
Method_3_Unown,
|
||||||
|
|
||||||
/// <summary> Method H4_Unown (FRLG) </summary>
|
/// <summary> Method H4_Unown (FRLG) </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
/// <remarks><see cref="LCRNG"/></remarks>
|
||||||
Method_4_Unown,
|
Method_4_Unown,
|
||||||
|
|
||||||
/// <summary> Method 1 Roamer (Gen3) </summary>
|
/// <summary> Method 1 Roamer (Gen3) </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
/// <remarks><see cref="LCRNG"/></remarks>
|
||||||
Method_1_Roamer,
|
Method_1_Roamer,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event Reversed Order PID restricted to 16bit Origin Seed
|
/// Event Reversed Order PID restricted to 16bit Origin Seed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/> seed is clamped to 16bits.</remarks>
|
/// <remarks><see cref="LCRNG"/> seed is clamped to 16bits.</remarks>
|
||||||
BACD_R,
|
BACD_R,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event Reversed Order PID without Origin Seed restrictions
|
/// Event Reversed Order PID without Origin Seed restrictions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
/// <remarks><see cref="LCRNG"/></remarks>
|
||||||
BACD_U,
|
BACD_U,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event Reversed Order PID restricted to 16bit Origin Seed, antishiny.
|
/// Event Reversed Order PID restricted to 16bit Origin Seed, antishiny.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/> seed is clamped to 16bits.</remarks>
|
/// <remarks><see cref="LCRNG"/> seed is clamped to 16bits.</remarks>
|
||||||
BACD_R_A,
|
BACD_R_A,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event Reversed Order PID without Origin Seed restrictions, antishiny.
|
/// Event Reversed Order PID without Origin Seed restrictions, antishiny.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
/// <remarks><see cref="LCRNG"/></remarks>
|
||||||
BACD_U_A,
|
BACD_U_A,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event Reversed Order PID restricted to 8bit Origin Seed, shiny
|
/// Event Reversed Order PID restricted to 8bit Origin Seed, shiny
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/> seed is clamped to 16bits.</remarks>
|
/// <remarks><see cref="LCRNG"/> seed is clamped to 16bits.</remarks>
|
||||||
BACD_R_S,
|
BACD_R_S,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event Reversed Order PID without Origin Seed restrictions, shiny
|
/// Event Reversed Order PID without Origin Seed restrictions, shiny
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
/// <remarks><see cref="LCRNG"/></remarks>
|
||||||
BACD_U_S,
|
BACD_U_S,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event Reversed Order PID restricted to 16bit Origin Seed, antishiny (nyx)
|
/// Event Reversed Order PID restricted to 16bit Origin Seed, antishiny (nyx)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/> seed is clamped to 16bits.</remarks>
|
/// <remarks><see cref="LCRNG"/> seed is clamped to 16bits.</remarks>
|
||||||
BACD_R_AX,
|
BACD_R_AX,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event Reversed Order PID without Origin Seed restrictions, antishiny (nyx)
|
/// Event Reversed Order PID without Origin Seed restrictions, antishiny (nyx)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
/// <remarks><see cref="LCRNG"/></remarks>
|
||||||
BACD_U_AX,
|
BACD_U_AX,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generation 4 Cute Charm PID, which is forced to an 8 bit PID value based on the gender & gender ratio value.
|
/// Generation 4 Cute Charm PID, which is forced to an 8 bit PID value based on the gender & gender ratio value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
/// <remarks><see cref="LCRNG"/></remarks>
|
||||||
CuteCharm,
|
CuteCharm,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generation 4 Chained Shiny
|
/// Generation 4 Chained Shiny
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
/// <remarks><see cref="LCRNG"/></remarks>
|
||||||
ChainShiny,
|
ChainShiny,
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -114,31 +114,31 @@ public enum PIDType
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generation 3 <see cref="GameVersion.CXD"/> PID+IV correlation.
|
/// Generation 3 <see cref="GameVersion.CXD"/> PID+IV correlation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks><see cref="RNG.XDRNG"/></remarks>
|
/// <remarks><see cref="XDRNG"/></remarks>
|
||||||
CXD,
|
CXD,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generation 3 <see cref="GameVersion.CXD"/> PID+IV correlation that was rerolled because it was shiny.
|
/// Generation 3 <see cref="GameVersion.CXD"/> PID+IV correlation that was rerolled because it was shiny.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks><see cref="RNG.XDRNG"/></remarks>
|
/// <remarks><see cref="XDRNG"/></remarks>
|
||||||
CXDAnti,
|
CXDAnti,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generation 3 <see cref="GameVersion.CXD"/> PID+IV which is created immediately after the TID and SID RNG calls.
|
/// Generation 3 <see cref="GameVersion.CXD"/> PID+IV which is created immediately after the TID and SID RNG calls.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks><see cref="RNG.XDRNG"/>. The second starter is created after the first starter, with the same TID and SID.</remarks>
|
/// <remarks><see cref="XDRNG"/>. The second starter is created after the first starter, with the same TID and SID.</remarks>
|
||||||
CXD_ColoStarter,
|
CXD_ColoStarter,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generation 3 Pokémon Channel Jirachi
|
/// Generation 3 Pokémon Channel Jirachi
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks><see cref="RNG.XDRNG"/></remarks>
|
/// <remarks><see cref="XDRNG"/></remarks>
|
||||||
Channel,
|
Channel,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generation 3 <see cref="GameVersion.CXD"/> PokeSpot PID
|
/// Generation 3 <see cref="GameVersion.CXD"/> PokeSpot PID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks><see cref="RNG.XDRNG"/></remarks>
|
/// <remarks><see cref="XDRNG"/></remarks>
|
||||||
PokeSpot,
|
PokeSpot,
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -148,7 +148,7 @@ public enum PIDType
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generation 4 Mystery Gift Anti-Shiny
|
/// Generation 4 Mystery Gift Anti-Shiny
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks><see cref="RNG.ARNG"/></remarks>
|
/// <remarks><see cref="ARNG"/></remarks>
|
||||||
G4MGAntiShiny,
|
G4MGAntiShiny,
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace PKHeX.Core;
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ public static class MystryMew
|
||||||
0xFE9D,
|
0xFE9D,
|
||||||
};
|
};
|
||||||
|
|
||||||
private const int FramesPerMew = 5;
|
//private const int FramesPerMew = 5;
|
||||||
private const int MewPerRestrictedSeed = 5;
|
private const int MewPerRestrictedSeed = 5;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -48,7 +48,7 @@ public static class MystryMew
|
||||||
|
|
||||||
uint position = (random % (MewPerRestrictedSeed - 1)) + 1;
|
uint position = (random % (MewPerRestrictedSeed - 1)) + 1;
|
||||||
for (int i = 0; i < position; i++)
|
for (int i = 0; i < position; i++)
|
||||||
seed = RNG.LCRNG.Advance(seed, FramesPerMew);
|
seed = LCRNG.Next5(seed);
|
||||||
|
|
||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ public static class MystryMew
|
||||||
{
|
{
|
||||||
if (seed <= ushort.MaxValue)
|
if (seed <= ushort.MaxValue)
|
||||||
return Array.BinarySearch(Seeds, (ushort)seed);
|
return Array.BinarySearch(Seeds, (ushort)seed);
|
||||||
seed = RNG.LCRNG.Reverse(seed, FramesPerMew);
|
seed = LCRNG.Prev5(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -208,8 +208,8 @@ public sealed class PGT : DataMysteryGift, IRibbonSetEvent3, IRibbonSetEvent4
|
||||||
// Generate IVs
|
// Generate IVs
|
||||||
if ((pk4.IV32 & 0x3FFF_FFFFu) == 0) // Ignore Nickname/Egg flag bits
|
if ((pk4.IV32 & 0x3FFF_FFFFu) == 0) // Ignore Nickname/Egg flag bits
|
||||||
{
|
{
|
||||||
uint iv1 = ((seed = RNG.LCRNG.Next(seed)) >> 16) & 0x7FFF;
|
uint iv1 = ((seed = LCRNG.Next(seed)) >> 16) & 0x7FFF;
|
||||||
uint iv2 = ((RNG.LCRNG.Next(seed)) >> 16) & 0x7FFF;
|
uint iv2 = ((LCRNG.Next(seed)) >> 16) & 0x7FFF;
|
||||||
pk4.IV32 |= iv1 | (iv2 << 15);
|
pk4.IV32 |= iv1 | (iv2 << 15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,14 +252,14 @@ public sealed class PGT : DataMysteryGift, IRibbonSetEvent3, IRibbonSetEvent4
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
uint pid1 = (seed = RNG.LCRNG.Next(seed)) >> 16; // low
|
uint pid1 = (seed = LCRNG.Next(seed)) >> 16; // low
|
||||||
uint pid2 = (seed = RNG.LCRNG.Next(seed)) & 0xFFFF0000; // hi
|
uint pid2 = (seed = LCRNG.Next(seed)) & 0xFFFF0000; // hi
|
||||||
pk4.PID = pid2 | pid1;
|
pk4.PID = pid2 | pid1;
|
||||||
// sanity check gender for non-genderless PID cases
|
// sanity check gender for non-genderless PID cases
|
||||||
} while (!pk4.IsGenderValid());
|
} while (!pk4.IsGenderValid());
|
||||||
|
|
||||||
while (pk4.IsShiny) // Call the ARNG to change the PID
|
while (pk4.IsShiny) // Call the ARNG to change the PID
|
||||||
pk4.PID = RNG.ARNG.Next(pk4.PID);
|
pk4.PID = ARNG.Next(pk4.PID);
|
||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using PKHeX.Core;
|
using PKHeX.Core;
|
||||||
|
@ -153,13 +153,17 @@ public static class PIDTests
|
||||||
public static void VerifyResults(IReadOnlyList<uint[]> results, TeamLock[] team)
|
public static void VerifyResults(IReadOnlyList<uint[]> results, TeamLock[] team)
|
||||||
{
|
{
|
||||||
var pk = new PK3();
|
var pk = new PK3();
|
||||||
|
Span<uint> seeds = stackalloc uint[XDRNG.MaxCountSeedsPID];
|
||||||
for (int i = 0; i < results.Count; i++)
|
for (int i = 0; i < results.Count; i++)
|
||||||
{
|
{
|
||||||
var result = results[i];
|
var result = results[i];
|
||||||
var seeds = getSeeds(result[^1]);
|
var pid = result[^1];
|
||||||
|
int count = XDRNG.GetSeeds(seeds, pid);
|
||||||
|
var reg = seeds[..count];
|
||||||
bool match = false;
|
bool match = false;
|
||||||
foreach (var seed in seeds)
|
foreach (var s in reg)
|
||||||
{
|
{
|
||||||
|
var seed = XDRNG.Prev3(s);
|
||||||
PIDGenerator.SetValuesFromSeed(pk, PIDType.CXD, seed);
|
PIDGenerator.SetValuesFromSeed(pk, PIDType.CXD, seed);
|
||||||
var info = MethodFinder.Analyze(pk);
|
var info = MethodFinder.Analyze(pk);
|
||||||
info.OriginSeed.Should().Be(seed);
|
info.OriginSeed.Should().Be(seed);
|
||||||
|
@ -171,16 +175,6 @@ public static class PIDTests
|
||||||
}
|
}
|
||||||
match.Should().BeTrue($"because the lock conditions for result {i} and species {team[0].Species} should have been verified");
|
match.Should().BeTrue($"because the lock conditions for result {i} and species {team[0].Species} should have been verified");
|
||||||
}
|
}
|
||||||
|
|
||||||
static IEnumerable<uint> getSeeds(uint PID)
|
|
||||||
{
|
|
||||||
var top = PID >> 16;
|
|
||||||
var bot = PID & 0xFFFF;
|
|
||||||
|
|
||||||
var seeds = MethodFinder.GetSeedsFromPIDEuclid(RNG.XDRNG, top, bot);
|
|
||||||
foreach (var s in seeds)
|
|
||||||
yield return RNG.XDRNG.Reverse(s, 3);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly uint[] Mawile =
|
public static readonly uint[] Mawile =
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using PKHeX.Core;
|
using PKHeX.Core;
|
||||||
|
@ -145,11 +146,11 @@ public class PIDIVTest
|
||||||
{
|
{
|
||||||
// XD PokeSpots: Check all 3 Encounter Slots (examples are one for each location).
|
// XD PokeSpots: Check all 3 Encounter Slots (examples are one for each location).
|
||||||
var pkPS0 = new PK3 { PID = 0x7B2D9DA7 }; // Zubat (Cave)
|
var pkPS0 = new PK3 { PID = 0x7B2D9DA7 }; // Zubat (Cave)
|
||||||
Assert.True(MethodFinder.GetPokeSpotSeeds(pkPS0, 0).Any(), "PokeSpot encounter info mismatch (Common)");
|
Assert.True(MethodFinder.GetPokeSpotSeedFirst(pkPS0, 0).Type == PIDType.PokeSpot, "PokeSpot encounter info mismatch (Common)");
|
||||||
var pkPS1 = new PK3 { PID = 0x3EE9AF66 }; // Gligar (Rock)
|
var pkPS1 = new PK3 { PID = 0x3EE9AF66 }; // Gligar (Rock)
|
||||||
Assert.True(MethodFinder.GetPokeSpotSeeds(pkPS1, 1).Any(), "PokeSpot encounter info mismatch (Uncommon)");
|
Assert.True(MethodFinder.GetPokeSpotSeedFirst(pkPS1, 1).Type == PIDType.PokeSpot, "PokeSpot encounter info mismatch (Uncommon)");
|
||||||
var pkPS2 = new PK3 { PID = 0x9B667F3C }; // Surskit (Oasis)
|
var pkPS2 = new PK3 { PID = 0x9B667F3C }; // Surskit (Oasis)
|
||||||
Assert.True(MethodFinder.GetPokeSpotSeeds(pkPS2, 2).Any(), "PokeSpot encounter info mismatch (Rare)");
|
Assert.True(MethodFinder.GetPokeSpotSeedFirst(pkPS2, 2).Type == PIDType.PokeSpot, "PokeSpot encounter info mismatch (Rare)");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -208,6 +209,20 @@ public class PIDIVTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0x00001234, 0x4DCB, 0xE161)]
|
||||||
|
[InlineData(0x00005678, 0x734D, 0xC596)]
|
||||||
|
public void Method1(uint seed, ushort rand0, ushort rand1)
|
||||||
|
{
|
||||||
|
uint first = (uint)(rand0 << 16);
|
||||||
|
uint second = (uint)(rand1 << 16);
|
||||||
|
Span<uint> seeds = stackalloc uint[LCRNG.MaxCountSeedsPID];
|
||||||
|
int count = LCRNGReversal.GetSeeds(seeds, first, second);
|
||||||
|
count.Should().NotBe(0);
|
||||||
|
|
||||||
|
seeds[..count].IndexOf(seed).Should().NotBe(-1);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void PIDIVMethod4IVs()
|
public void PIDIVMethod4IVs()
|
||||||
{
|
{
|
||||||
|
@ -225,7 +240,11 @@ public class PIDIVTest
|
||||||
rand1 |= (uint)IVs[i] << (5 * i);
|
rand1 |= (uint)IVs[i] << (5 * i);
|
||||||
rand3 |= (uint)IVs[i+3] << (5 * i);
|
rand3 |= (uint)IVs[i+3] << (5 * i);
|
||||||
}
|
}
|
||||||
Assert.Contains(MethodFinder.GetSeedsFromIVsSkip(RNG.LCRNG, rand1, rand3), z => z == 0xFEE7047C);
|
|
||||||
|
Span<uint> seeds = stackalloc uint[LCRNG.MaxCountSeedsIV];
|
||||||
|
int count = LCRNGReversalSkip.GetSeedsIVs(seeds, rand1 << 16, rand3 << 16);
|
||||||
|
var reg = seeds[..count];
|
||||||
|
reg.IndexOf(0xFEE7047C).Should().NotBe(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -234,9 +253,16 @@ public class PIDIVTest
|
||||||
const uint seed = 0x2E15555E;
|
const uint seed = 0x2E15555E;
|
||||||
const uint rand0 = 0x20AD96A9;
|
const uint rand0 = 0x20AD96A9;
|
||||||
const uint rand1 = 0x7E1DBEC8;
|
const uint rand1 = 0x7E1DBEC8;
|
||||||
var pidseeds = MethodFinder.GetSeedsFromPIDEuclid(RNG.XDRNG, rand0 >> 16, rand1 >> 16);
|
|
||||||
var ivseeds = MethodFinder.GetSeedsFromIVsEuclid(RNG.XDRNG, (rand0 >> 16) & 0x7FFF, (rand1 >> 16) & 0x7FFF);
|
XDRNG.MaxCountSeedsIV.Should().BeGreaterThan(XDRNG.MaxCountSeedsPID);
|
||||||
Assert.Contains(pidseeds, z => z == seed);
|
|
||||||
Assert.Contains(ivseeds, z => z == seed);
|
Span<uint> seeds = stackalloc uint[XDRNG.MaxCountSeedsIV];
|
||||||
|
var cp = XDRNG.GetSeeds(seeds, rand0 & 0xFFFF0000, rand1 & 0xFFFF0000);
|
||||||
|
var p = seeds[..cp];
|
||||||
|
p.IndexOf(seed).Should().NotBe(-1);
|
||||||
|
|
||||||
|
var ci = XDRNG.GetSeedsIVs(seeds, rand0 & 0x7FFF0000, rand1 & 0x7FFF0000);
|
||||||
|
var i = seeds[..ci];
|
||||||
|
i.IndexOf(seed).Should().NotBe(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue