PKHeX/PKHeX.Core/Saves/Encryption/SwishCrypto/SCXorShift32.cs
Kurt 47071b41f3
Refactoring: Span-based value writes and method signatures (#3361)
Existing `get`/`set` logic is flawed in that it doesn't work on Big Endian operating systems, and it allocates heap objects when it doesn't need to.

`System.Buffers.Binary.BinaryPrimitives` in the `System.Memory` NuGet package provides both Little Endian and Big Endian methods to read and write data; all the `get`/`set` operations have been reworked to use this new API. This removes the need for PKHeX's manual `BigEndian` class, as all functions are already covered by the BinaryPrimitives API.

The `StringConverter` has now been rewritten to accept a Span to read from & write to, no longer requiring a temporary StringBuilder.

Other Fixes included:
- The Super Training UI for Gen6 has been reworked according to the latest block structure additions.
- Cloning a Stadium2 Save File now works correctly (opening from the Folder browser list).
- Checksum & Sanity properties removed from parent PKM class, and is now implemented via interface.
2022-01-02 21:35:59 -08:00

82 lines
2.1 KiB
C#

using System.Runtime.CompilerServices;
namespace PKHeX.Core
{
/// <summary>
/// Self-mutating value that returns a crypto value to be xor-ed with another (unaligned) byte stream.
/// <para>
/// This implementation allows for yielding crypto bytes on demand.
/// </para>
/// </summary>
public ref struct SCXorShift32
{
private int Counter;
private uint Seed;
public SCXorShift32(uint seed)
{
#if NET5
var pop_count = System.Numerics.BitOperations.PopCount(seed);
#else
var pop_count = PopCount(seed);
#endif
for (var i = 0; i < pop_count; i++)
seed = XorshiftAdvance(seed);
Counter = 0;
Seed = seed;
}
/// <summary>
/// Gets a <see cref="byte"/> from the current state.
/// </summary>
public uint Next()
{
var c = Counter;
var result = (Seed >> (c << 3)) & 0xFF;
if (c == 3)
{
Seed = XorshiftAdvance(Seed);
Counter = 0;
}
else
{
++Counter;
}
return result;
}
/// <summary>
/// Gets a <see cref="uint"/> from the current state.
/// </summary>
public uint Next32()
{
return Next() | (Next() << 8) | (Next() << 16) | (Next() << 24);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint XorshiftAdvance(uint key)
{
key ^= key << 2;
key ^= key >> 15;
key ^= key << 13;
return key;
}
#if !NET5
/// <summary>
/// Count of bits set in value
/// </summary>
private static uint PopCount(uint x)
{
x -= ((x >> 1) & 0x55555555u);
x = (x & 0x33333333u) + ((x >> 2) & 0x33333333u);
x = (x + (x >> 4)) & 0x0F0F0F0Fu;
x += (x >> 8);
x += (x >> 16);
return x & 0x0000003Fu;
}
#endif
}
}