CXD: check naming screen->TID/SID frame pattern

Adapted from e5255b1313/PokemonCoRNGLibrary/Util/LCGExtensions.cs (L15)
This commit is contained in:
Kurt
2025-03-10 22:25:53 -05:00
parent f4cfc39173
commit ca81fcd250
3 changed files with 57 additions and 16 deletions

View File

@@ -94,6 +94,9 @@ public static class XDRNG
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev11(uint seed) => (seed * rMult11) + rAdd11;
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev12(uint seed) => (seed * rMult12) + rAdd12;
public static uint Next1000(uint seed) => 0xDD867B21 * seed + 0xD252C5A8;
public static uint Prev1000(uint seed) => 0x251CC8E1 * seed + 0x94750758;
/// <summary>
/// Gets the upper 16 bits of the next RNG seed.
/// </summary>

View File

@@ -100,20 +100,20 @@ public static bool SetFromIVsCXD<TEntity>(TEntity pk, EncounterCriteria criteria
/// </summary>
/// <param name="tid">Generation 3 Trainer ID</param>
/// <param name="sid">Generation 3 Secret ID</param>
/// <param name="seed">Possible starting seed</param>
/// <param name="origin">Possible starting seed</param>
/// <returns>True if a seed was found, false if no seed was found</returns>
public static bool TryGetSeedTrainerID(ushort tid, ushort sid, out uint seed)
public static bool TryGetSeedTrainerID(ushort tid, ushort sid, out uint origin)
{
Span<uint> seeds = stackalloc uint[XDRNG.MaxCountSeedsPID];
var count = XDRNG.GetSeeds(seeds, (uint)tid << 16, (uint)sid << 16);
if (count == 0)
foreach (var s in seeds[..count])
{
seed = 0;
return false;
var prev1k = XDRNG.Prev1000(s);
if (IsReachableFromNameScreen(prev1k, out origin))
return true;
}
seed = seeds[0];
return true;
origin = 0;
return false;
}
public static bool TryGetShinySID(ushort tid, out ushort sid, uint xor, uint bits)
@@ -626,4 +626,36 @@ public static bool TryGetOriginSeedStarterXD(PKM pk, out uint result)
result = 0;
return false;
}
/// <summary>
/// Checks if the provided <see cref="seed"/> can be landed at after confirming the player name.
/// </summary>
/// <param name="seed">Seed that goes on to generate the player TID/SID (after advancing 1000 frames)</param>
/// <param name="origin">Current seed that confirms the player name</param>
/// <returns>True if <see cref="seed"/> can be reached via <see cref="origin"/></returns>
/// <remarks>https://github.com/yatsuna827/PokemonCoRNGLibrary/blob/e5255b13134ab3a2119788c40b5e71ee48d849b0/PokemonCoRNGLibrary/Util/LCGExtensions.cs#L15</remarks>
public static bool IsReachableFromNameScreen(uint seed, out uint origin)
{
// rand() / 10 == 0 is a skip
const ushort SEED_THRESHOLD = 0x1999; // Only need the upper 16 bits
var p1 = XDRNG.Prev16(ref seed) > SEED_THRESHOLD;
var p2 = XDRNG.Prev16(ref seed) > SEED_THRESHOLD;
var p3 = XDRNG.Prev16(ref seed) > SEED_THRESHOLD;
var p4 = XDRNG.Prev16(ref seed) > SEED_THRESHOLD;
if (p1 && p2 && p3 && p4)
{
origin = seed;
return true;
}
if (XDRNG.Prev16(ref seed) <= SEED_THRESHOLD && IsReachableFromNameScreen(XDRNG.Prev(seed), out origin)) return true;
if (XDRNG.Prev16(ref seed) <= SEED_THRESHOLD && p1 && IsReachableFromNameScreen(XDRNG.Prev(seed), out origin)) return true;
if (XDRNG.Prev16(ref seed) <= SEED_THRESHOLD && p1 && p2 && IsReachableFromNameScreen(XDRNG.Prev(seed), out origin)) return true;
if (XDRNG.Prev16(ref seed) <= SEED_THRESHOLD && p1 && p2 && p3 && IsReachableFromNameScreen(XDRNG.Prev(seed), out origin)) return true;
origin = 0;
return false;
}
}

View File

@@ -142,14 +142,20 @@ public void PIDIVMatchingTest5()
MethodFinder.Analyze(pkS5).Type.Should().Be(PIDType.G5MGShiny);
}
[Fact]
public void PIDIVPokeSpotTest()
{
// XD PokeSpots: Check all 3 Encounter Slots (examples are one for each location).
MethodPokeSpot.TryGetOriginSeedPID(0x7B2D9DA7, 0, out _).Should().BeTrue(); // Zubat (Cave) (Common)
MethodPokeSpot.TryGetOriginSeedPID(0x3EE9AF66, 1, out _).Should().BeTrue(); // Gligar (Rock) (Uncommon)
MethodPokeSpot.TryGetOriginSeedPID(0x9B667F3C, 2, out _).Should().BeTrue(); // Surskit (Oasis) (Rare)
}
[Theory]
[InlineData(0x7B2D9DA7, 0)] // Zubat (Cave) (Common)
[InlineData(0x3EE9AF66, 1)] // Gligar (Rock) (Uncommon)
[InlineData(0x9B667F3C, 2)] // Surskit (Oasis) (Rare)
public void PIDIVPokeSpotTest(uint pid, byte slot) => MethodPokeSpot.TryGetOriginSeedPID(pid, slot, out _).Should().BeTrue();
[Theory]
[InlineData(62714, 62938)]
[InlineData(05724, 31840)]
[InlineData(31827, 52374)]
[InlineData(34952, 34952)]
[InlineData(31827, 00123, false)]
[InlineData(34952, 01337, false)]
public void CXDTrainerTest(ushort tid, ushort sid, bool expect = true) => MethodCXD.TryGetSeedTrainerID(tid, sid, out _).Should().Be(expect);
[Theory]
[InlineData(30, 31, 31, 14, 31, 31, 0x28070031, 024, PokewalkerSeedType.NoStroll)]