diff --git a/PKHeX.Core/Editing/Saves/Editors/EventOld/EventWorkspace.cs b/PKHeX.Core/Editing/Saves/Editors/EventOld/EventWorkspace.cs index df57f9979..cebbb0706 100644 --- a/PKHeX.Core/Editing/Saves/Editors/EventOld/EventWorkspace.cs +++ b/PKHeX.Core/Editing/Saves/Editors/EventOld/EventWorkspace.cs @@ -24,8 +24,8 @@ public sealed class EventWorkspace where TSave : class, IEventFlag { SAV.SetEventFlags(Flags); SAV.SetAllEventWork(Values); - if (SAV is EventWork7SM s7) // Ensure Magearna event flag has magic constant - s7.UpdateMagearnaConstant(); + if (SAV is EventWork7 sm) // Ensure QR event flag has magic constant(s) + sm.UpdateQrConstants(); } private static string GetResourceSuffix(GameVersion version) => version switch diff --git a/PKHeX.Core/Game/GameStrings/FilteredGameDataSource.cs b/PKHeX.Core/Game/GameStrings/FilteredGameDataSource.cs index 006cef1f2..5b9f4f817 100644 --- a/PKHeX.Core/Game/GameStrings/FilteredGameDataSource.cs +++ b/PKHeX.Core/Game/GameStrings/FilteredGameDataSource.cs @@ -56,23 +56,10 @@ public sealed class FilteredGameDataSource }; static IEnumerable FilterAbove(IReadOnlyList species, int limit) - { - foreach (var s in species) - { - if (s.Value <= limit) - yield return s; - } - } + => species.Where(s => s.Value <= limit); static IEnumerable FilterUnavailable(IReadOnlyList source, T table) where T : IPersonalTable - { - foreach (var s in source) - { - var species = s.Value; - if (table.IsSpeciesInGame((ushort)species)) - yield return s; - } - } + => source.Where(s => table.IsSpeciesInGame((ushort)s.Value)); } private static IEnumerable GetFilteredMoves(IGameValueLimit limit, GameDataSource source, bool HaX = false) @@ -103,27 +90,27 @@ public sealed class FilteredGameDataSource public readonly IReadOnlyList G4GroundTiles; public readonly IReadOnlyList ConsoleRegions = GameDataSource.Regions; - public IReadOnlyList GetAbilityList(PKM pk) - { - return GetAbilityList(pk.PersonalInfo); - } + private const char HiddenAbilitySuffix = 'H'; + private const char AbilityIndexSuffix = '1'; + + public IReadOnlyList GetAbilityList(PKM pk) => GetAbilityList(pk.PersonalInfo); public IReadOnlyList GetAbilityList(IPersonalAbility pi) { var list = new ComboItem[pi.AbilityCount]; - - 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); - } - + LoadAbilityList(pi, list, Source.Strings.abilitylist); return list; } - private static ReadOnlySpan AbilityIndexSuffixes => ['1', '2', 'H']; + private static void LoadAbilityList(IPersonalAbility pi, Span list, ReadOnlySpan 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); + } + } } diff --git a/PKHeX.Core/Legality/Encounters/Generator/Possible/EncounterPossible3.cs b/PKHeX.Core/Legality/Encounters/Generator/Possible/EncounterPossible3.cs index 48ff38e73..b58278e93 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/Possible/EncounterPossible3.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/Possible/EncounterPossible3.cs @@ -261,8 +261,6 @@ public record struct EncounterPossible3(EvoCriteria[] Chain, EncounterTypeGroup { if (evo.Species != enc.Species) continue; - if (enc.NotDistributed) - break; return SetCurrent(enc); } } diff --git a/PKHeX.Core/Legality/Encounters/Verifiers/EncounterVerifier.cs b/PKHeX.Core/Legality/Encounters/Verifiers/EncounterVerifier.cs index b390d77e3..d8672b860 100644 --- a/PKHeX.Core/Legality/Encounters/Verifiers/EncounterVerifier.cs +++ b/PKHeX.Core/Legality/Encounters/Verifiers/EncounterVerifier.cs @@ -23,8 +23,8 @@ public static class EncounterVerifier { EncounterEgg e => VerifyEncounterEgg(pk, e.Generation), 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.Deoxys, Location: 200 } when pk.Language == (int)LanguageID.Japanese => GetInvalid(LEncUnreleased), + EncounterStatic3 { Species: (int)Species.Mew } when pk.Language != (int)LanguageID.Japanese => GetInvalid(LEncUnreleasedEMewJP), + 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), MysteryGift g => VerifyEncounterEvent(pk, g), { 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 // 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 evolved = names[species + 1]; var unevolved = names[species]; diff --git a/PKHeX.Core/Legality/LegalityAnalysis.cs b/PKHeX.Core/Legality/LegalityAnalysis.cs index 48a79e3a3..07e4b7f96 100644 --- a/PKHeX.Core/Legality/LegalityAnalysis.cs +++ b/PKHeX.Core/Legality/LegalityAnalysis.cs @@ -114,18 +114,14 @@ public sealed class LegalityAnalysis Valid = false; // Moves and Relearn arrays can potentially be empty on error. - var moves = Info.Moves; - for (var i = 0; i < moves.Length; i++) + foreach (ref var p in Info.Moves.AsSpan()) { - ref var p = ref moves[i]; if (!p.IsParsed) p = MoveResult.Unobtainable(); } - moves = Info.Relearn; - for (var i = 0; i < moves.Length; i++) + foreach (ref var p in Info.Relearn.AsSpan()) { - ref var p = ref moves[i]; if (!p.IsParsed) p = MoveResult.Unobtainable(); } @@ -205,9 +201,6 @@ public sealed class LegalityAnalysis if (Entity.Version == GameVersion.CXD) CXD.Verify(this); - if (Info.EncounterMatch is WC3 {NotDistributed: true}) - AddLine(Severity.Invalid, LEncUnreleased, CheckIdentifier.Encounter); - if (Entity.Format >= 8) Transfer.VerifyTransferLegalityG8(this); } diff --git a/PKHeX.Core/Legality/Verifiers/EffortValueVerifier.cs b/PKHeX.Core/Legality/Verifiers/EffortValueVerifier.cs index ad651a645..eeee6827a 100644 --- a/PKHeX.Core/Legality/Verifiers/EffortValueVerifier.cs +++ b/PKHeX.Core/Legality/Verifiers/EffortValueVerifier.cs @@ -69,16 +69,10 @@ public sealed class EffortValueVerifier : Verifier } // Hard cap at 252 for Gen6+ - private static bool IsAnyAboveHardLimit6(ReadOnlySpan evs) => evs.ContainsAny(253, 254, 255); + private static bool IsAnyAboveHardLimit6(ReadOnlySpan evs) + => evs.ContainsAnyExceptInRange(0, EffortValues.Max252); // Vitamins can only raise to 100 in Gen3/4 private static bool IsAnyAboveVitaminLimit(ReadOnlySpan evs) - { - foreach (var iv in evs) - { - if (iv > EffortValues.MaxVitamins34) - return true; - } - return false; - } + => evs.ContainsAnyExceptInRange(0, EffortValues.MaxVitamins34); } diff --git a/PKHeX.Core/Moves/MoveInfo.cs b/PKHeX.Core/Moves/MoveInfo.cs index 961e26388..f730a1f41 100644 --- a/PKHeX.Core/Moves/MoveInfo.cs +++ b/PKHeX.Core/Moves/MoveInfo.cs @@ -68,10 +68,13 @@ public static class MoveInfo _ => false, }; + private const uint DynamaxMoveCount = (int)MaxSteelspike - (int)MaxFlare + 1; + private const uint TorqueMoveCount = (int)MagicalTorque - (int)BlazingTorque + 1; + /// /// Checks if the move is a Dynamax-only move. /// - public static bool IsMoveDynamax(ushort move) => move is (>= (int)MaxFlare and <= (int)MaxSteelspike); + public static bool IsMoveDynamax(ushort move) => move - (uint)MaxFlare < DynamaxMoveCount; /// /// Checks if the move can be known by anything in any context. @@ -82,7 +85,7 @@ public static class MoveInfo /// /// Checks if the move is a Starmobile-only move. /// - public static bool IsMoveTorque(ushort move) => move - (uint)BlazingTorque <= 4; + public static bool IsMoveTorque(ushort move) => move - (uint)BlazingTorque < TorqueMoveCount; /// /// Checks if the is unable to be used in battle. @@ -157,6 +160,7 @@ public static class MoveInfo /// /// Checks if the move can be sketched in any game. /// + /// Sketched move private static bool IsSketchPossible(ushort move) => move switch { // Can't Sketch @@ -172,8 +176,8 @@ public static class MoveInfo /// /// Checks if the move can be sketched in a specific game context. Pre-check with . /// - /// - /// + /// Sketched move + /// Context currently present in private static bool IsSketchPossible(ushort move, EntityContext context) => context switch { Gen6 when move is (int)ThousandArrows or (int)ThousandWaves => false, diff --git a/PKHeX.Core/MysteryGifts/WC3.cs b/PKHeX.Core/MysteryGifts/WC3.cs index e596374bc..904da4801 100644 --- a/PKHeX.Core/MysteryGifts/WC3.cs +++ b/PKHeX.Core/MysteryGifts/WC3.cs @@ -43,7 +43,6 @@ public sealed class WC3(GameVersion Version, bool Fateful = false) public override ushort Species { get; set; } public override bool IsEgg { get; set; } public override Moveset Moves { get; set; } - public bool NotDistributed { get; init; } public override Shiny Shiny { get; init; } public override GameVersion Version { get; } = Version; public override bool FatefulEncounter { get; } = Fateful; // Obedience Flag diff --git a/PKHeX.Core/PKM/PKM.cs b/PKHeX.Core/PKM/PKM.cs index 55af82b11..a53c9b7be 100644 --- a/PKHeX.Core/PKM/PKM.cs +++ b/PKHeX.Core/PKM/PKM.cs @@ -931,10 +931,8 @@ public abstract class PKM : ISpeciesForm, ITrainerID32, IGeneration, IShiny, ILa var rnd = Util.Rand; for (int i = 0; i < ivs.Length; i++) { - if (template[i] == -1) - ivs[i] = rnd.Next(MaxIV + 1); - else - ivs[i] = template[i]; + var spec = template[i]; + ivs[i] = spec != -1 ? spec : rnd.Next(MaxIV + 1); } SetIVs(ivs); } diff --git a/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeCrypto.cs b/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeCrypto.cs index 60f19f9b3..1d32c0241 100644 --- a/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeCrypto.cs +++ b/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeCrypto.cs @@ -97,20 +97,6 @@ public static class MemeCrypto return hash[..8].SequenceEqual(output[^8..]); } - public static bool VerifyMemeData(ReadOnlySpan 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 input, MemeKeyIndex keyIndex = MemeKeyIndex.PokedexAndSaveFile) { var output = input.ToArray(); diff --git a/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeKey.cs b/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeKey.cs index 18952b949..9eb152964 100644 --- a/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeKey.cs +++ b/PKHeX.Core/Saves/Encryption/MemeCrypto/MemeKey.cs @@ -144,11 +144,11 @@ public readonly ref struct MemeKey private byte[] GetAesKey(ReadOnlySpan data) { // HashLengthInBytes is 20. - Span hash = stackalloc byte[20]; + Span hash = stackalloc byte[SHA1.HashSizeInBytes]; using var h = IncrementalHash.CreateHash(HashAlgorithmName.SHA1); h.AppendData(DER); h.AppendData(data); - h.TryGetCurrentHash(hash, out _); + h.GetCurrentHash(hash); return hash[..chunk].ToArray(); // need a byte[0x10] (not 0x14) for the AES impl } diff --git a/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlock.cs b/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlock.cs index 9a8f6dfa9..3fa79a56c 100644 --- a/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlock.cs +++ b/PKHeX.Core/Saves/Encryption/SwishCrypto/SCBlock.cs @@ -136,7 +136,7 @@ public sealed class SCBlock 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())); } diff --git a/PKHeX.Core/Saves/Encryption/SwishCrypto/SwishCrypto.cs b/PKHeX.Core/Saves/Encryption/SwishCrypto/SwishCrypto.cs index 3d04aeb44..0a2c0b2eb 100644 --- a/PKHeX.Core/Saves/Encryption/SwishCrypto/SwishCrypto.cs +++ b/PKHeX.Core/Saves/Encryption/SwishCrypto/SwishCrypto.cs @@ -77,7 +77,7 @@ public static class SwishCrypto h.AppendData(IntroHashBytes); h.AppendData(data); h.AppendData(OutroHashBytes); - h.TryGetCurrentHash(hash, out _); + h.GetCurrentHash(hash); } /// diff --git a/PKHeX.Core/Saves/MemoryCard/SAV3GCMemoryCard.cs b/PKHeX.Core/Saves/MemoryCard/SAV3GCMemoryCard.cs index ba4104b95..abfa17de7 100644 --- a/PKHeX.Core/Saves/MemoryCard/SAV3GCMemoryCard.cs +++ b/PKHeX.Core/Saves/MemoryCard/SAV3GCMemoryCard.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; using System.Text; using static System.Buffers.Binary.BinaryPrimitives; @@ -75,9 +76,10 @@ public sealed class SAV3GCMemoryCard(Memory Raw) ushort csum = 0; ushort inv_csum = 0; - for (int i = 0; i < span.Length; i += 2) + var cast = MemoryMarshal.Cast(span); + foreach (var u16 in cast) { - var value = ReadUInt16BigEndian(span[i..]); + var value = BitConverter.IsLittleEndian ? ReverseEndianness(u16) : u16; csum += value; inv_csum += (ushort)~value; } @@ -155,6 +157,9 @@ public sealed class SAV3GCMemoryCard(Memory Raw) private bool IsCorruptedMemoryCard() { var csums = VerifyChecksums(); + if (csums == MemoryCardChecksumStatus.None) + return false; // eager return, true for all correct Memory Cards. + if (csums.HasFlag(MemoryCardChecksumStatus.HeaderBad)) return true; diff --git a/PKHeX.Core/Saves/SAV3E.cs b/PKHeX.Core/Saves/SAV3E.cs index feff41d84..c8a0d46b8 100644 --- a/PKHeX.Core/Saves/SAV3E.cs +++ b/PKHeX.Core/Saves/SAV3E.cs @@ -281,11 +281,13 @@ public sealed class SAV3E : SAV3, IGen3Hoenn, IGen3Joyful, IGen3Wonder, IDaycare private const uint EXTRADATA_SENTINEL = 0x0000B39D; 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 void SetExtraDataSentinelBattleVideo() => WriteUInt32LittleEndian(Data.AsSpan(OFS_BV), EXTRADATA_SENTINEL); - private Span BattleVideoData => Data.AsSpan(OFS_BV + 4, BattleVideo3.SIZE); + public Memory BattleVideoData => Data.AsMemory(OFS_BV + 4, BattleVideo3.SIZE); 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(); - set => SetData(BattleVideoData, value.Data); + set => SetData(BattleVideoData.Span, value.Data); } } diff --git a/PKHeX.Core/Saves/SAV7.cs b/PKHeX.Core/Saves/SAV7.cs index 226dabb7d..a9665bfc6 100644 --- a/PKHeX.Core/Saves/SAV7.cs +++ b/PKHeX.Core/Saves/SAV7.cs @@ -251,4 +251,6 @@ public abstract class SAV7 : SAV_BEEF, ITrainerStatRecord, ISaveBlock7Main, IReg IEventFlag37 IEventFlagProvider37.EventWork => EventWork; IMysteryGiftStorage IMysteryGiftStorageProvider.MysteryGiftStorage => MysteryGift; + + public abstract void UpdateQrConstants(); } diff --git a/PKHeX.Core/Saves/SAV7SM.cs b/PKHeX.Core/Saves/SAV7SM.cs index 963bc2433..c51a2588c 100644 --- a/PKHeX.Core/Saves/SAV7SM.cs +++ b/PKHeX.Core/Saves/SAV7SM.cs @@ -69,7 +69,7 @@ public sealed class SAV7SM : SAV7, ISaveBlock7SM private const ulong MagearnaConst = 0xCBE05F18356504AC; - public void UpdateMagearnaConstant() + public override void UpdateQrConstants() { var flag = EventWork.GetEventFlag(EventWork7SM.MagearnaEventFlag); // 3100 ulong value = flag ? MagearnaConst : 0ul; diff --git a/PKHeX.Core/Saves/SAV7USUM.cs b/PKHeX.Core/Saves/SAV7USUM.cs index d65b2728e..b6b12bebe 100644 --- a/PKHeX.Core/Saves/SAV7USUM.cs +++ b/PKHeX.Core/Saves/SAV7USUM.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -66,4 +67,19 @@ public sealed class SAV7USUM : SAV7, ISaveBlock7USUM public override GTS7 GTS => Blocks.GTS; public BattleAgency7 BattleAgency => Blocks.BattleAgency; #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); + } } diff --git a/PKHeX.Core/Saves/SAV8BS.cs b/PKHeX.Core/Saves/SAV8BS.cs index 5c95d4fe6..2c7c1b682 100644 --- a/PKHeX.Core/Saves/SAV8BS.cs +++ b/PKHeX.Core/Saves/SAV8BS.cs @@ -171,7 +171,7 @@ public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord, IE #region Checksums - private const int HashLength = 0x10; + private const int HashLength = MD5.HashSizeInBytes; private const int HashOffset = SaveUtil.SIZE_G8BDSP - HashLength; private Span CurrentHash => Data.AsSpan(HashOffset, HashLength); private static void ComputeHash(ReadOnlySpan data, Span dest) @@ -181,7 +181,7 @@ public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord, IE Span zeroes = stackalloc byte[HashLength]; // Hash is zeroed prior to computing over the payload. Treat it as zero. h.AppendData(zeroes); h.AppendData(data[(HashOffset + HashLength)..]); - h.TryGetCurrentHash(dest, out _); + h.GetCurrentHash(dest); } protected override void SetChecksums() => ComputeHash(Data, CurrentHash); diff --git a/PKHeX.Core/Saves/SaveFile.cs b/PKHeX.Core/Saves/SaveFile.cs index 845dc1e6c..12ef9ebe8 100644 --- a/PKHeX.Core/Saves/SaveFile.cs +++ b/PKHeX.Core/Saves/SaveFile.cs @@ -825,63 +825,3 @@ public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IGeneration, IVe } #endregion } - -public static class StorageUtil -{ - public static bool CompressStorage(this SaveFile sav, Span storage, out int storedCount, Span slotPointers) - { - // keep track of empty slots, and only write them at the end if slots were shifted (no need otherwise). - var empty = new List(); - 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 method, int maxCount) - { - for (int i = 0; i < maxCount; i++) - { - var pk = sav.GetBoxSlotAtIndex(i); - if (method(pk)) - return i; - } - return -1; - } -} diff --git a/PKHeX.Core/Saves/Substructures/Battle Videos/BattleVideo3.cs b/PKHeX.Core/Saves/Substructures/Battle Videos/BattleVideo3.cs index cdc534a7f..97048c5a0 100644 --- a/PKHeX.Core/Saves/Substructures/Battle Videos/BattleVideo3.cs +++ b/PKHeX.Core/Saves/Substructures/Battle Videos/BattleVideo3.cs @@ -5,12 +5,11 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; -public sealed class BattleVideo3(byte[] Data) : IBattleVideo +public sealed class BattleVideo3(Memory Raw) : IBattleVideo { public BattleVideo3() : this(new byte[SIZE]) { } - public readonly byte[] Data = (byte[])Data.Clone(); - + public Span Data => Raw.Span; internal const int SIZE = 0xF80; public byte Generation => 3; @@ -49,7 +48,7 @@ public sealed class BattleVideo3(byte[] Data) : IBattleVideo for (int p = 0; p < 6; 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()); } @@ -62,7 +61,7 @@ public sealed class BattleVideo3(byte[] Data) : IBattleVideo for (int p = 0; p < 6; 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 { - get => ReadUInt32LittleEndian(Data.AsSpan(0x4E8)); - set => WriteUInt32LittleEndian(Data.AsSpan(0x4E8), value); + get => ReadUInt32LittleEndian(Data[0x4E8..]); + set => WriteUInt32LittleEndian(Data[0x4E8..], value); } public uint Mode { - get => ReadUInt32LittleEndian(Data.AsSpan(0x4EC)); - set => WriteUInt32LittleEndian(Data.AsSpan(0x4EC), value); + get => ReadUInt32LittleEndian(Data[0x4EC..]); + set => WriteUInt32LittleEndian(Data[0x4EC..], value); } // ... public uint Checksum { - get => ReadUInt32LittleEndian(Data.AsSpan(SIZE - 4)); - set => WriteUInt32LittleEndian(Data.AsSpan(SIZE - 4), value); + get => ReadUInt32LittleEndian(Data[^4..]); + 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 data) { diff --git a/PKHeX.Core/Saves/Substructures/Gen7/EventWork7.cs b/PKHeX.Core/Saves/Substructures/Gen7/EventWork7.cs index 3ff582446..f185c0455 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/EventWork7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/EventWork7.cs @@ -5,6 +5,7 @@ namespace PKHeX.Core; public abstract class EventWork7(SAV7 sav, Memory raw) : SaveBlock(sav, raw), IEventFlag37 { + private readonly SAV7 sav7 = sav; protected abstract Span WorkSpan { get; } protected abstract Span FlagSpan { get; } protected abstract Memory FameSpan { get; } @@ -37,6 +38,8 @@ public abstract class EventWork7(SAV7 sav, Memory raw) : SaveBlock(s ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)TotalZygardeCellCount); SetWork(celloffset + index, val); } + + public void UpdateQrConstants() => sav7.UpdateQrConstants(); } public sealed class EventWork7SM(SAV7SM sav, Memory raw) : EventWork7(sav, raw) @@ -58,8 +61,6 @@ public sealed class EventWork7SM(SAV7SM sav, Memory raw) : EventWork7(sav, protected override Memory FameSpan => Raw.Slice(OffsetPostData, HallOfFame7.SIZE); public const int MagearnaEventFlag = 3100; - - public void UpdateMagearnaConstant() => ((SAV7SM)SAV).UpdateMagearnaConstant(); } public sealed class EventWork7USUM(SAV7USUM sav, Memory raw) : EventWork7(sav, raw) @@ -79,6 +80,9 @@ public sealed class EventWork7USUM(SAV7USUM sav, Memory raw) : EventWork7( protected override Span WorkSpan => Raw.Span[..(EventWorkCount * sizeof(ushort))]; protected override Span FlagSpan => Raw.Span.Slice(OffsetFlag, FlagCount / 8); protected override Memory FameSpan => Raw.Slice(OffsetPostData, HallOfFame7.SIZE); + + public const int MagearnaEventFlag = 4060; + public const int CapPikachuEventFlag = 4562; } public sealed class HallOfFame7(Memory raw) diff --git a/PKHeX.Core/Saves/Substructures/Gen7/Misc7.cs b/PKHeX.Core/Saves/Substructures/Gen7/Misc7.cs index b11e18701..596fe200e 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/Misc7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/Misc7.cs @@ -38,22 +38,22 @@ public sealed class Misc7(SAV7 sav, Memory raw) : SaveBlock(sav, raw } } + public int DaysFromRefreshed + { + get => Data[0x123]; + set => Data[0x123] = (byte)value; + } + public int Vivillon { get => Data[0x130] & 0x1F; set => Data[0x130] = (byte)((Data[0x130] & ~0x1F) | (value & 0x1F)); } - public uint StarterEncryptionConstant + public bool IsWormholeShiny { - get => ReadUInt32LittleEndian(Data[0x148..]); - set => WriteUInt32LittleEndian(Data[0x148..], value); - } - - public int DaysFromRefreshed - { - get => Data[0x123]; - set => Data[0x123] = (byte)value; + get => Data[0x136] == 1; + set => Data[0x136] = (byte)(value ? 1 : 0); } public int GetSurfScore(int recordID) @@ -69,4 +69,10 @@ public sealed class Misc7(SAV7 sav, Memory raw) : SaveBlock(sav, raw recordID = 0; WriteInt32LittleEndian(Data[(0x138 + (4 * recordID))..], score); } + + public uint StarterEncryptionConstant + { + get => ReadUInt32LittleEndian(Data[0x148..]); + set => WriteUInt32LittleEndian(Data[0x148..], value); + } } diff --git a/PKHeX.Core/Saves/Util/StorageUtil.cs b/PKHeX.Core/Saves/Util/StorageUtil.cs new file mode 100644 index 000000000..7930e716a --- /dev/null +++ b/PKHeX.Core/Saves/Util/StorageUtil.cs @@ -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 storage, out int storedCount, Span slotPointers) + { + // keep track of empty slots, and only write them at the end if slots were shifted (no need otherwise). + var empty = new List(); + 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 method, int maxCount) + { + for (int i = 0; i < maxCount; i++) + { + var pk = sav.GetBoxSlotAtIndex(i); + if (method(pk)) + return i; + } + return -1; + } + + /// + /// Copies a list to the destination list, with an option to copy to a starting point. + /// + /// Source list to copy from + /// Destination list/array + /// Criteria for skipping a slot + /// Starting point to copy to + /// Count of copied. + public static int CopyTo(this IEnumerable list, Span dest, Func 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(Span dest, Func skip, int ctr) + { + while (true) + { + if ((uint)ctr >= dest.Length) + return -1; + var exist = dest[ctr]; + if (exist == null || !skip(ctr)) + return ctr; + ctr++; + } + } +} diff --git a/PKHeX.Core/Util/ArrayUtil.cs b/PKHeX.Core/Util/ArrayUtil.cs deleted file mode 100644 index 400acb036..000000000 --- a/PKHeX.Core/Util/ArrayUtil.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace PKHeX.Core; - -/// -/// Array reusable logic -/// -public static class ArrayUtil -{ - /// - /// Copies a list to the destination list, with an option to copy to a starting point. - /// - /// Source list to copy from - /// Destination list/array - /// Criteria for skipping a slot - /// Starting point to copy to - /// Count of copied. - public static int CopyTo(this IEnumerable list, IList dest, Func 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(IList dest, Func skip, int ctr) - { - while (true) - { - if ((uint)ctr >= dest.Count) - return -1; - var exist = dest[ctr]; - if (exist == null || !skip(ctr)) - return ctr; - ctr++; - } - } -} diff --git a/PKHeX.Core/Util/StringUtil.cs b/PKHeX.Core/Util/StringUtil.cs index 7819bccc6..0afea5c7f 100644 --- a/PKHeX.Core/Util/StringUtil.cs +++ b/PKHeX.Core/Util/StringUtil.cs @@ -64,20 +64,10 @@ public static class StringUtil return -1; } - /// - /// Converts an all-caps hex string to a byte array. Expects no separation between byte tuples. - /// - public static byte[] ToByteArray(this string toTransform) - { - var result = new byte[toTransform.Length / 2]; - LoadHexBytesTo(toTransform, result, 2); - return result; - } - /// /// Converts an all-caps encoded ASCII-Text hex string to a byte array. /// - public static void LoadHexBytesTo(Span dest, ReadOnlySpan str, int tupleSize) + public static void LoadHexBytesTo(ReadOnlySpan str, Span dest, int tupleSize) { // 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. diff --git a/Tests/PKHeX.Core.Tests/Legality/RNG/RaidTests.cs b/Tests/PKHeX.Core.Tests/Legality/RNG/RaidTests.cs index 787013fe0..299b040d2 100644 --- a/Tests/PKHeX.Core.Tests/Legality/RNG/RaidTests.cs +++ b/Tests/PKHeX.Core.Tests/Legality/RNG/RaidTests.cs @@ -6,18 +6,45 @@ namespace PKHeX.Core.Tests.Legality; public class RaidTests { - public const string Charizard = "65E79FC0000085F1060060045CF0A3AA142C10005E00140015010000EE5501E2080A00000000000004FCFC0000000000000000008800000000001C0000000000000000000000000000000000000000008AAD000000000000430068006100720069007A00610072006400000000000000000035001E024C009B01181010080303030300000000000000002901FF7F1E3F0A000000000000000000000000000000000000000000000056006900630074006F0072006900610000000000000000000000010201000000320104080000000000000000000000000000000000002C000000020000000000FF0000000000000000000000000000004100720063006800690074000000000000000000000000000000FF000000000000000000140117000000A20009372804000000000000000100000200000000000000000000000000000000000000000064002901B700C10048013D01CE000000"; + // Just a regular Charizard pk8 + private static ReadOnlySpan 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] - [InlineData(Charizard, 0xbefd08cf9e027d0a)] - public void CheckMatch(string raw, ulong seed) + [Fact] + public void CheckDynamaxRaidCharizardPK8() { - byte[] data = raw.ToByteArray(); - var pk8 = new PK8(data); + var pk8 = new PK8(); + + const ulong seed = 0xbefd08cf9e027d0a; + Charizard.CopyTo(pk8.Data); bool found = IsPotentialRaidSeed(pk8.EncryptionConstant, pk8.PID, seed); found.Should().BeTrue(); + // Ensure it matches encounter data and the actual seed is matched. var la = new LegalityAnalysis(pk8); var enc = la.EncounterMatch; if (enc is not ISeedCorrelation64 s64) diff --git a/Tests/PKHeX.Core.Tests/Saves/MemeCrypto/MemeCryptoTests.cs b/Tests/PKHeX.Core.Tests/Saves/MemeCrypto/MemeCryptoTests.cs index 5aded5b58..6ca1f3117 100644 --- a/Tests/PKHeX.Core.Tests/Saves/MemeCrypto/MemeCryptoTests.cs +++ b/Tests/PKHeX.Core.Tests/Saves/MemeCrypto/MemeCryptoTests.cs @@ -1,3 +1,4 @@ +using System; using FluentAssertions; using Xunit; @@ -6,95 +7,329 @@ namespace PKHeX.Core.Tests.Saves; public class MemeCryptoTests { [Fact] - public void TestVerifySaveSignature() - { - var savebuffer = - "58EA53A7133F34DA9F2BEC12F1560354E8BDF8A484ADE4E2954D3C48673118EB67E2D52ED0196E54DC5D93013E9F3B00C8A43B556AEE8C2F763EA9DC125988C6B5F2D3C74CA2C58026BB024B403D09BC5950C54CEB6F21E45D0B66B68791BCBB6D7E67C2F7E4A7F4A517FC50B4FEED9A65BF901ABEB0FFAC44AE07237BE5DD2D" - .ToByteArray(); - Assert.True(MemeCrypto.VerifyMemeData(savebuffer, out _)); - } + public void TestVerifySaveSignature() => Assert.True(MemeCrypto.VerifyMemeData(SaveSignature, out _)); [Fact] public void TestVerifyMemeBuffer() { - var encrypted = - "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F4185F2713D5A8BCFFBFF47F86867DF680354913FEFEF08D0D0F1054CB054B508E7DE3AA789AC444D087AB9412CEE965A6D38E45EA010C77FF174F8F8D9993AEAFB9C0616E287B74487E0FF3F1874291B8170C71113971752717F8D188A319EA6" - .ToByteArray(); - var decrypted = - "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]); - } + const MemeKeyIndex type = MemeKeyIndex.PokedexAndSaveFile; + var result = MemeCrypto.VerifyMemeData(Encrypted00FFSigned, out var actual, type); + result.Should().BeTrue(); + DecryptedAsDexSave.SequenceEqual(actual).Should().BeTrue(); } [Fact] public void TestSignMemeBuffer() { - var encrypted = - "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F4185F2713D5A8BCFFBFF47F86867DF680354913FEFEF08D0D0F1054CB054B508E7DE3AA789AC444D087AB9412CEE965A6D38E45EA010C77FF174F8F8D9993AEAFB9C0616E287B74487E0FF3F1874291B8170C71113971752717F8D188A319EA6" - .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]); - } + var actual = MemeCrypto.SignMemeData(Decrypted00FF); + Encrypted00FFSigned.SequenceEqual(actual).Should().BeTrue(); } [Fact] public void TestVerifyPoke() { - var vector = - "A96E2D8D9B99DBFB934939C097E3AC101C7D48CEC52FCA717B14B19890208592045C430035DD09A31446142E9EA33CF3E6B6E69484B6D2EED500B8389048013491602403DBE7B814EA069667CFADAFE74895217D78037B4A456FAB2CAFD71E69504F4B4509000000000000" - .ToByteArray(); - var vector2 = - "A96E2D8D9B99DBFB934939C097E3AC101C7D48CEC52FCA717B14B19890208592045C430035DD09A31446142E9EA33CF3E6B6E69484B6D2EED500B8389048013491602403DBE7B814EA069667CFADAFE74895217D78037B4A456FAB2CAFD71E690000504F4B4509000000000000" - .ToByteArray(); - - Assert.True(MemeCrypto.VerifyMemePOKE(vector, out _)); - Assert.True(MemeCrypto.VerifyMemePOKE(vector2, out _)); + Assert.True(MemeCrypto.VerifyMemePOKE(Vector2[..^2], out _)); + Assert.True(MemeCrypto.VerifyMemePOKE(Vector2, out _)); } - [Theory] - [InlineData(MemeKeyIndex.LocalWireless, "1010030000030000000A0070300051308B3001FFD43020AB301003B430FC30B90830C830D10007C630A382000D6E300A005F00055700305D3046306A300000307530933044304D28306B000B5500158F308C203066002581305A3089A0002744002344308D30612A304C00276E0031DF0001C30830AD30E500110A0042A0004973003D4D305F308880007B0A006830823060C4002F10516A3063004D42305F5200536D009900A6F002F014F026FFF038F04AF05CF06EF080F092F0A4F0B6FFF0C8F0DAF0ECF0FEF110F122F134F1460000B5E400004646F86000F1AB31AB0030D730EC0E30BC30F3020FF173A723C162C9700608C5DCAFAB898032382AF1FA717BBC5D91C61AD7B6EA356220742C2DD4595765FCD8123D1E10ED6B0A1DA45D0C2148035A560BB1C5C246FA7EB64D729FC1DD8B61B4737DC51E184A97E4795E82BFD045743EC2AEAED26E87")] - [InlineData(MemeKeyIndex.FriendlyCompetition, "0300000077000000000000000D0106060404003204000101000000000000000000000000000000000000C00000000000000000000000000E000000000000000000000000000000C0070000000000000000000000987E000000000000000000000000000000000000D80300000000000000F00300000000000000E001070000000000000000000000000000000000000000000000000000000101920200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E2D0100015A03000000BE0700000000C287010050006F006B00E9006D006F006E00200054007200610069006E006500720020004F006E006C0069006E006500200043006F006D007000650074006900740069006F006E000000000000007700770077002E0070006F006B0065006D006F006E002D0074007200610069007DC67F945B27557C97D6C0B6D20FF11AFCE1580C5E9E80C7C3FBBA53B67F24D1A91BA2FB8CDD31F7FE29D894B3764A45407658BB9A6E6DC8B3F6A45F77686AC56EA64C0298D1B22F3D3850ADE10AEA6E185FD0EEA91AA38E3F0F0AA9E7ADA132")] - [InlineData(MemeKeyIndex.LiveCompetition, "0300000077000000000000000D0006060303003204000101000000000000000000000000000000000000C00000000000000000000000000E000000000000000000000000000000C0070000000000000000000000987E000000000000000000000000000000000000D80300000000000000F00300000000000000E001070000000000000000000000000000000000000000000000000000000101920200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E2D0100015A03000000BE07000000000B9101004900720069007300200043006D007000650074006900740069006F006E00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066007200650065000000000000000000000000000000000000000000000000006425CA0A78E79154EBA19550A0734F84A0C49F8B02E51FA8310548FFF14B7338276188C11C85C15701DAED228099FBF95A277E6249625208D8992FF027385D897AC097F0240C93A70E35FB8AD971DFE14832988DC70F5D1BE19C7EF5FBAFCC12")] - [InlineData(MemeKeyIndex.RentalTeam, "0300000077000000B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C74E35E4783A7E9E161C919C0E736F61B31741B8C1873C5D06D1D408D9DA9FE28DCB53851518FCC0758915EE2911B2194B20131C2E87BC63A255F3A5187763FAF99B1B99D0E4460125CCB7F1CE4A3CC0FC64463454DB25CC851EF9A360EA3D4BDB44B8DE96D1C9839744A3BF7A27FDF41A86491666DAA7BEF55EBFB954BA3FF741657252490BD11350EEF367DA0D229E04EE61DCA1220576014AC2E24232337AB4E7ADEE92B59C44E9AB8D19E0BA87547A0351C4088EDF8808D692461C84EB5D07C795D3B037290AC7C5A9FD2775A86EAD628F545F6E5193145ED22B3BCDE16FD3CCEEA80A5FD90CF8FD320C1D064C8881BF369F0D82EE0E637C434EC6CA4221FE0952C28A7E930956DC2B990817BCC2349EC4339CDD09E2ABB94DC44C171A1E70E2CC5538B9686A1E4AFF907436378FE2915F217EC7C3BA69E47C913794FFE8285DD399D3E39154CE51BA0C74663BD685634697D76EAB8890CF77E371BA7E5CD5FA78FAC7D58A3843C993331C8AFC5ABDCD17FC42B7C28A196C2FB0EF03FE84CAF3FD626A584D9406BC6FC5DC20DD0E19D1D08C96D9065F376E052639FFC0F61CCCE84CDC833F31B9A6C830DE9F58B257F5CBE5991FCA51871752B611AE3B21F5E1F1AE3CFAB49BC6F6721C1C18B74B11400D029E268D")] - [InlineData(MemeKeyIndex.PokedexAndSaveFile, "4F3994147824D4F9D9B3EEBDC2DA6777585413FA41D22347A3593E22467E2F117B51EDFEAE5977E19AD88EB1C63ADB7E9DEDB7F5AEEFECA5F777C7F12527729025C82DC4A12C8B344EB472B9F68BFD999916F0D03460FF1D3FDC5EF0F7A7F8FE")] - [InlineData(MemeKeyIndex.GaOle, "7C7F70413806DFD1D4E8F5610BE3CA705245593C24AB3102D7768D43B3D873EEB5BB6B188FD4F38F06EB06C1A27369E9822D10093DAD17692EA2D71BCB8941F883F3F0E2068941F65FB26950B7E54F5990E163809594381C99CF3330F052452C")] - [InlineData(MemeKeyIndex.MagearnaEvent, "9D741CBA2ABA23408461AC168B3227199C15FFA2784662FE169459DDC2409AE08FA302A6E7026051C8378D09898500028021FE21B1A8165C9918779BA140597E03ED3EAF2FD82C026B17E6BB0D1C4BC6CE06E9FAC2E5DCDD4025983DE57C67CA")] - [InlineData(MemeKeyIndex.MoncolleGet, "9914945B3280796FF8EC694681ED2218F8FCD1C6628FCFACD633A86622D278C09FD1BA1B2E60AF2896EDDAF76110CA246AED9E08D7D90F7F285764BBD3E20E05A11BA214C96B1A54E1A8D48DE6EF060C79011A4B3326FBE55F29FDE40B31F470")] - [InlineData(MemeKeyIndex.IslandScanEventSpecial, "34B08442E3D68D3730CF5CEE9E82BCFD5F6664168F43E9F2EA26CC7B94B1F44FF7B3C0BAD5C346693DDAD5461E6087AD061015744F6DBA294F9BCD6F0E7A35859F0A15F358EB00BED5A4C18841A8B6A69D91FC603E6DCE058DD96983C5DFBB04")] - [InlineData(MemeKeyIndex.TvTokyoDataBroadcasting, "195B4EA9F48E6ADBEE73EB0D529C14EDC99BB011F214E0812E5198BE40739783646FB8C75ABFFD84AA11FB095EFFF5E383F7F45A56A96780C79EBABB430D860FA6CB3D0BFC163C52EDC5C199963D3E518758F911569E1C667BF8A15643A19E2F")] - [InlineData(MemeKeyIndex.CapPikachuEvent, "A96E2D8D9B99DBFB934939C097E3AC101C7D48CEC52FCA717B14B19890208592045C430035DD09A31446142E9EA33CF3E6B6E69484B6D2EED500B8389048013491602403DBE7B814EA069667CFADAFE74895217D78037B4A456FAB2CAFD71E69")] - public void TestVerifyKnownKeys(MemeKeyIndex keyIndex, string keyVal) - { - var key = keyVal.ToByteArray(); - MemeCrypto.VerifyMemeData(key, out _, keyIndex).Should().BeTrue("because the key should be valid"); - } + private static void Verify(ReadOnlySpan span, MemeKeyIndex type) + => MemeCrypto.VerifyMemeData(span, out _, type).Should().BeTrue(); + + // Various payloads that have been signed with Memecrypto Keys + [Fact] public void VerifyMemeKeyLocalWireless() => Verify(MemeKeyLocalWireless, MemeKeyIndex.LocalWireless); + [Fact] public void VerifyMemeKeyFriendlyCompetition() => Verify(MemeKeyFriendlyCompetition, MemeKeyIndex.FriendlyCompetition); + [Fact] public void VerifyMemeKeyLiveCompetition() => Verify(MemeKeyLiveCompetition, MemeKeyIndex.LiveCompetition); + [Fact] public void VerifyMemeKeyRentalTeam() => Verify(MemeKeyRentalTeam, MemeKeyIndex.RentalTeam); + [Fact] public void VerifyMemeKeyPokedexAndSaveFile() => Verify(MemeKeyPokedexAndSaveFile, MemeKeyIndex.PokedexAndSaveFile); + [Fact] public void VerifyMemeKeyGaOle() => Verify(MemeKeyGaOle, MemeKeyIndex.GaOle); + [Fact] public void VerifyMemeKeyMagearnaEvent() => Verify(MemeKeyMagearnaEvent, MemeKeyIndex.MagearnaEvent); + [Fact] public void VerifyMemeKeyMoncolleGet() => Verify(MemeKeyMoncolleGet, MemeKeyIndex.MoncolleGet); + [Fact] public void VerifyMemeKeyIslandScanEventSpecial() => Verify(MemeKeyIslandScan, MemeKeyIndex.IslandScanEventSpecial); + [Fact] public void VerifyMemeKeyTvTokyoDataBroadcasting() => Verify(MemeKeyTvTokyo, MemeKeyIndex.TvTokyoDataBroadcasting); + [Fact] public void VerifyMemeKeyCapPikachuEvent() => Verify(MemeKeyCapPikachu, MemeKeyIndex.CapPikachuEvent); + + private static ReadOnlySpan 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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, + ]; }