Misc tweaks

Add Cap Pikachu const value updating for USUM, also set Magearna const for USUM (not only SM)
Remove `NotDistributed` property from WC3 as only CXD events used it, no longer WC3 type.
Delete ArrayUtil; move remaining methods into the StorageUtil class that used them for compressing/inserting bulk into boxes.
Add shiny wormhole flag
Extract byte array strings into spans, faster tests and less overhead.
This commit is contained in:
Kurt 2024-06-09 08:52:32 -05:00
parent 84d42807e0
commit 67fc0d0885
28 changed files with 552 additions and 312 deletions

View file

@ -24,8 +24,8 @@ public sealed class EventWorkspace<TSave, TWork> where TSave : class, IEventFlag
{ {
SAV.SetEventFlags(Flags); SAV.SetEventFlags(Flags);
SAV.SetAllEventWork(Values); SAV.SetAllEventWork(Values);
if (SAV is EventWork7SM s7) // Ensure Magearna event flag has magic constant if (SAV is EventWork7 sm) // Ensure QR event flag has magic constant(s)
s7.UpdateMagearnaConstant(); sm.UpdateQrConstants();
} }
private static string GetResourceSuffix(GameVersion version) => version switch private static string GetResourceSuffix(GameVersion version) => version switch

View file

@ -56,23 +56,10 @@ public sealed class FilteredGameDataSource
}; };
static IEnumerable<ComboItem> FilterAbove(IReadOnlyList<ComboItem> species, int limit) static IEnumerable<ComboItem> FilterAbove(IReadOnlyList<ComboItem> species, int limit)
{ => species.Where(s => s.Value <= limit);
foreach (var s in species)
{
if (s.Value <= limit)
yield return s;
}
}
static IEnumerable<ComboItem> FilterUnavailable<T>(IReadOnlyList<ComboItem> source, T table) where T : IPersonalTable static IEnumerable<ComboItem> FilterUnavailable<T>(IReadOnlyList<ComboItem> source, T table) where T : IPersonalTable
{ => source.Where(s => table.IsSpeciesInGame((ushort)s.Value));
foreach (var s in source)
{
var species = s.Value;
if (table.IsSpeciesInGame((ushort)species))
yield return s;
}
}
} }
private static IEnumerable<ComboItem> GetFilteredMoves(IGameValueLimit limit, GameDataSource source, bool HaX = false) private static IEnumerable<ComboItem> GetFilteredMoves(IGameValueLimit limit, GameDataSource source, bool HaX = false)
@ -103,27 +90,27 @@ public sealed class FilteredGameDataSource
public readonly IReadOnlyList<ComboItem> G4GroundTiles; public readonly IReadOnlyList<ComboItem> G4GroundTiles;
public readonly IReadOnlyList<ComboItem> ConsoleRegions = GameDataSource.Regions; public readonly IReadOnlyList<ComboItem> ConsoleRegions = GameDataSource.Regions;
public IReadOnlyList<ComboItem> GetAbilityList(PKM pk) private const char HiddenAbilitySuffix = 'H';
{ private const char AbilityIndexSuffix = '1';
return GetAbilityList(pk.PersonalInfo);
} public IReadOnlyList<ComboItem> GetAbilityList(PKM pk) => GetAbilityList(pk.PersonalInfo);
public IReadOnlyList<ComboItem> GetAbilityList(IPersonalAbility pi) public IReadOnlyList<ComboItem> GetAbilityList(IPersonalAbility pi)
{ {
var list = new ComboItem[pi.AbilityCount]; var list = new ComboItem[pi.AbilityCount];
LoadAbilityList(pi, list, Source.Strings.abilitylist);
var alist = Source.Strings.Ability;
var suffixes = AbilityIndexSuffixes;
for (int i = 0; i < list.Length; i++)
{
var ability = pi.GetAbilityAtIndex(i);
var suffix = suffixes[i];
var display = $"{alist[ability]} ({suffix})";
list[i] = new ComboItem(display, ability);
}
return list; return list;
} }
private static ReadOnlySpan<char> AbilityIndexSuffixes => ['1', '2', 'H']; private static void LoadAbilityList(IPersonalAbility pi, Span<ComboItem> list, ReadOnlySpan<string> names)
{
for (int i = 0; i < list.Length; i++)
{
var value = pi.GetAbilityAtIndex(i);
var name = names[value];
char suffix = i == 2 ? HiddenAbilitySuffix : (char)(AbilityIndexSuffix + i);
var display = $"{name} ({suffix})";
list[i] = new ComboItem(display, value);
}
}
} }

View file

@ -261,8 +261,6 @@ public record struct EncounterPossible3(EvoCriteria[] Chain, EncounterTypeGroup
{ {
if (evo.Species != enc.Species) if (evo.Species != enc.Species)
continue; continue;
if (enc.NotDistributed)
break;
return SetCurrent(enc); return SetCurrent(enc);
} }
} }

View file

@ -23,8 +23,8 @@ public static class EncounterVerifier
{ {
EncounterEgg e => VerifyEncounterEgg(pk, e.Generation), EncounterEgg e => VerifyEncounterEgg(pk, e.Generation),
EncounterShadow3Colo { IsEReader: true } when pk.Language != (int)LanguageID.Japanese => GetInvalid(LG3EReader), EncounterShadow3Colo { IsEReader: true } when pk.Language != (int)LanguageID.Japanese => GetInvalid(LG3EReader),
EncounterStatic3 { Species: (int)Species.Mew, Location: 201 } when pk.Language != (int)LanguageID.Japanese => GetInvalid(LEncUnreleasedEMewJP), EncounterStatic3 { Species: (int)Species.Mew } when pk.Language != (int)LanguageID.Japanese => GetInvalid(LEncUnreleasedEMewJP),
EncounterStatic3 { Species: (int)Species.Deoxys, Location: 200 } when pk.Language == (int)LanguageID.Japanese => GetInvalid(LEncUnreleased), EncounterStatic3 { Species: (int)Species.Deoxys } when pk.Language == (int)LanguageID.Japanese => GetInvalid(LEncUnreleased),
EncounterStatic4 { Roaming: true } when pk is G4PKM { MetLocation: 193, GroundTile: GroundTileType.Water } => GetInvalid(LG4InvalidTileR45Surf), EncounterStatic4 { Roaming: true } when pk is G4PKM { MetLocation: 193, GroundTile: GroundTileType.Water } => GetInvalid(LG4InvalidTileR45Surf),
MysteryGift g => VerifyEncounterEvent(pk, g), MysteryGift g => VerifyEncounterEvent(pk, g),
{ IsEgg: true } when !pk.IsEgg => VerifyEncounterEgg(pk, enc.Generation), { IsEgg: true } when !pk.IsEgg => VerifyEncounterEgg(pk, enc.Generation),
@ -315,6 +315,7 @@ public static class EncounterVerifier
{ {
// Pokémon that evolve on trade can not be in the phase evolution after the trade // Pokémon that evolve on trade can not be in the phase evolution after the trade
// If the trade holds an Everstone, EvolveOnTrade will be false for the encounter // If the trade holds an Everstone, EvolveOnTrade will be false for the encounter
// No need to range check the species, as it matched to a valid encounter species.
var names = ParseSettings.SpeciesStrings; var names = ParseSettings.SpeciesStrings;
var evolved = names[species + 1]; var evolved = names[species + 1];
var unevolved = names[species]; var unevolved = names[species];

View file

@ -114,18 +114,14 @@ public sealed class LegalityAnalysis
Valid = false; Valid = false;
// Moves and Relearn arrays can potentially be empty on error. // Moves and Relearn arrays can potentially be empty on error.
var moves = Info.Moves; foreach (ref var p in Info.Moves.AsSpan())
for (var i = 0; i < moves.Length; i++)
{ {
ref var p = ref moves[i];
if (!p.IsParsed) if (!p.IsParsed)
p = MoveResult.Unobtainable(); p = MoveResult.Unobtainable();
} }
moves = Info.Relearn; foreach (ref var p in Info.Relearn.AsSpan())
for (var i = 0; i < moves.Length; i++)
{ {
ref var p = ref moves[i];
if (!p.IsParsed) if (!p.IsParsed)
p = MoveResult.Unobtainable(); p = MoveResult.Unobtainable();
} }
@ -205,9 +201,6 @@ public sealed class LegalityAnalysis
if (Entity.Version == GameVersion.CXD) if (Entity.Version == GameVersion.CXD)
CXD.Verify(this); CXD.Verify(this);
if (Info.EncounterMatch is WC3 {NotDistributed: true})
AddLine(Severity.Invalid, LEncUnreleased, CheckIdentifier.Encounter);
if (Entity.Format >= 8) if (Entity.Format >= 8)
Transfer.VerifyTransferLegalityG8(this); Transfer.VerifyTransferLegalityG8(this);
} }

View file

@ -69,16 +69,10 @@ public sealed class EffortValueVerifier : Verifier
} }
// Hard cap at 252 for Gen6+ // Hard cap at 252 for Gen6+
private static bool IsAnyAboveHardLimit6(ReadOnlySpan<int> evs) => evs.ContainsAny(253, 254, 255); private static bool IsAnyAboveHardLimit6(ReadOnlySpan<int> evs)
=> evs.ContainsAnyExceptInRange(0, EffortValues.Max252);
// Vitamins can only raise to 100 in Gen3/4 // Vitamins can only raise to 100 in Gen3/4
private static bool IsAnyAboveVitaminLimit(ReadOnlySpan<int> evs) private static bool IsAnyAboveVitaminLimit(ReadOnlySpan<int> evs)
{ => evs.ContainsAnyExceptInRange(0, EffortValues.MaxVitamins34);
foreach (var iv in evs)
{
if (iv > EffortValues.MaxVitamins34)
return true;
}
return false;
}
} }

View file

