diff --git a/PKHeX.Core/Editing/Showdown/ShowdownSet.cs b/PKHeX.Core/Editing/Showdown/ShowdownSet.cs index aff8b89f2..b7f069f87 100644 --- a/PKHeX.Core/Editing/Showdown/ShowdownSet.cs +++ b/PKHeX.Core/Editing/Showdown/ShowdownSet.cs @@ -373,7 +373,7 @@ public sealed class ShowdownSet : IBattleTemplate result.Add($"Ability: {Strings.Ability[Ability]}"); if (Context == EntityContext.Gen9 && TeraType != MoveType.Any) { - if ((uint)TeraType <= (int)MoveType.Fairy) + if ((uint)TeraType <= TeraTypeUtil.MaxType) // Fairy result.Add($"Tera Type: {Strings.Types[(int)TeraType]}"); else if ((uint)TeraType == TeraTypeUtil.Stellar) result.Add($"Tera Type: {Strings.Types[TeraTypeUtil.StellarTypeDisplayStringIndex]}"); @@ -740,7 +740,7 @@ public sealed class ShowdownSet : IBattleTemplate return type; if (type[0] == '(' && type[^1] == ')') - return type[1..^1].Trim(); + return type[1..^1].Trim(); if (type[0] == '[' && type[^1] == ']') return type[1..^1].Trim(); diff --git a/PKHeX.Core/Items/ItemStorage8BDSP.cs b/PKHeX.Core/Items/ItemStorage8BDSP.cs index f5fdf4ec7..af81a9d80 100644 --- a/PKHeX.Core/Items/ItemStorage8BDSP.cs +++ b/PKHeX.Core/Items/ItemStorage8BDSP.cs @@ -155,7 +155,7 @@ public sealed class ItemStorage8BDSP : IItemStorage InventoryType.Berries, InventoryType.Balls, InventoryType.BattleItems, InventoryType.Treasure, ]; - public static ushort[] GetAll() => [..Pouch_Regular_BS, ..Pouch_Ball_BS, ..Pouch_Battle_BS, ..Pouch_Berries_BS, ..Pouch_TMHM_BS, ..Pouch_Medicine_BS, ..Pouch_Treasure_BS]; + public static ushort[] GetAllHeld() => [..Pouch_Regular_BS, ..Pouch_Ball_BS, ..Pouch_Battle_BS, ..Pouch_Berries_BS, ..Pouch_TMHM_BS, ..Pouch_Medicine_BS, ..Pouch_Treasure_BS]; public static InventoryType GetInventoryPouch(ushort itemIndex) { diff --git a/PKHeX.Core/Legality/Core.cs b/PKHeX.Core/Legality/Core.cs index 09151431e..412034d2a 100644 --- a/PKHeX.Core/Legality/Core.cs +++ b/PKHeX.Core/Legality/Core.cs @@ -150,7 +150,7 @@ public static class Legal internal static readonly ushort[] HeldItems_USUM = ItemStorage7USUM.GetAllHeld(); internal static readonly ushort[] HeldItems_GG = []; internal static readonly ushort[] HeldItems_SWSH = ItemStorage8SWSH.GetAllHeld(); - internal static readonly ushort[] HeldItems_BS = ItemStorage8BDSP.GetAll(); + internal static readonly ushort[] HeldItems_BS = ItemStorage8BDSP.GetAllHeld(); internal static readonly ushort[] HeldItems_LA = []; internal static readonly ushort[] HeldItems_SV = ItemStorage9SV.GetAllHeld(); diff --git a/PKHeX.Core/Legality/Encounters/Information/EncounterLearn.cs b/PKHeX.Core/Legality/Encounters/Information/EncounterLearn.cs index 82c53ec27..a5a410632 100644 --- a/PKHeX.Core/Legality/Encounters/Information/EncounterLearn.cs +++ b/PKHeX.Core/Legality/Encounters/Information/EncounterLearn.cs @@ -48,41 +48,44 @@ public static class EncounterLearn if (speciesID <= 0) return []; - var allMoves = moves.ToList(); - var span = new ushort[allMoves.Count]; - for (int i = 0; i < span.Length; i++) + const int maxMoves = 4; + Span span = stackalloc ushort[maxMoves]; + int ctr = 0; + foreach (var move in moves) { - var move = allMoves[i]; var index = StringUtil.FindIndexIgnoreCase(str.movelist, move); if (index <= 0) return []; - span[i] = (ushort)index; + span[ctr++] = (ushort)index; + if (ctr >= span.Length) + break; } + var moveset = span[..ctr].ToArray(); - return GetLearn((ushort)speciesID, span, form); + return GetLearn((ushort)speciesID, moveset, form); } /// /// Gets all encounters where a can learn all input . /// - public static IEnumerable GetLearn(ushort species, ushort[] moves, byte form = 0) + public static IEnumerable GetLearn(ushort species, ReadOnlyMemory moves, byte form = 0) { if (species == 0) return []; - if (moves.AsSpan().Contains(0)) + if (moves.Span.Contains(0)) return []; var vers = GameUtil.GameVersions; return GetLearnInternal(species, form, moves, vers); } - private static IEnumerable GetLearnInternal(ushort species, byte form, ushort[] moves, GameVersion[] vers) + private static IEnumerable GetLearnInternal(ushort species, byte form, ReadOnlyMemory moves, GameVersion[] vers) { bool iterated = false; if (PersonalTable.SV.IsPresentInGame(species, form)) { var blank = new PK9 { Species = species, Form = form }; - var encs = EncounterMovesetGenerator.GenerateEncounters(blank, moves, vers); + var encs = EncounterMovesetGenerator.GenerateEncounters(blank, moves, GameVersion.SL, GameVersion.VL); foreach (var enc in encs) yield return enc; } @@ -92,7 +95,6 @@ public static class EncounterLearn var encs = EncounterMovesetGenerator.GenerateEncounters(blank, moves, GameVersion.PLA); foreach (var enc in encs) yield return enc; - iterated = true; } if (PersonalTable.BDSP.IsPresentInGame(species, form)) { @@ -100,7 +102,6 @@ public static class EncounterLearn var encs = EncounterMovesetGenerator.GenerateEncounters(blank, moves, GameVersion.BD, GameVersion.SP); foreach (var enc in encs) yield return enc; - iterated = true; } if (PersonalTable.SWSH.IsPresentInGame(species, form)) { diff --git a/PKHeX.Core/Legality/Learnset/EggMoves.cs b/PKHeX.Core/Legality/Learnset/EggMoves.cs index 5c9ab1b97..614953a0b 100644 --- a/PKHeX.Core/Legality/Learnset/EggMoves.cs +++ b/PKHeX.Core/Legality/Learnset/EggMoves.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -73,10 +74,9 @@ public sealed class EggMoves6 : EggMoves continue; } - var moves = new ushort[count]; - var span = data[2..]; - for (int j = 0; j < moves.Length; j++) - moves[j] = ReadUInt16LittleEndian(span[(j * 2)..]); + var moves = MemoryMarshal.Cast(data).Slice(1, count).ToArray(); + if (!BitConverter.IsLittleEndian) + ReverseEndianness(moves, moves); result[i] = new EggMoves6(moves); } return result; @@ -91,6 +91,7 @@ public sealed class EggMoves7 : EggMoves /// /// Points to the index where form data is, within the parent Egg Move object array. /// + /// This value is the same for all form entries for a given species. public readonly ushort FormTableIndex; private EggMoves7(ushort[] moves, ushort formIndex = 0) : base(moves) => FormTableIndex = formIndex; @@ -114,10 +115,9 @@ public sealed class EggMoves7 : EggMoves continue; } - var moves = new ushort[count]; - var span = data[4..]; - for (int j = 0; j < moves.Length; j++) - moves[j] = ReadUInt16LittleEndian(span[(j * 2)..]); + var moves = MemoryMarshal.Cast(data).Slice(2, count).ToArray(); + if (!BitConverter.IsLittleEndian) + ReverseEndianness(moves, moves); result[i] = new EggMoves7(moves, formIndex); } return result; @@ -130,17 +130,23 @@ internal static class EggMovesExtensions { if (species >= table.Length) return []; - var entry = table[species]; - if (form == 0 || species >= entry.FormTableIndex) + + // Sanity check species in the event it is out of range. + var baseIndex = entry.FormTableIndex; + if (species > baseIndex) + return []; + + if (form == 0) return entry.Moves; - // Sanity check form in the event it is out of range. - var baseIndex = entry.FormTableIndex; - var index = baseIndex + form - 1; - if ((uint)index >= table.Length) + // Jump to the associated form's entry within the table. + var index = form - 1u + baseIndex; + if (index >= table.Length) return []; entry = table[index]; + + // Double-check that the entry is still associated to the species. if (entry.FormTableIndex != baseIndex) return []; @@ -165,9 +171,9 @@ public static class EggMoves9 result[i] = empty; continue; } - var moves = new ushort[data.Length >> 1]; - for (int j = 0; j < data.Length; j+=2) - moves[j >> 1] = ReadUInt16LittleEndian(data[j..]); + var moves = MemoryMarshal.Cast(data).ToArray(); + if (!BitConverter.IsLittleEndian) + ReverseEndianness(moves, moves); result[i] = moves; } return result; diff --git a/PKHeX.Core/Saves/Util/SaveFinder.cs b/PKHeX.Core/Saves/Util/SaveFinder.cs index b05671b39..70a6d4989 100644 --- a/PKHeX.Core/Saves/Util/SaveFinder.cs +++ b/PKHeX.Core/Saves/Util/SaveFinder.cs @@ -165,20 +165,17 @@ public static class SaveFinder /// public static IEnumerable DetectSaveFiles() => GetSaveFiles(Environment.GetLogicalDrives(), true, CustomBackupPaths, true); - /// + /// + /// True if a valid save file was found, false otherwise. + /// + /// public static bool TryDetectSaveFile([NotNullWhen(true)] out SaveFile? sav) => TryDetectSaveFile(Environment.GetLogicalDrives(), out sav); + /// public static bool TryDetectSaveFile(IReadOnlyList drives, [NotNullWhen(true)] out SaveFile? sav) { - var result = FindMostRecentSaveFile(drives, CustomBackupPaths); - if (result == null) - { - sav = null; - return false; - } - - var path = result.Metadata.FilePath!; - sav = result; + sav = FindMostRecentSaveFile(drives, CustomBackupPaths); + var path = sav?.Metadata.FilePath; return File.Exists(path); } } diff --git a/PKHeX.WinForms/Subforms/SAV_Database.cs b/PKHeX.WinForms/Subforms/SAV_Database.cs index b3c0c5c34..f838955f5 100644 --- a/PKHeX.WinForms/Subforms/SAV_Database.cs +++ b/PKHeX.WinForms/Subforms/SAV_Database.cs @@ -402,18 +402,9 @@ public partial class SAV_Database : Form if (Main.Settings.EntityDb.FilterUnavailableSpecies) { - static bool IsPresentInGameSV(ISpeciesForm pk) => pk is PK9 || PersonalTable.SV.IsPresentInGame(pk.Species, pk.Form); - static bool IsPresentInGameSWSH(ISpeciesForm pk) => pk is PK8 || PersonalTable.SWSH.IsPresentInGame(pk.Species, pk.Form); - static bool IsPresentInGameBDSP(ISpeciesForm pk) => pk is PB8 || PersonalTable.BDSP.IsPresentInGame(pk.Species, pk.Form); - static bool IsPresentInGamePLA(ISpeciesForm pk) => pk is PA8 || PersonalTable.LA.IsPresentInGame(pk.Species, pk.Form); - if (sav is SAV9SV) - result.RemoveAll(z => !IsPresentInGameSV(z.Entity)); - else if (sav is SAV8SWSH) - result.RemoveAll(z => !IsPresentInGameSWSH(z.Entity)); - else if (sav is SAV8BS) - result.RemoveAll(z => !IsPresentInGameBDSP(z.Entity)); - else if (sav is SAV8LA) - result.RemoveAll(z => !IsPresentInGamePLA(z.Entity)); + var filter = GetFilterForSaveFile(sav); + if (filter != null) + result.RemoveAll(z => !filter(z.Entity)); } var sort = Main.Settings.EntityDb.InitialSortMode; @@ -426,6 +417,15 @@ public partial class SAV_Database : Form return result; } + private static Func? GetFilterForSaveFile(SaveFile sav) => sav switch + { + SAV8SWSH => static pk => pk is PK9 || PersonalTable.SV.IsPresentInGame(pk.Species, pk.Form), + SAV8BS => static pk => pk is PK8 || PersonalTable.SWSH.IsPresentInGame(pk.Species, pk.Form), + SAV8LA => static pk => pk is PB8 || PersonalTable.BDSP.IsPresentInGame(pk.Species, pk.Form), + SAV9SV => static pk => pk is PA8 || PersonalTable.LA.IsPresentInGame(pk.Species, pk.Form), + _ => null, + }; + private static void TryAddPKMsFromSaveFilePath(ConcurrentBag dbTemp, string file) { var sav = SaveUtil.GetVariantSAV(file); diff --git a/PKHeX.WinForms/Subforms/SAV_Encounters.cs b/PKHeX.WinForms/Subforms/SAV_Encounters.cs index 191c6447b..50f4f30f9 100644 --- a/PKHeX.WinForms/Subforms/SAV_Encounters.cs +++ b/PKHeX.WinForms/Subforms/SAV_Encounters.cs @@ -299,7 +299,7 @@ public partial class SAV_Encounters : Form yield return i; } - private IEnumerable GetAllSpeciesFormEncounters(IEnumerable species, IPersonalTable pt, IReadOnlyList versions, ushort[] moves, PKM pk, CancellationToken token) + private IEnumerable GetAllSpeciesFormEncounters(IEnumerable species, IPersonalTable pt, IReadOnlyList versions, ReadOnlyMemory moves, PKM pk, CancellationToken token) { foreach (var s in species) { @@ -339,7 +339,7 @@ public partial class SAV_Encounters : Form public int GetHashCode(T obj) => RuntimeHelpers.GetHashCode(obj); } - private IEnumerable GetEncounters(ushort species, byte form, ushort[] moves, PKM pk, IReadOnlyList vers) + private IEnumerable GetEncounters(ushort species, byte form, ReadOnlyMemory moves, PKM pk, IReadOnlyList vers) { pk.Species = species; pk.Form = form; diff --git a/Tests/PKHeX.Core.Tests/Simulator/ShowdownSetTests.cs b/Tests/PKHeX.Core.Tests/Simulator/ShowdownSetTests.cs index 22ed89189..16e3e0a01 100644 --- a/Tests/PKHeX.Core.Tests/Simulator/ShowdownSetTests.cs +++ b/Tests/PKHeX.Core.Tests/Simulator/ShowdownSetTests.cs @@ -54,7 +54,7 @@ public class ShowdownSetTests public void SimGetVivillonPostcardSV(byte form) { var pk9 = new PK9 { Species = (int)Species.Vivillon, Form = form }; - ushort[] moves = []; + var moves = ReadOnlyMemory.Empty; var encounters = EncounterMovesetGenerator.GenerateEncounters(pk9, moves, GameVersion.SL); encounters.OfType().Should().NotBeEmpty(); }