mirror of
https://github.com/kwsch/PKHeX
synced 2024-09-20 06:11:55 +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.Linq;
|
||||
|
||||
|
@ -46,12 +47,9 @@ public static class EncounterGenerator3
|
|||
{
|
||||
if (z is EncounterSlot3PokeSpot w)
|
||||
{
|
||||
var seeds = MethodFinder.GetPokeSpotSeeds(pk, w.SlotNumber);
|
||||
foreach (var s in seeds)
|
||||
{
|
||||
info.PIDIV = s;
|
||||
break;
|
||||
}
|
||||
var pidiv = MethodFinder.GetPokeSpotSeedFirst(pk, w.SlotNumber);
|
||||
if (pidiv.Type == PIDType.PokeSpot)
|
||||
info.PIDIV = pidiv;
|
||||
}
|
||||
else if (z is EncounterStaticShadow s)
|
||||
{
|
||||
|
@ -229,12 +227,15 @@ public static class EncounterGenerator3
|
|||
return LockFinder.IsAllShadowLockValid(s, info.PIDIV, pk);
|
||||
|
||||
// E-Reader have fixed IVs, and aren't recognized as CXD (no PID-IV correlation).
|
||||
var possible = MethodFinder.GetColoEReaderMatches(pk.EncryptionConstant);
|
||||
foreach (var poss in possible)
|
||||
Span<uint> seeds = stackalloc uint[4];
|
||||
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;
|
||||
info.PIDIV = poss;
|
||||
info.PIDIV = pidiv;
|
||||
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;
|
||||
|
||||
|
@ -13,39 +13,76 @@ namespace PKHeX.Core;
|
|||
/// seed_n+1 = seed_n * <see cref="Mult"/> + <see cref="Add"/>
|
||||
/// </br>
|
||||
/// </remarks>
|
||||
public class LCRNG
|
||||
public static class LCRNG
|
||||
{
|
||||
// Forward
|
||||
protected readonly uint Mult;
|
||||
private readonly uint Add;
|
||||
// Forward and reverse constants
|
||||
public const uint Mult = 0x41C64E6D;
|
||||
public const uint Add = 0x00006073;
|
||||
public const uint rMult = 0xEEB9EB65;
|
||||
public const uint rAdd = 0x0A3561A1;
|
||||
|
||||
// Reverse
|
||||
private readonly uint rMult;
|
||||
private readonly uint rAdd;
|
||||
private const uint Mult2 = unchecked(Mult * Mult); // 0xC2A29A69
|
||||
private const uint rMult2 = unchecked(rMult * rMult); // 0xDC6C95D9
|
||||
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)
|
||||
{
|
||||
Mult = f_mult;
|
||||
Add = f_add;
|
||||
rMult = r_mult;
|
||||
rAdd = r_add;
|
||||
}
|
||||
private const uint Mult3 = unchecked(Mult2 * Mult); // 0x807DBCB5
|
||||
private const uint rMult3 = unchecked(rMult2 * rMult); // 0xAC36519D
|
||||
private const uint Add3 = unchecked((Add2 * Mult) + Add); // 0x52713895
|
||||
private const uint rAdd3 = unchecked((rAdd2 * rMult) + rAdd);// 0x923B279F
|
||||
|
||||
/// <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 uint Next(uint seed) => (seed * Mult) + Add;
|
||||
private const uint Mult4 = unchecked(Mult3 * Mult); // 0xEE067F11
|
||||
private const uint rMult4 = unchecked(rMult3 * rMult); // 0xBECE51F1
|
||||
private const uint Add4 = unchecked((Add3 * Mult) + Add); // 0x31B0DDE4
|
||||
private const uint rAdd4 = unchecked((rAdd3 * rMult) + rAdd);// 0x7CD1F85C
|
||||
|
||||
/// <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 uint Prev(uint seed) => (seed * rMult) + rAdd;
|
||||
private const uint Mult5 = unchecked(Mult4 * Mult); // 0xEBA1483D
|
||||
private const uint rMult5 = unchecked(rMult4 * rMult); // 0xF1C78F15
|
||||
private const uint Add5 = unchecked((Add4 * Mult) + Add); // 0x8E425287
|
||||
private const uint rAdd5 = unchecked((rAdd4 * rMult) + rAdd);// 0x0A84D1ED
|
||||
|
||||
private const uint Mult6 = unchecked(Mult5 * Mult); // 0xD3DC57F9
|
||||
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>
|
||||
/// 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>
|
||||
/// <returns>Seed advanced the specified amount of times.</returns>
|
||||
[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++)
|
||||
seed = Next(seed);
|
||||
|
@ -67,8 +104,7 @@ public class LCRNG
|
|||
/// <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 uint Reverse(uint seed, int frames)
|
||||
public static uint Reverse(uint seed, int frames)
|
||||
{
|
||||
for (int i = 0; i < frames; i++)
|
||||
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;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
@ -21,7 +21,7 @@ public ref struct XorShift128
|
|||
[FieldOffset(0x8)] private readonly ulong s1;
|
||||
|
||||
/// <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>
|
||||
/// <param name="seed">32 bit seed</param>
|
||||
/// <remarks>sub_E0F5E0 in v1.1.3</remarks>
|
||||
|
|
|
@ -1,34 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <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>
|
||||
public sealed class FrameCache
|
||||
{
|
||||
private const int DefaultSize = 32;
|
||||
private readonly List<uint> Seeds = new(DefaultSize);
|
||||
private readonly List<uint> Values = new(DefaultSize);
|
||||
private readonly Func<uint, uint> Advance;
|
||||
private uint Last;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="FrameCache"/>.
|
||||
/// </summary>
|
||||
/// <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, Func<uint, uint> advance)
|
||||
{
|
||||
Advance = advance;
|
||||
Add(origin);
|
||||
}
|
||||
public FrameCache(uint origin) => Add(origin);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Add(uint seed)
|
||||
{
|
||||
Seeds.Add(seed);
|
||||
Seeds.Add(Last = seed);
|
||||
Values.Add(seed >> 16);
|
||||
}
|
||||
|
||||
|
@ -41,7 +35,7 @@ public sealed class FrameCache
|
|||
get
|
||||
{
|
||||
while (index >= Seeds.Count)
|
||||
Add(Advance(Seeds[^1]));
|
||||
Add(XDRNG.Prev(Last));
|
||||
return Values[index];
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +48,7 @@ public sealed class FrameCache
|
|||
public uint GetSeed(int index)
|
||||
{
|
||||
while (index >= Seeds.Count)
|
||||
Add(Advance(Seeds[^1]));
|
||||
Add(XDRNG.Prev(Last));
|
||||
return Seeds[index];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,11 +67,11 @@ public static class FrameFinder
|
|||
if (noLead)
|
||||
continue;
|
||||
|
||||
var prev = info.RNG.Prev(f.Seed); // ESV
|
||||
var prev = LCRNG.Prev(f.Seed); // ESV
|
||||
var rand = prev >> 16;
|
||||
f.RandESV = rand;
|
||||
f.RandLevel = f.Seed >> 16;
|
||||
f.OriginSeed = info.RNG.Prev(prev);
|
||||
f.OriginSeed = LCRNG.Prev(prev);
|
||||
if (f.Lead != LeadRequired.CuteCharm) // needs proc checking
|
||||
yield return f;
|
||||
|
||||
|
@ -94,9 +94,9 @@ public static class FrameFinder
|
|||
// 3 different rand places
|
||||
LeadRequired lead;
|
||||
var prev0 = f.Seed; // 0
|
||||
var prev1 = info.RNG.Prev(f.Seed); // -1
|
||||
var prev2 = info.RNG.Prev(prev1); // -2
|
||||
var prev3 = info.RNG.Prev(prev2); // -3
|
||||
var prev1 = LCRNG.Prev(f.Seed); // -1
|
||||
var prev2 = LCRNG.Prev(prev1); // -2
|
||||
var prev3 = LCRNG.Prev(prev2); // -3
|
||||
|
||||
// Rand call raw values
|
||||
var p0 = prev0 >> 16;
|
||||
|
@ -167,12 +167,12 @@ public static class FrameFinder
|
|||
var rand = f.Seed >> 16;
|
||||
f.RandESV = rand;
|
||||
f.RandLevel = rand; // unused
|
||||
f.OriginSeed = info.RNG.Prev(f.Seed);
|
||||
f.OriginSeed = LCRNG.Prev(f.Seed);
|
||||
yield return f;
|
||||
|
||||
// Create a copy for level; shift ESV and origin back
|
||||
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);
|
||||
yield return withLevel;
|
||||
|
||||
|
@ -194,9 +194,9 @@ public static class FrameFinder
|
|||
{
|
||||
LeadRequired lead;
|
||||
var prev0 = f.Seed; // 0
|
||||
var prev1 = info.RNG.Prev(f.Seed); // -1
|
||||
var prev2 = info.RNG.Prev(prev1); // -2
|
||||
var prev3 = info.RNG.Prev(prev2); // -3
|
||||
var prev1 = LCRNG.Prev(f.Seed); // -1
|
||||
var prev2 = LCRNG.Prev(prev1); // -2
|
||||
var prev3 = LCRNG.Prev(prev2); // -3
|
||||
|
||||
// Rand call raw values
|
||||
var p0 = prev0 >> 16;
|
||||
|
@ -290,12 +290,12 @@ public static class FrameFinder
|
|||
if (!sync && !reg) // doesn't generate nature frame
|
||||
continue;
|
||||
|
||||
uint prev = RNG.LCRNG.Prev(s);
|
||||
uint prev = LCRNG.Prev(s);
|
||||
if (info.AllowLeads && reg) // check for failed sync
|
||||
{
|
||||
var failsync = (info.DPPt ? prev >> 31 : (prev >> 16) & 1) != 1;
|
||||
if (failsync)
|
||||
yield return info.GetFrame(RNG.LCRNG.Prev(prev), LeadRequired.SynchronizeFail);
|
||||
yield return info.GetFrame(LCRNG.Prev(prev), LeadRequired.SynchronizeFail);
|
||||
}
|
||||
if (sync)
|
||||
yield return info.GetFrame(prev, LeadRequired.Synchronize);
|
||||
|
@ -308,7 +308,7 @@ public static class FrameFinder
|
|||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -328,10 +328,10 @@ public static class FrameFinder
|
|||
for (uint i = 0; i < 25; i++)
|
||||
{
|
||||
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
|
||||
return false;
|
||||
|
||||
|
@ -368,7 +368,7 @@ public static class FrameFinder
|
|||
return false; // current nature is chosen instead, fail!
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -389,13 +389,13 @@ public static class FrameFinder
|
|||
if (nature != info.Nature)
|
||||
continue;
|
||||
|
||||
var prev = RNG.LCRNG.Prev(s);
|
||||
var prev = LCRNG.Prev(s);
|
||||
var proc = prev >> 16;
|
||||
bool charmProc = (info.DPPt ? proc / 0x5556 : proc % 3) != 0; // 2/3 odds
|
||||
if (!charmProc)
|
||||
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;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
@ -15,7 +15,6 @@ public sealed class FrameGenerator
|
|||
public readonly bool DPPt;
|
||||
public readonly bool AllowLeads;
|
||||
public readonly FrameType FrameType;
|
||||
public readonly RNG RNG = RNG.LCRNG;
|
||||
public readonly bool Safari3;
|
||||
|
||||
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);
|
||||
|
||||
var s1 = seed;
|
||||
var s2 = RNG.LCRNG.Prev(s1);
|
||||
var s2 = LCRNG.Prev(s1);
|
||||
bool charm3 = false;
|
||||
while (true)
|
||||
{
|
||||
|
@ -38,8 +38,8 @@ public readonly record struct SeedInfo(uint Seed, bool Charm3 = false)
|
|||
break;
|
||||
}
|
||||
|
||||
s1 = RNG.LCRNG.Prev(s2);
|
||||
s2 = RNG.LCRNG.Prev(s1);
|
||||
s1 = LCRNG.Prev(s2);
|
||||
s2 = LCRNG.Prev(s1);
|
||||
|
||||
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 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.
|
||||
var seeds = MethodFinder.GetCuteCharmSeeds(pk);
|
||||
foreach (var seed in seeds)
|
||||
yield return new SeedInfo(seed);
|
||||
|
||||
var seeds = new uint[LCRNG.MaxCountSeedsIV];
|
||||
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>
|
||||
|
@ -73,7 +75,7 @@ public readonly record struct SeedInfo(uint Seed, bool Charm3 = false)
|
|||
yield return new SeedInfo(seed);
|
||||
|
||||
var s1 = seed;
|
||||
var s2 = RNG.LCRNG.Prev(s1);
|
||||
var s2 = LCRNG.Prev(s1);
|
||||
while (true)
|
||||
{
|
||||
var a = s2 >> 16;
|
||||
|
@ -91,8 +93,8 @@ public readonly record struct SeedInfo(uint Seed, bool Charm3 = false)
|
|||
}
|
||||
}
|
||||
|
||||
s1 = RNG.LCRNG.Prev(s2);
|
||||
s2 = RNG.LCRNG.Prev(s1);
|
||||
s1 = LCRNG.Prev(s2);
|
||||
s2 = LCRNG.Prev(s1);
|
||||
|
||||
yield return new SeedInfo(s1);
|
||||
}
|
||||
|
|
|
@ -53,8 +53,8 @@ public static class LockFinder
|
|||
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
|
||||
var SIDf = RNG.XDRNG.Reverse(seed, 2);
|
||||
var TIDf = RNG.XDRNG.Prev(SIDf);
|
||||
var SIDf = XDRNG.Prev2(seed);
|
||||
var TIDf = XDRNG.Prev(SIDf);
|
||||
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)
|
||||
{
|
||||
// reverse the seed the bare minimum
|
||||
int rev = 2;
|
||||
if (species == (int)Species.Espeon)
|
||||
rev += 7;
|
||||
uint SIDf = species == (int)Species.Espeon
|
||||
? XDRNG.Prev9(seed)
|
||||
: XDRNG.Prev2(seed);
|
||||
|
||||
// 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;
|
||||
uint temp;
|
||||
while ((temp = rng.Prev(SIDf)) >> 16 != TID || SIDf >> 16 != SID)
|
||||
while ((temp = XDRNG.Prev(SIDf)) >> 16 != TID || SIDf >> 16 != SID)
|
||||
{
|
||||
SIDf = temp;
|
||||
if (ctr > 32) // arbitrary
|
||||
|
@ -88,7 +86,7 @@ public static class LockFinder
|
|||
ctr++;
|
||||
}
|
||||
|
||||
var next = rng.Next(SIDf);
|
||||
var next = XDRNG.Next(SIDf);
|
||||
|
||||
// generate Umbreon
|
||||
var PIDIV = GenerateValidColoStarterPID(ref next, TID, SID);
|
||||
|
@ -97,7 +95,7 @@ public static class LockFinder
|
|||
|
||||
if (!PIDIV.Equals(pkPID, IV1, IV2))
|
||||
return false;
|
||||
seed = rng.Reverse(SIDf, 2);
|
||||
seed = XDRNG.Prev2(SIDf);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -108,17 +106,15 @@ public static class LockFinder
|
|||
|
||||
private static PIDIVGroup GenerateValidColoStarterPID(ref uint uSeed, int TID, int SID)
|
||||
{
|
||||
var rng = RNG.XDRNG;
|
||||
|
||||
uSeed = rng.Advance(uSeed, 2); // skip fakePID
|
||||
uSeed = XDRNG.Next2(uSeed); // skip fakePID
|
||||
var IV1 = (uSeed >> 16) & 0x7FFF;
|
||||
uSeed = rng.Next(uSeed);
|
||||
uSeed = XDRNG.Next(uSeed);
|
||||
var IV2 = (uSeed >> 16) & 0x7FFF;
|
||||
uSeed = rng.Next(uSeed);
|
||||
uSeed = rng.Advance(uSeed, 1); // skip ability call
|
||||
uSeed = XDRNG.Next(uSeed);
|
||||
uSeed = XDRNG.Next(uSeed); // skip ability call
|
||||
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);
|
||||
}
|
||||
|
@ -131,11 +127,11 @@ public static class LockFinder
|
|||
const byte ratio = 0x1F; // 12.5% F (can't be female)
|
||||
while (true)
|
||||
{
|
||||
var next = RNG.XDRNG.Next(uSeed);
|
||||
var next = XDRNG.Next(uSeed);
|
||||
PID = (uSeed & 0xFFFF0000) | (next >> 16);
|
||||
if ((PID & 0xFF) >= ratio && !IsShiny(TID, SID, PID))
|
||||
break;
|
||||
uSeed = RNG.XDRNG.Next(next);
|
||||
uSeed = XDRNG.Next(next);
|
||||
}
|
||||
|
||||
return PID;
|
||||
|
|
|
@ -62,7 +62,7 @@ public sealed class TeamLockResult
|
|||
{
|
||||
Locks = new Stack<NPCLock>((Specifications = teamSpec).Locks);
|
||||
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;
|
||||
|
||||
Valid = FindLockSeed();
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using static PKHeX.Core.PIDType;
|
||||
|
||||
|
@ -22,44 +20,49 @@ public static class MethodFinder
|
|||
return AnalyzeGB(pk);
|
||||
var pid = pk.EncryptionConstant;
|
||||
|
||||
var top = pid >> 16;
|
||||
var bot = pid & 0xFFFF;
|
||||
var top = pid & 0xFFFF0000;
|
||||
var bot = pid << 16;
|
||||
|
||||
Span<uint> temp = stackalloc uint[6];
|
||||
for (int i = 0; i < 6; i++)
|
||||
temp[i] = (uint)pk.GetIV(i);
|
||||
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;
|
||||
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;
|
||||
if (GetColoStarterMatch(pk, top, bot, IVs, out pidiv))
|
||||
if (GetColoStarterMatch(seeds, pk, top, bot, IVs, out pidiv))
|
||||
return pidiv;
|
||||
if (GetXDRNGMatch(pk, top, bot, IVs, out pidiv))
|
||||
if (GetXDRNGMatch(seeds, pk, top, bot, IVs, out pidiv))
|
||||
return pidiv;
|
||||
|
||||
// Special cases
|
||||
if (GetLCRNGRoamerMatch(top, bot, IVs, out pidiv))
|
||||
if (GetLCRNGRoamerMatch(seeds, top, bot, IVs, out pidiv))
|
||||
return pidiv;
|
||||
if (GetChannelMatch(top, bot, IVs, out pidiv, pk))
|
||||
if (GetChannelMatch(seeds, top, bot, IVs, out pidiv, pk))
|
||||
return pidiv;
|
||||
if (GetMG4Match(pid, IVs, out pidiv))
|
||||
if (GetMG4Match(seeds, pid, IVs, out pidiv))
|
||||
return pidiv;
|
||||
|
||||
if (GetBACDMatch(pk, pid, IVs, out pidiv))
|
||||
if (GetBACDMatch(seeds, pk, pid, IVs, out pidiv))
|
||||
return pidiv;
|
||||
if (GetModifiedPIDMatch(pk, pid, IVs, out pidiv))
|
||||
if (GetModifiedPIDMatch(seeds, pk, pid, IVs, out pidiv))
|
||||
return pidiv;
|
||||
|
||||
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 (GetChainShinyMatch(pk, pid, IVs, out pidiv))
|
||||
if (GetChainShinyMatch(seeds, pk, pid, IVs, out pidiv))
|
||||
return true;
|
||||
if (GetModified8BitMatch(pk, pid, out pidiv))
|
||||
return true;
|
||||
|
@ -80,22 +83,23 @@ public static class MethodFinder
|
|||
: 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 iv2 = GetIVChunk(IVs, 3);
|
||||
foreach (var seed in reg)
|
||||
{
|
||||
// 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
|
||||
var C = RNG.LCRNG.Next(B);
|
||||
var C = LCRNG.Next(B);
|
||||
var ivC = C >> 16 & 0x7FFF;
|
||||
if (iv1 == ivC)
|
||||
{
|
||||
var D = RNG.LCRNG.Next(C);
|
||||
var D = LCRNG.Next(C);
|
||||
var ivD = D >> 16 & 0x7FFF;
|
||||
if (iv2 == ivD) // ABCD
|
||||
{
|
||||
|
@ -103,7 +107,7 @@ public static class MethodFinder
|
|||
return true;
|
||||
}
|
||||
|
||||
var E = RNG.LCRNG.Next(D);
|
||||
var E = LCRNG.Next(D);
|
||||
var ivE = E >> 16 & 0x7FFF;
|
||||
if (iv2 == ivE) // ABCE
|
||||
{
|
||||
|
@ -113,12 +117,12 @@ public static class MethodFinder
|
|||
}
|
||||
else
|
||||
{
|
||||
var D = RNG.LCRNG.Next(C);
|
||||
var D = LCRNG.Next(C);
|
||||
var ivD = D >> 16 & 0x7FFF;
|
||||
if (iv1 != ivD)
|
||||
continue;
|
||||
|
||||
var E = RNG.LCRNG.Next(D);
|
||||
var E = LCRNG.Next(D);
|
||||
var ivE = E >> 16 & 0x7FFF;
|
||||
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)
|
||||
{
|
||||
// A and B are already used by PID
|
||||
var C = RNG.LCRNG.Advance(seed, 3);
|
||||
var C = LCRNG.Next3(seed);
|
||||
|
||||
// Method 3
|
||||
var D = RNG.LCRNG.Next(C);
|
||||
var D = LCRNG.Next(C);
|
||||
var ivD = D >> 16 & 0x7FFF;
|
||||
if (iv1 != ivD)
|
||||
continue;
|
||||
var E = RNG.LCRNG.Next(D);
|
||||
var E = LCRNG.Next(D);
|
||||
var ivE = E >> 16 & 0x7FFF;
|
||||
if (iv2 != ivE)
|
||||
continue;
|
||||
|
@ -148,23 +153,24 @@ public static class MethodFinder
|
|||
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)
|
||||
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 iv2 = GetIVChunk(IVs, 3);
|
||||
foreach (var seed in reg)
|
||||
{
|
||||
// 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
|
||||
var C = RNG.LCRNG.Next(B);
|
||||
var C = LCRNG.Next(B);
|
||||
var ivC = C >> 16 & 0x7FFF;
|
||||
if (iv1 == ivC)
|
||||
{
|
||||
var D = RNG.LCRNG.Next(C);
|
||||
var D = LCRNG.Next(C);
|
||||
var ivD = D >> 16 & 0x7FFF;
|
||||
if (iv2 == ivD) // BACD
|
||||
{
|
||||
|
@ -172,7 +178,7 @@ public static class MethodFinder
|
|||
return true;
|
||||
}
|
||||
|
||||
var E = RNG.LCRNG.Next(D);
|
||||
var E = LCRNG.Next(D);
|
||||
var ivE = E >> 16 & 0x7FFF;
|
||||
if (iv2 == ivE) // BACE
|
||||
{
|
||||
|
@ -182,12 +188,12 @@ public static class MethodFinder
|
|||
}
|
||||
else
|
||||
{
|
||||
var D = RNG.LCRNG.Next(C);
|
||||
var D = LCRNG.Next(C);
|
||||
var ivD = D >> 16 & 0x7FFF;
|
||||
if (iv1 != ivD)
|
||||
continue;
|
||||
|
||||
var E = RNG.LCRNG.Next(D);
|
||||
var E = LCRNG.Next(D);
|
||||
var ivE = E >> 16 & 0x7FFF;
|
||||
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)
|
||||
{
|
||||
// A and B are already used by PID
|
||||
var C = RNG.LCRNG.Advance(seed, 3);
|
||||
var C = LCRNG.Next3(seed);
|
||||
|
||||
// Method 3
|
||||
var D = RNG.LCRNG.Next(C);
|
||||
var D = LCRNG.Next(C);
|
||||
var ivD = D >> 16 & 0x7FFF;
|
||||
if (iv1 != ivD)
|
||||
continue;
|
||||
var E = RNG.LCRNG.Next(D);
|
||||
var E = LCRNG.Next(D);
|
||||
var ivE = E >> 16 & 0x7FFF;
|
||||
if (iv2 != ivE)
|
||||
continue;
|
||||
|
@ -217,16 +224,18 @@ public static class MethodFinder
|
|||
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)
|
||||
return GetNonMatch(out pidiv);
|
||||
|
||||
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)
|
||||
{
|
||||
// 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)
|
||||
continue;
|
||||
|
||||
|
@ -236,19 +245,20 @@ public static class MethodFinder
|
|||
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)
|
||||
{
|
||||
var B = RNG.XDRNG.Prev(seed);
|
||||
var A = RNG.XDRNG.Prev(B);
|
||||
var B = XDRNG.Prev(seed);
|
||||
var A = XDRNG.Prev(B);
|
||||
|
||||
var hi = A >> 16;
|
||||
var lo = B >> 16;
|
||||
if (IVsMatch(hi, lo, IVs))
|
||||
{
|
||||
pidiv = new PIDIV(CXD, RNG.XDRNG.Prev(A));
|
||||
pidiv = new PIDIV(CXD, XDRNG.Prev(A));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -266,18 +276,18 @@ public static class MethodFinder
|
|||
|
||||
do
|
||||
{
|
||||
B = RNG.XDRNG.Prev(A);
|
||||
A = RNG.XDRNG.Prev(B);
|
||||
B = XDRNG.Prev(A);
|
||||
A = XDRNG.Prev(B);
|
||||
hi = A >> 16;
|
||||
lo = B >> 16;
|
||||
if (IVsMatch(hi, lo, IVs))
|
||||
{
|
||||
pidiv = new PIDIV(CXDAnti, RNG.XDRNG.Prev(A));
|
||||
pidiv = new PIDIV(CXDAnti, XDRNG.Prev(A));
|
||||
return true;
|
||||
}
|
||||
|
||||
p2 = RNG.XDRNG.Prev(p1);
|
||||
p1 = RNG.XDRNG.Prev(p2);
|
||||
p2 = XDRNG.Prev(p1);
|
||||
p1 = XDRNG.Prev(p2);
|
||||
psv = (p2 ^ p1) >> 19;
|
||||
}
|
||||
while (psv == tsv);
|
||||
|
@ -285,50 +295,54 @@ public static class MethodFinder
|
|||
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;
|
||||
if (ver is not ((int)GameVersion.R or (int)GameVersion.S))
|
||||
return GetNonMatch(out pidiv);
|
||||
|
||||
var undo = top ^ 0x8000;
|
||||
if ((undo > 7 ? 0 : 1) != (bot ^ pk.SID ^ 40122))
|
||||
top = undo;
|
||||
var channel = GetSeedsFromPIDEuclid(RNG.XDRNG, top, bot);
|
||||
var undo = (top >> 16) ^ 0x8000;
|
||||
if ((undo > 7 ? 0 : 1) != ((bot >> 16) ^ pk.SID ^ 40122))
|
||||
top = (undo << 16);
|
||||
|
||||
var count = XDRNG.GetSeeds(seeds, top, bot);
|
||||
var channel = seeds[..count];
|
||||
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
|
||||
|
||||
var D = RNG.XDRNG.Next(C); // Version
|
||||
var D = XDRNG.Next(C); // Version
|
||||
if ((D >> 31) + 1 != ver) // (0-Sapphire, 1-Ruby)
|
||||
continue;
|
||||
|
||||
var E = RNG.XDRNG.Next(D); // OT Gender
|
||||
var E = XDRNG.Next(D); // OT Gender
|
||||
if (E >> 31 != pk.OT_Gender)
|
||||
continue;
|
||||
|
||||
if (!RNG.XDRNG.GetSequentialIVsUInt32(E, IVs))
|
||||
if (!XDRNG.GetSequentialIVsUInt32(E, IVs))
|
||||
continue;
|
||||
|
||||
if (seed >> 16 != pk.SID)
|
||||
continue;
|
||||
|
||||
pidiv = new PIDIV(Channel, RNG.XDRNG.Prev(seed));
|
||||
pidiv = new PIDIV(Channel, XDRNG.Prev(seed));
|
||||
return true;
|
||||
}
|
||||
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);
|
||||
var mg4 = GetSeedsFromPID(RNG.LCRNG, mg4Rev >> 16, mg4Rev & 0xFFFF);
|
||||
uint mg4Rev = ARNG.Prev(pid);
|
||||
|
||||
var count = LCRNGReversal.GetSeeds(seeds, mg4Rev << 16, mg4Rev & 0xFFFF0000);
|
||||
var mg4 = seeds[..count];
|
||||
foreach (var seed in mg4)
|
||||
{
|
||||
var B = RNG.LCRNG.Advance(seed, 2);
|
||||
var C = RNG.LCRNG.Next(B);
|
||||
var D = RNG.LCRNG.Next(C);
|
||||
var B = LCRNG.Next2(seed);
|
||||
var C = LCRNG.Next(B);
|
||||
var D = LCRNG.Next(C);
|
||||
if (!IVsMatch(C >> 16, D >> 16, IVs))
|
||||
continue;
|
||||
|
||||
|
@ -361,10 +375,13 @@ public static class MethodFinder
|
|||
return GetNonMatch(out pidiv);
|
||||
|
||||
(var species, int genderValue) = GetCuteCharmGenderSpecies(pk, pid, pk.Species);
|
||||
if ((uint)species > Legal.MaxSpeciesID_4)
|
||||
return GetNonMatch(out pidiv);
|
||||
static int getRatio(ushort species)
|
||||
{
|
||||
return species <= Legal.MaxSpeciesID_4
|
||||
? PersonalTable.HGSS[species].Gender
|
||||
: PKX.Personal[species].Gender;
|
||||
}
|
||||
|
||||
static int getRatio(ushort species) => PersonalTable.HGSS[species].Gender;
|
||||
switch (genderValue)
|
||||
{
|
||||
case 2: break; // can't cute charm a genderless pk
|
||||
|
@ -391,15 +408,17 @@ public static class MethodFinder
|
|||
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
|
||||
// PIDH & 7
|
||||
// PIDL & 7
|
||||
// IVs
|
||||
var bot = GetIVChunk(IVs, 0);
|
||||
var top = GetIVChunk(IVs, 3);
|
||||
var reg = GetSeedsFromIVs(RNG.LCRNG, top, bot);
|
||||
var bot = GetIVChunk(IVs, 0) << 16;
|
||||
var top = GetIVChunk(IVs, 3) << 16;
|
||||
|
||||
var count = LCRNGReversal.GetSeedsIVs(seeds, bot, top);
|
||||
var reg = seeds[..count];
|
||||
foreach (var seed in reg)
|
||||
{
|
||||
// check the individual bits
|
||||
|
@ -410,7 +429,7 @@ public static class MethodFinder
|
|||
var bit = s >> 16 & 1;
|
||||
if (bit != (pid >> i & 1))
|
||||
break;
|
||||
s = RNG.LCRNG.Prev(s);
|
||||
s = LCRNG.Prev(s);
|
||||
}
|
||||
while (--i != 2);
|
||||
if (i != 2) // bit failed
|
||||
|
@ -419,7 +438,7 @@ public static class MethodFinder
|
|||
var upper = s;
|
||||
if ((upper >> 16 & 7) != (pid >> 16 & 7))
|
||||
continue;
|
||||
var lower = RNG.LCRNG.Prev(upper);
|
||||
var lower = LCRNG.Prev(upper);
|
||||
if ((lower >> 16 & 7) != (pid & 7))
|
||||
continue;
|
||||
|
||||
|
@ -427,34 +446,25 @@ public static class MethodFinder
|
|||
if (upid != pid >> 16)
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
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];
|
||||
for (int i = 0; i < 6; i++)
|
||||
IVs[i] = (uint)pk.GetIV(i);
|
||||
var bot = GetIVChunk(IVs, 0);
|
||||
var top = GetIVChunk(IVs, 3);
|
||||
var bot = GetIVChunk(IVs, 0) << 16;
|
||||
var top = GetIVChunk(IVs, 3) << 16;
|
||||
|
||||
return GetSeedsFromIVs(RNG.LCRNG, top, bot);
|
||||
}
|
||||
|
||||
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);
|
||||
var count = LCRNGReversal.GetSeedsIVs(seeds, bot, top);
|
||||
var reg = seeds[..count];
|
||||
PIDType type = BACD_U;
|
||||
foreach (var seed in reg)
|
||||
{
|
||||
var B = seed;
|
||||
var A = RNG.LCRNG.Prev(B);
|
||||
var A = LCRNG.Prev(B);
|
||||
var low = B >> 16;
|
||||
|
||||
var PID = (A & 0xFFFF0000) | low;
|
||||
|
@ -482,11 +492,11 @@ public static class MethodFinder
|
|||
type = BACD_U_A;
|
||||
}
|
||||
}
|
||||
var s = RNG.LCRNG.Prev(A);
|
||||
var s = LCRNG.Prev(A);
|
||||
|
||||
// Check for prior Restricted seed
|
||||
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)
|
||||
continue;
|
||||
|
@ -540,7 +550,7 @@ public static class MethodFinder
|
|||
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
|
||||
{
|
||||
|
@ -553,7 +563,9 @@ public static class MethodFinder
|
|||
|
||||
var iv1 = GetIVChunk(IVs, 0);
|
||||
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)
|
||||
{
|
||||
uint origin = seed;
|
||||
|
@ -597,7 +609,7 @@ public static class MethodFinder
|
|||
// 3-FORCEBITS
|
||||
// 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);
|
||||
PID &= 0xFFFFFFF8;
|
||||
PID |= low & 0x7; // lowest 3 bits
|
||||
|
@ -644,75 +656,6 @@ public static class MethodFinder
|
|||
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>
|
||||
/// Generates IVs from 2 RNG calls using 15 bits of each to generate 6 IVs (5bits each).
|
||||
/// </summary>
|
||||
|
@ -762,41 +705,7 @@ public static class MethodFinder
|
|||
val |= IVs[i+start] << (5*i);
|
||||
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)
|
||||
{
|
||||
s = seed;
|
||||
|
@ -806,14 +715,14 @@ public static class MethodFinder
|
|||
// todo
|
||||
}
|
||||
// check for valid activation
|
||||
s = RNG.XDRNG.Prev(seed);
|
||||
s = XDRNG.Prev(seed);
|
||||
if ((s >> 16) % 3 != 0)
|
||||
{
|
||||
if ((s >> 16) % 100 < 10) // can't fail a munchlax/bonsly encounter check
|
||||
{
|
||||
// todo
|
||||
}
|
||||
s = RNG.XDRNG.Prev(s);
|
||||
s = XDRNG.Prev(s);
|
||||
if ((s >> 16) % 3 != 0) // can't activate even if generous
|
||||
{
|
||||
// todo
|
||||
|
@ -913,7 +822,7 @@ public static class MethodFinder
|
|||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -954,7 +863,27 @@ public static class MethodFinder
|
|||
|
||||
// Future evolutions
|
||||
(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),
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
|
@ -9,12 +9,11 @@ public static class PIDGenerator
|
|||
{
|
||||
private static void SetValuesFromSeedLCRNG(PKM pk, PIDType type, uint seed)
|
||||
{
|
||||
var rng = RNG.LCRNG;
|
||||
var A = rng.Next(seed);
|
||||
var B = rng.Next(A);
|
||||
var A = LCRNG.Next(seed);
|
||||
var B = LCRNG.Next(A);
|
||||
var skipBetweenPID = type is PIDType.Method_3 or PIDType.Method_3_Unown;
|
||||
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;
|
||||
if (swappedPIDHalves) // switched order of PID halves, "BA.."
|
||||
|
@ -22,15 +21,15 @@ public static class PIDGenerator
|
|||
else
|
||||
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;
|
||||
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;
|
||||
if (skipIV2Frame) // VBlank skip between IVs
|
||||
D = rng.Next(D);
|
||||
D = LCRNG.Next(D);
|
||||
|
||||
Span<int> IVs = stackalloc int[6];
|
||||
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)
|
||||
{
|
||||
var rng = RNG.LCRNG;
|
||||
bool shiny = type is PIDType.BACD_R_S or PIDType.BACD_U_S;
|
||||
uint X = shiny ? rng.Next(seed) : seed;
|
||||
var A = rng.Next(X);
|
||||
var B = rng.Next(A);
|
||||
var C = rng.Next(B);
|
||||
var D = rng.Next(C);
|
||||
uint X = shiny ? LCRNG.Next(seed) : seed;
|
||||
var A = LCRNG.Next(X);
|
||||
var B = LCRNG.Next(A);
|
||||
var C = LCRNG.Next(B);
|
||||
var D = LCRNG.Next(C);
|
||||
|
||||
if (shiny)
|
||||
{
|
||||
|
@ -83,25 +81,24 @@ public static class PIDGenerator
|
|||
|
||||
private static void SetValuesFromSeedXDRNG(PKM pk, uint seed)
|
||||
{
|
||||
var rng = RNG.XDRNG;
|
||||
switch (pk.Species)
|
||||
{
|
||||
case (int)Species.Umbreon or (int)Species.Eevee: // Colo Umbreon, XD Eevee
|
||||
pk.TID = (int)((seed = rng.Next(seed)) >> 16);
|
||||
pk.SID = (int)((seed = rng.Next(seed)) >> 16);
|
||||
seed = rng.Advance(seed, 2); // PID calls consumed
|
||||
pk.TID = (int)((seed = XDRNG.Next(seed)) >> 16);
|
||||
pk.SID = (int)((seed = XDRNG.Next(seed)) >> 16);
|
||||
seed = XDRNG.Next2(seed); // PID calls consumed
|
||||
break;
|
||||
case (int)Species.Espeon: // Colo Espeon
|
||||
pk.TID = (int)((seed = rng.Next(seed)) >> 16);
|
||||
pk.SID = (int)((seed = rng.Next(seed)) >> 16);
|
||||
seed = rng.Advance(seed, 9); // PID calls consumed, skip over Umbreon
|
||||
pk.TID = (int)((seed = XDRNG.Next(seed)) >> 16);
|
||||
pk.SID = (int)((seed = XDRNG.Next(seed)) >> 16);
|
||||
seed = XDRNG.Next9(seed); // PID calls consumed, skip over Umbreon
|
||||
break;
|
||||
}
|
||||
var A = rng.Next(seed); // IV1
|
||||
var B = rng.Next(A); // IV2
|
||||
var C = rng.Next(B); // Ability?
|
||||
var D = rng.Next(C); // PID
|
||||
var E = rng.Next(D); // PID
|
||||
var A = XDRNG.Next(seed); // IV1
|
||||
var B = XDRNG.Next(A); // IV2
|
||||
var C = XDRNG.Next(B); // Ability?
|
||||
var D = XDRNG.Next(C); // PID
|
||||
var E = XDRNG.Next(D); // PID
|
||||
|
||||
pk.PID = (D & 0xFFFF0000) | (E >> 16);
|
||||
Span<int> IVs = stackalloc int[6];
|
||||
|
@ -111,23 +108,20 @@ public static class PIDGenerator
|
|||
|
||||
public static void SetValuesFromSeedXDRNG_EReader(PKM pk, uint seed)
|
||||
{
|
||||
var rng = RNG.XDRNG;
|
||||
var A = rng.Reverse(seed, 4);
|
||||
var D = rng.Next(A); // PID
|
||||
var E = rng.Next(D); // PID
|
||||
var D = XDRNG.Prev3(seed); // PID
|
||||
var E = XDRNG.Next(D); // PID
|
||||
|
||||
pk.PID = (D & 0xFFFF0000) | (E >> 16);
|
||||
}
|
||||
|
||||
private static void SetValuesFromSeedChannel(PKM pk, uint seed)
|
||||
{
|
||||
var rng = RNG.XDRNG;
|
||||
var O = rng.Next(seed); // SID
|
||||
var A = rng.Next(O); // PID
|
||||
var B = rng.Next(A); // PID
|
||||
var C = rng.Next(B); // Held Item
|
||||
var D = rng.Next(C); // Version
|
||||
var E = rng.Next(D); // OT Gender
|
||||
var O = XDRNG.Next(seed); // SID
|
||||
var A = XDRNG.Next(O); // PID
|
||||
var B = XDRNG.Next(A); // PID
|
||||
var C = XDRNG.Next(B); // Held Item
|
||||
var D = XDRNG.Next(C); // Version
|
||||
var E = XDRNG.Next(D); // OT Gender
|
||||
|
||||
const int TID = 40122;
|
||||
var SID = (int)(O >> 16);
|
||||
|
@ -143,7 +137,7 @@ public static class PIDGenerator
|
|||
pk.Version = (int)(D >> 31) + 1; // 0-Sapphire, 1-Ruby
|
||||
pk.OT_Gender = (int)(E >> 31);
|
||||
Span<int> ivs = stackalloc int[6];
|
||||
rng.GetSequentialIVsInt32(E, ivs);
|
||||
XDRNG.GetSequentialIVsInt32(E, ivs);
|
||||
pk.SetIVs(ivs);
|
||||
}
|
||||
|
||||
|
@ -204,7 +198,7 @@ public static class PIDGenerator
|
|||
// 1 3-bit for upper
|
||||
// 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 upper = Next() & 7;
|
||||
for (int i = 0; i < 13; i++)
|
||||
|
@ -226,9 +220,8 @@ public static class PIDGenerator
|
|||
if (!MethodFinder.IsPokeSpotActivation(slot, seed, out _))
|
||||
continue;
|
||||
|
||||
var rng = RNG.XDRNG;
|
||||
var D = rng.Next(seed); // PID
|
||||
var E = rng.Next(D); // PID
|
||||
var D = XDRNG.Next(seed); // PID
|
||||
var E = XDRNG.Next(D); // PID
|
||||
|
||||
pk.PID = (D & 0xFFFF0000) | (E >> 16);
|
||||
if (!IsValidCriteria4(pk, nature, ability, gender))
|
||||
|
|
|
@ -12,99 +12,99 @@ public enum PIDType
|
|||
#region LCRNG
|
||||
|
||||
/// <summary> Method 1 Variants (H1/J/K) </summary>
|
||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
||||
/// <remarks><see cref="LCRNG"/></remarks>
|
||||
Method_1,
|
||||
|
||||
/// <summary> Method H2 </summary>
|
||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
||||
/// <remarks><see cref="LCRNG"/></remarks>
|
||||
Method_2,
|
||||
|
||||
/// <summary> Method H3 </summary>
|
||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
||||
/// <remarks><see cref="LCRNG"/></remarks>
|
||||
Method_3,
|
||||
|
||||
/// <summary> Method H4 </summary>
|
||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
||||
/// <remarks><see cref="LCRNG"/></remarks>
|
||||
Method_4,
|
||||
|
||||
/// <summary> Method H1_Unown (FRLG) </summary>
|
||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
||||
/// <remarks><see cref="LCRNG"/></remarks>
|
||||
Method_1_Unown,
|
||||
|
||||
/// <summary> Method H2_Unown (FRLG) </summary>
|
||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
||||
/// <remarks><see cref="LCRNG"/></remarks>
|
||||
Method_2_Unown,
|
||||
|
||||
/// <summary> Method H3_Unown (FRLG) </summary>
|
||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
||||
/// <remarks><see cref="LCRNG"/></remarks>
|
||||
Method_3_Unown,
|
||||
|
||||
/// <summary> Method H4_Unown (FRLG) </summary>
|
||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
||||
/// <remarks><see cref="LCRNG"/></remarks>
|
||||
Method_4_Unown,
|
||||
|
||||
/// <summary> Method 1 Roamer (Gen3) </summary>
|
||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
||||
/// <remarks><see cref="LCRNG"/></remarks>
|
||||
Method_1_Roamer,
|
||||
|
||||
/// <summary>
|
||||
/// Event Reversed Order PID restricted to 16bit Origin Seed
|
||||
/// </summary>
|
||||
/// <remarks><see cref="RNG.LCRNG"/> seed is clamped to 16bits.</remarks>
|
||||
/// <remarks><see cref="LCRNG"/> seed is clamped to 16bits.</remarks>
|
||||
BACD_R,
|
||||
|
||||
/// <summary>
|
||||
/// Event Reversed Order PID without Origin Seed restrictions
|
||||
/// </summary>
|
||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
||||
/// <remarks><see cref="LCRNG"/></remarks>
|
||||
BACD_U,
|
||||
|
||||
/// <summary>
|
||||
/// Event Reversed Order PID restricted to 16bit Origin Seed, antishiny.
|
||||
/// </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,
|
||||
|
||||
/// <summary>
|
||||
/// Event Reversed Order PID without Origin Seed restrictions, antishiny.
|
||||
/// </summary>
|
||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
||||
/// <remarks><see cref="LCRNG"/></remarks>
|
||||
BACD_U_A,
|
||||
|
||||
/// <summary>
|
||||
/// Event Reversed Order PID restricted to 8bit Origin Seed, shiny
|
||||
/// </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,
|
||||
|
||||
/// <summary>
|
||||
/// Event Reversed Order PID without Origin Seed restrictions, shiny
|
||||
/// </summary>
|
||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
||||
/// <remarks><see cref="LCRNG"/></remarks>
|
||||
BACD_U_S,
|
||||
|
||||
/// <summary>
|
||||
/// Event Reversed Order PID restricted to 16bit Origin Seed, antishiny (nyx)
|
||||
/// </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,
|
||||
|
||||
/// <summary>
|
||||
/// Event Reversed Order PID without Origin Seed restrictions, antishiny (nyx)
|
||||
/// </summary>
|
||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
||||
/// <remarks><see cref="LCRNG"/></remarks>
|
||||
BACD_U_AX,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 4 Cute Charm PID, which is forced to an 8 bit PID value based on the gender & gender ratio value.
|
||||
/// </summary>
|
||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
||||
/// <remarks><see cref="LCRNG"/></remarks>
|
||||
CuteCharm,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 4 Chained Shiny
|
||||
/// </summary>
|
||||
/// <remarks><see cref="RNG.LCRNG"/></remarks>
|
||||
/// <remarks><see cref="LCRNG"/></remarks>
|
||||
ChainShiny,
|
||||
|
||||
#endregion
|
||||
|
@ -114,31 +114,31 @@ public enum PIDType
|
|||
/// <summary>
|
||||
/// Generation 3 <see cref="GameVersion.CXD"/> PID+IV correlation.
|
||||
/// </summary>
|
||||
/// <remarks><see cref="RNG.XDRNG"/></remarks>
|
||||
/// <remarks><see cref="XDRNG"/></remarks>
|
||||
CXD,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 3 <see cref="GameVersion.CXD"/> PID+IV correlation that was rerolled because it was shiny.
|
||||
/// </summary>
|
||||
/// <remarks><see cref="RNG.XDRNG"/></remarks>
|
||||
/// <remarks><see cref="XDRNG"/></remarks>
|
||||
CXDAnti,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 3 <see cref="GameVersion.CXD"/> PID+IV which is created immediately after the TID and SID RNG calls.
|
||||
/// </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,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 3 Pokémon Channel Jirachi
|
||||
/// </summary>
|
||||
/// <remarks><see cref="RNG.XDRNG"/></remarks>
|
||||
/// <remarks><see cref="XDRNG"/></remarks>
|
||||
Channel,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 3 <see cref="GameVersion.CXD"/> PokeSpot PID
|
||||
/// </summary>
|
||||
/// <remarks><see cref="RNG.XDRNG"/></remarks>
|
||||
/// <remarks><see cref="XDRNG"/></remarks>
|
||||
PokeSpot,
|
||||
|
||||
#endregion
|
||||
|
@ -148,7 +148,7 @@ public enum PIDType
|
|||
/// <summary>
|
||||
/// Generation 4 Mystery Gift Anti-Shiny
|
||||
/// </summary>
|
||||
/// <remarks><see cref="RNG.ARNG"/></remarks>
|
||||
/// <remarks><see cref="ARNG"/></remarks>
|
||||
G4MGAntiShiny,
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
|
@ -33,7 +33,7 @@ public static class MystryMew
|
|||
0xFE9D,
|
||||
};
|
||||
|
||||
private const int FramesPerMew = 5;
|
||||
//private const int FramesPerMew = 5;
|
||||
private const int MewPerRestrictedSeed = 5;
|
||||
|
||||
/// <summary>
|
||||
|
@ -48,7 +48,7 @@ public static class MystryMew
|
|||
|
||||
uint position = (random % (MewPerRestrictedSeed - 1)) + 1;
|
||||
for (int i = 0; i < position; i++)
|
||||
seed = RNG.LCRNG.Advance(seed, FramesPerMew);
|
||||
seed = LCRNG.Next5(seed);
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ public static class MystryMew
|
|||
{
|
||||
if (seed <= ushort.MaxValue)
|
||||
return Array.BinarySearch(Seeds, (ushort)seed);
|
||||
seed = RNG.LCRNG.Reverse(seed, FramesPerMew);
|
||||
seed = LCRNG.Prev5(seed);
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
|
|
@ -208,8 +208,8 @@ public sealed class PGT : DataMysteryGift, IRibbonSetEvent3, IRibbonSetEvent4
|
|||
// Generate IVs
|
||||
if ((pk4.IV32 & 0x3FFF_FFFFu) == 0) // Ignore Nickname/Egg flag bits
|
||||
{
|
||||
uint iv1 = ((seed = RNG.LCRNG.Next(seed)) >> 16) & 0x7FFF;
|
||||
uint iv2 = ((RNG.LCRNG.Next(seed)) >> 16) & 0x7FFF;
|
||||
uint iv1 = ((seed = LCRNG.Next(seed)) >> 16) & 0x7FFF;
|
||||
uint iv2 = ((LCRNG.Next(seed)) >> 16) & 0x7FFF;
|
||||
pk4.IV32 |= iv1 | (iv2 << 15);
|
||||
}
|
||||
}
|
||||
|
@ -252,14 +252,14 @@ public sealed class PGT : DataMysteryGift, IRibbonSetEvent3, IRibbonSetEvent4
|
|||
{
|
||||
do
|
||||
{
|
||||
uint pid1 = (seed = RNG.LCRNG.Next(seed)) >> 16; // low
|
||||
uint pid2 = (seed = RNG.LCRNG.Next(seed)) & 0xFFFF0000; // hi
|
||||
uint pid1 = (seed = LCRNG.Next(seed)) >> 16; // low
|
||||
uint pid2 = (seed = LCRNG.Next(seed)) & 0xFFFF0000; // hi
|
||||
pk4.PID = pid2 | pid1;
|
||||
// sanity check gender for non-genderless PID cases
|
||||
} while (!pk4.IsGenderValid());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
using PKHeX.Core;
|
||||
|
@ -153,13 +153,17 @@ public static class PIDTests
|
|||
public static void VerifyResults(IReadOnlyList<uint[]> results, TeamLock[] team)
|
||||
{
|
||||
var pk = new PK3();
|
||||
Span<uint> seeds = stackalloc uint[XDRNG.MaxCountSeedsPID];
|
||||
for (int i = 0; i < results.Count; 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;
|
||||
foreach (var seed in seeds)
|
||||
foreach (var s in reg)
|
||||
{
|
||||
var seed = XDRNG.Prev3(s);
|
||||
PIDGenerator.SetValuesFromSeed(pk, PIDType.CXD, seed);
|
||||
var info = MethodFinder.Analyze(pk);
|
||||
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");
|
||||
}
|
||||
|
||||
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 =
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using PKHeX.Core;
|
||||
|
@ -145,11 +146,11 @@ public class PIDIVTest
|
|||
{
|
||||
// XD PokeSpots: Check all 3 Encounter Slots (examples are one for each location).
|
||||
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)
|
||||
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)
|
||||
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]
|
||||
|
@ -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]
|
||||
public void PIDIVMethod4IVs()
|
||||
{
|
||||
|
@ -225,7 +240,11 @@ public class PIDIVTest
|
|||
rand1 |= (uint)IVs[i] << (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]
|
||||
|
@ -234,9 +253,16 @@ public class PIDIVTest
|
|||
const uint seed = 0x2E15555E;
|
||||
const uint rand0 = 0x20AD96A9;
|
||||
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);
|
||||
Assert.Contains(pidseeds, z => z == seed);
|
||||
Assert.Contains(ivseeds, z => z == seed);
|
||||
|
||||
XDRNG.MaxCountSeedsIV.Should().BeGreaterThan(XDRNG.MaxCountSeedsPID);
|
||||
|
||||
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