@ -68,10 +68,13 @@ public static class MoveInfo
_ => false, _ => false,
}; };
private const uint DynamaxMoveCount = (int)MaxSteelspike - (int)MaxFlare + 1;
private const uint TorqueMoveCount = (int)MagicalTorque - (int)BlazingTorque + 1;
/// <summary> /// <summary>
/// Checks if the move is a Dynamax-only move. /// Checks if the move is a Dynamax-only move.
/// </summary> /// </summary>
public static bool IsMoveDynamax(ushort move) => move is (>= (int)MaxFlare and <= (int)MaxSteelspike); public static bool IsMoveDynamax(ushort move) => move - (uint)MaxFlare < DynamaxMoveCount;
/// <summary> /// <summary>
/// Checks if the move can be known by anything in any context. /// Checks if the move can be known by anything in any context.
@ -82,7 +85,7 @@ public static class MoveInfo
/// <summary> /// <summary>
/// Checks if the move is a Starmobile-only move. /// Checks if the move is a Starmobile-only move.
/// </summary> /// </summary>
public static bool IsMoveTorque(ushort move) => move - (uint)BlazingTorque <= 4; public static bool IsMoveTorque(ushort move) => move - (uint)BlazingTorque < TorqueMoveCount;
/// <summary> /// <summary>
/// Checks if the <see cref="move"/> is unable to be used in battle. /// Checks if the <see cref="move"/> is unable to be used in battle.
@ -157,6 +160,7 @@ public static class MoveInfo
/// <summary> /// <summary>
/// Checks if the move can be sketched in any game. /// Checks if the move can be sketched in any game.
/// </summary> /// </summary>
/// <param name="move">Sketched move</param>
private static bool IsSketchPossible(ushort move) => move switch private static bool IsSketchPossible(ushort move) => move switch
{ {
// Can't Sketch // Can't Sketch
@ -172,8 +176,8 @@ public static class MoveInfo
/// <summary> /// <summary>
/// Checks if the move can be sketched in a specific game context. Pre-check with <see cref="IsSketchPossible(ushort)"/>. /// Checks if the move can be sketched in a specific game context. Pre-check with <see cref="IsSketchPossible(ushort)"/>.
/// </summary> /// </summary>
/// <param name="move"></param> /// <param name="move">Sketched move</param>
/// <param name="context"></param> /// <param name="context">Context currently present in</param>
private static bool IsSketchPossible(ushort move, EntityContext context) => context switch private static bool IsSketchPossible(ushort move, EntityContext context) => context switch
{ {
Gen6 when move is (int)ThousandArrows or (int)ThousandWaves => false, Gen6 when move is (int)ThousandArrows or (int)ThousandWaves => false,

View file

@ -43,7 +43,6 @@ public sealed class WC3(GameVersion Version, bool Fateful = false)
public override ushort Species { get; set; } public override ushort Species { get; set; }
public override bool IsEgg { get; set; } public override bool IsEgg { get; set; }
public override Moveset Moves { get; set; } public override Moveset Moves { get; set; }
public bool NotDistributed { get; init; }
public override Shiny Shiny { get; init; } public override Shiny Shiny { get; init; }
public override GameVersion Version { get; } = Version; public override GameVersion Version { get; } = Version;
public override bool FatefulEncounter { get; } = Fateful; // Obedience Flag public override bool FatefulEncounter { get; } = Fateful; // Obedience Flag

View file

@ -931,10 +931,8 @@ public abstract class PKM : ISpeciesForm, ITrainerID32, IGeneration, IShiny, ILa
var rnd = Util.Rand; var rnd = Util.Rand;
for (int i = 0; i < ivs.Length; i++) for (int i = 0; i < ivs.Length; i++)
{ {
if (template[i] == -1) var spec = template[i];
ivs[i] = rnd.Next(MaxIV + 1); ivs[i] = spec != -1 ? spec : rnd.Next(MaxIV + 1);
else
ivs[i] = template[i];
} }
SetIVs(ivs); SetIVs(ivs);
} }

View file

@ -97,20 +97,6 @@ public static class MemeCrypto
return hash[..8].SequenceEqual(output[^8..]); return hash[..8].SequenceEqual(output[^8..]);
} }
public static bool VerifyMemeData(ReadOnlySpan<byte> input, out byte[] output, int offset, int length, MemeKeyIndex keyIndex)
{
var data = input.Slice(offset, length);
if (VerifyMemeData(data, out output, keyIndex))
{
var newOutput = input.ToArray();
output.CopyTo(newOutput, offset);
output = newOutput;
return true;
}
output = [];
return false;
}
public static byte[] SignMemeData(ReadOnlySpan<byte> input, MemeKeyIndex keyIndex = MemeKeyIndex.PokedexAndSaveFile) public static byte[] SignMemeData(ReadOnlySpan<byte> input, MemeKeyIndex keyIndex = MemeKeyIndex.PokedexAndSaveFile)
{ {
var output = input.ToArray(); var output = input.ToArray();

View file

@ -144,11 +144,11 @@ public readonly ref struct MemeKey
private byte[] GetAesKey(ReadOnlySpan<byte> data) private byte[] GetAesKey(ReadOnlySpan<byte> data)
{ {
// HashLengthInBytes is 20. // HashLengthInBytes is 20.
Span<byte> hash = stackalloc byte[20]; Span<byte> hash = stackalloc byte[SHA1.HashSizeInBytes];
using var h = IncrementalHash.CreateHash(HashAlgorithmName.SHA1); using var h = IncrementalHash.CreateHash(HashAlgorithmName.SHA1);
h.AppendData(DER); h.AppendData(DER);
h.AppendData(data); h.AppendData(data);
h.TryGetCurrentHash(hash, out _); h.GetCurrentHash(hash);
return hash[..chunk].ToArray(); // need a byte[0x10] (not 0x14) for the AES impl return hash[..chunk].ToArray(); // need a byte[0x10] (not 0x14) for the AES impl
} }

View file

@ -136,7 +136,7 @@ public sealed class SCBlock
bw.Write((byte)((byte)SubType ^ xk.Next())); bw.Write((byte)((byte)SubType ^ xk.Next()));
} }
foreach (ref var b in Data.AsSpan()) foreach (var b in Data)
bw.Write((byte)(b ^ xk.Next())); bw.Write((byte)(b ^ xk.Next()));
} }

View file

@ -77,7 +77,7 @@ public static class SwishCrypto
h.AppendData(IntroHashBytes); h.AppendData(IntroHashBytes);
h.AppendData(data); h.AppendData(data);
h.AppendData(OutroHashBytes); h.AppendData(OutroHashBytes);
h.TryGetCurrentHash(hash, out _); h.GetCurrentHash(hash);
} }
/// <summary> /// <summary>

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using static System.Buffers.Binary.BinaryPrimitives; using static System.Buffers.Binary.BinaryPrimitives;
@ -75,9 +76,10 @@ public sealed class SAV3GCMemoryCard(Memory<byte> Raw)
ushort csum = 0; ushort csum = 0;
ushort inv_csum = 0; ushort inv_csum = 0;
for (int i = 0; i < span.Length; i += 2) var cast = MemoryMarshal.Cast<byte, ushort>(span);
foreach (var u16 in cast)
{ {
var value = ReadUInt16BigEndian(span[i..]); var value = BitConverter.IsLittleEndian ? ReverseEndianness(u16) : u16;
csum += value; csum += value;
inv_csum += (ushort)~value; inv_csum += (ushort)~value;
} }
@ -155,6 +157,9 @@ public sealed class SAV3GCMemoryCard(Memory<byte> Raw)
private bool IsCorruptedMemoryCard() private bool IsCorruptedMemoryCard()
{ {
var csums = VerifyChecksums(); var csums = VerifyChecksums();
if (csums == MemoryCardChecksumStatus.None)
return false; // eager return, true for all correct Memory Cards.
if (csums.HasFlag(MemoryCardChecksumStatus.HeaderBad)) if (csums.HasFlag(MemoryCardChecksumStatus.HeaderBad))
return true; return true;

View file

@ -281,11 +281,13 @@ public sealed class SAV3E : SAV3, IGen3Hoenn, IGen3Joyful, IGen3Wonder, IDaycare
private const uint EXTRADATA_SENTINEL = 0x0000B39D; private const uint EXTRADATA_SENTINEL = 0x0000B39D;
private const int OFS_BV = 31 * 0x1000; // last sector of the save private const int OFS_BV = 31 * 0x1000; // last sector of the save
public bool HasBattleVideo => Data.Length > SaveUtil.SIZE_G3RAWHALF && ReadUInt32LittleEndian(Data.AsSpan(OFS_BV)) == EXTRADATA_SENTINEL; public bool HasBattleVideo => Data.Length > SaveUtil.SIZE_G3RAWHALF && ReadUInt32LittleEndian(Data.AsSpan(OFS_BV)) == EXTRADATA_SENTINEL;
public void SetExtraDataSentinelBattleVideo() => WriteUInt32LittleEndian(Data.AsSpan(OFS_BV), EXTRADATA_SENTINEL);
private Span<byte> BattleVideoData => Data.AsSpan(OFS_BV + 4, BattleVideo3.SIZE); public Memory<byte> BattleVideoData => Data.AsMemory(OFS_BV + 4, BattleVideo3.SIZE);
public BattleVideo3 BattleVideo public BattleVideo3 BattleVideo
{ {
// decouple from the save file object on get, as the consumer might not be aware that mutations will touch the save.
get => HasBattleVideo ? new BattleVideo3(BattleVideoData.ToArray()) : new BattleVideo3(); get => HasBattleVideo ? new BattleVideo3(BattleVideoData.ToArray()) : new BattleVideo3();
set => SetData(BattleVideoData, value.Data); set => SetData(BattleVideoData.Span, value.Data);
} }
} }

View file

@ -251,4 +251,6 @@ public abstract class SAV7 : SAV_BEEF, ITrainerStatRecord, ISaveBlock7Main, IReg
IEventFlag37 IEventFlagProvider37.EventWork => EventWork; IEventFlag37 IEventFlagProvider37.EventWork => EventWork;
IMysteryGiftStorage IMysteryGiftStorageProvider.MysteryGiftStorage => MysteryGift; IMysteryGiftStorage IMysteryGiftStorageProvider.MysteryGiftStorage => MysteryGift;
public abstract void UpdateQrConstants();
} }

View file

@ -69,7 +69,7 @@ public sealed class SAV7SM : SAV7, ISaveBlock7SM
private const ulong MagearnaConst = 0xCBE05F18356504AC; private const ulong MagearnaConst = 0xCBE05F18356504AC;
public void UpdateMagearnaConstant() public override void UpdateQrConstants()
{ {
var flag = EventWork.GetEventFlag(EventWork7SM.MagearnaEventFlag); // 3100 var flag = EventWork.GetEventFlag(EventWork7SM.MagearnaEventFlag); // 3100
ulong value = flag ? MagearnaConst : 0ul; ulong value = flag ? MagearnaConst : 0ul;

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core; namespace PKHeX.Core;
@ -66,4 +67,19 @@ public sealed class SAV7USUM : SAV7, ISaveBlock7USUM
public override GTS7 GTS => Blocks.GTS; public override GTS7 GTS => Blocks.GTS;
public BattleAgency7 BattleAgency => Blocks.BattleAgency; public BattleAgency7 BattleAgency => Blocks.BattleAgency;
#endregion #endregion
private const ulong MagearnaConst = 0xCBE05F18356504AC;
private const ulong CapPikachuConst = 0xF44E94EA7D19A8D6;
public override void UpdateQrConstants()
{
var qr = Blocks.BlockInfo[35];
var flag = EventWork.GetEventFlag(EventWork7USUM.MagearnaEventFlag); // 4060
ulong value = flag ? MagearnaConst : 0ul;
WriteUInt64LittleEndian(Data.AsSpan(qr.Offset + 0x168), value);
flag = EventWork.GetEventFlag(EventWork7USUM.CapPikachuEventFlag); // 4060
value = flag ? CapPikachuConst : 0ul;
WriteUInt64LittleEndian(Data.AsSpan(qr.Offset + 0x16C), value);
}
} }

