Add ivs->channel seed recovery

Still lack IVs/nature/shiny request for the BACD/M2 variants, maybe in the future those can be added.
This commit is contained in:
Kurt
2025-03-05 22:45:41 -06:00
parent 8b5c07b07d
commit 4d702d261d
2 changed files with 92 additions and 2 deletions

View File

@@ -269,6 +269,24 @@ private static uint GetMethod2(ref uint seed)
private static uint SetPINGAChannel(PK3 pk, EncounterCriteria criteria)
{
if (criteria.IsSpecifiedIVsAll())
{
Span<uint> seeds = stackalloc uint[XDRNG.MaxCountSeedsChannel];
var count = XDRNG.GetSeedsChannel(seeds, (uint)criteria.IV_HP, (uint)criteria.IV_ATK, (uint)criteria.IV_DEF, (uint)criteria.IV_SPA, (uint)criteria.IV_SPD, (uint)criteria.IV_SPE);
foreach (var seed in seeds[..count])
{
if (!ChannelJirachi.IsPossible(seed))
continue;
PIDGenerator.SetValuesFromSeedChannel(pk, seed);
var pid = pk.EncryptionConstant;
if (criteria.IsSpecifiedNature() && !criteria.IsSatisfiedNature((Nature)(pid % 25)))
continue; // try again
if (criteria.Shiny.IsShiny() != ShinyUtil.GetIsShiny(pk.ID32, pid, 8))
continue; // try again
return seed;
}
}
while (true)
{
uint seed = Util.Rand32();

View File

@@ -105,6 +105,17 @@ public static uint Next16(ref uint seed)
return seed >> 16;
}
/// <summary>
/// Gets the upper 5 bits of the next RNG seed.
/// </summary>
/// <param name="seed">Seed to advance one step.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Next5(ref uint seed)
{
seed = Next(seed);
return seed >> 27;
}
/// <summary>
/// Gets the upper 0x7FFF bits of the next RNG seed.
/// </summary>
@@ -177,8 +188,7 @@ public static uint GetSequentialIV32(uint seed)
uint result = 0;
for (int i = 0; i < 6; i++)
{
seed = Next(seed);
var value = seed >> 27; // extract top 5 bits
var value = Next5(ref seed); // extract top 5 bits
result |= value << (i * 5);
}
return result;
@@ -190,6 +200,7 @@ public static uint GetSequentialIV32(uint seed)
// Instead of using yield and iterators, we calculate all results in a tight loop and return the count found.
public const int MaxCountSeedsPID = 2;
public const int MaxCountSeedsIV = 6;
public const int MaxCountSeedsChannel = 12;
// Euclidean division constants
private const uint Sub = Add - 0xFFFF;
@@ -272,6 +283,67 @@ public static int GetSeedsIVs(Span<uint> result, uint first, uint second)
return ctr;
}
public static int GetSeedsChannel(Span<uint> result, uint hp, uint atk, uint def, uint spa, uint spd, uint spe)
{
// Recovers seeds from 6 successive calls to rand() that returns the top 5 bits of each.
// Mult(j) = Mult^j
// Add(j) = Add * (Mult^0 + Mult^1 + ... + Mult^(j-1))
// Using j = 3 and XDRNG gives Mult = 0x45c82be5 and Add = 0xd2f65b55
const uint mult = Mult3; // Modified mult (3 advances)
const uint sub = 0xcaf65b56; // Modified add - 0x7ffffff
const ulong b = 0x22e415e_ea37d41a; // (Modified mult + 1) * 0x7ffffff
const ulong prime = 3;
const ulong add = 0x3_0000_0000; // prime * 0x1_0000_0000
const uint rmax = 0x__1800_0000; // prime * 0x__0800_0000
const ulong skip = 0x661D29; // prime * 2^32 % mult
uint first = hp << 27;
uint t = (((spe << 27) - mult * first) - sub);
uint kmax = (uint)((b - t) >> 32);
ulong x = (t * prime) % mult;
int ctr = 0;
for (ulong k = 0; k <= kmax;)
{
var r = (x + skip * k) % mult;
var m = r % prime;
if (m != 0)
{
m = m == 1 ? 2u : 1u;
r += m * skip;
k += (byte)m;
}
var tmp = k << 32 | t;
while (r < rmax && k <= kmax)
{
uint seed = first | (uint)(tmp / mult); // hp
if (Next5(ref seed) == atk)
{
if (Next5(ref seed) == def)
{
_ = Next5(ref seed); // spe
if (Next5(ref seed) == spa)
{
if (Next5(ref seed) == spd)
result[ctr++] = Prev12(seed); // unroll to origin
}
}
}
r += prime * skip;
k += prime;
tmp += add;
}
// Rounding up without using floats
k += ((mult - r) + skip - 1) / skip;
}
return ctr;
}
/// <summary>
/// Multiplication constants for jumping 2^(index) frames forward.
/// </summary>