diff --git a/PKHeX.Core/Game/GameUtil.cs b/PKHeX.Core/Game/GameUtil.cs index c3a4ec18c..2d54e1e58 100644 --- a/PKHeX.Core/Game/GameUtil.cs +++ b/PKHeX.Core/Game/GameUtil.cs @@ -127,36 +127,23 @@ public static byte GetGeneration(this GameVersion game) /// /// Game to retrieve the generation for /// Generation ID - public static ushort GetMaxSpeciesID(this GameVersion game) + public static ushort GetMaxSpeciesID(this GameVersion game) => game switch { - if (Gen1.Contains(game)) return Legal.MaxSpeciesID_1; - if (Gen2.Contains(game)) return Legal.MaxSpeciesID_2; - if (Gen3.Contains(game)) return Legal.MaxSpeciesID_3; - if (Gen4.Contains(game)) return Legal.MaxSpeciesID_4; - if (Gen5.Contains(game)) return Legal.MaxSpeciesID_5; - if (Gen6.Contains(game)) return Legal.MaxSpeciesID_6; - if (Gen7b.Contains(game)) return Legal.MaxSpeciesID_7b; - if (Gen7.Contains(game)) - { - if (SM.Contains(game)) - return Legal.MaxSpeciesID_7; - if (USUM.Contains(game)) - return Legal.MaxSpeciesID_7_USUM; - return Legal.MaxSpeciesID_7_USUM; - } - if (PLA == game) return Legal.MaxSpeciesID_8a; - if (BDSP.Contains(game)) return Legal.MaxSpeciesID_8b; - if (Gen8.Contains(game)) return Legal.MaxSpeciesID_8; - if (Gen9.Contains(game)) return Legal.MaxSpeciesID_9; - return 0; - } - - /// - /// Checks if the version (or subset versions) is equivalent to . - /// - /// Version (set) - /// Individual version - public static bool Contains(this GameVersion g1, int g2) => g1.Contains((GameVersion) g2); + RD or GN or BU or YW => Legal.MaxSpeciesID_1, + GD or SI or C => Legal.MaxSpeciesID_2, + S or R or E or FR or LG or CXD => Legal.MaxSpeciesID_3, + D or P or Pt or HG or SS => Legal.MaxSpeciesID_4, + B or W or B2 or W2 => Legal.MaxSpeciesID_5, + X or Y or AS or OR => Legal.MaxSpeciesID_6, + GP or GE => Legal.MaxSpeciesID_7b, + SN or MN => Legal.MaxSpeciesID_7, + US or UM => Legal.MaxSpeciesID_7_USUM, + PLA => Legal.MaxSpeciesID_8a, + BD or SP => Legal.MaxSpeciesID_8b, + SW or SH => Legal.MaxSpeciesID_8, + SL or VL => Legal.MaxSpeciesID_9, + _ => 0 + }; /// /// Checks if the version (or subset versions) is equivalent to . @@ -172,6 +159,17 @@ public static bool Contains(this GameVersion g1, GameVersion g2) return g1.ContainsFromLumped(g2); } + public static bool IsGen1(this GameVersion version) => version is RD or GN or BU or YW; + public static bool IsGen2(this GameVersion version) => version is GD or SI or C; + public static bool IsGen3(this GameVersion version) => version is S or R or E or FR or LG or CXD; + public static bool IsGen4(this GameVersion version) => version is HG or SS or D or P or Pt; + public static bool IsGen5(this GameVersion version) => version is W or B or W2 or B2; + public static bool IsGen6(this GameVersion version) => version is X or Y or AS or OR; + public static bool IsGen7(this GameVersion version) => version is SN or MN or US or UM; + public static bool IsGen7b(this GameVersion version) => version is GP or GE; + public static bool IsGen8(this GameVersion version) => version is SW or SH or PLA or BD or SP; + public static bool IsGen9(this GameVersion version) => version is SL or VL; + /// /// Checks if the version is the lump of the requested saved . /// diff --git a/PKHeX.Core/Legality/Encounters/Data/Gen8/Encounters8Nest.cs b/PKHeX.Core/Legality/Encounters/Data/Gen8/Encounters8Nest.cs index ffd5aa5c0..1b720d7a9 100644 --- a/PKHeX.Core/Legality/Encounters/Data/Gen8/Encounters8Nest.cs +++ b/PKHeX.Core/Legality/Encounters/Data/Gen8/Encounters8Nest.cs @@ -5,9 +5,16 @@ namespace PKHeX.Core; -internal static class Encounters8Nest +/// +/// Logic for Generation 8 Nest Encounters. +/// +public static class Encounters8Nest { - internal static ReadOnlySpan GetNestLocations(byte loc) => loc switch + /// + /// Get the locations for a given nest index. + /// + /// + public static ReadOnlySpan GetNestLocations(byte nestIndex) => nestIndex switch { 000 => [144, 134, 122], // 000 : Stony Wilderness, South Lake Miloch, Rolling Fields 001 => [144, 126], // 001 : Stony Wilderness, Watchtower Ruins @@ -214,18 +221,22 @@ internal static class Encounters8Nest /// /// Location IDs containing Dens that cannot be accessed without Rotom Bike's Water Mode. /// - internal static ReadOnlySpan InaccessibleRank12DistributionLocations => [154,178,186,188,190,192,194,226,228,230,234]; // Areas that are entirely restricted to water + public static ReadOnlySpan InaccessibleRank12DistributionLocations => [154,178,186,188,190,192,194,226,228,230,234]; // Areas that are entirely restricted to water /// /// Location IDs containing Dens that cannot be accessed without Rotom Bike's Water Mode. /// - internal static bool IsInaccessibleRank12Nest(byte nestID, byte location) + public static bool IsInaccessibleRank12Nest(byte nestID, byte location) { var noNest = GetInaccessibleRank12Nests(location); return noNest.Length != 0 && noNest.Contains(nestID); } - private static ReadOnlySpan GetInaccessibleRank12Nests(byte location) => location switch + /// + /// Returns the list of Nests that cannot be accessed without Rotom Bike's Water Mode. + /// + /// Met location + public static ReadOnlySpan GetInaccessibleRank12Nests(byte location) => location switch { 128 => [6,43], // East Lake Axewell 130 => [6,41,43], // West Lake Axewell diff --git a/PKHeX.Core/Legality/Encounters/Generator/Moveset/EncounterMovesetGenerator.cs b/PKHeX.Core/Legality/Encounters/Generator/Moveset/EncounterMovesetGenerator.cs index 16c18600a..7b1793b3e 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/Moveset/EncounterMovesetGenerator.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/Moveset/EncounterMovesetGenerator.cs @@ -51,7 +51,7 @@ public static IEnumerable GenerateEncounters(PKM pk, ITrainerInf /// /// Rough Pokémon data which contains the requested species, gender, and form. /// Trainer information of the receiver. - public static void OptimizeCriteria(PKM pk, ITrainerID32 info) + public static void OptimizeCriteria(PKM pk, ITrainerID32ReadOnly info) { pk.ID32 = info.ID32; // Necessary for Gen2 Headbutt encounters and Honey Tree encounters var htTrash = pk.HandlingTrainerTrash; diff --git a/PKHeX.Core/Legality/Encounters/Generator/Search/EncounterEnumerator2.cs b/PKHeX.Core/Legality/Encounters/Generator/Search/EncounterEnumerator2.cs index f05dc980c..974e49077 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/Search/EncounterEnumerator2.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/Search/EncounterEnumerator2.cs @@ -27,16 +27,23 @@ public EncounterEnumerator2(PKM pk, EvoCriteria[] chain) Entity = pk; Chain = chain; - if (pk is ICaughtData2 { CaughtData: not 0 } c2) + if (pk.Korean) + return; + + if (pk is not ICaughtData2 c2) { canOriginateCrystal = true; - hasOriginalMet = true; - met = c2.MetLocation; + return; } - else + if (c2.CaughtData == 0) { - canOriginateCrystal = pk is { Format: >= 7, Korean: false } || pk.CanInhabitGen1(); + canOriginateCrystal = GBRestrictions.CanVisitGen1(chain[0].Species); // can visit & wipe met + return; } + + canOriginateCrystal = true; + hasOriginalMet = true; + met = c2.MetLocation; } readonly object IEnumerator.Current => Current; diff --git a/PKHeX.Core/Legality/Verifiers/LevelVerifier.cs b/PKHeX.Core/Legality/Verifiers/LevelVerifier.cs index a60cadca2..04b9b4d1c 100644 --- a/PKHeX.Core/Legality/Verifiers/LevelVerifier.cs +++ b/PKHeX.Core/Legality/Verifiers/LevelVerifier.cs @@ -96,9 +96,10 @@ public void VerifyG1(LegalityAnalysis data) { // Pokémon has been traded illegally between games without evolving. // Trade evolution species IDs for Gen1 are sequential dex numbers. + var names = ParseSettings.SpeciesStrings; var species = enc.Species; - var evolved = ParseSettings.SpeciesStrings[species + 1]; - var unevolved = ParseSettings.SpeciesStrings[species]; + var evolved = names[species + 1]; + var unevolved = names[species]; data.AddLine(GetInvalid(string.Format(LEvoTradeReqOutsider, unevolved, evolved))); } } diff --git a/PKHeX.Core/Legality/Verifiers/TransferVerifier.cs b/PKHeX.Core/Legality/Verifiers/TransferVerifier.cs index 38deddde8..b6fde1171 100644 --- a/PKHeX.Core/Legality/Verifiers/TransferVerifier.cs +++ b/PKHeX.Core/Legality/Verifiers/TransferVerifier.cs @@ -197,7 +197,7 @@ public void VerifyVCEncounter(PKM pk, IEncounterTemplate original, EncounterTran if (original is EncounterStatic2 { DizzyPunchEgg: true}) // Dizzy Punch Gifts FlagIncompatibleTransferMove(pk, data.Info.Moves, 146, 2); // can't have Dizzy Punch at all - bool checkShiny = pk.VC2 || (pk.VC1 && GBRestrictions.IsTimeCapsuleTransferred(pk, data.Info.Moves, original).WasTimeCapsuleTransferred()); + bool checkShiny = pk.VC2 || original.Generation == 2 || MoveInfo.IsAnyFromGeneration(2, data.Info.Moves); if (!checkShiny) return; diff --git a/PKHeX.Core/PKM/Util/EntityBlank.cs b/PKHeX.Core/PKM/Util/EntityBlank.cs index 7b5d87b14..d601cc504 100644 --- a/PKHeX.Core/PKM/Util/EntityBlank.cs +++ b/PKHeX.Core/PKM/Util/EntityBlank.cs @@ -46,10 +46,10 @@ public static PKM GetBlank(TypeInfo type) public static PKM GetBlank(byte gen, GameVersion version) => gen switch { - 1 when version == GameVersion.BU => new PK1(true), - 7 when GameVersion.Gen7b.Contains(version) => new PB7(), - 8 when GameVersion.BDSP.Contains(version) => new PB8(), - 8 when GameVersion.PLA == version => new PA8(), + 1 when version is GameVersion.BU => new PK1(true), + 7 when version is GameVersion.GP or GameVersion.GE => new PB7(), + 8 when version is GameVersion.BD or GameVersion.SP => new PB8(), + 8 when version is GameVersion.PLA => new PA8(), _ => GetBlank(gen), }; diff --git a/PKHeX.Core/PKM/Util/Language.cs b/PKHeX.Core/PKM/Util/Language.cs index 684c76cb5..36f53347d 100644 --- a/PKHeX.Core/PKM/Util/Language.cs +++ b/PKHeX.Core/PKM/Util/Language.cs @@ -45,6 +45,9 @@ public static class Language private static bool HasLanguage(ReadOnlySpan permitted, byte language) => permitted.Contains(language); + /// + public static LanguageID GetSafeLanguage(byte generation, LanguageID prefer) => GetSafeLanguage(generation, prefer, GameVersion.Any); + /// /// Returns the language that is safe to use for the given generation. /// @@ -52,11 +55,11 @@ public static class Language /// Preferred language. /// Game version to check. /// Language that is safe to use for the given generation. - public static LanguageID GetSafeLanguage(byte generation, LanguageID prefer, GameVersion game = GameVersion.Any) => generation switch + public static LanguageID GetSafeLanguage(byte generation, LanguageID prefer, GameVersion game) => generation switch { 1 when game == GameVersion.BU => Japanese, 1 => HasLanguage(Languages_3, (byte)prefer) ? prefer : SafeLanguage, - 2 => HasLanguage(Languages_GB, (byte)prefer) && (prefer != Korean || game == GameVersion.C) ? prefer : SafeLanguage, + 2 => HasLanguage(Languages_GB, (byte)prefer) ? prefer : SafeLanguage, 3 => HasLanguage(Languages_3 , (byte)prefer) ? prefer : SafeLanguage, 4 or 5 or 6 => HasLanguage(Languages_GB, (byte)prefer) ? prefer : SafeLanguage, _ => HasLanguage(Languages, (byte)prefer) ? prefer : SafeLanguage, diff --git a/PKHeX.Core/PKM/Util/PokeCrypto.cs b/PKHeX.Core/PKM/Util/PokeCrypto.cs index dbbe1d27a..f5ee9728c 100644 --- a/PKHeX.Core/PKM/Util/PokeCrypto.cs +++ b/PKHeX.Core/PKM/Util/PokeCrypto.cs @@ -45,11 +45,11 @@ public static class PokeCrypto // Gen7 Format is the same size as Gen6. - internal const int SIZE_8STORED = 8 + (4 * SIZE_8BLOCK); // 0x148 + internal const int SIZE_8STORED = 8 + (BlockCount * SIZE_8BLOCK); // 0x148 internal const int SIZE_8PARTY = SIZE_8STORED + 0x10; // 0x158 private const int SIZE_8BLOCK = 80; // 0x50 - internal const int SIZE_8ASTORED = 8 + (4 * SIZE_8ABLOCK); // 0x168 + internal const int SIZE_8ASTORED = 8 + (BlockCount * SIZE_8ABLOCK); // 0x168 internal const int SIZE_8APARTY = SIZE_8ASTORED + 0x10; // 0x178 private const int SIZE_8ABLOCK = 88; // 0x58 @@ -57,6 +57,8 @@ public static class PokeCrypto internal const int SIZE_9PARTY = SIZE_8PARTY; private const int SIZE_9BLOCK = SIZE_8BLOCK; + private const int BlockCount = 4; + /// /// Positions for shuffling. /// @@ -108,7 +110,7 @@ public static class PokeCrypto ]; /// - /// Shuffles a 232 byte array containing Pokémon data. + /// Shuffles a 4-block byte array containing Pokémon data. /// /// Data to shuffle /// Block Shuffle order @@ -124,12 +126,12 @@ public static byte[] ShuffleArray(ReadOnlySpan data, uint sv, [ConstantExp private static void ShuffleArray(ReadOnlySpan data, Span result, uint sv, [ConstantExpected(Min = 0)] int blockSize) { - int index = (int)sv * 4; + int index = (int)sv * BlockCount; const int start = 8; data[..start].CopyTo(result[..start]); - var end = start + (blockSize * 4); + var end = start + (blockSize * BlockCount); data[end..].CopyTo(result[end..]); - for (int block = 3; block >= 0; block--) + for (int block = 0; block < BlockCount; block++) { var dest = result.Slice(start + (blockSize * block), blockSize); int ofs = BlockPosition[index + block]; @@ -313,7 +315,7 @@ public static byte[] EncryptArray4BE(ReadOnlySpan pk) private static void CryptPKM(Span data, uint pv, [ConstantExpected(Min = 0)] int blockSize) { const int start = 8; - int end = (4 * blockSize) + start; + int end = (BlockCount * blockSize) + start; CryptArray(data[start..end], pv); // Blocks if (data.Length > end) CryptArray(data[end..], pv); // Party Stats @@ -323,7 +325,7 @@ private static void CryptPKM(Span data, uint pv, [ConstantExpected(Min = 0 private static void CryptPKM45(Span data, uint pv, uint chk, [ConstantExpected(Min = 0)] int blockSize) { const int start = 8; - int end = (4 * blockSize) + start; + int end = (BlockCount * blockSize) + start; CryptArray(data[start..end], chk); // Blocks if (data.Length > end) CryptArray(data[end..], pv); // Party Stats @@ -332,13 +334,13 @@ private static void CryptPKM45(Span data, uint pv, uint chk, [ConstantExpe [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CryptArray(Span data, uint seed) { - foreach (ref var u32 in MemoryMarshal.Cast(data)) + foreach (ref var u16 in MemoryMarshal.Cast(data)) { seed = (0x41C64E6D * seed) + 0x00006073; var xor = (ushort)(seed >> 16); if (!BitConverter.IsLittleEndian) xor = ReverseEndianness(xor); - u32 ^= xor; + u16 ^= xor; } } @@ -382,10 +384,10 @@ private static byte[] ShuffleArray3(ReadOnlySpan data, uint sv) private static void ShuffleArray3(ReadOnlySpan data, Span result, uint sv) { - int index = (int)sv * 4; + int index = (int)sv * BlockCount; data[..SIZE_3HEADER].CopyTo(result[..SIZE_3HEADER]); data[SIZE_3STORED..].CopyTo(result[SIZE_3STORED..]); - for (int block = 3; block >= 0; block--) + for (int block = 0; block < BlockCount; block++) { var dest = result.Slice(SIZE_3HEADER + (SIZE_3BLOCK * block), SIZE_3BLOCK); int ofs = BlockPosition[index + block]; @@ -418,7 +420,7 @@ public static byte[] EncryptArray3(ReadOnlySpan pk) /// Generation 3 Format encryption check which verifies the checksum public static void DecryptIfEncrypted3(ref byte[] pk) { - ushort chk = Checksums.Add16(pk.AsSpan(0x20, 4 * SIZE_3BLOCK)); + ushort chk = Checksums.Add16(pk.AsSpan(0x20, BlockCount * SIZE_3BLOCK)); if (chk != ReadUInt16LittleEndian(pk.AsSpan(0x1C))) pk = DecryptArray3(pk); } diff --git a/PKHeX.Core/Saves/SaveFile.cs b/PKHeX.Core/Saves/SaveFile.cs index 649351392..6d5317c26 100644 --- a/PKHeX.Core/Saves/SaveFile.cs +++ b/PKHeX.Core/Saves/SaveFile.cs @@ -1,8 +1,6 @@ -using Microsoft.VisualBasic.FileIO; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Linq; namespace PKHeX.Core; @@ -10,7 +8,7 @@ namespace PKHeX.Core; /// /// Base Class for Save Files /// -public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IGeneration, IVersion, IStringConverter +public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IStringConverter, ITrainerID32 { // General Object Properties public byte[] Data; diff --git a/PKHeX.Core/Saves/Util/SaveUtil.cs b/PKHeX.Core/Saves/Util/SaveUtil.cs index 8b28a9da4..2fc76b59b 100644 --- a/PKHeX.Core/Saves/Util/SaveUtil.cs +++ b/PKHeX.Core/Saves/Util/SaveUtil.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Linq; using System.Threading; using static System.Buffers.Binary.BinaryPrimitives; using static PKHeX.Core.MessageStrings; @@ -921,7 +920,7 @@ public static bool IsBackup(ReadOnlySpan path) /// Determines whether the save data size is valid for automatically detecting saves. /// /// Only checks the list. - public static bool IsSizeValidHandler(long size) => Handlers.Any(z => z.IsRecognized(size)); + public static bool IsSizeValidHandler(long size) => Handlers.Exists(z => z.IsRecognized(size)); /// /// Determines whether the save data size is valid for automatically detecting saves.