View file

@ -171,7 +171,7 @@ public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord, IE
#region Checksums #region Checksums
private const int HashLength = 0x10; private const int HashLength = MD5.HashSizeInBytes;
private const int HashOffset = SaveUtil.SIZE_G8BDSP - HashLength; private const int HashOffset = SaveUtil.SIZE_G8BDSP - HashLength;
private Span<byte> CurrentHash => Data.AsSpan(HashOffset, HashLength); private Span<byte> CurrentHash => Data.AsSpan(HashOffset, HashLength);
private static void ComputeHash(ReadOnlySpan<byte> data, Span<byte> dest) private static void ComputeHash(ReadOnlySpan<byte> data, Span<byte> dest)
@ -181,7 +181,7 @@ public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord, IE
Span<byte> zeroes = stackalloc byte[HashLength]; // Hash is zeroed prior to computing over the payload. Treat it as zero. Span<byte> zeroes = stackalloc byte[HashLength]; // Hash is zeroed prior to computing over the payload. Treat it as zero.
h.AppendData(zeroes); h.AppendData(zeroes);
h.AppendData(data[(HashOffset + HashLength)..]); h.AppendData(data[(HashOffset + HashLength)..]);
h.TryGetCurrentHash(dest, out _); h.GetCurrentHash(dest);
} }
protected override void SetChecksums() => ComputeHash(Data, CurrentHash); protected override void SetChecksums() => ComputeHash(Data, CurrentHash);

View file

@ -825,63 +825,3 @@ public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IGeneration, IVe
} }
#endregion #endregion
} }
public static class StorageUtil
{
public static bool CompressStorage(this SaveFile sav, Span<byte> storage, out int storedCount, Span<int> slotPointers)
{
// keep track of empty slots, and only write them at the end if slots were shifted (no need otherwise).
var empty = new List<byte[]>();
bool shiftedSlots = false;
ushort ctr = 0;
int size = sav.SIZE_BOXSLOT;
int count = sav.BoxSlotCount * sav.BoxCount;
for (int i = 0; i < count; i++)
{
int offset = sav.GetBoxSlotOffset(i);
if (sav.IsPKMPresent(storage[offset..]))
{
if (ctr != i) // copy required
{
shiftedSlots = true; // appending empty slots afterward is now required since a rewrite was done
int destOfs = sav.GetBoxSlotOffset(ctr);
storage[offset..(offset + size)].CopyTo(storage[destOfs..(destOfs + size)]);
SlotPointerUtil.UpdateRepointFrom(ctr, i, slotPointers);
}
ctr++;
continue;
}
// pop out an empty slot; save all unused data & preserve order
var data = storage.Slice(offset, size).ToArray();
empty.Add(data);
}
storedCount = ctr;
if (!shiftedSlots)
return false;
for (int i = ctr; i < count; i++)
{
var data = empty[i - ctr];
int offset = sav.GetBoxSlotOffset(i);
data.CopyTo(storage[offset..]);
}
return true;
}
public static int FindSlotIndex(this SaveFile sav, Func<PKM, bool> method, int maxCount)
{
for (int i = 0; i < maxCount; i++)
{
var pk = sav.GetBoxSlotAtIndex(i);
if (method(pk))
return i;
}
return -1;
}
}

View file

@ -5,12 +5,11 @@ using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core; namespace PKHeX.Core;
public sealed class BattleVideo3(byte[] Data) : IBattleVideo public sealed class BattleVideo3(Memory<byte> Raw) : IBattleVideo
{ {
public BattleVideo3() : this(new byte[SIZE]) { } public BattleVideo3() : this(new byte[SIZE]) { }
public readonly byte[] Data = (byte[])Data.Clone(); public Span<byte> Data => Raw.Span;
internal const int SIZE = 0xF80; internal const int SIZE = 0xF80;
public byte Generation => 3; public byte Generation => 3;
@ -49,7 +48,7 @@ public sealed class BattleVideo3(byte[] Data) : IBattleVideo
for (int p = 0; p < 6; p++) for (int p = 0; p < 6; p++)
{ {
int offset = ofs + (PokeCrypto.SIZE_3PARTY * p); int offset = ofs + (PokeCrypto.SIZE_3PARTY * p);
var span = Data.AsSpan(offset, PokeCrypto.SIZE_3PARTY); var span = Data.Slice(offset, PokeCrypto.SIZE_3PARTY);
team[p] = new PK3(span.ToArray()); team[p] = new PK3(span.ToArray());
} }
@ -62,7 +61,7 @@ public sealed class BattleVideo3(byte[] Data) : IBattleVideo
for (int p = 0; p < 6; p++) for (int p = 0; p < 6; p++)
{ {
int offset = ofs + (PokeCrypto.SIZE_3PARTY * p); int offset = ofs + (PokeCrypto.SIZE_3PARTY * p);
team[p].EncryptedPartyData.CopyTo(Data, offset); team[p].EncryptedPartyData.CopyTo(Data[offset..]);
} }
} }
@ -73,25 +72,25 @@ public sealed class BattleVideo3(byte[] Data) : IBattleVideo
public uint Seed public uint Seed
{ {
get => ReadUInt32LittleEndian(Data.AsSpan(0x4E8)); get => ReadUInt32LittleEndian(Data[0x4E8..]);
set => WriteUInt32LittleEndian(Data.AsSpan(0x4E8), value); set => WriteUInt32LittleEndian(Data[0x4E8..], value);
} }
public uint Mode public uint Mode
{ {
get => ReadUInt32LittleEndian(Data.AsSpan(0x4EC)); get => ReadUInt32LittleEndian(Data[0x4EC..]);
set => WriteUInt32LittleEndian(Data.AsSpan(0x4EC), value); set => WriteUInt32LittleEndian(Data[0x4EC..], value);
} }
// ... // ...
public uint Checksum public uint Checksum
{ {
get => ReadUInt32LittleEndian(Data.AsSpan(SIZE - 4)); get => ReadUInt32LittleEndian(Data[^4..]);
set => WriteUInt32LittleEndian(Data.AsSpan(SIZE - 4), value); set => WriteUInt32LittleEndian(Data[^4..], value);
} }
public bool IsChecksumValid() => Checksum == GetByteSum(Data.AsSpan()[..^4]); public bool IsChecksumValid() => Checksum == GetByteSum(Data[..^4]);
public static uint GetByteSum(ReadOnlySpan<byte> data) public static uint GetByteSum(ReadOnlySpan<byte> data)
{ {

View file

@ -5,6 +5,7 @@ namespace PKHeX.Core;
public abstract class EventWork7(SAV7 sav, Memory<byte> raw) : SaveBlock<SAV7>(sav, raw), IEventFlag37 public abstract class EventWork7(SAV7 sav, Memory<byte> raw) : SaveBlock<SAV7>(sav, raw), IEventFlag37
{ {
private readonly SAV7 sav7 = sav;
protected abstract Span<byte> WorkSpan { get; } protected abstract Span<byte> WorkSpan { get; }
protected abstract Span<byte> FlagSpan { get; } protected abstract Span<byte> FlagSpan { get; }
protected abstract Memory<byte> FameSpan { get; } protected abstract Memory<byte> FameSpan { get; }
@ -37,6 +38,8 @@ public abstract class EventWork7(SAV7 sav, Memory<byte> raw) : SaveBlock<SAV7>(s
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)TotalZygardeCellCount); ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)TotalZygardeCellCount);
SetWork(celloffset + index, val); SetWork(celloffset + index, val);
} }
public void UpdateQrConstants() => sav7.UpdateQrConstants();
} }
public sealed class EventWork7SM(SAV7SM sav, Memory<byte> raw) : EventWork7(sav, raw) public sealed class EventWork7SM(SAV7SM sav, Memory<byte> raw) : EventWork7(sav, raw)
@ -58,8 +61,6 @@ public sealed class EventWork7SM(SAV7SM sav, Memory<byte> raw) : EventWork7(sav,
protected override Memory<byte> FameSpan => Raw.Slice(OffsetPostData, HallOfFame7.SIZE); protected override Memory<byte> FameSpan => Raw.Slice(OffsetPostData, HallOfFame7.SIZE);
public const int MagearnaEventFlag = 3100; public const int MagearnaEventFlag = 3100;
public void UpdateMagearnaConstant() => ((SAV7SM)SAV).UpdateMagearnaConstant();
} }
public sealed class EventWork7USUM(SAV7USUM sav, Memory<byte> raw) : EventWork7(sav, raw) public sealed class EventWork7USUM(SAV7USUM sav, Memory<byte> raw) : EventWork7(sav, raw)
@ -79,6 +80,9 @@ public sealed class EventWork7USUM(SAV7USUM sav, Memory<byte> raw) : EventWork7(
protected override Span<byte> WorkSpan => Raw.Span[..(EventWorkCount * sizeof(ushort))]; protected override Span<byte> WorkSpan => Raw.Span[..(EventWorkCount * sizeof(ushort))];
protected override Span<byte> FlagSpan => Raw.Span.Slice(OffsetFlag, FlagCount / 8); protected override Span<byte> FlagSpan => Raw.Span.Slice(OffsetFlag, FlagCount / 8);
protected override Memory<byte> FameSpan => Raw.Slice(OffsetPostData, HallOfFame7.SIZE); protected override Memory<byte> FameSpan => Raw.Slice(OffsetPostData, HallOfFame7.SIZE);
public const int MagearnaEventFlag = 4060;
public const int CapPikachuEventFlag = 4562;
} }
public sealed class HallOfFame7(Memory<byte> raw) public sealed class HallOfFame7(Memory<byte> raw)

View file

@ -38,22 +38,22 @@ public sealed class Misc7(SAV7 sav, Memory<byte> raw) : SaveBlock<SAV7>(sav, raw
} }
} }
public int DaysFromRefreshed
{
get => Data[0x123];
set => Data[0x123] = (byte)value;
}
public int Vivillon public int Vivillon
{ {
get => Data[0x130] & 0x1F; get => Data[0x130] & 0x1F;
set => Data[0x130] = (byte)((Data[0x130] & ~0x1F) | (value & 0x1F)); set => Data[0x130] = (byte)((Data[0x130] & ~0x1F) | (value & 0x1F));
} }
public uint StarterEncryptionConstant public bool IsWormholeShiny
{ {
get => ReadUInt32LittleEndian(Data[0x148..]); get => Data[0x136] == 1;
set => WriteUInt32LittleEndian(Data[0x148..], value); set => Data[0x136] = (byte)(value ? 1 : 0);
}
public int DaysFromRefreshed
{
get => Data[0x123];
set => Data[0x123] = (byte)value;
} }
public int GetSurfScore(int recordID) public int GetSurfScore(int recordID)
@ -69,4 +69,10 @@ public sealed class Misc7(SAV7 sav, Memory<byte> raw) : SaveBlock<SAV7>(sav, raw
recordID = 0; recordID = 0;
WriteInt32LittleEndian(Data[(0x138 + (4 * recordID))..], score); WriteInt32LittleEndian(Data[(0x138 + (4 * recordID))..], score);
} }
public uint StarterEncryptionConstant
{
get => ReadUInt32LittleEndian(Data[0x148..]);
set => WriteUInt32LittleEndian(Data[0x148..], value);
}
} }

