diff --git a/PKHeX.Core/Legality/RNG/Algorithms/LCRNG.cs b/PKHeX.Core/Legality/RNG/Algorithms/LCRNG.cs
new file mode 100644
index 000000000..582278c0b
--- /dev/null
+++ b/PKHeX.Core/Legality/RNG/Algorithms/LCRNG.cs
@@ -0,0 +1,78 @@
+using System.Runtime.CompilerServices;
+
+namespace PKHeX.Core
+{
+ ///
+ /// 32 Bit Linear Congruential Random Number Generator
+ ///
+ /// Frame advancement for forward and reverse.
+ ///
+ /// https://en.wikipedia.org/wiki/Linear_congruential_generator
+ ///
+ ///
+ /// seed_n+1 = seed_n * +
+ ///
+ ///
+ public class LCRNG
+ {
+ // Forward
+ protected readonly uint Mult;
+ private readonly uint Add;
+
+ // Reverse
+ private readonly uint rMult;
+ private readonly uint rAdd;
+
+ 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;
+ }
+
+ ///
+ /// Advances the RNG seed to the next state value.
+ ///
+ /// Current seed
+ /// Seed advanced a single time.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public uint Next(uint seed) => (seed * Mult) + Add;
+
+ ///
+ /// Reverses the RNG seed to the previous state value.
+ ///
+ /// Current seed
+ /// Seed reversed a single time.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public uint Prev(uint seed) => (seed * rMult) + rAdd;
+
+ ///
+ /// Advances the RNG seed to the next state value a specified amount of times.
+ ///
+ /// Current seed
+ /// Amount of times to advance.
+ /// Seed advanced the specified amount of times.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public uint Advance(uint seed, int frames)
+ {
+ for (int i = 0; i < frames; i++)
+ seed = Next(seed);
+ return seed;
+ }
+
+ ///
+ /// Reverses the RNG seed to the previous state value a specified amount of times.
+ ///
+ /// Current seed
+ /// Amount of times to reverse.
+ /// Seed reversed the specified amount of times.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public uint Reverse(uint seed, int frames)
+ {
+ for (int i = 0; i < frames; i++)
+ seed = Prev(seed);
+ return seed;
+ }
+ }
+}
diff --git a/PKHeX.Core/Legality/RNG/RNG.cs b/PKHeX.Core/Legality/RNG/Algorithms/RNG.cs
similarity index 64%
rename from PKHeX.Core/Legality/RNG/RNG.cs
rename to PKHeX.Core/Legality/RNG/Algorithms/RNG.cs
index f07734b8e..269747889 100644
--- a/PKHeX.Core/Legality/RNG/RNG.cs
+++ b/PKHeX.Core/Legality/RNG/Algorithms/RNG.cs
@@ -1,26 +1,31 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace PKHeX.Core
{
///
- /// 32 Bit Linear Congruential Random Number Generator
+ ///
///
///
+ ///
+ ///
/// Provides common RNG algorithms used by Generation 3 & 4.
- /// https://en.wikipedia.org/wiki/Linear_congruential_generator
+ /// 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.
+ ///
///
- public sealed class RNG
+ public sealed class RNG : LCRNG
{
/// LCRNG used for Encryption and mainline game RNG calls.
public static readonly RNG LCRNG = new(0x41C64E6D, 0x00006073, 0xEEB9EB65, 0x0A3561A1);
+
/// LCRNG used by Colosseum & XD for game RNG calls.
public static readonly RNG XDRNG = new(0x000343FD, 0x00269EC3, 0xB9B33155, 0xA170F641);
- /// Alternate LCRNG used by mainline game RNG calls to disassociate the seed from the , for anti-shiny and other purposes.
- public static readonly RNG ARNG = new(0x6C078965, 0x00000001, 0x9638806D, 0x69C77F93);
- private readonly uint Mult, Add, rMult, rAdd;
+ /// Alternate LCRNG used by mainline game RNG calls to disassociate the seed from the , for anti-shiny and other purposes.
+ 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;
@@ -33,36 +38,35 @@ public sealed class RNG
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)
- private RNG(uint f_mult, uint f_add, uint r_mult, uint r_add)
- {
- Mult = f_mult;
- Add = f_add;
- rMult = r_mult;
- rAdd = r_add;
+ #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 = Mult << 8;
- k0g = Mult * Mult;
+ k2 = f_mult << 8;
+ k0g = f_mult * f_mult;
k2s = k0g << 8;
- PopulateMeetMiddleArrays();
- t0 = Add - 0xFFFF;
- t1 = 0xFFFF * ((long) Mult + 1);
- }
- private void PopulateMeetMiddleArrays()
- {
- uint k4g = Add * (Mult + 1); // 1,3's multiplier
+ // 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, Mult, Add, flags, low8); // 1,2
- SetFlagData(i, k0g, k4g, g_flags, g_low8); // 1,3
+ 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.
@@ -79,83 +83,7 @@ private static void SetFlagData(uint i, uint mult, uint add, bool[] f, byte[] v)
// now the search only has to access the flags array once per loop.
}
- ///
- /// Advances the RNG seed to the next state value.
- ///
- /// Current seed
- /// Seed advanced a single time.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public uint Next(uint seed) => (seed * Mult) + Add;
-
- ///
- /// Reverses the RNG seed to the previous state value.
- ///
- /// Current seed
- /// Seed reversed a single time.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public uint Prev(uint seed) => (seed * rMult) + rAdd;
-
- ///
- /// Advances the RNG seed to the next state value a specified amount of times.
- ///
- /// Current seed
- /// Amount of times to advance.
- /// Seed advanced the specified amount of times.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public uint Advance(uint seed, int frames)
- {
- for (int i = 0; i < frames; i++)
- seed = Next(seed);
- return seed;
- }
-
- ///
- /// Reverses the RNG seed to the previous state value a specified amount of times.
- ///
- /// Current seed
- /// Amount of times to reverse.
- /// Seed reversed the specified amount of times.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public uint Reverse(uint seed, int frames)
- {
- for (int i = 0; i < frames; i++)
- seed = Prev(seed);
- return seed;
- }
-
- ///
- /// Generates an IV for each RNG call using the top 5 bits of frame seeds.
- ///
- /// RNG seed
- /// Array of 6 IVs as .
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal uint[] GetSequentialIVsUInt32(uint seed)
- {
- uint[] ivs = new uint[6];
- for (int i = 0; i < 6; i++)
- {
- seed = Next(seed);
- ivs[i] = seed >> 27;
- }
- return ivs;
- }
-
- ///
- /// Generates an IV for each RNG call using the top 5 bits of frame seeds.
- ///
- /// RNG seed
- /// Array of 6 IVs as .
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal int[] GetSequentialIVsInt32(uint seed)
- {
- int[] ivs = new int[6];
- for (int i = 0; i < 6; i++)
- {
- seed = Next(seed);
- ivs[i] = (int)(seed >> 27);
- }
- return ivs;
- }
+ #endregion
///
/// Gets the origin seeds for two successive 16 bit rand() calls using a meet-in-the-middle approach.
@@ -257,30 +185,4 @@ private IEnumerable GetPossibleSeedsEuclid(uint first, uint second, int bi
}
}
}
-
- public enum RNGType
- {
- /// No RNG type specified
- None,
-
- ///
- LCRNG,
-
- ///
- XDRNG,
-
- ///
- ARNG,
- }
-
- public static class RNGTypeUtil
- {
- public static RNG GetRNG(this RNGType type) => type switch
- {
- RNGType.LCRNG => RNG.LCRNG,
- RNGType.XDRNG => RNG.XDRNG,
- RNGType.ARNG => RNG.ARNG,
- _ => throw new ArgumentException(nameof(type))
- };
- }
}
diff --git a/PKHeX.Core/Legality/RNG/Algorithms/RNGType.cs b/PKHeX.Core/Legality/RNG/Algorithms/RNGType.cs
new file mode 100644
index 000000000..cd18e610d
--- /dev/null
+++ b/PKHeX.Core/Legality/RNG/Algorithms/RNGType.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace PKHeX.Core
+{
+ public enum RNGType
+ {
+ /// No RNG type specified
+ None,
+
+ ///
+ LCRNG,
+
+ ///
+ XDRNG,
+
+ ///
+ ARNG,
+ }
+
+ public static class RNGTypeUtil
+ {
+ public static LCRNG GetRNG(this RNGType type) => type switch
+ {
+ RNGType.LCRNG => RNG.LCRNG,
+ RNGType.XDRNG => RNG.XDRNG,
+ RNGType.ARNG => RNG.ARNG,
+ _ => throw new ArgumentException(nameof(type))
+ };
+ }
+}
diff --git a/PKHeX.Core/Legality/RNG/Algorithms/RNGUtil.cs b/PKHeX.Core/Legality/RNG/Algorithms/RNGUtil.cs
new file mode 100644
index 000000000..115a015c4
--- /dev/null
+++ b/PKHeX.Core/Legality/RNG/Algorithms/RNGUtil.cs
@@ -0,0 +1,43 @@
+using System.Runtime.CompilerServices;
+
+namespace PKHeX.Core
+{
+ public static class RNGUtil
+ {
+ ///
+ /// Generates an IV for each RNG call using the top 5 bits of frame seeds.
+ ///
+ /// RNG to use
+ /// RNG seed
+ /// Array of 6 IVs as .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static uint[] GetSequentialIVsUInt32(this LCRNG rng, uint seed)
+ {
+ uint[] ivs = new uint[6];
+ for (int i = 0; i < 6; i++)
+ {
+ seed = rng.Next(seed);
+ ivs[i] = seed >> 27;
+ }
+ return ivs;
+ }
+
+ ///
+ /// Generates an IV for each RNG call using the top 5 bits of frame seeds.
+ ///
+ /// RNG to use
+ /// RNG seed
+ /// Array of 6 IVs as .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static int[] GetSequentialIVsInt32(this LCRNG rng, uint seed)
+ {
+ int[] ivs = new int[6];
+ for (int i = 0; i < 6; i++)
+ {
+ seed = rng.Next(seed);
+ ivs[i] = (int)(seed >> 27);
+ }
+ return ivs;
+ }
+ }
+}
diff --git a/PKHeX.Core/Legality/RNG/Xoroshiro128Plus.cs b/PKHeX.Core/Legality/RNG/Algorithms/Xoroshiro128Plus.cs
similarity index 92%
rename from PKHeX.Core/Legality/RNG/Xoroshiro128Plus.cs
rename to PKHeX.Core/Legality/RNG/Algorithms/Xoroshiro128Plus.cs
index 22e52651b..93f84b57d 100644
--- a/PKHeX.Core/Legality/RNG/Xoroshiro128Plus.cs
+++ b/PKHeX.Core/Legality/RNG/Algorithms/Xoroshiro128Plus.cs
@@ -2,6 +2,10 @@
namespace PKHeX.Core
{
+ ///
+ /// Self-modifying RNG structure that implements xoroshiro128+
+ ///
+ /// https://en.wikipedia.org/wiki/Xoroshiro128%2B
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Unused")]
public ref struct Xoroshiro128Plus
{
diff --git a/PKHeX.Core/Legality/RNG/PIDGenerator.cs b/PKHeX.Core/Legality/RNG/PIDGenerator.cs
index 30faf91f2..9ea960cea 100644
--- a/PKHeX.Core/Legality/RNG/PIDGenerator.cs
+++ b/PKHeX.Core/Legality/RNG/PIDGenerator.cs
@@ -2,6 +2,9 @@
namespace PKHeX.Core
{
+ ///
+ /// Contains a collection of methods that mutate the input Pokémon object, usually to obtain a correlation.
+ ///
public static class PIDGenerator
{
private static void SetValuesFromSeedLCRNG(PKM pk, PIDType type, uint seed)
diff --git a/PKHeX.Core/Legality/RNG/PIDType.cs b/PKHeX.Core/Legality/RNG/PIDType.cs
index f066bc9ab..97c62f44e 100644
--- a/PKHeX.Core/Legality/RNG/PIDType.cs
+++ b/PKHeX.Core/Legality/RNG/PIDType.cs
@@ -1,5 +1,9 @@
namespace PKHeX.Core
{
+ ///
+ /// PID + IV correlation.
+ ///
+ /// This is just a catch-all enumeration to describe the different correlations.
public enum PIDType
{
/// No relationship between the PID and IVs
@@ -46,7 +50,7 @@ public enum PIDType
///
/// Event Reversed Order PID restricted to 16bit Origin Seed
///
- ///
+ /// seed is clamped to 16bits.
BACD_R,
///
@@ -58,7 +62,7 @@ public enum PIDType
///
/// Event Reversed Order PID restricted to 16bit Origin Seed, antishiny.
///
- ///
+ /// seed is clamped to 16bits.
BACD_R_A,
///
@@ -70,7 +74,7 @@ public enum PIDType
///
/// Event Reversed Order PID restricted to 8bit Origin Seed, shiny
///
- ///
+ /// seed is clamped to 16bits.
BACD_R_S,
///
@@ -82,7 +86,7 @@ public enum PIDType
///
/// Event Reversed Order PID restricted to 16bit Origin Seed, antishiny (nyx)
///
- ///
+ /// seed is clamped to 16bits.
BACD_R_AX,
///
@@ -92,7 +96,7 @@ public enum PIDType
BACD_U_AX,
///
- /// Generation 4 Cute Charm forced to an 8 bit buffered PID
+ /// Generation 4 Cute Charm PID, which is forced to an 8 bit PID value based on the gender & gender ratio value.
///
///
CuteCharm,
@@ -108,31 +112,31 @@ public enum PIDType
#region XDRNG
///
- /// Standard PIDIV
+ /// Generation 3 PID+IV correlation.
///
///
CXD,
///
- /// Antishiny Rerolled PIDIV
+ /// Generation 3 PID+IV correlation that was rerolled because it was shiny.
///
///
CXDAnti,
///
- /// Standard PIDIV which is immediately after the RNG calls that create the TID and SID.
+ /// Generation 3 PID+IV which is created immediately after the TID and SID RNG calls.
///
- ///
+ /// . The second starter is created after the first starter, with the same TID and SID.
CXD_ColoStarter,
///
- /// Pokémon Channel Jirachi
+ /// Generation 3 Pokémon Channel Jirachi
///
///
Channel,
///
- /// XD PokeSpot PID
+ /// Generation 3 PokeSpot PID
///
///
PokeSpot,
@@ -142,7 +146,7 @@ public enum PIDType
#region ARNG
///
- /// 4th Generation Mystery Gift Anti-Shiny
+ /// Generation 4 Mystery Gift Anti-Shiny
///
///
G4MGAntiShiny,
@@ -152,27 +156,27 @@ public enum PIDType
#region Formulaic
///
- /// 5th Generation Mystery Gift Shiny
+ /// Generation 5 Mystery Gift Shiny
///
/// Formulaic based on TID, SID, and Gender bytes.
/// Unrelated to IVs
G5MGShiny,
///
- /// 4th Generation Pokewalker PID, never Shiny.
+ /// Generation 4 Pokewalker PID, never Shiny.
///
/// Formulaic based on TID, SID, and Gender bytes.
/// Unrelated to IVs
Pokewalker,
///
- /// 8th Generation Raid PID
+ /// Generation 8 Raid PID
///
/// Formulaic based on PID & EC values from a 64bit-seed.
Raid8,
///
- /// 8th Generation Overworld Spawn PID
+ /// Generation 8 Overworld Spawn PID
///
/// Formulaic based on PID & EC values from a 32bit-seed.
Overworld8,