diff --git a/PKHeX.Core/Legality/Core.cs b/PKHeX.Core/Legality/Core.cs
index bac64a89a..79ecda0dd 100644
--- a/PKHeX.Core/Legality/Core.cs
+++ b/PKHeX.Core/Legality/Core.cs
@@ -17,7 +17,6 @@ public static class Legal
internal const int MaxItemID_2 = 255;
internal const int MaxAbilityID_2 = 0;
- internal const int MaxSpeciesIndex_3 = 412;
internal const int MaxSpeciesID_3 = 386;
internal const int MaxMoveID_3 = 354;
internal const int MaxItemID_3 = 374;
diff --git a/PKHeX.Core/Legality/Tables/FormInfo.cs b/PKHeX.Core/Legality/Tables/FormInfo.cs
index 941d6c9b2..60430be57 100644
--- a/PKHeX.Core/Legality/Tables/FormInfo.cs
+++ b/PKHeX.Core/Legality/Tables/FormInfo.cs
@@ -16,27 +16,19 @@ public static class FormInfo
/// Entity form
/// Current generation format
/// True if it can only exist in a battle, false if it can exist outside of battle.
- public static bool IsBattleOnlyForm(ushort species, byte form, int format)
+ public static bool IsBattleOnlyForm(ushort species, byte form, int format) => BattleOnly.Contains(species) && species switch
{
- if (!BattleOnly.Contains(species))
- return false;
-
+ // Only continue checking if the species is in the list of Battle Only forms.
// Some species have battle only forms as well as out-of-battle forms (other than base form).
- switch (species)
- {
- case (int)Slowbro when form == 2 && format >= 8: // this one is OK, Galarian Slowbro (not a Mega)
- case (int)Darmanitan when form == 2 && format >= 8: // this one is OK, Galarian non-Zen
- case (int)Zygarde when form < 4: // Zygarde Complete
- case (int)Mimikyu when form == 2: // Totem disguise Mimikyu
- case (int)Necrozma when form < 3: // Only mark Ultra Necrozma as Battle Only
- return false;
- case (int)Minior: return form < 7; // Minior Shields-Down
- case (int)Ogerpon: return form >= 4; // Embody Aspect
-
- default:
- return form != 0;
- }
- }
+ (ushort)Slowbro => form == 1, // Mega
+ (ushort)Darmanitan => (form & 1) == 1, // Zen
+ (ushort)Zygarde => form == 4, // Zygarde Complete
+ (ushort)Minior => form < 7, // Minior Shields-Down
+ (ushort)Mimikyu => (form & 1) == 1, // Busted
+ (ushort)Necrozma => form == 3, // Ultra Necrozma
+ (ushort)Ogerpon => form >= 4, // Embody Aspect
+ _ => form != 0,
+ };
///
/// Reverts the Battle Form to the form it would have outside of Battle.
@@ -48,10 +40,11 @@ public static bool IsBattleOnlyForm(ushort species, byte form, int format)
/// Suggested alt form value.
public static byte GetOutOfBattleForm(ushort species, byte form, int format) => species switch
{
- (int)Darmanitan => (byte)(form & 2),
- (int)Zygarde when format > 6 => 3,
- (int)Minior => (byte)(form + 7),
- (int)Ogerpon => (byte)(form & 3),
+ (ushort)Darmanitan => (byte)(form & 2),
+ (ushort)Zygarde when format > 6 => 3,
+ (ushort)Minior => (byte)(form + 7),
+ (ushort)Mimikyu => (byte)(form & 2),
+ (ushort)Ogerpon => (byte)(form & 3),
_ => 0,
};
@@ -65,9 +58,9 @@ public static bool IsBattleOnlyForm(ushort species, byte form, int format)
/// True if it trading should be disallowed.
public static bool IsUntradable(ushort species, byte form, uint formArg, int format) => species switch
{
- (int)Koraidon or (int)Miraidon when formArg == 1 => true, // Ride-able Box Legend
- (int)Pikachu when form == 8 && format == 7 => true, // Let's Go Pikachu Starter
- (int)Eevee when form == 1 && format == 7 => true, // Let's Go Eevee Starter
+ (ushort)Koraidon or (int)Miraidon => formArg == 1, // Ride-able Box Legend
+ (ushort)Pikachu => format == 7 && form == 8, // Let's Go Pikachu Starter
+ (ushort)Eevee => format == 7 && form == 1, // Let's Go Eevee Starter
_ => IsFusedForm(species, form, format),
};
@@ -80,9 +73,9 @@ public static bool IsBattleOnlyForm(ushort species, byte form, int format)
/// True if it is a fused species-form, false if it is not fused.
public static bool IsFusedForm(ushort species, byte form, int format) => species switch
{
- (int)Kyurem when form != 0 && format >= 5 => true,
- (int)Necrozma when form != 0 && format >= 7 => true,
- (int)Calyrex when form != 0 && format >= 8 => true,
+ (ushort)Kyurem => form != 0 && format >= 5,
+ (ushort)Necrozma => form != 0 && format >= 7,
+ (ushort)Calyrex => form != 0 && format >= 8,
_ => false,
};
@@ -224,18 +217,16 @@ public static bool IsFormChangeable(ushort species, byte oldForm, byte newForm,
(int)Necrozma, // Ultra Necrozma
};
- ///
- /// Species that have a primal form that cannot exist outside of battle.
- ///
- private static readonly HashSet BattlePrimals = new() { (int)Kyogre, (int)Groudon };
-
private static readonly HashSet BattleOnly = GetBattleFormSet();
private static HashSet GetBattleFormSet()
{
var hs = new HashSet(BattleForms);
hs.UnionWith(BattleMegas);
- hs.UnionWith(BattlePrimals);
+
+ // Primals
+ hs.Add((ushort)Kyogre);
+ hs.Add((ushort)Groudon);
return hs;
}
diff --git a/PKHeX.Core/Legality/Verifiers/Ability/AbilityBreedLegality.cs b/PKHeX.Core/Legality/Verifiers/Ability/AbilityBreedLegality.cs
index 6cf487ee8..e314380a4 100644
--- a/PKHeX.Core/Legality/Verifiers/Ability/AbilityBreedLegality.cs
+++ b/PKHeX.Core/Legality/Verifiers/Ability/AbilityBreedLegality.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System;
using static PKHeX.Core.Species;
namespace PKHeX.Core;
@@ -8,147 +8,73 @@ namespace PKHeX.Core;
///
internal static class AbilityBreedLegality
{
+ private static ReadOnlySpan BanHidden5 => new byte[]
+ {
+ 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x10, 0x10, 0x20, 0x00, 0x01, 0x11, 0x02, 0x00, 0x49, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x90, 0x04,
+ 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x86, 0x80,
+ 0x49, 0x00, 0x40, 0x00, 0x48, 0x02, 0x00, 0x00, 0x10, 0x00, 0x12,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x82, 0x24, 0x80, 0x0A, 0x00,
+ 0x00, 0x0C, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00, 0xA0, 0x84, 0x80,
+ 0x40, 0x08, 0x12,
+ };
+
///
/// Species that cannot be bred with a Hidden Ability originating in
///
- internal static readonly HashSet BanHidden5 = new()
+ public static bool IsHiddenPossible5(ushort species)
{
- // Only males distributed; unable to pass to offspring
- (int)Bulbasaur, (int)Charmander, (int)Squirtle,
- (int)Tauros,
- (int)Chikorita, (int)Cyndaquil, (int)Totodile,
- (int)Tyrogue,
- (int)Treecko, (int)Torchic, (int)Mudkip,
- (int)Turtwig, (int)Chimchar, (int)Piplup,
- (int)Pansage, (int)Pansear, (int)Panpour,
- (int)Gothita,
-
- // Genderless; unable to pass to offspring
- (int)Magnemite,
- (int)Voltorb,
- (int)Staryu,
- (int)Ditto,
- (int)Porygon,
- (int)Beldum,
- (int)Bronzor,
- (int)Golett,
-
- // Not available at all
- (int)Gastly,
- (int)Koffing,
- (int)Misdreavus,
- (int)Unown,
- (int)Slakoth,
- (int)Plusle,
- (int)Plusle,
- (int)Lunatone,
- (int)Solrock,
- (int)Baltoy,
- (int)Castform,
- (int)Kecleon,
- (int)Duskull,
- (int)Chimecho,
- (int)Cherubi,
- (int)Chingling,
- (int)Rotom,
- (int)Phione,
- (int)Snivy, (int)Tepig, (int)Oshawott,
- (int)Throh, (int)Sawk,
- (int)Yamask,
- (int)Archen,
- (int)Zorua,
- (int)Ferroseed,
- (int)Klink,
- (int)Tynamo,
- (int)Litwick,
- (int)Cryogonal,
- (int)Rufflet,
- (int)Deino,
- (int)Larvesta,
- };
+ var index = species >> 3;
+ var table = BanHidden5;
+ if (index >= table.Length)
+ return true;
+ return (BanHidden5[index] & (1 << (species & 7))) == 0;
+ }
///
/// Species that cannot be bred with a Hidden Ability originating in
///
- internal static readonly HashSet BanHidden6 = new()
+ public static bool IsHiddenPossible6(ushort species, byte form) => species switch
{
- // Not available at Friend Safari or Horde Encounter
- (int)Flabébé + (2 << 11), // Orange
- (int)Flabébé + (4 << 11), // White
-
- // Super Size can be obtained as a Pumpkaboo from event distributions
- (int)Pumpkaboo + (1 << 11), // Small
- (int)Pumpkaboo + (2 << 11), // Large
-
// Same abilities (1/2/H), not available as H
- (int)Honedge,
- (int)Carnivine,
- (int)Cryogonal,
- (int)Archen,
- (int)Rotom,
- (int)Rotom + (1 << 11),
- (int)Rotom + (2 << 11),
- (int)Rotom + (3 << 11),
- (int)Rotom + (4 << 11),
- (int)Rotom + (5 << 11),
+ (int)Castform => false,
+ (int)Carnivine => false,
+ (int)Rotom => false,
+ (int)Phione => false,
+ (int)Archen => false,
+ (int)Cryogonal => false,
- (int)Castform,
- (int)Furfrou,
- (int)Furfrou + (1 << 11),
- (int)Furfrou + (2 << 11),
- (int)Furfrou + (3 << 11),
- (int)Furfrou + (4 << 11),
- (int)Furfrou + (5 << 11),
- (int)Furfrou + (6 << 11),
- (int)Furfrou + (7 << 11),
- (int)Furfrou + (8 << 11),
- (int)Furfrou + (9 << 11),
+ (int)Flabébé => form is not (2 or 4), // Orange or White - not available in Friend Safari or Horde
+ (int)Honedge => false,
+ (int)Furfrou => false,
+ (int)Pumpkaboo => form is not (1 or 2), // Previous-Gen: Size & Ability inherit from mother
+
+ _ => true,
};
///
/// Species that cannot be bred with a Hidden Ability originating in
///
- internal static readonly HashSet BanHidden7 = new()
+ public static bool IsHiddenPossible7(ushort species, byte form) => species switch
{
- // SOS slots have 0 call rate
- (int)Wimpod,
- (int)Golisopod,
- (int)Komala,
-
- // No Encounter
- (int)Minior + (07 << 11),
- (int)Minior + (08 << 11),
- (int)Minior + (09 << 11),
- (int)Minior + (10 << 11),
- (int)Minior + (11 << 11),
- (int)Minior + (12 << 11),
- (int)Minior + (13 << 11),
-
- // Previous-Gen
- (int)Pumpkaboo + (1 << 11), // Small
- (int)Pumpkaboo + (2 << 11), // Large
-
// Same abilities (1/2/H), not available as H
- (int)Honedge,
- (int)Doublade,
- (int)Aegislash,
- (int)Carnivine,
- (int)Cryogonal,
- (int)Archen,
- (int)Archeops,
- (int)Rotom,
- (int)Rotom + (1 << 11),
- (int)Rotom + (2 << 11),
- (int)Rotom + (3 << 11),
- (int)Rotom + (4 << 11),
- (int)Rotom + (5 << 11),
+ (int)Carnivine => false,
+ (int)Rotom => false,
+ (int)Phione => false,
+ (int)Archen => false,
+ (int)Cryogonal => false,
+ (int)Honedge => false,
+ (int)Pumpkaboo => form is not (1 or 2), // Previous-Gen: Size & Ability inherit from mother
+
+ (int)Minior => false, // No SOS Encounter
+ (int)Wimpod => false, // SOS slots have 0 call rate
+ (int)Komala => false, // SOS slots have 0 call rate
+ _ => true,
};
///
/// Species that cannot be bred with a Hidden Ability originating in
///
- internal static readonly HashSet BanHidden8b = new()
- {
- (int)Phione,
- };
+ public static bool IsHiddenPossibleHOME(ushort eggSpecies) => eggSpecies is not (int)Phione; // Everything else can!
}
diff --git a/PKHeX.Core/Legality/Verifiers/Ability/AbilityVerifier.cs b/PKHeX.Core/Legality/Verifiers/Ability/AbilityVerifier.cs
index a2ae5b3fd..21fa3dcd5 100644
--- a/PKHeX.Core/Legality/Verifiers/Ability/AbilityVerifier.cs
+++ b/PKHeX.Core/Legality/Verifiers/Ability/AbilityVerifier.cs
@@ -341,7 +341,7 @@ private CheckResult VerifyAbility5(LegalityAnalysis data, IEncounterTemplate enc
// Eggs and Encounter Slots are not yet checked for Hidden Ability potential.
return enc switch
{
- EncounterEgg e when pk.AbilityNumber == 4 && AbilityBreedLegality.BanHidden5.Contains(e.Species) => GetInvalid(LAbilityHiddenUnavailable),
+ EncounterEgg e when pk.AbilityNumber == 4 && !AbilityBreedLegality.IsHiddenPossible5(e.Species) => GetInvalid(LAbilityHiddenUnavailable),
_ => CheckMatch(data.Entity, abilities, 5, pk.Format == 5 ? AbilityState.MustMatch : AbilityState.CanMismatch, enc),
};
}
@@ -352,10 +352,9 @@ private CheckResult VerifyAbility6(LegalityAnalysis data, IEncounterTemplate enc
if (pk.AbilityNumber != 4)
return VALID;
- // Eggs and Encounter Slots are not yet checked for Hidden Ability potential.
return enc switch
{
- EncounterEgg egg when AbilityBreedLegality.BanHidden6.Contains((ushort)(egg.Species | (egg.Form << 11))) => GetInvalid(LAbilityHiddenUnavailable),
+ EncounterEgg egg when !AbilityBreedLegality.IsHiddenPossible6(egg.Species, egg.Form) => GetInvalid(LAbilityHiddenUnavailable),
_ => VALID,
};
}
@@ -368,7 +367,7 @@ private CheckResult VerifyAbility7(LegalityAnalysis data, IEncounterTemplate enc
return enc switch
{
- EncounterEgg egg when AbilityBreedLegality.BanHidden7.Contains((ushort)(egg.Species | (egg.Form << 11))) => GetInvalid(LAbilityHiddenUnavailable),
+ EncounterEgg egg when !AbilityBreedLegality.IsHiddenPossible7(egg.Species, egg.Form) => GetInvalid(LAbilityHiddenUnavailable),
_ => VALID,
};
}
@@ -381,7 +380,7 @@ private CheckResult VerifyAbility8BDSP(LegalityAnalysis data, IEncounterable enc
return enc switch
{
- EncounterEgg egg when AbilityBreedLegality.BanHidden8b.Contains((ushort)(egg.Species | (egg.Form << 11))) => GetInvalid(LAbilityHiddenUnavailable),
+ EncounterEgg egg when !AbilityBreedLegality.IsHiddenPossibleHOME(egg.Species) => GetInvalid(LAbilityHiddenUnavailable),
_ => VALID,
};
}
diff --git a/PKHeX.Core/Legality/Verifiers/FormVerifier.cs b/PKHeX.Core/Legality/Verifiers/FormVerifier.cs
index b90cd9c77..438dcfdd2 100644
--- a/PKHeX.Core/Legality/Verifiers/FormVerifier.cs
+++ b/PKHeX.Core/Legality/Verifiers/FormVerifier.cs
@@ -44,19 +44,26 @@ private CheckResult VerifyForm(LegalityAnalysis data)
switch ((Species)species)
{
case Pikachu when Info.Generation == 6: // Cosplay
- bool isStatic = enc is EncounterStatic6;
- bool validCosplay = form == (isStatic ? enc.Form : 0);
- if (!validCosplay)
- return GetInvalid(isStatic ? LFormPikachuCosplayInvalid : LFormPikachuCosplay);
+ if (enc is not EncounterStatic6 s6)
+ {
+ if (form == 0)
+ break; // Regular Pikachu, OK.
+ return GetInvalid(LFormPikachuCosplay);
+ }
+ if (form != s6.Form)
+ return GetInvalid(LFormPikachuCosplayInvalid);
+ if (pk.Format != 6)
+ return GetInvalid(LTransferBad); // Can't transfer.
break;
+ // LGP/E: Can't get the other game's Starter form.
case Pikachu when form is not 0 && ParseSettings.ActiveTrainer is SAV7b {Version:GameVersion.GE}:
case Eevee when form is not 0 && ParseSettings.ActiveTrainer is SAV7b {Version:GameVersion.GP}:
return GetInvalid(LFormBattle);
case Pikachu when Info.Generation >= 7: // Cap
- bool validCap = form == (enc is EncounterInvalid or EncounterEgg ? 0 : enc.Form);
- if (!validCap)
+ var expectForm = enc is EncounterInvalid or EncounterEgg ? 0 : enc.Form;
+ if (form != expectForm)
{
bool gift = enc is MysteryGift g && g.Form != form;
var msg = gift ? LFormPikachuEventInvalid : LFormInvalidGame;
@@ -77,10 +84,8 @@ private CheckResult VerifyForm(LegalityAnalysis data)
return GetInvalid(LFormItemInvalid);
case Arceus:
- {
var arceus = FormItem.GetFormArceus(pk.HeldItem, pk.Format);
return arceus != form ? GetInvalid(LFormItemInvalid) : GetValid(LFormItem);
- }
case Keldeo when enc.Generation != 5 || pk.Format >= 8:
// can mismatch in gen5 via BW tutor and transfer up
// can mismatch in gen8+ as the form activates in battle when knowing the move; outside of battle can be either state.
@@ -91,10 +96,8 @@ private CheckResult VerifyForm(LegalityAnalysis data)
return GetInvalid(LMoveKeldeoMismatch);
break;
case Genesect:
- {
var genesect = FormItem.GetFormGenesect(pk.HeldItem);
return genesect != form ? GetInvalid(LFormItemInvalid) : GetValid(LFormItem);
- }
case Greninja:
if (form > 1) // Ash Battle Bond active
return GetInvalid(LFormBattle);
@@ -141,10 +144,8 @@ private CheckResult VerifyForm(LegalityAnalysis data)
return GetInvalid(LGenderInvalidNone);
case Silvally:
- {
var silvally = FormItem.GetFormSilvally(pk.HeldItem);
return silvally != form ? GetInvalid(LFormItemInvalid) : GetValid(LFormItem);
- }
// Form doesn't exist in SM; cannot originate from that game.
case Rockruff when enc.Generation == 7 && form == 1 && pk.SM:
@@ -153,12 +154,10 @@ private CheckResult VerifyForm(LegalityAnalysis data)
// Toxel encounters have already been checked for the nature-specific evolution criteria.
case Toxtricity when enc.Species == (int)Toxtricity:
- {
// The game enforces the Nature for Toxtricity encounters too!
if (pk.Form != ToxtricityUtil.GetAmpLowKeyResult(pk.Nature))
return GetInvalid(LFormInvalidNature);
break;
- }
// Ogerpon's form changes depending on its held mask
case Ogerpon when (form & 3) != FormItem.GetFormOgerpon(pk.HeldItem):
diff --git a/PKHeX.Core/Legality/Verifiers/IndividualValueVerifier.cs b/PKHeX.Core/Legality/Verifiers/IndividualValueVerifier.cs
index 2ef84fdf2..e7a7ee552 100644
--- a/PKHeX.Core/Legality/Verifiers/IndividualValueVerifier.cs
+++ b/PKHeX.Core/Legality/Verifiers/IndividualValueVerifier.cs
@@ -14,9 +14,8 @@ public override void Verify(LegalityAnalysis data)
{
switch (data.EncounterMatch)
{
- case EncounterSlot7GO:
- case EncounterSlot8GO:
- VerifyIVsGoTransfer(data);
+ case IPogoSlot s:
+ VerifyIVsGoTransfer(data, s);
break;
case IFlawlessIVCount s:
VerifyIVsFlawless(data, s);
@@ -83,9 +82,9 @@ private void VerifyIVsFlawless(LegalityAnalysis data, int count)
data.AddLine(GetInvalid(string.Format(LIVF_COUNT0_31, count)));
}
- private void VerifyIVsGoTransfer(LegalityAnalysis data)
+ private void VerifyIVsGoTransfer(LegalityAnalysis data, IPogoSlot g)
{
- if (data.EncounterMatch is IPogoSlot g && !g.GetIVsValid(data.Entity))
+ if (!g.GetIVsValid(data.Entity))
data.AddLine(GetInvalid(LIVNotCorrect));
}
}
diff --git a/PKHeX.Core/Legality/Verifiers/MiscVerifier.cs b/PKHeX.Core/Legality/Verifiers/MiscVerifier.cs
index eaa1d0d65..6f7213af6 100644
--- a/PKHeX.Core/Legality/Verifiers/MiscVerifier.cs
+++ b/PKHeX.Core/Legality/Verifiers/MiscVerifier.cs
@@ -799,9 +799,10 @@ private void VerifyStatNature(LegalityAnalysis data, PKM pk)
data.AddLine(GetInvalid(LStatNatureInvalid));
}
+ private static string GetMoveName(T pk, int index) where T : PKM, ITechRecord => ParseSettings.MoveStrings[pk.Permit.RecordPermitIndexes[index]];
+
private void VerifyTechRecordSWSH(LegalityAnalysis data, T pk) where T : PKM, ITechRecord
{
- string GetMoveName(int index) => ParseSettings.MoveStrings[pk.Permit.RecordPermitIndexes[index]];
var evos = data.Info.EvoChainsAllGens.Gen8;
if (evos.Length == 0)
{
@@ -810,7 +811,7 @@ private void VerifyStatNature(LegalityAnalysis data, PKM pk)
{
if (!pk.GetMoveRecordFlag(i))
continue;
- data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(i))));
+ data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(pk, i))));
}
}
else
@@ -836,7 +837,7 @@ private void VerifyStatNature(LegalityAnalysis data, PKM pk)
continue;
}
- data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(i))));
+ data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(pk, i))));
}
}
}
@@ -849,7 +850,6 @@ private static bool CanLearnTR(ushort species, byte form, int tr)
private void VerifyTechRecordSV(LegalityAnalysis data, PK9 pk)
{
- string GetMoveName(int index) => ParseSettings.MoveStrings[pk.Permit.RecordPermitIndexes[index]];
var evos = data.Info.EvoChainsAllGens.Gen9;
if (evos.Length == 0)
{
@@ -858,7 +858,7 @@ private void VerifyTechRecordSV(LegalityAnalysis data, PK9 pk)
{
if (!pk.GetMoveRecordFlag(i))
continue;
- data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(i))));
+ data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(pk, i))));
}
}
else
@@ -884,7 +884,7 @@ private void VerifyTechRecordSV(LegalityAnalysis data, PK9 pk)
break;
}
if (!preEvoHas)
- data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(i))));
+ data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(pk, i))));
}
}
}
diff --git a/PKHeX.Core/PKM/Util/Conversion/FormConverter.cs b/PKHeX.Core/PKM/Util/Conversion/FormConverter.cs
index 66c2afbe9..17272aaf2 100644
--- a/PKHeX.Core/PKM/Util/Conversion/FormConverter.cs
+++ b/PKHeX.Core/PKM/Util/Conversion/FormConverter.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using static PKHeX.Core.Species;
using static PKHeX.Core.EntityContext;
@@ -907,11 +908,26 @@ private static string[] GetFormsGalarSlowbro(IReadOnlyList types, IReadO
public static string GetGigantamaxName(IReadOnlyList forms) => forms[Gigantamax];
+ private const byte AlcremieCountDecoration = 7;
+ private const byte AlcremieCountForms = 9;
+ private const byte AlcremieCountDifferent = AlcremieCountDecoration * AlcremieCountForms;
+
+ ///
+ /// Used to enumerate the possible combinations of Alcremie forms and decorations.
+ ///
+ /// Form names
+ ///
+ /// Used for Pokédex display listings.
+ /// >
public static string[] GetAlcremieFormList(IReadOnlyList forms)
{
- const int deco = 7;
- const byte fc = 9;
- var result = new string[deco * fc]; // 63
+ var result = new string[AlcremieCountDifferent]; // 63
+ SetAlcremieFormList(forms, result);
+ return result;
+ }
+
+ private static void SetAlcremieFormList(IReadOnlyList forms, Span result)
+ {
SetDecorations(result, 0, forms[(int)Alcremie]); // Vanilla Cream
SetDecorations(result, 1, forms[RubyCream]);
SetDecorations(result, 2, forms[MatchaCream]);
@@ -922,12 +938,12 @@ public static string[] GetAlcremieFormList(IReadOnlyList forms)
SetDecorations(result, 7, forms[CaramelSwirl]);
SetDecorations(result, 8, forms[RainbowSwirl]);
- return result;
+ return;
- static void SetDecorations(string[] result, int f, string baseName)
+ static void SetDecorations(Span result, [ConstantExpected] byte f, string baseName)
{
- int start = f * deco;
- var slice = result.AsSpan(start, deco);
+ int start = f * AlcremieCountDecoration;
+ var slice = result.Slice(start, AlcremieCountDecoration);
for (int i = 0; i < slice.Length; i++)
slice[i] = $"{baseName} ({(AlcremieDecoration)i})";
}
diff --git a/PKHeX.Core/PKM/Util/EntityContext.cs b/PKHeX.Core/PKM/Util/EntityContext.cs
index 737206695..89e5842b1 100644
--- a/PKHeX.Core/PKM/Util/EntityContext.cs
+++ b/PKHeX.Core/PKM/Util/EntityContext.cs
@@ -13,6 +13,7 @@ namespace PKHeX.Core;
///
public enum EntityContext : byte
{
+ // Generation numerically so we can cast to and from int for most cases.
None = 0,
Gen1 = 1,
Gen2 = 2,
@@ -24,16 +25,28 @@ public enum EntityContext : byte
Gen8 = 8,
Gen9 = 9,
+ ///
+ /// Internal separator to pivot between adjacent contexts.
+ ///
SplitInvalid,
+ /// Let's Go, Pikachu! & Let's Go, Eevee!
Gen7b,
+ /// Legends: Arceus
Gen8a,
+ /// Brilliant Diamond & Shining Pearl
Gen8b,
+ ///
+ /// Internal separator to bounds check count.
+ ///
MaxInvalid,
}
public static class EntityContextExtensions
{
+ ///
+ /// Get the generation number of the context.
+ ///
public static int Generation(this EntityContext value) => value < SplitInvalid ? (int)value : value switch
{
Gen7b => 7,
@@ -42,8 +55,16 @@ public static class EntityContextExtensions
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null),
};
+ ///
+ /// Checks if the context is a defined value assigned to a valid context.
+ ///
+ /// True if the context is valid.
public static bool IsValid(this EntityContext value) => value is not (0 or SplitInvalid) and < MaxInvalid;
+ ///
+ /// Get a pre-defined single game version associated with the context.
+ ///
+ /// Game ID choice here is the developer's choice; if multiple game sets exist for a context, one from the most recent was chosen.
public static GameVersion GetSingleGameVersion(this EntityContext value) => value switch
{
Gen1 => GameVersion.RD,
@@ -63,6 +84,9 @@ public static class EntityContextExtensions
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null),
};
+ ///
+ /// Get the game console associated with the context.
+ ///
public static GameConsole GetConsole(this EntityContext value) => value switch
{
Gen1 or Gen2 => GameConsole.GB,
@@ -74,8 +98,15 @@ public static class EntityContextExtensions
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null),
};
+ ///
+ /// Gets all values that fall within the context.
+ ///
public static GameVersion[] GetVersionsWithin(this EntityContext value, GameVersion[] source) => value.GetVersionLump().GetVersionsWithinRange(source);
+ ///
+ /// Gets the corresponding lumped value for the context.
+ ///
+ /// Shouldn't really use this; see .
public static GameVersion GetVersionLump(this EntityContext value) => value switch
{
Gen1 => GameVersion.Gen1,
@@ -95,6 +126,9 @@ public static class EntityContextExtensions
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null),
};
+ ///
+ /// Gets the corresponding value for the .
+ ///
public static EntityContext GetContext(this GameVersion version) => version switch
{
GameVersion.GP or GameVersion.GE or GameVersion.GO => Gen7b,
diff --git a/PKHeX.Core/PKM/Util/GameConsole.cs b/PKHeX.Core/PKM/Util/GameConsole.cs
index b8732a984..d7e1c21e0 100644
--- a/PKHeX.Core/PKM/Util/GameConsole.cs
+++ b/PKHeX.Core/PKM/Util/GameConsole.cs
@@ -3,7 +3,10 @@ namespace PKHeX.Core;
///
/// Hardware console types.
///
-/// Related to ; no need to specify side-game consoles like the N64 as they're tied to the mainline console.
+///
+/// Related to ; no need to specify side-game consoles like the N64 as they're tied to the mainline console.
+/// Console revisions (like GameBoy Color) or 3DS-XL are not included, again, only care about console limitations that run the games.
+///
public enum GameConsole : byte
{
/// Invalid console type.
diff --git a/PKHeX.Core/Saves/SAV3GCMemoryCard.cs b/PKHeX.Core/Saves/SAV3GCMemoryCard.cs
index a17e43734..b3864aa36 100644
--- a/PKHeX.Core/Saves/SAV3GCMemoryCard.cs
+++ b/PKHeX.Core/Saves/SAV3GCMemoryCard.cs
@@ -321,9 +321,14 @@ private string GCISaveGameName()
int offset = (DirectoryBlock_Used * BLOCK_SIZE) + (EntrySelected * DENTRY_SIZE);
string GameCode = EncodingType.GetString(Data, offset, 4);
string Makercode = EncodingType.GetString(Data, offset + 0x04, 2);
- string FileName = EncodingType.GetString(Data, offset + 0x08, DENTRY_STRLEN);
- return $"{Makercode}-{GameCode}-{Util.TrimFromZero(FileName)}.gci";
+ Span FileName = stackalloc char[DENTRY_STRLEN];
+ EncodingType.GetString(Data.AsSpan(offset + 0x08, DENTRY_STRLEN));
+ var zero = FileName.IndexOf('\0');
+ if (zero >= 0)
+ FileName = FileName[..zero];
+
+ return $"{Makercode}-{GameCode}-{FileName}.gci";
}
public ReadOnlyMemory ReadSaveGameData()
diff --git a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/GP1.cs b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/GP1.cs
index 1c2d606eb..7d1a033a9 100644
--- a/PKHeX.Core/Saves/Substructures/Gen7/LGPE/GP1.cs
+++ b/PKHeX.Core/Saves/Substructures/Gen7/LGPE/GP1.cs
@@ -51,8 +51,18 @@ private static GP1 FromData(ReadOnlySpan span)
public static void InitializeBlank(Span data) => Blank20.CopyTo(data);
- public string Username1 => Util.TrimFromZero(Encoding.ASCII.GetString(Data.AsSpan(0x00, 0x10)));
- public string Username2 => Util.TrimFromZero(Encoding.ASCII.GetString(Data.AsSpan(0x10, 0x10)));
+ private static ReadOnlySpan GetLength(ReadOnlySpan buffer)
+ {
+ var length = buffer.IndexOf((byte)0);
+ if (length == -1)
+ return buffer;
+ return buffer[..length];
+ }
+
+ private static string GetString(ReadOnlySpan buffer) => Encoding.ASCII.GetString(GetLength(buffer));
+
+ public string Username1 => GetString(Data.AsSpan(0x00, 0x10));
+ public string Username2 => GetString(Data.AsSpan(0x10, 0x10));
public ushort Species => ReadUInt16LittleEndian(Data.AsSpan(0x28)); // s32, just read as u16
public int CP => ReadInt32LittleEndian(Data.AsSpan(0x2C));
@@ -104,9 +114,9 @@ public byte WeightScalar
public int Move1 => ReadInt32LittleEndian(Data.AsSpan(0x74)); // uses Go Indexes
public int Move2 => ReadInt32LittleEndian(Data.AsSpan(0x78)); // uses Go Indexes
- public string GeoCityName => Util.TrimFromZero(Encoding.ASCII.GetString(Data, 0x7C, 0x60)); // dunno length
+ public string GeoCityName => GetString(Data.AsSpan(0x7C, 0x60)); // dunno length
- public string Nickname => Util.TrimFromZero(Encoding.ASCII.GetString(Data, 0x12D, 0x20)); // dunno length
+ public string Nickname => GetString(Data.AsSpan(0x12D, 0x20)); // dunno length
public static readonly IReadOnlyList Genders = GameInfo.GenderSymbolASCII;
public string GenderString => (uint) Gender >= Genders.Count ? string.Empty : Genders[Gender];
diff --git a/PKHeX.Core/Saves/Substructures/Gen7/QR7.cs b/PKHeX.Core/Saves/Substructures/Gen7/QR7.cs
index 7b0a1f728..2b226012e 100644
--- a/PKHeX.Core/Saves/Substructures/Gen7/QR7.cs
+++ b/PKHeX.Core/Saves/Substructures/Gen7/QR7.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
@@ -20,20 +19,30 @@ namespace PKHeX.Core;
///
public static class QR7
{
- private static readonly HashSet GenderDifferences = new()
+ public const int SIZE = 0x1A2;
+
+ private static ReadOnlySpan GenderDifferences => new byte[]
{
- 003, 012, 019, 020, 025, 026, 041, 042, 044, 045,
- 064, 065, 084, 085, 097, 111, 112, 118, 119, 123,
- 129, 130, 154, 165, 166, 178, 185, 186, 190, 194,
- 195, 198, 202, 203, 207, 208, 212, 214, 215, 217,
- 221, 224, 229, 232, 255, 256, 257, 267, 269, 272,
- 274, 275, 307, 308, 315, 316, 317, 322, 323, 332,
- 350, 369, 396, 397, 398, 399, 400, 401, 402, 403,
- 404, 405, 407, 415, 417, 418, 419, 424, 443, 444,
- 445, 449, 450, 453, 454, 456, 457, 459, 460, 461,
- 464, 465, 473, 521, 592, 593, 668, 678,
+ 0x08, 0x10, 0x18, 0x06, 0x00, 0x36, 0x00, 0x00, 0x03, 0x00,
+ 0x30, 0x00, 0x02, 0x80, 0xC1, 0x08, 0x06, 0x00, 0x00, 0x04,
+ 0x60, 0x00, 0x04, 0x46, 0x4C, 0x8C, 0xD1, 0x22, 0x21, 0x01,
+ 0x00, 0x80, 0x03, 0x28, 0x0D, 0x00, 0x00, 0x00, 0x18, 0x38,
+ 0x0C, 0x10, 0x00, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0xF0,
+ 0xBF, 0x80, 0x0E, 0x01, 0x00, 0x38, 0x66, 0x3B, 0x03, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x40,
};
+ private static bool IsGenderDifferent(ushort species)
+ {
+ var index = species >> 3;
+ var table = GenderDifferences;
+ if (index >= table.Length)
+ return false;
+ return (table[index] & (1 << (species & 7))) != 0;
+ }
+
private static void GetRawQR(Span dest, ushort species, byte form, bool shiny, byte gender)
{
dest[..6].Fill(0xFF);
@@ -48,7 +57,7 @@ private static void GetRawQR(Span dest, ushort species, byte form, bool sh
else if (pi.Genderless)
gender = 2;
else
- biGender = !GenderDifferences.Contains(species);
+ biGender = !IsGenderDifferent(species);
dest[0x2A] = form;
dest[0x2B] = gender;
@@ -58,21 +67,21 @@ private static void GetRawQR(Span dest, ushort species, byte form, bool sh
public static byte[] GenerateQRData(PK7 pk7, int box = 0, int slot = 0, int num_copies = 1)
{
- if (box > 31)
- box = 31;
- if (slot > 29)
- slot = 29;
- if (box < 0)
- box = 0;
- if (slot < 0)
- slot = 0;
- if (num_copies < 0)
- num_copies = 1;
+ byte[] data = new byte[SIZE];
+ SetQRData(pk7, data, box, slot, num_copies);
+ return data;
+ }
+
+ public static void SetQRData(PK7 pk7, Span span, int box = 0, int slot = 0, int num_copies = 1)
+ {
+ box = Math.Clamp(box, 0, 31);
+ slot = Math.Clamp(slot, 0, 29);
+ num_copies = Math.Min(num_copies, 1);
+ if (span.Length < SIZE)
+ throw new ArgumentException($"Span must be at least {SIZE} bytes long.", nameof(span));
- byte[] data = new byte[0x1A2];
- var span = data.AsSpan();
WriteUInt32LittleEndian(span, 0x454B4F50); // POKE magic
- data[0x4] = 0xFF; // QR Type
+ span[0x4] = 0xFF; // QR Type
WriteInt32LittleEndian(span[0x08..], box);
WriteInt32LittleEndian(span[0x0C..], slot);
WriteInt32LittleEndian(span[0x10..], num_copies); // No need to check max num_copies, payload parser handles it on-console.
@@ -82,6 +91,5 @@ public static byte[] GenerateQRData(PK7 pk7, int box = 0, int slot = 0, int num_
var chk = Checksums.CRC16Invert(span[..0x1A0]);
WriteUInt16LittleEndian(span[0x1A0..], chk);
- return data;
}
}
diff --git a/PKHeX.Core/Util/Util.cs b/PKHeX.Core/Util/Util.cs
index e972d615c..5c7925168 100644
--- a/PKHeX.Core/Util/Util.cs
+++ b/PKHeX.Core/Util/Util.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace PKHeX.Core;
@@ -141,10 +140,22 @@ public static void GetBytesFromHexString(ReadOnlySpan input, Span re
private const string HexChars = "0123456789ABCDEF";
+ ///
+ /// Converts the byte array into a hex string (non-spaced, bytes in reverse order).
+ ///
public static string GetHexStringFromBytes(ReadOnlySpan data)
{
System.Diagnostics.Debug.Assert(data.Length is (4 or 8 or 12 or 16));
Span result = stackalloc char[data.Length * 2];
+ GetHexStringFromBytes(data, result);
+ return new string(result);
+ }
+
+ ///
+ public static void GetHexStringFromBytes(ReadOnlySpan data, Span result)
+ {
+ if (result.Length != data.Length * 2)
+ throw new ArgumentException("Result buffer must be twice the size of the input buffer.");
for (int i = 0; i < data.Length; i++)
{
// Write tuples from the opposite side of the result buffer.
@@ -152,7 +163,6 @@ public static string GetHexStringFromBytes(ReadOnlySpan data)
result[offset + 0] = HexChars[data[i] >> 4];
result[offset + 1] = HexChars[data[i] & 0xF];
}
- return new string(result);
}
///
@@ -164,14 +174,21 @@ public static string GetOnlyHex(ReadOnlySpan str)
if (str.IsWhiteSpace())
return string.Empty;
- int ctr = 0;
Span result = stackalloc char[str.Length];
+ int ctr = GetOnlyHex(str, ref result);
+ return new string(result[..ctr]);
+ }
+
+ ///
+ public static int GetOnlyHex(ReadOnlySpan str, ref Span result)
+ {
+ int ctr = 0;
foreach (var c in str)
{
if (char.IsAsciiHexDigit(c))
result[ctr++] = c;
}
- return new string(result[..ctr]);
+ return ctr;
}
///
@@ -184,6 +201,13 @@ public static string ToTitleCase(ReadOnlySpan span)
return string.Empty;
Span result = stackalloc char[span.Length];
+ ToTitleCase(span, result);
+ return new string(result);
+ }
+
+ ///
+ public static void ToTitleCase(ReadOnlySpan span, Span result)
+ {
// Add each word to the string builder. Continue from the first index that isn't a space.
// Add the first character as uppercase, then add each successive character as lowercase.
bool first = true;
@@ -205,7 +229,6 @@ public static string ToTitleCase(ReadOnlySpan span)
}
result[i] = c;
}
- return new string(result);
}
///
@@ -213,12 +236,15 @@ public static string ToTitleCase(ReadOnlySpan span)
///
/// String to trim.
/// Trimmed string.
- public static string TrimFromZero(string input) => TrimFromFirst(input, '\0');
+ public static ReadOnlySpan TrimFromZero(ReadOnlySpan input) => TrimFromFirst(input, '\0');
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static string TrimFromFirst(string input, char c)
+ private static ReadOnlySpan TrimFromFirst(ReadOnlySpan input, char c)
{
int index = input.IndexOf(c);
return index < 0 ? input : input[..index];
}
+
+ ///
+ public static string TrimFromZero(string input) => TrimFromZero(input.AsSpan()).ToString();
}
diff --git a/PKHeX.Drawing.Misc/QR/QRImageUtil.cs b/PKHeX.Drawing.Misc/QR/QRImageUtil.cs
index 2b0f857ae..286a1ee23 100644
--- a/PKHeX.Drawing.Misc/QR/QRImageUtil.cs
+++ b/PKHeX.Drawing.Misc/QR/QRImageUtil.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Drawing;
namespace PKHeX.Drawing.Misc;
@@ -11,7 +11,7 @@ public static Bitmap GetQRImage(Image qr, Image preview)
var foreground = new Bitmap(preview.Width + 4, preview.Height + 4);
using (Graphics gfx = Graphics.FromImage(foreground))
{
- gfx.FillRectangle(new SolidBrush(Color.White), 0, 0, foreground.Width, foreground.Height);
+ gfx.FillRectangle(Brushes.White, 0, 0, foreground.Width, foreground.Height);
int x = (foreground.Width / 2) - (preview.Width / 2);
int y = (foreground.Height / 2) - (preview.Height / 2);
gfx.DrawImage(preview, x, y);
@@ -25,17 +25,17 @@ public static Bitmap GetQRImage(Image qr, Image preview)
}
}
- public static Bitmap GetQRImageExtended(Font font, Image qr, Image pk, int width, int height, string[] lines, string extraText)
+ public static Bitmap GetQRImageExtended(Font font, Image qr, Image pk, int width, int height, ReadOnlySpan lines, string extraText)
{
var pic = GetQRImage(qr, pk);
return ExtendImage(font, qr, width, height, pic, lines, extraText);
}
- private static Bitmap ExtendImage(Font font, Image qr, int width, int height, Image pic, string[] lines, string extraText)
+ private static Bitmap ExtendImage(Font font, Image qr, int width, int height, Image pic, ReadOnlySpan lines, string extraText)
{
var newpic = new Bitmap(width, height);
using Graphics g = Graphics.FromImage(newpic);
- g.FillRectangle(new SolidBrush(Color.White), 0, 0, newpic.Width, newpic.Height);
+ g.FillRectangle(Brushes.White, 0, 0, newpic.Width, newpic.Height);
g.DrawImage(pic, 0, 0);
g.DrawString(GetLine(lines, 0), font, Brushes.Black, new PointF(18, qr.Height - 5));
@@ -46,5 +46,5 @@ private static Bitmap ExtendImage(Font font, Image qr, int width, int height, Im
return newpic;
}
- private static string GetLine(string[] lines, int line) => lines.Length <= line ? string.Empty : lines[line];
+ private static string GetLine(ReadOnlySpan lines, int line) => lines.Length <= line ? string.Empty : lines[line];
}
diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_PokedexGG.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_PokedexGG.cs
index d992c7797..29eee8e78 100644
--- a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_PokedexGG.cs
+++ b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_PokedexGG.cs
@@ -134,7 +134,7 @@ private bool FillLBForms()
return false;
}
- // sanity check forms -- SM does not have totem form dex bits
+ // sanity check forms -- GG does not have totem form dex bits
int count = SAV.Personal[bspecies].FormCount;
if (count < ds.Count)
ds.RemoveAt(count); // remove last
diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_PokedexLA.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_PokedexLA.cs
index 1472e6c83..029a9cbc6 100644
--- a/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_PokedexLA.cs
+++ b/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_PokedexLA.cs
@@ -167,8 +167,8 @@ private bool FillLBForms(int index)
if (!hasForms)
return false;
- var ds = FormConverter.GetFormList(species, GameInfo.Strings.types, GameInfo.Strings.forms, Main.GenderSymbols, SAV.Context).ToList();
- if (ds.Count == 1 && string.IsNullOrEmpty(ds[0]))
+ var ds = FormConverter.GetFormList(species, GameInfo.Strings.types, GameInfo.Strings.forms, Main.GenderSymbols, SAV.Context);
+ if (ds.Length == 1 && string.IsNullOrEmpty(ds[0]))
{
// empty
LB_Forms.Enabled = CB_DisplayForm.Enabled = false;
diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_PokedexSWSH.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_PokedexSWSH.cs
index 25a84a3b7..436058689 100644
--- a/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_PokedexSWSH.cs
+++ b/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_PokedexSWSH.cs
@@ -143,7 +143,7 @@ private static string[] GetFormList(in ushort species)
var s = GameInfo.Strings;
if (species == (int)Species.Alcremie)
return FormConverter.GetAlcremieFormList(s.forms);
- return FormConverter.GetFormList(species, s.Types, s.forms, GameInfo.GenderSymbolASCII, EntityContext.Gen8).ToArray();
+ return FormConverter.GetFormList(species, s.Types, s.forms, GameInfo.GenderSymbolASCII, EntityContext.Gen8);
}
private void SetEntry(int index)
diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_PokedexSV.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_PokedexSV.cs
index 1b28c6735..53864e2d0 100644
--- a/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_PokedexSV.cs
+++ b/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_PokedexSV.cs
@@ -179,7 +179,7 @@ private static string[] GetFormList(in ushort species)
var s = GameInfo.Strings;
if (species == (int)Species.Alcremie)
return FormConverter.GetAlcremieFormList(s.forms);
- return FormConverter.GetFormList(species, s.Types, s.forms, GameInfo.GenderSymbolASCII, EntityContext.Gen9).ToArray();
+ return FormConverter.GetFormList(species, s.Types, s.forms, GameInfo.GenderSymbolASCII, EntityContext.Gen9);
}
private void SetEntry(int index)
diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_PokedexSVKitakami.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_PokedexSVKitakami.cs
index b4bfff334..5cd5444c2 100644
--- a/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_PokedexSVKitakami.cs
+++ b/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_PokedexSVKitakami.cs
@@ -239,7 +239,7 @@ private static string[] GetFormList(in ushort species)
var s = GameInfo.Strings;
if (species == (int)Species.Alcremie)
return FormConverter.GetAlcremieFormList(s.forms);
- return FormConverter.GetFormList(species, s.Types, s.forms, GameInfo.GenderSymbolASCII, EntityContext.Gen9).ToArray();
+ return FormConverter.GetFormList(species, s.Types, s.forms, GameInfo.GenderSymbolASCII, EntityContext.Gen9);
}
private void SetEntry(int index)
diff --git a/Tests/PKHeX.Core.Tests/Legality/RaidTests.cs b/Tests/PKHeX.Core.Tests/Legality/RaidTests.cs
index 8debfe334..787013fe0 100644
--- a/Tests/PKHeX.Core.Tests/Legality/RaidTests.cs
+++ b/Tests/PKHeX.Core.Tests/Legality/RaidTests.cs
@@ -15,32 +15,26 @@ public void CheckMatch(string raw, ulong seed)
byte[] data = raw.ToByteArray();
var pk8 = new PK8(data);
- bool found = false;
- var seeds = new XoroMachineSkip(pk8.EncryptionConstant, pk8.PID);
- foreach (var s in seeds)
- {
- if (s != seed)
- continue;
- found = true;
- break;
- }
+ bool found = IsPotentialRaidSeed(pk8.EncryptionConstant, pk8.PID, seed);
found.Should().BeTrue();
var la = new LegalityAnalysis(pk8);
var enc = la.EncounterMatch;
-
- var compare = enc switch
- {
- EncounterStatic8N r => r.Verify(pk8, seed),
- EncounterStatic8ND r => r.Verify(pk8, seed),
- EncounterStatic8NC r => r.Verify(pk8, seed),
- EncounterStatic8U r => r.Verify(pk8, seed),
- _ => throw new ArgumentException(nameof(enc)),
- };
- compare.Should().BeTrue();
-
- var s64 = (ISeedCorrelation64)enc;
+ if (enc is not ISeedCorrelation64 s64)
+ throw new ArgumentException(nameof(enc));
s64.TryGetSeed(pk8, out ulong detected).Should().BeTrue();
detected.Should().Be(seed);
}
+
+ private static bool IsPotentialRaidSeed(uint ec, uint pid, ulong expect)
+ {
+ var seeds = new XoroMachineSkip(ec, pid);
+ foreach (var seed in seeds)
+ {
+ if (seed != expect)
+ continue;
+ return true;
+ }
+ return false;
+ }
}