View file

@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core;
public static class StorageUtil
{
public static bool CompressStorage(this SaveFile sav, Span<byte> storage, out int storedCount, Span<int> slotPointers)
{
// keep track of empty slots, and only write them at the end if slots were shifted (no need otherwise).
var empty = new List<byte[]>();
bool shiftedSlots = false;
ushort ctr = 0;
int size = sav.SIZE_BOXSLOT;
int count = sav.BoxSlotCount * sav.BoxCount;
for (int i = 0; i < count; i++)
{
int offset = sav.GetBoxSlotOffset(i);
if (sav.IsPKMPresent(storage[offset..]))
{
if (ctr != i) // copy required
{
shiftedSlots = true; // appending empty slots afterward is now required since a rewrite was done
int destOfs = sav.GetBoxSlotOffset(ctr);
storage[offset..(offset + size)].CopyTo(storage[destOfs..(destOfs + size)]);
SlotPointerUtil.UpdateRepointFrom(ctr, i, slotPointers);
}
ctr++;
continue;
}
// pop out an empty slot; save all unused data & preserve order
var data = storage.Slice(offset, size).ToArray();
empty.Add(data);
}
storedCount = ctr;
if (!shiftedSlots)
return false;
for (int i = ctr; i < count; i++)
{
var data = empty[i - ctr];
int offset = sav.GetBoxSlotOffset(i);
data.CopyTo(storage[offset..]);
}
return true;
}
public static int FindSlotIndex(this SaveFile sav, Func<PKM, bool> method, int maxCount)
{
for (int i = 0; i < maxCount; i++)
{
var pk = sav.GetBoxSlotAtIndex(i);
if (method(pk))
return i;
}
return -1;
}
/// <summary>
/// Copies a <see cref="T"/> list to the destination list, with an option to copy to a starting point.
/// </summary>
/// <param name="list">Source list to copy from</param>
/// <param name="dest">Destination list/array</param>
/// <param name="skip">Criteria for skipping a slot</param>
/// <param name="start">Starting point to copy to</param>
/// <returns>Count of <see cref="T"/> copied.</returns>
public static int CopyTo<T>(this IEnumerable<T> list, Span<T> dest, Func<int, bool> skip, int start = 0)
{
int ctr = start;
int skipped = 0;
foreach (var z in list)
{
// seek forward to next open slot
int next = FindNextValidIndex(dest, skip, ctr);
if (next == -1)
break;
skipped += next - ctr;
ctr = next;
dest[ctr++] = z;
}
return ctr - start - skipped;
}
public static int FindNextValidIndex<T>(Span<T> dest, Func<int, bool> skip, int ctr)
{
while (true)
{
if ((uint)ctr >= dest.Length)
return -1;
var exist = dest[ctr];
if (exist == null || !skip(ctr))
return ctr;
ctr++;
}
}
}

View file

@ -1,48 +0,0 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core;
/// <summary>
/// Array reusable logic
/// </summary>
public static class ArrayUtil
{
/// <summary>
/// Copies a <see cref="T"/> list to the destination list, with an option to copy to a starting point.
/// </summary>
/// <param name="list">Source list to copy from</param>
/// <param name="dest">Destination list/array</param>
/// <param name="skip">Criteria for skipping a slot</param>
/// <param name="start">Starting point to copy to</param>
/// <returns>Count of <see cref="T"/> copied.</returns>
public static int CopyTo<T>(this IEnumerable<T> list, IList<T> dest, Func<int, bool> skip, int start = 0)
{
int ctr = start;
int skipped = 0;
foreach (var z in list)
{
// seek forward to next open slot
int next = FindNextValidIndex(dest, skip, ctr);
if (next == -1)
break;
skipped += next - ctr;
ctr = next;
dest[ctr++] = z;
}
return ctr - start - skipped;
}
public static int FindNextValidIndex<T>(IList<T> dest, Func<int, bool> skip, int ctr)
{
while (true)
{
if ((uint)ctr >= dest.Count)
return -1;
var exist = dest[ctr];
if (exist == null || !skip(ctr))
return ctr;
ctr++;
}
}
}

View file

@ -64,20 +64,10 @@ public static class StringUtil
return -1; return -1;
} }
/// <summary>
/// Converts an all-caps hex string to a byte array. Expects no separation between byte tuples.
/// </summary>
public static byte[] ToByteArray(this string toTransform)
{
var result = new byte[toTransform.Length / 2];
LoadHexBytesTo(toTransform, result, 2);
return result;
}
/// <summary> /// <summary>
/// Converts an all-caps encoded ASCII-Text hex string to a byte array. /// Converts an all-caps encoded ASCII-Text hex string to a byte array.
/// </summary> /// </summary>
public static void LoadHexBytesTo(Span<byte> dest, ReadOnlySpan<byte> str, int tupleSize) public static void LoadHexBytesTo(ReadOnlySpan<byte> str, Span<byte> dest, int tupleSize)
{ {
// The input string is 2-char hex values optionally separated. // The input string is 2-char hex values optionally separated.
// The destination array should always be larger or equal than the bytes written. Let the runtime bounds check us. // The destination array should always be larger or equal than the bytes written. Let the runtime bounds check us.

View file

@ -6,18 +6,45 @@ namespace PKHeX.Core.Tests.Legality;
public class RaidTests public class RaidTests
{ {
public const string Charizard = "65E79FC0000085F1060060045CF0A3AA142C10005E00140015010000EE5501E2080A00000000000004FCFC0000000000000000008800000000001C0000000000000000000000000000000000000000008AAD000000000000430068006100720069007A00610072006400000000000000000035001E024C009B01181010080303030300000000000000002901FF7F1E3F0A000000000000000000000000000000000000000000000056006900630074006F0072006900610000000000000000000000010201000000320104080000000000000000000000000000000000002C000000020000000000FF0000000000000000000000000000004100720063006800690074000000000000000000000000000000FF000000000000000000140117000000A20009372804000000000000000100000200000000000000000000000000000000000000000064002901B700C10048013D01CE000000"; // Just a regular Charizard pk8
private static ReadOnlySpan<byte> Charizard =>
[
0x65, 0xE7, 0x9F, 0xC0, 0x00, 0x00, 0x85, 0xF1, 0x06, 0x00, 0x60, 0x04, 0x5C, 0xF0, 0xA3, 0xAA,
0x14, 0x2C, 0x10, 0x00, 0x5E, 0x00, 0x14, 0x00, 0x15, 0x01, 0x00, 0x00, 0xEE, 0x55, 0x01, 0xE2,
0x08, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x8A, 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x68, 0x00, 0x61, 0x00, 0x72, 0x00,
0x69, 0x00, 0x7A, 0x00, 0x61, 0x00, 0x72, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x35, 0x00, 0x1E, 0x02, 0x4C, 0x00, 0x9B, 0x01, 0x18, 0x10, 0x10, 0x08, 0x03, 0x03,
0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x01, 0xFF, 0x7F, 0x1E, 0x3F,
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, 0x69, 0x00, 0x63, 0x00, 0x74, 0x00,
0x6F, 0x00, 0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0x00, 0x32, 0x01, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x72, 0x00, 0x63, 0x00, 0x68, 0x00,
0x69, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x01, 0x17, 0x00,
0x00, 0x00, 0xA2, 0x00, 0x09, 0x37, 0x28, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x29, 0x01, 0xB7, 0x00, 0xC1, 0x00,
0x48, 0x01, 0x3D, 0x01, 0xCE, 0x00, 0x00, 0x00,
];
[Theory] [Fact]
[InlineData(Charizard, 0xbefd08cf9e027d0a)] public void CheckDynamaxRaidCharizardPK8()
public void CheckMatch(string raw, ulong seed)
{ {
byte[] data = raw.ToByteArray(); var pk8 = new PK8();
var pk8 = new PK8(data);
const ulong seed = 0xbefd08cf9e027d0a;
Charizard.CopyTo(pk8.Data);
bool found = IsPotentialRaidSeed(pk8.EncryptionConstant, pk8.PID, seed); bool found = IsPotentialRaidSeed(pk8.EncryptionConstant, pk8.PID, seed);
found.Should().BeTrue(); found.Should().BeTrue();
// Ensure it matches encounter data and the actual seed is matched.
var la = new LegalityAnalysis(pk8); var la = new LegalityAnalysis(pk8);
var enc = la.EncounterMatch; var enc = la.EncounterMatch;
if (enc is not ISeedCorrelation64<Core.PKM> s64) if (enc is not ISeedCorrelation64<Core.PKM> s64)

View file

@ -1,3 +1,4 @@
using System;
using FluentAssertions; using FluentAssertions;
using Xunit; using Xunit;
@ -6,95 +7,329 @@ namespace PKHeX.Core.Tests.Saves;
public class MemeCryptoTests public class MemeCryptoTests
{ {
[Fact] [Fact]
public void TestVerifySaveSignature() public void TestVerifySaveSignature() => Assert.True(MemeCrypto.VerifyMemeData(SaveSignature, out _));
{
var savebuffer =
"58EA53A7133F34DA9F2BEC12F1560354E8BDF8A484ADE4E2954D3C48673118EB67E2D52ED0196E54DC5D93013E9F3B00C8A43B556AEE8C2F763EA9DC125988C6B5F2D3C74CA2C58026BB024B403D09BC5950C54CEB6F21E45D0B66B68791BCBB6D7E67C2F7E4A7F4A517FC50B4FEED9A65BF901ABEB0FFAC44AE07237BE5DD2D"
.ToByteArray();
Assert.True(MemeCrypto.VerifyMemeData(savebuffer, out _));
}
[Fact] [Fact]
public void TestVerifyMemeBuffer() public void TestVerifyMemeBuffer()
{ {
var encrypted = const MemeKeyIndex type = MemeKeyIndex.PokedexAndSaveFile;
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F4185F2713D5A8BCFFBFF47F86867DF680354913FEFEF08D0D0F1054CB054B508E7DE3AA789AC444D087AB9412CEE965A6D38E45EA010C77FF174F8F8D9993AEAFB9C0616E287B74487E0FF3F1874291B8170C71113971752717F8D188A319EA6" var result = MemeCrypto.VerifyMemeData(Encrypted00FFSigned, out var actual, type);
.ToByteArray(); result.Should().BeTrue();
var decrypted = DecryptedAsDexSave.SequenceEqual(actual).Should().BeTrue();
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7431E10EF7681217C"
.ToByteArray();
Assert.True(MemeCrypto.VerifyMemeData(encrypted, out var actual, MemeKeyIndex.PokedexAndSaveFile));
for (var i = 0; i < decrypted.Length; i++)
{
Assert.Equal(decrypted[i], actual[i]);
}
} }
[Fact] [Fact]
public void TestSignMemeBuffer() public void TestSignMemeBuffer()
{ {
var encrypted = var actual = MemeCrypto.SignMemeData(Decrypted00FF);
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F4185F2713D5A8BCFFBFF47F86867DF680354913FEFEF08D0D0F1054CB054B508E7DE3AA789AC444D087AB9412CEE965A6D38E45EA010C77FF174F8F8D9993AEAFB9C0616E287B74487E0FF3F1874291B8170C71113971752717F8D188A319EA6" Encrypted00FFSigned.SequenceEqual(actual).Should().BeTrue();
.ToByteArray();
var decrypted =
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"
.ToByteArray();
var actual = MemeCrypto.SignMemeData(decrypted);
for (var i = 0; i < encrypted.Length; i++)
{
Assert.Equal(encrypted[i], actual[i]);
}
}
[Fact]
public void TestVerifyMemeBufferOffsetLength()
{
var encrypted =
"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F4185F2713D5A8BCFFBFF47F86867DF680354913FEFEF08D0D0F1054CB054B508E7DE3AA789AC444D087AB9412CEE965A6D38E45EA010C77FF174F8F8D9993AEAFB9C0616E287B74487E0FF3F1874291B8170C71113971752717F8D188A319EA6CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
.ToByteArray();
var decrypted =
"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7431E10EF7681217CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
.ToByteArray();
Assert.True(MemeCrypto.VerifyMemeData(encrypted, out var actual, 0x10, 0x100,
MemeKeyIndex.PokedexAndSaveFile));
for (var i = 0; i < decrypted.Length; i++)
{
Assert.Equal(decrypted[i], actual[i]);
}
} }
[Fact] [Fact]
public void TestVerifyPoke() public void TestVerifyPoke()
{ {
var vector = Assert.True(MemeCrypto.VerifyMemePOKE(Vector2[..^2], out _));
"A96E2D8D9B99DBFB934939C097E3AC101C7D48CEC52FCA717B14B19890208592045C430035DD09A31446142E9EA33CF3E6B6E69484B6D2EED500B8389048013491602403DBE7B814EA069667CFADAFE74895217D78037B4A456FAB2CAFD71E69504F4B4509000000000000" Assert.True(MemeCrypto.VerifyMemePOKE(Vector2, out _));
.ToByteArray();
var vector2 =
"A96E2D8D9B99DBFB934939C097E3AC101C7D48CEC52FCA717B14B19890208592045C430035DD09A31446142E9EA33CF3E6B6E69484B6D2EED500B8389048013491602403DBE7B814EA069667CFADAFE74895217D78037B4A456FAB2CAFD71E690000504F4B4509000000000000"
.ToByteArray();
Assert.True(MemeCrypto.VerifyMemePOKE(vector, out _));
Assert.True(MemeCrypto.VerifyMemePOKE(vector2, out _));
} }
[Theory] private static void Verify(ReadOnlySpan<byte> span, MemeKeyIndex type)
[InlineData(MemeKeyIndex.LocalWireless, "1010030000030000000A0070300051308B3001FFD43020AB301003B430FC30B90830C830D10007C630A382000D6E300A005F00055700305D3046306A300000307530933044304D28306B000B5500158F308C203066002581305A3089A0002744002344308D30612A304C00276E0031DF0001C30830AD30E500110A0042A0004973003D4D305F308880007B0A006830823060C4002F10516A3063004D42305F5200536D009900A6F002F014F026FFF038F04AF05CF06EF080F092F0A4F0B6FFF0C8F0DAF0ECF0FEF110F122F134F1460000B5E400004646F86000F1AB31AB0030D730EC0E30BC30F3020FF173A723C162C9700608C5DCAFAB898032382AF1FA717BBC5D91C61AD7B6EA356220742C2DD4595765FCD8123D1E10ED6B0A1DA45D0C2148035A560BB1C5C246FA7EB64D729FC1DD8B61B4737DC51E184A97E4795E82BFD045743EC2AEAED26E87")] => MemeCrypto.VerifyMemeData(span, out _, type).Should().BeTrue();
[InlineData(MemeKeyIndex.FriendlyCompetition, "0300000077000000000000000D0106060404003204000101000000000000000000000000000000000000C00000000000000000000000000E000000000000000000000000000000C0070000000000000000000000987E000000000000000000000000000000000000D80300000000000000F00300000000000000E001070000000000000000000000000000000000000000000000000000000101920200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E2D0100015A03000000BE0700000000C287010050006F006B00E9006D006F006E00200054007200610069006E006500720020004F006E006C0069006E006500200043006F006D007000650074006900740069006F006E000000000000007700770077002E0070006F006B0065006D006F006E002D0074007200610069007DC67F945B27557C97D6C0B6D20FF11AFCE1580C5E9E80C7C3FBBA53B67F24D1A91BA2FB8CDD31F7FE29D894B3764A45407658BB9A6E6DC8B3F6A45F77686AC56EA64C0298D1B22F3D3850ADE10AEA6E185FD0EEA91AA38E3F0F0AA9E7ADA132")]
[InlineData(MemeKeyIndex.LiveCompetition, "0300000077000000000000000D0006060303003204000101000000000000000000000000000000000000C00000000000000000000000000E000000000000000000000000000000C0070000000000000000000000987E000000000000000000000000000000000000D80300000000000000F00300000000000000E001070000000000000000000000000000000000000000000000000000000101920200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E2D0100015A03000000BE07000000000B9101004900720069007300200043006D007000650074006900740069006F006E00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066007200650065000000000000000000000000000000000000000000000000006425CA0A78E79154EBA19550A0734F84A0C49F8B02E51FA8310548FFF14B7338276188C11C85C15701DAED228099FBF95A277E6249625208D8992FF027385D897AC097F0240C93A70E35FB8AD971DFE14832988DC70F5D1BE19C7EF5FBAFCC12")] // Various payloads that have been signed with Memecrypto Keys
[InlineData(MemeKeyIndex.RentalTeam, "0300000077000000B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C74E35E4783A7E9E161C919C0E736F61B31741B8C1873C5D06D1D408D9DA9FE28DCB53851518FCC0758915EE2911B2194B20131C2E87BC63A255F3A5187763FAF99B1B99D0E4460125CCB7F1CE4A3CC0FC64463454DB25CC851EF9A360EA3D4BDB44B8DE96D1C9839744A3BF7A27FDF41A86491666DAA7BEF55EBFB954BA3FF741657252490BD11350EEF367DA0D229E04EE61DCA1220576014AC2E24232337AB4E7ADEE92B59C44E9AB8D19E0BA87547A0351C4088EDF8808D692461C84EB5D07C795D3B037290AC7C5A9FD2775A86EAD628F545F6E5193145ED22B3BCDE16FD3CCEEA80A5FD90CF8FD320C1D064C8881BF369F0D82EE0E637C434EC6CA4221FE0952C28A7E930956DC2B990817BCC2349EC4339CDD09E2ABB94DC44C171A1E70E2CC5538B9686A1E4AFF907436378FE2915F217EC7C3BA69E47C913794FFE8285DD399D3E39154CE51BA0C74663BD685634697D76EAB8890CF77E371BA7E5CD5FA78FAC7D58A3843C993331C8AFC5ABDCD17FC42B7C28A196C2FB0EF03FE84CAF3FD626A584D9406BC6FC5DC20DD0E19D1D08C96D9065F376E052639FFC0F61CCCE84CDC833F31B9A6C830DE9F58B257F5CBE5991FCA51871752B611AE3B21F5E1F1AE3CFAB49BC6F6721C1C18B74B11400D029E268D")] [Fact] public void VerifyMemeKeyLocalWireless() => Verify(MemeKeyLocalWireless, MemeKeyIndex.LocalWireless);
[InlineData(MemeKeyIndex.PokedexAndSaveFile, "4F3994147824D4F9D9B3EEBDC2DA6777585413FA41D22347A3593E22467E2F117B51EDFEAE5977E19AD88EB1C63ADB7E9DEDB7F5AEEFECA5F777C7F12527729025C82DC4A12C8B344EB472B9F68BFD999916F0D03460FF1D3FDC5EF0F7A7F8FE")] [Fact] public void VerifyMemeKeyFriendlyCompetition() => Verify(MemeKeyFriendlyCompetition, MemeKeyIndex.FriendlyCompetition);
[InlineData(MemeKeyIndex.GaOle, "7C7F70413806DFD1D4E8F5610BE3CA705245593C24AB3102D7768D43B3D873EEB5BB6B188FD4F38F06EB06C1A27369E9822D10093DAD17692EA2D71BCB8941F883F3F0E2068941F65FB26950B7E54F5990E163809594381C99CF3330F052452C")] [Fact] public void VerifyMemeKeyLiveCompetition() => Verify(MemeKeyLiveCompetition, MemeKeyIndex.LiveCompetition);
[InlineData(MemeKeyIndex.MagearnaEvent, "9D741CBA2ABA23408461AC168B3227199C15FFA2784662FE169459DDC2409AE08FA302A6E7026051C8378D09898500028021FE21B1A8165C9918779BA140597E03ED3EAF2FD82C026B17E6BB0D1C4BC6CE06E9FAC2E5DCDD4025983DE57C67CA")] [Fact] public void VerifyMemeKeyRentalTeam() => Verify(MemeKeyRentalTeam, MemeKeyIndex.RentalTeam);
[InlineData(MemeKeyIndex.MoncolleGet, "9914945B3280796FF8EC694681ED2218F8FCD1C6628FCFACD633A86622D278C09FD1BA1B2E60AF2896EDDAF76110CA246AED9E08D7D90F7F285764BBD3E20E05A11BA214C96B1A54E1A8D48DE6EF060C79011A4B3326FBE55F29FDE40B31F470")] [Fact] public void VerifyMemeKeyPokedexAndSaveFile() => Verify(MemeKeyPokedexAndSaveFile, MemeKeyIndex.PokedexAndSaveFile);
[InlineData(MemeKeyIndex.IslandScanEventSpecial, "34B08442E3D68D3730CF5CEE9E82BCFD5F6664168F43E9F2EA26CC7B94B1F44FF7B3C0BAD5C346693DDAD5461E6087AD061015744F6DBA294F9BCD6F0E7A35859F0A15F358EB00BED5A4C18841A8B6A69D91FC603E6DCE058DD96983C5DFBB04")] [Fact] public void VerifyMemeKeyGaOle() => Verify(MemeKeyGaOle, MemeKeyIndex.GaOle);
[InlineData(MemeKeyIndex.TvTokyoDataBroadcasting, "195B4EA9F48E6ADBEE73EB0D529C14EDC99BB011F214E0812E5198BE40739783646FB8C75ABFFD84AA11FB095EFFF5E383F7F45A56A96780C79EBABB430D860FA6CB3D0BFC163C52EDC5C199963D3E518758F911569E1C667BF8A15643A19E2F")] [Fact] public void VerifyMemeKeyMagearnaEvent() => Verify(MemeKeyMagearnaEvent, MemeKeyIndex.MagearnaEvent);
[InlineData(MemeKeyIndex.CapPikachuEvent, "A96E2D8D9B99DBFB934939C097E3AC101C7D48CEC52FCA717B14B19890208592045C430035DD09A31446142E9EA33CF3E6B6E69484B6D2EED500B8389048013491602403DBE7B814EA069667CFADAFE74895217D78037B4A456FAB2CAFD71E69")] [Fact] public void VerifyMemeKeyMoncolleGet() => Verify(MemeKeyMoncolleGet, MemeKeyIndex.MoncolleGet);
public void TestVerifyKnownKeys(MemeKeyIndex keyIndex, string keyVal) [Fact] public void VerifyMemeKeyIslandScanEventSpecial() => Verify(MemeKeyIslandScan, MemeKeyIndex.IslandScanEventSpecial);
{ [Fact] public void VerifyMemeKeyTvTokyoDataBroadcasting() => Verify(MemeKeyTvTokyo, MemeKeyIndex.TvTokyoDataBroadcasting);
var key = keyVal.ToByteArray(); [Fact] public void VerifyMemeKeyCapPikachuEvent() => Verify(MemeKeyCapPikachu, MemeKeyIndex.CapPikachuEvent);
MemeCrypto.VerifyMemeData(key, out _, keyIndex).Should().BeTrue("because the key should be valid");
} private static ReadOnlySpan<byte> SaveSignature =>
[
0x58, 0xEA, 0x53, 0xA7, 0x13, 0x3F, 0x34, 0xDA, 0x9F, 0x2B, 0xEC, 0x12, 0xF1, 0x56, 0x03, 0x54,
0xE8, 0xBD, 0xF8, 0xA4, 0x84, 0xAD, 0xE4, 0xE2, 0x95, 0x4D, 0x3C, 0x48, 0x67, 0x31, 0x18, 0xEB,
0x67, 0xE2, 0xD5, 0x2E, 0xD0, 0x19, 0x6E, 0x54, 0xDC, 0x5D, 0x93, 0x01, 0x3E, 0x9F, 0x3B, 0x00,
0xC8, 0xA4, 0x3B, 0x55, 0x6A, 0xEE, 0x8C, 0x2F, 0x76, 0x3E, 0xA9, 0xDC, 0x12, 0x59, 0x88, 0xC6,
0xB5, 0xF2, 0xD3, 0xC7, 0x4C, 0xA2, 0xC5, 0x80, 0x26, 0xBB, 0x02, 0x4B, 0x40, 0x3D, 0x09, 0xBC,
0x59, 0x50, 0xC5, 0x4C, 0xEB, 0x6F, 0x21, 0xE4, 0x5D, 0x0B, 0x66, 0xB6, 0x87, 0x91, 0xBC, 0xBB,
0x6D, 0x7E, 0x67, 0xC2, 0xF7, 0xE4, 0xA7, 0xF4, 0xA5, 0x17, 0xFC, 0x50, 0xB4, 0xFE, 0xED, 0x9A,
0x65, 0xBF, 0x90, 0x1A, 0xBE, 0xB0, 0xFF, 0xAC, 0x44, 0xAE, 0x07, 0x23, 0x7B, 0xE5, 0xDD, 0x2D,
];
private static ReadOnlySpan<byte> Decrypted00FF =>
[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
];
private static ReadOnlySpan<byte> Encrypted00FFSigned =>
[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0x41, 0x85, 0xF2, 0x71, 0x3D, 0x5A, 0x8B, 0xCF, 0xFB, 0xFF, 0x47, 0xF8, 0x68, 0x67, 0xDF, 0x68,
0x03, 0x54, 0x91, 0x3F, 0xEF, 0xEF, 0x08, 0xD0, 0xD0, 0xF1, 0x05, 0x4C, 0xB0, 0x54, 0xB5, 0x08,
0xE7, 0xDE, 0x3A, 0xA7, 0x89, 0xAC, 0x44, 0x4D, 0x08, 0x7A, 0xB9, 0x41, 0x2C, 0xEE, 0x96, 0x5A,
0x6D, 0x38, 0xE4, 0x5E, 0xA0, 0x10, 0xC7, 0x7F, 0xF1, 0x74, 0xF8, 0xF8, 0xD9, 0x99, 0x3A, 0xEA,
0xFB, 0x9C, 0x06, 0x16, 0xE2, 0x87, 0xB7, 0x44, 0x87, 0xE0, 0xFF, 0x3F, 0x18, 0x74, 0x29, 0x1B,
0x81, 0x70, 0xC7, 0x11, 0x13, 0x97, 0x17, 0x52, 0x71, 0x7F, 0x8D, 0x18, 0x8A, 0x31, 0x9E, 0xA6,
];
private static ReadOnlySpan<byte> DecryptedAsDexSave =>
[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0x43, 0x1E, 0x10, 0xEF, 0x76, 0x81, 0x21, 0x7C,
];
private static ReadOnlySpan<byte> Vector2 =>
[
0xA9, 0x6E, 0x2D, 0x8D, 0x9B, 0x99, 0xDB, 0xFB, 0x93, 0x49, 0x39, 0xC0, 0x97, 0xE3, 0xAC, 0x10,
0x1C, 0x7D, 0x48, 0xCE, 0xC5, 0x2F, 0xCA, 0x71, 0x7B, 0x14, 0xB1, 0x98, 0x90, 0x20, 0x85, 0x92,
0x04, 0x5C, 0x43, 0x00, 0x35, 0xDD, 0x09, 0xA3, 0x14, 0x46, 0x14, 0x2E, 0x9E, 0xA3, 0x3C, 0xF3,
0xE6, 0xB6, 0xE6, 0x94, 0x84, 0xB6, 0xD2, 0xEE, 0xD5, 0x00, 0xB8, 0x38, 0x90, 0x48, 0x01, 0x34,
0x91, 0x60, 0x24, 0x03, 0xDB, 0xE7, 0xB8, 0x14, 0xEA, 0x06, 0x96, 0x67, 0xCF, 0xAD, 0xAF, 0xE7,
0x48, 0x95, 0x21, 0x7D, 0x78, 0x03, 0x7B, 0x4A, 0x45, 0x6F, 0xAB, 0x2C, 0xAF, 0xD7, 0x1E, 0x69,
0x00, 0x00, 0x50, 0x4F, 0x4B, 0x45, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
private static ReadOnlySpan<byte> MemeKeyLocalWireless =>
[
0x10, 0x10, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x70, 0x30, 0x00, 0x51, 0x30,
0x8B, 0x30, 0x01, 0xFF, 0xD4, 0x30, 0x20, 0xAB, 0x30, 0x10, 0x03, 0xB4, 0x30, 0xFC, 0x30, 0xB9,
0x08, 0x30, 0xC8, 0x30, 0xD1, 0x00, 0x07, 0xC6, 0x30, 0xA3, 0x82, 0x00, 0x0D, 0x6E, 0x30, 0x0A,
0x00, 0x5F, 0x00, 0x05, 0x57, 0x00, 0x30, 0x5D, 0x30, 0x46, 0x30, 0x6A, 0x30, 0x00, 0x00, 0x30,
0x75, 0x30, 0x93, 0x30, 0x44, 0x30, 0x4D, 0x28, 0x30, 0x6B, 0x00, 0x0B, 0x55, 0x00, 0x15, 0x8F,
0x30, 0x8C, 0x20, 0x30, 0x66, 0x00, 0x25, 0x81, 0x30, 0x5A, 0x30, 0x89, 0xA0, 0x00, 0x27, 0x44,
0x00, 0x23, 0x44, 0x30, 0x8D, 0x30, 0x61, 0x2A, 0x30, 0x4C, 0x00, 0x27, 0x6E, 0x00, 0x31, 0xDF,
0x00, 0x01, 0xC3, 0x08, 0x30, 0xAD, 0x30, 0xE5, 0x00, 0x11, 0x0A, 0x00, 0x42, 0xA0, 0x00, 0x49,
0x73, 0x00, 0x3D, 0x4D, 0x30, 0x5F, 0x30, 0x88, 0x80, 0x00, 0x7B, 0x0A, 0x00, 0x68, 0x30, 0x82,
0x30, 0x60, 0xC4, 0x00, 0x2F, 0x10, 0x51, 0x6A, 0x30, 0x63, 0x00, 0x4D, 0x42, 0x30, 0x5F, 0x52,
0x00, 0x53, 0x6D, 0x00, 0x99, 0x00, 0xA6, 0xF0, 0x02, 0xF0, 0x14, 0xF0, 0x26, 0xFF, 0xF0, 0x38,
0xF0, 0x4A, 0xF0, 0x5C, 0xF0, 0x6E, 0xF0, 0x80, 0xF0, 0x92, 0xF0, 0xA4, 0xF0, 0xB6, 0xFF, 0xF0,
0xC8, 0xF0, 0xDA, 0xF0, 0xEC, 0xF0, 0xFE, 0xF1, 0x10, 0xF1, 0x22, 0xF1, 0x34, 0xF1, 0x46, 0x00,
0x00, 0xB5, 0xE4, 0x00, 0x00, 0x46, 0x46, 0xF8, 0x60, 0x00, 0xF1, 0xAB, 0x31, 0xAB, 0x00, 0x30,
0xD7, 0x30, 0xEC, 0x0E, 0x30, 0xBC, 0x30, 0xF3, 0x02, 0x0F, 0xF1, 0x73, 0xA7, 0x23, 0xC1, 0x62,
0xC9, 0x70, 0x06, 0x08, 0xC5, 0xDC, 0xAF, 0xAB, 0x89, 0x80, 0x32, 0x38, 0x2A, 0xF1, 0xFA, 0x71,
0x7B, 0xBC, 0x5D, 0x91, 0xC6, 0x1A, 0xD7, 0xB6, 0xEA, 0x35, 0x62, 0x20, 0x74, 0x2C, 0x2D, 0xD4,
0x59, 0x57, 0x65, 0xFC, 0xD8, 0x12, 0x3D, 0x1E, 0x10, 0xED, 0x6B, 0x0A, 0x1D, 0xA4, 0x5D, 0x0C,
0x21, 0x48, 0x03, 0x5A, 0x56, 0x0B, 0xB1, 0xC5, 0xC2, 0x46, 0xFA, 0x7E, 0xB6, 0x4D, 0x72, 0x9F,
0xC1, 0xDD, 0x8B, 0x61, 0xB4, 0x73, 0x7D, 0xC5, 0x1E, 0x18, 0x4A, 0x97, 0xE4, 0x79, 0x5E, 0x82,
0xBF, 0xD0, 0x45, 0x74, 0x3E, 0xC2, 0xAE, 0xAE, 0xD2, 0x6E, 0x87,
];
private static ReadOnlySpan<byte> MemeKeyFriendlyCompetition =>
[
0x03, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x01, 0x06, 0x06,
0x04, 0x04, 0x00, 0x32, 0x04, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x98, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x92, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1E, 0x2D, 0x01, 0x00, 0x01, 0x5A, 0x03, 0x00, 0x00, 0x00, 0xBE, 0x07, 0x00, 0x00, 0x00, 0x00,
0xC2, 0x87, 0x01, 0x00, 0x50, 0x00, 0x6F, 0x00, 0x6B, 0x00, 0xE9, 0x00, 0x6D, 0x00, 0x6F, 0x00,
0x6E, 0x00, 0x20, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x65, 0x00,
0x72, 0x00, 0x20, 0x00, 0x4F, 0x00, 0x6E, 0x00, 0x6C, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x65, 0x00,
0x20, 0x00, 0x43, 0x00, 0x6F, 0x00, 0x6D, 0x00, 0x70, 0x00, 0x65, 0x00, 0x74, 0x00, 0x69, 0x00,
0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00,
0x77, 0x00, 0x77, 0x00, 0x2E, 0x00, 0x70, 0x00, 0x6F, 0x00, 0x6B, 0x00, 0x65, 0x00, 0x6D, 0x00,
0x6F, 0x00, 0x6E, 0x00, 0x2D, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x69, 0x00, 0x7D, 0xC6,
0x7F, 0x94, 0x5B, 0x27, 0x55, 0x7C, 0x97, 0xD6, 0xC0, 0xB6, 0xD2, 0x0F, 0xF1, 0x1A, 0xFC, 0xE1,
0x58, 0x0C, 0x5E, 0x9E, 0x80, 0xC7, 0xC3, 0xFB, 0xBA, 0x53, 0xB6, 0x7F, 0x24, 0xD1, 0xA9, 0x1B,
0xA2, 0xFB, 0x8C, 0xDD, 0x31, 0xF7, 0xFE, 0x29, 0xD8, 0x94, 0xB3, 0x76, 0x4A, 0x45, 0x40, 0x76,
0x58, 0xBB, 0x9A, 0x6E, 0x6D, 0xC8, 0xB3, 0xF6, 0xA4, 0x5F, 0x77, 0x68, 0x6A, 0xC5, 0x6E, 0xA6,
0x4C, 0x02, 0x98, 0xD1, 0xB2, 0x2F, 0x3D, 0x38, 0x50, 0xAD, 0xE1, 0x0A, 0xEA, 0x6E, 0x18, 0x5F,
0xD0, 0xEE, 0xA9, 0x1A, 0xA3, 0x8E, 0x3F, 0x0F, 0x0A, 0xA9, 0xE7, 0xAD, 0xA1, 0x32,
];
private static ReadOnlySpan<byte> MemeKeyLiveCompetition =>
[
0x03, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x06, 0x06,
0x03, 0x03, 0x00, 0x32, 0x04, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x98, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x92, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1E, 0x2D, 0x01, 0x00, 0x01, 0x5A, 0x03, 0x00, 0x00, 0x00, 0xBE, 0x07, 0x00, 0x00, 0x00, 0x00,
0x0B, 0x91, 0x01, 0x00, 0x49, 0x00, 0x72, 0x00, 0x69, 0x00, 0x73, 0x00, 0x20, 0x00, 0x43, 0x00,
0x6D, 0x00, 0x70, 0x00, 0x65, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00,
0x72, 0x00, 0x65, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x25,
0xCA, 0x0A, 0x78, 0xE7, 0x91, 0x54, 0xEB, 0xA1, 0x95, 0x50, 0xA0, 0x73, 0x4F, 0x84, 0xA0, 0xC4,
0x9F, 0x8B, 0x02, 0xE5, 0x1F, 0xA8, 0x31, 0x05, 0x48, 0xFF, 0xF1, 0x4B, 0x73, 0x38, 0x27, 0x61,
0x88, 0xC1, 0x1C, 0x85, 0xC1, 0x57, 0x01, 0xDA, 0xED, 0x22, 0x80, 0x99, 0xFB, 0xF9, 0x5A, 0x27,
0x7E, 0x62, 0x49, 0x62, 0x52, 0x08, 0xD8, 0x99, 0x2F, 0xF0, 0x27, 0x38, 0x5D, 0x89, 0x7A, 0xC0,
0x97, 0xF0, 0x24, 0x0C, 0x93, 0xA7, 0x0E, 0x35, 0xFB, 0x8A, 0xD9, 0x71, 0xDF, 0xE1, 0x48, 0x32,
0x98, 0x8D, 0xC7, 0x0F, 0x5D, 0x1B, 0xE1, 0x9C, 0x7E, 0xF5, 0xFB, 0xAF, 0xCC, 0x12,
];
private static ReadOnlySpan<byte> MemeKeyRentalTeam =>
[
0x03, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0x4E, 0x35, 0xE4, 0x78, 0x3A, 0x7E, 0x9E, 0x16,
0x1C, 0x91, 0x9C, 0x0E, 0x73, 0x6F, 0x61, 0xB3, 0x17, 0x41, 0xB8, 0xC1, 0x87, 0x3C, 0x5D, 0x06,
0xD1, 0xD4, 0x08, 0xD9, 0xDA, 0x9F, 0xE2, 0x8D, 0xCB, 0x53, 0x85, 0x15, 0x18, 0xFC, 0xC0, 0x75,
0x89, 0x15, 0xEE, 0x29, 0x11, 0xB2, 0x19, 0x4B, 0x20, 0x13, 0x1C, 0x2E, 0x87, 0xBC, 0x63, 0xA2,
0x55, 0xF3, 0xA5, 0x18, 0x77, 0x63, 0xFA, 0xF9, 0x9B, 0x1B, 0x99, 0xD0, 0xE4, 0x46, 0x01, 0x25,
0xCC, 0xB7, 0xF1, 0xCE, 0x4A, 0x3C, 0xC0, 0xFC, 0x64, 0x46, 0x34, 0x54, 0xDB, 0x25, 0xCC, 0x85,
0x1E, 0xF9, 0xA3, 0x60, 0xEA, 0x3D, 0x4B, 0xDB, 0x44, 0xB8, 0xDE, 0x96, 0xD1, 0xC9, 0x83, 0x97,
0x44, 0xA3, 0xBF, 0x7A, 0x27, 0xFD, 0xF4, 0x1A, 0x86, 0x49, 0x16, 0x66, 0xDA, 0xA7, 0xBE, 0xF5,
0x5E, 0xBF, 0xB9, 0x54, 0xBA, 0x3F, 0xF7, 0x41, 0x65, 0x72, 0x52, 0x49, 0x0B, 0xD1, 0x13, 0x50,
0xEE, 0xF3, 0x67, 0xDA, 0x0D, 0x22, 0x9E, 0x04, 0xEE, 0x61, 0xDC, 0xA1, 0x22, 0x05, 0x76, 0x01,
0x4A, 0xC2, 0xE2, 0x42, 0x32, 0x33, 0x7A, 0xB4, 0xE7, 0xAD, 0xEE, 0x92, 0xB5, 0x9C, 0x44, 0xE9,
0xAB, 0x8D, 0x19, 0xE0, 0xBA, 0x87, 0x54, 0x7A, 0x03, 0x51, 0xC4, 0x08, 0x8E, 0xDF, 0x88, 0x08,
0xD6, 0x92, 0x46, 0x1C, 0x84, 0xEB, 0x5D, 0x07, 0xC7, 0x95, 0xD3, 0xB0, 0x37, 0x29, 0x0A, 0xC7,
0xC5, 0xA9, 0xFD, 0x27, 0x75, 0xA8, 0x6E, 0xAD, 0x62, 0x8F, 0x54, 0x5F, 0x6E, 0x51, 0x93, 0x14,
0x5E, 0xD2, 0x2B, 0x3B, 0xCD, 0xE1, 0x6F, 0xD3, 0xCC, 0xEE, 0xA8, 0x0A, 0x5F, 0xD9, 0x0C, 0xF8,
0xFD, 0x32, 0x0C, 0x1D, 0x06, 0x4C, 0x88, 0x81, 0xBF, 0x36, 0x9F, 0x0D, 0x82, 0xEE, 0x0E, 0x63,
0x7C, 0x43, 0x4E, 0xC6, 0xCA, 0x42, 0x21, 0xFE, 0x09, 0x52, 0xC2, 0x8A, 0x7E, 0x93, 0x09, 0x56,
0xDC, 0x2B, 0x99, 0x08, 0x17, 0xBC, 0xC2, 0x34, 0x9E, 0xC4, 0x33, 0x9C, 0xDD, 0x09, 0xE2, 0xAB,
0xB9, 0x4D, 0xC4, 0x4C, 0x17, 0x1A, 0x1E, 0x70, 0xE2, 0xCC, 0x55, 0x38, 0xB9, 0x68, 0x6A, 0x1E,
0x4A, 0xFF, 0x90, 0x74, 0x36, 0x37, 0x8F, 0xE2, 0x91, 0x5F, 0x21, 0x7E, 0xC7, 0xC3, 0xBA, 0x69,
0xE4, 0x7C, 0x91, 0x37, 0x94, 0xFF, 0xE8, 0x28, 0x5D, 0xD3, 0x99, 0xD3, 0xE3, 0x91, 0x54, 0xCE,
0x51, 0xBA, 0x0C, 0x74, 0x66, 0x3B, 0xD6, 0x85, 0x63, 0x46, 0x97, 0xD7, 0x6E, 0xAB, 0x88, 0x90,
0xCF, 0x77, 0xE3, 0x71, 0xBA, 0x7E, 0x5C, 0xD5, 0xFA, 0x78, 0xFA, 0xC7, 0xD5, 0x8A, 0x38, 0x43,
0xC9, 0x93, 0x33, 0x1C, 0x8A, 0xFC, 0x5A, 0xBD, 0xCD, 0x17, 0xFC, 0x42, 0xB7, 0xC2, 0x8A, 0x19,
0x6C, 0x2F, 0xB0, 0xEF, 0x03, 0xFE, 0x84, 0xCA, 0xF3, 0xFD, 0x62, 0x6A, 0x58, 0x4D, 0x94, 0x06,
0xBC, 0x6F, 0xC5, 0xDC, 0x20, 0xDD, 0x0E, 0x19, 0xD1, 0xD0, 0x8C, 0x96, 0xD9, 0x06, 0x5F, 0x37,
0x6E, 0x05, 0x26, 0x39, 0xFF, 0xC0, 0xF6, 0x1C, 0xCC, 0xE8, 0x4C, 0xDC, 0x83, 0x3F, 0x31, 0xB9,
0xA6, 0xC8, 0x30, 0xDE, 0x9F, 0x58, 0xB2, 0x57, 0xF5, 0xCB, 0xE5, 0x99, 0x1F, 0xCA, 0x51, 0x87,
0x17, 0x52, 0xB6, 0x11, 0xAE, 0x3B, 0x21, 0xF5, 0xE1, 0xF1, 0xAE, 0x3C, 0xFA, 0xB4, 0x9B, 0xC6,
0xF6, 0x72, 0x1C, 0x1C, 0x18, 0xB7, 0x4B, 0x11, 0x40, 0x0D, 0x02, 0x9E, 0x26, 0x8D,
];
private static ReadOnlySpan<byte> MemeKeyPokedexAndSaveFile =>
[
0x4F, 0x39, 0x94, 0x14, 0x78, 0x24, 0xD4, 0xF9, 0xD9, 0xB3, 0xEE, 0xBD, 0xC2, 0xDA, 0x67, 0x77,
0x58, 0x54, 0x13, 0xFA, 0x41, 0xD2, 0x23, 0x47, 0xA3, 0x59, 0x3E, 0x22, 0x46, 0x7E, 0x2F, 0x11,
0x7B, 0x51, 0xED, 0xFE, 0xAE, 0x59, 0x77, 0xE1, 0x9A, 0xD8, 0x8E, 0xB1, 0xC6, 0x3A, 0xDB, 0x7E,
0x9D, 0xED, 0xB7, 0xF5, 0xAE, 0xEF, 0xEC, 0xA5, 0xF7, 0x77, 0xC7, 0xF1, 0x25, 0x27, 0x72, 0x90,
0x25, 0xC8, 0x2D, 0xC4, 0xA1, 0x2C, 0x8B, 0x34, 0x4E, 0xB4, 0x72, 0xB9, 0xF6, 0x8B, 0xFD, 0x99,
0x99, 0x16, 0xF0, 0xD0, 0x34, 0x60, 0xFF, 0x1D, 0x3F, 0xDC, 0x5E, 0xF0, 0xF7, 0xA7, 0xF8, 0xFE,
];
private static ReadOnlySpan<byte> MemeKeyGaOle =>
[
0x7C, 0x7F, 0x70, 0x41, 0x38, 0x06, 0xDF, 0xD1, 0xD4, 0xE8, 0xF5, 0x61, 0x0B, 0xE3, 0xCA, 0x70,
0x52, 0x45, 0x59, 0x3C, 0x24, 0xAB, 0x31, 0x02, 0xD7, 0x76, 0x8D, 0x43, 0xB3, 0xD8, 0x73, 0xEE,
0xB5, 0xBB, 0x6B, 0x18, 0x8F, 0xD4, 0xF3, 0x8F, 0x06, 0xEB, 0x06, 0xC1, 0xA2, 0x73, 0x69, 0xE9,
0x82, 0x2D, 0x10, 0x09, 0x3D, 0xAD, 0x17, 0x69, 0x2E, 0xA2, 0xD7, 0x1B, 0xCB, 0x89, 0x41, 0xF8,
0x83, 0xF3, 0xF0, 0xE2, 0x06, 0x89, 0x41, 0xF6, 0x5F, 0xB2, 0x69, 0x50, 0xB7, 0xE5, 0x4F, 0x59,
0x90, 0xE1, 0x63, 0x80, 0x95, 0x94, 0x38, 0x1C, 0x99, 0xCF, 0x33, 0x30, 0xF0, 0x52, 0x45, 0x2C,
];
private static ReadOnlySpan<byte> MemeKeyMagearnaEvent =>
[
0x9D, 0x74, 0x1C, 0xBA, 0x2A, 0xBA, 0x23, 0x40, 0x84, 0x61, 0xAC, 0x16, 0x8B, 0x32, 0x27, 0x19,
0x9C, 0x15, 0xFF, 0xA2, 0x78, 0x46, 0x62, 0xFE, 0x16, 0x94, 0x59, 0xDD, 0xC2, 0x40, 0x9A, 0xE0,
0x8F, 0xA3, 0x02, 0xA6, 0xE7, 0x02, 0x60, 0x51, 0xC8, 0x37, 0x8D, 0x09, 0x89, 0x85, 0x00, 0x02,
0x80, 0x21, 0xFE, 0x21, 0xB1, 0xA8, 0x16, 0x5C, 0x99, 0x18, 0x77, 0x9B, 0xA1, 0x40, 0x59, 0x7E,
0x03, 0xED, 0x3E, 0xAF, 0x2F, 0xD8, 0x2C, 0x02, 0x6B, 0x17, 0xE6, 0xBB, 0x0D, 0x1C, 0x4B, 0xC6,
0xCE, 0x06, 0xE9, 0xFA, 0xC2, 0xE5, 0xDC, 0xDD, 0x40, 0x25, 0x98, 0x3D, 0xE5, 0x7C, 0x67, 0xCA,
];
private static ReadOnlySpan<byte> MemeKeyMoncolleGet =>
[
0x99, 0x14, 0x94, 0x5B, 0x32, 0x80, 0x79, 0x6F, 0xF8, 0xEC, 0x69, 0x46, 0x81, 0xED, 0x22, 0x18,
0xF8, 0xFC, 0xD1, 0xC6, 0x62, 0x8F, 0xCF, 0xAC, 0xD6, 0x33, 0xA8, 0x66, 0x22, 0xD2, 0x78, 0xC0,
0x9F, 0xD1, 0xBA, 0x1B, 0x2E, 0x60, 0xAF, 0x28, 0x96, 0xED, 0xDA, 0xF7, 0x61, 0x10, 0xCA, 0x24,
0x6A, 0xED, 0x9E, 0x08, 0xD7, 0xD9, 0x0F, 0x7F, 0x28, 0x57, 0x64, 0xBB, 0xD3, 0xE2, 0x0E, 0x05,
0xA1, 0x1B, 0xA2, 0x14, 0xC9, 0x6B, 0x1A, 0x54, 0xE1, 0xA8, 0xD4, 0x8D, 0xE6, 0xEF, 0x06, 0x0C,
0x79, 0x01, 0x1A, 0x4B, 0x33, 0x26, 0xFB, 0xE5, 0x5F, 0x29, 0xFD, 0xE4, 0x0B, 0x31, 0xF4, 0x70,
];
private static ReadOnlySpan<byte> MemeKeyIslandScan =>
[
0x34, 0xB0, 0x84, 0x42, 0xE3, 0xD6, 0x8D, 0x37, 0x30, 0xCF, 0x5C, 0xEE, 0x9E, 0x82, 0xBC, 0xFD,
0x5F, 0x66, 0x64, 0x16, 0x8F, 0x43, 0xE9, 0xF2, 0xEA, 0x26, 0xCC, 0x7B, 0x94, 0xB1, 0xF4, 0x4F,
0xF7, 0xB3, 0xC0, 0xBA, 0xD5, 0xC3, 0x46, 0x69, 0x3D, 0xDA, 0xD5, 0x46, 0x1E, 0x60, 0x87, 0xAD,
0x06, 0x10, 0x15, 0x74, 0x4F, 0x6D, 0xBA, 0x29, 0x4F, 0x9B, 0xCD, 0x6F, 0x0E, 0x7A, 0x35, 0x85,
0x9F, 0x0A, 0x15, 0xF3, 0x58, 0xEB, 0x00, 0xBE, 0xD5, 0xA4, 0xC1, 0x88, 0x41, 0xA8, 0xB6, 0xA6,
0x9D, 0x91, 0xFC, 0x60, 0x3E, 0x6D, 0xCE, 0x05, 0x8D, 0xD9, 0x69, 0x83, 0xC5, 0xDF, 0xBB, 0x04,
];
private static ReadOnlySpan<byte> MemeKeyTvTokyo =>
[
0x19, 0x5B, 0x4E, 0xA9, 0xF4, 0x8E, 0x6A, 0xDB, 0xEE, 0x73, 0xEB, 0x0D, 0x52, 0x9C, 0x14, 0xED,
0xC9, 0x9B, 0xB0, 0x11, 0xF2, 0x14, 0xE0, 0x81, 0x2E, 0x51, 0x98, 0xBE, 0x40, 0x73, 0x97, 0x83,
0x64, 0x6F, 0xB8, 0xC7, 0x5A, 0xBF, 0xFD, 0x84, 0xAA, 0x11, 0xFB, 0x09, 0x5E, 0xFF, 0xF5, 0xE3,
0x83, 0xF7, 0xF4, 0x5A, 0x56, 0xA9, 0x67, 0x80, 0xC7, 0x9E, 0xBA, 0xBB, 0x43, 0x0D, 0x86, 0x0F,
0xA6, 0xCB, 0x3D, 0x0B, 0xFC, 0x16, 0x3C, 0x52, 0xED, 0xC5, 0xC1, 0x99, 0x96, 0x3D, 0x3E, 0x51,
0x87, 0x58, 0xF9, 0x11, 0x56, 0x9E, 0x1C, 0x66, 0x7B, 0xF8, 0xA1, 0x56, 0x43, 0xA1, 0x9E, 0x2F,
];
private static ReadOnlySpan<byte> MemeKeyCapPikachu =>
[
0xA9, 0x6E, 0x2D, 0x8D, 0x9B, 0x99, 0xDB, 0xFB, 0x93, 0x49, 0x39, 0xC0, 0x97, 0xE3, 0xAC, 0x10,
0x1C, 0x7D, 0x48, 0xCE, 0xC5, 0x2F, 0xCA, 0x71, 0x7B, 0x14, 0xB1, 0x98, 0x90, 0x20, 0x85, 0x92,
0x04, 0x5C, 0x43, 0x00, 0x35, 0xDD, 0x09, 0xA3, 0x14, 0x46, 0x14, 0x2E, 0x9E, 0xA3, 0x3C, 0xF3,
0xE6, 0xB6, 0xE6, 0x94, 0x84, 0xB6, 0xD2, 0xEE, 0xD5, 0x00, 0xB8, 0x38, 0x90, 0x48, 0x01, 0x34,
0x91, 0x60, 0x24, 0x03, 0xDB, 0xE7, 0xB8, 0x14, 0xEA, 0x06, 0x96, 0x67, 0xCF, 0xAD, 0xAF, 0xE7,
0x48, 0x95, 0x21, 0x7D, 0x78, 0x03, 0x7B, 0x4A, 0x45, 0x6F, 0xAB, 0x2C, 0xAF, 0xD7, 0x1E, 0x69,
];
} }