Minor clean

indexof -> contains
trailing space
some variable reuse
pcdata/boxdata less janky handling
This commit is contained in:
Kurt 2024-05-01 00:49:43 -05:00
parent 40353c7922
commit 0f4024952e
34 changed files with 190 additions and 150 deletions

View file

@ -158,61 +158,66 @@ public static class CommonEdits
/// Copies <see cref="IBattleTemplate"/> details to the <see cref="PKM"/>. /// Copies <see cref="IBattleTemplate"/> details to the <see cref="PKM"/>.
/// </summary> /// </summary>
/// <param name="pk">Pokémon to modify.</param> /// <param name="pk">Pokémon to modify.</param>
/// <param name="Set"><see cref="IBattleTemplate"/> details to copy from.</param> /// <param name="set"><see cref="IBattleTemplate"/> details to copy from.</param>
public static void ApplySetDetails(this PKM pk, IBattleTemplate Set) public static void ApplySetDetails(this PKM pk, IBattleTemplate set)
{ {
pk.Species = Math.Min(pk.MaxSpeciesID, Set.Species); pk.Species = Math.Min(pk.MaxSpeciesID, set.Species);
pk.Form = Set.Form; pk.Form = set.Form;
if (Set.Moves[0] != 0)
pk.SetMoves(Set.Moves, true); ReadOnlySpan<ushort> moves = set.Moves;
pk.ApplyHeldItem(Set.HeldItem, Set.Context); if (moves[0] != 0)
pk.CurrentLevel = Set.Level; pk.SetMoves(moves, true);
pk.CurrentFriendship = Set.Friendship; if (Legal.IsPPUpAvailable(pk))
pk.SetIVs(Set.IVs); pk.SetMaximumPPUps(moves);
pk.ApplyHeldItem(set.HeldItem, set.Context);
pk.CurrentLevel = set.Level;
pk.CurrentFriendship = set.Friendship;
ReadOnlySpan<int> ivs = set.IVs;
ReadOnlySpan<int> evs = set.EVs;
pk.SetIVs(ivs);
if (pk is GBPKM gb) if (pk is GBPKM gb)
{ {
// In Generation 1/2 Format sets, when IVs are not specified with a Hidden Power set, we might not have the hidden power type. // In Generation 1/2 Format sets, when IVs are not specified with a Hidden Power set, we might not have the hidden power type.
// Under this scenario, just force the Hidden Power type. // Under this scenario, just force the Hidden Power type.
if (Array.IndexOf(Set.Moves, (ushort)Move.HiddenPower) != -1 && gb.HPType != Set.HiddenPowerType) if (moves.Contains((ushort)Move.HiddenPower) && gb.HPType != set.HiddenPowerType)
{ {
if (Set.IVs.AsSpan().ContainsAny(30, 31)) if (ivs.ContainsAny(30, 31))
gb.SetHiddenPower(Set.HiddenPowerType); gb.SetHiddenPower(set.HiddenPowerType);
} }
// In Generation 1/2 Format sets, when EVs are not specified at all, it implies maximum EVs instead! // In Generation 1/2 Format sets, when EVs are not specified at all, it implies maximum EVs instead!
// Under this scenario, just apply maximum EVs (65535). // Under this scenario, just apply maximum EVs (65535).
if (!Set.EVs.AsSpan().ContainsAnyExcept(0)) if (!evs.ContainsAnyExcept(0))
gb.MaxEVs(); gb.MaxEVs();
else else
gb.SetEVs(Set.EVs); gb.SetEVs(evs);
} }
else else
{ {
pk.SetEVs(Set.EVs); pk.SetEVs(evs);
} }
// IVs have no side effects such as hidden power type in gen 8 // IVs have no side effects such as hidden power type in gen 8
// therefore all specified IVs are deliberate and should not be Hyper Trained for Pokémon met in gen 8 // therefore all specified IVs are deliberate and should not be Hyper Trained for Pokémon met in gen 8
if (pk.Generation < 8) if (pk.Generation < 8)
pk.SetSuggestedHyperTrainingData(Set.IVs); pk.SetSuggestedHyperTrainingData(ivs);
if (ShowdownSetIVMarkings) if (ShowdownSetIVMarkings)
pk.SetMarkings(); pk.SetMarkings();
pk.SetNickname(Set.Nickname); pk.SetNickname(set.Nickname);
pk.SetSaneGender(Set.Gender); pk.SetSaneGender(set.Gender);
if (Legal.IsPPUpAvailable(pk))
pk.SetMaximumPPUps(Set.Moves);
if (pk.Format >= 3) if (pk.Format >= 3)
{ {
pk.SetAbility(Set.Ability); pk.SetAbility(set.Ability);
pk.SetNature(Set.Nature); pk.SetNature(set.Nature);
} }
pk.SetIsShiny(Set.Shiny); pk.SetIsShiny(set.Shiny);
pk.SetRandomEC(); pk.SetRandomEC();
if (pk is IAwakened a) if (pk is IAwakened a)
@ -229,17 +234,17 @@ public static class CommonEdits
g.SetSuggestedGanbaruValues(pk); g.SetSuggestedGanbaruValues(pk);
if (pk is IGigantamax c) if (pk is IGigantamax c)
c.CanGigantamax = Set.CanGigantamax; c.CanGigantamax = set.CanGigantamax;
if (pk is IDynamaxLevel d) if (pk is IDynamaxLevel d)
d.DynamaxLevel = d.GetSuggestedDynamaxLevel(pk, requested: Set.DynamaxLevel); d.DynamaxLevel = d.GetSuggestedDynamaxLevel(pk, requested: set.DynamaxLevel);
if (pk is ITeraType tera) if (pk is ITeraType tera)
{ {
var type = Set.TeraType == MoveType.Any ? (MoveType)pk.PersonalInfo.Type1 : Set.TeraType; var type = set.TeraType == MoveType.Any ? (MoveType)pk.PersonalInfo.Type1 : set.TeraType;
tera.SetTeraType(type); tera.SetTeraType(type);
} }
if (pk is IMoveShop8Mastery s) if (pk is IMoveShop8Mastery s)
s.SetMoveShopFlags(Set.Moves, pk); s.SetMoveShopFlags(set.Moves, pk);
if (ShowdownSetBehaviorNature && pk.Format >= 8) if (ShowdownSetBehaviorNature && pk.Format >= 8)
pk.Nature = pk.StatNature; pk.Nature = pk.StatNature;
@ -248,7 +253,7 @@ public static class CommonEdits
if (pk is ITechRecord t) if (pk is ITechRecord t)
{ {
t.ClearRecordFlags(); t.ClearRecordFlags();
t.SetRecordFlags(Set.Moves, legal.Info.EvoChainsAllGens.Get(pk.Context)); t.SetRecordFlags(set.Moves, legal.Info.EvoChainsAllGens.Get(pk.Context));
} }
if (legal.Parsed && !MoveResult.AllValid(legal.Info.Relearn)) if (legal.Parsed && !MoveResult.AllValid(legal.Info.Relearn))
pk.SetRelearnMoves(legal); pk.SetRelearnMoves(legal);

View file

@ -212,16 +212,17 @@ public sealed class ShowdownSet : IBattleTemplate
private bool ParseLine(ReadOnlySpan<char> line, ref int movectr) private bool ParseLine(ReadOnlySpan<char> line, ref int movectr)
{ {
var moves = Moves.AsSpan();
if (line[0] is '-' or '') if (line[0] is '-' or '')
{ {
var moveString = ParseLineMove(line); var moveString = ParseLineMove(line);
int move = StringUtil.FindIndexIgnoreCase(Strings.movelist, moveString); int move = StringUtil.FindIndexIgnoreCase(Strings.movelist, moveString);
if (move < 0) if (move < 0)
InvalidLines.Add($"Unknown Move: {moveString}"); InvalidLines.Add($"Unknown Move: {moveString}");
else if (Array.IndexOf(Moves, (ushort)move) != -1) else if (moves.Contains((ushort)move))
InvalidLines.Add($"Duplicate Move: {moveString}"); InvalidLines.Add($"Duplicate Move: {moveString}");
else else
Moves[movectr++] = (ushort)move; moves[movectr++] = (ushort)move;
return movectr == MaxMoveCount; return movectr == MaxMoveCount;
} }
@ -507,7 +508,12 @@ public sealed class ShowdownSet : IBattleTemplate
Ability = pk.Ability; Ability = pk.Ability;
pk.GetEVs(EVs); pk.GetEVs(EVs);
pk.GetIVs(IVs); pk.GetIVs(IVs);
pk.GetMoves(Moves);
var moves = Moves.AsSpan();
pk.GetMoves(moves);
if (moves.Contains((ushort)Move.HiddenPower))
HiddenPowerType = HiddenPower.GetType(IVs, Context);
Nature = pk.StatNature; Nature = pk.StatNature;
Gender = pk.Gender < 2 ? pk.Gender : (byte)2; Gender = pk.Gender < 2 ? pk.Gender : (byte)2;
Friendship = pk.CurrentFriendship; Friendship = pk.CurrentFriendship;
@ -520,8 +526,6 @@ public sealed class ShowdownSet : IBattleTemplate
DynamaxLevel = g.DynamaxLevel; DynamaxLevel = g.DynamaxLevel;
} }
if (Array.IndexOf(Moves, (ushort)Move.HiddenPower) != -1)
HiddenPowerType = HiddenPower.GetType(IVs, Context);
if (pk is ITeraType t) if (pk is ITeraType t)
TeraType = t.TeraType; TeraType = t.TeraType;
if (pk is IHyperTrain h) if (pk is IHyperTrain h)

View file

@ -25,10 +25,7 @@ public sealed record EncounterArea8a : IEncounterArea<EncounterSlot8a>, IAreaLoc
public ushort Location => Locations[0]; public ushort Location => Locations[0];
public bool IsMatchLocation(ushort location) public bool IsMatchLocation(ushort location) => Locations.AsSpan().Contains((byte)location);
{
return Array.IndexOf(Locations, (byte)location) != -1;
}
public static EncounterArea8a[] GetAreas(BinLinkerAccessor input) public static EncounterArea8a[] GetAreas(BinLinkerAccessor input)
{ {

View file

@ -86,7 +86,7 @@ public sealed class LearnSource8BDSP : ILearnSource<PersonalInfo8BDSP>, IEggSour
{ {
var baseSpecies = pi.HatchSpecies; var baseSpecies = pi.HatchSpecies;
var baseForm = pi.HatchFormIndex; var baseForm = pi.HatchFormIndex;
return GetEggMoves(baseSpecies, baseForm).IndexOf(move) != -1; return GetEggMoves(baseSpecies, baseForm).Contains(move);
} }
public void GetAllMoves(Span<bool> result, PKM pk, EvoCriteria evo, MoveSourceType types = MoveSourceType.All) public void GetAllMoves(Span<bool> result, PKM pk, EvoCriteria evo, MoveSourceType types = MoveSourceType.All)

View file

@ -98,7 +98,7 @@ public sealed class LearnSource8SWSH : ILearnSource<PersonalInfo8SWSH>, IEggSour
{ {
var baseSpecies = pi.HatchSpecies; var baseSpecies = pi.HatchSpecies;
var baseForm = pi.HatchFormIndexEverstone; var baseForm = pi.HatchFormIndexEverstone;
return GetEggMoves(baseSpecies, baseForm).IndexOf(move) != -1; return GetEggMoves(baseSpecies, baseForm).Contains(move);
} }
private static bool GetIsTR(PersonalInfo8SWSH info, PKM pk, EvoCriteria evo, ushort move, LearnOption option) private static bool GetIsTR(PersonalInfo8SWSH info, PKM pk, EvoCriteria evo, ushort move, LearnOption option)

View file

@ -35,7 +35,7 @@ public sealed class LearnSource9SV : ILearnSource<PersonalInfo9SV>, IEggSource,
if (index >= EggMoves.Length) if (index >= EggMoves.Length)
return false; return false;
var moves = EggMoves[index].AsSpan(); var moves = EggMoves[index].AsSpan();
return moves.IndexOf(move) != -1; return moves.Contains(move);
} }
public ReadOnlySpan<ushort> GetEggMoves(ushort species, byte form) public ReadOnlySpan<ushort> GetEggMoves(ushort species, byte form)
@ -52,7 +52,7 @@ public sealed class LearnSource9SV : ILearnSource<PersonalInfo9SV>, IEggSource,
if (index >= Reminder.Length) if (index >= Reminder.Length)
return false; return false;
var moves = Reminder[index].AsSpan(); var moves = Reminder[index].AsSpan();
return moves.IndexOf(move) != -1; return moves.Contains(move);
} }
public ReadOnlySpan<ushort> GetReminderMoves(ushort species, byte form) public ReadOnlySpan<ushort> GetReminderMoves(ushort species, byte form)
@ -146,7 +146,7 @@ public sealed class LearnSource9SV : ILearnSource<PersonalInfo9SV>, IEggSource,
{ {
var baseSpecies = pi.HatchSpecies; var baseSpecies = pi.HatchSpecies;
var baseForm = pi.HatchFormIndexEverstone; var baseForm = pi.HatchFormIndexEverstone;
return GetEggMoves(baseSpecies, baseForm).IndexOf(move) != -1; return GetEggMoves(baseSpecies, baseForm).Contains(move);
} }
public void GetAllMoves(Span<bool> result, PKM pk, EvoCriteria evo, MoveSourceType types = MoveSourceType.All) public void GetAllMoves(Span<bool> result, PKM pk, EvoCriteria evo, MoveSourceType types = MoveSourceType.All)

View file

@ -187,7 +187,7 @@ public sealed class Learnset(ushort[] Moves, byte[] Levels)
for (int i = startIndex; i < endIndex; i++) for (int i = startIndex; i < endIndex; i++)
{ {
var move = Moves[i]; var move = Moves[i];
if (ignore.IndexOf(move) >= 0) if (ignore.Contains(move))
continue; continue;
AddMoveShiftLater(moves, ref ctr, move); AddMoveShiftLater(moves, ref ctr, move);
@ -207,7 +207,7 @@ public sealed class Learnset(ushort[] Moves, byte[] Levels)
break; break;
var move = Moves[i]; var move = Moves[i];
if (ignore.IndexOf(move) >= 0) if (ignore.Contains(move))
continue; continue;
AddMoveShiftLater(moves, ref ctr, move); AddMoveShiftLater(moves, ref ctr, move);

View file

@ -161,7 +161,7 @@ public static class MoveBreed2
if (move > Legal.MaxMoveID_2) // byte if (move > Legal.MaxMoveID_2) // byte
continue; continue;
if (baseEgg.IndexOf(move) != -1) if (baseEgg.Contains(move))
possible[i] |= 1 << (int)Base; possible[i] |= 1 << (int)Base;
if (inheritLevelUp && learn.GetIsLearn(move)) if (inheritLevelUp && learn.GetIsLearn(move))

View file

@ -162,7 +162,7 @@ public static class MoveBreed3
{ {
var move = moves[i]; var move = moves[i];
if (baseEgg.IndexOf(move) != -1) if (baseEgg.Contains(move))
possible[i] |= 1 << (int)Base; possible[i] |= 1 << (int)Base;
if (inheritLevelUp && learn.GetIsLearn(move)) if (inheritLevelUp && learn.GetIsLearn(move))

View file

@ -165,7 +165,7 @@ public static class MoveBreed4
{ {
var move = moves[i]; var move = moves[i];
if (baseEgg.IndexOf(move) != -1) if (baseEgg.Contains(move))
possible[i] |= 1 << (int)Base; possible[i] |= 1 << (int)Base;
if (inheritLevelUp && learn.GetIsLearn(move)) if (inheritLevelUp && learn.GetIsLearn(move))

View file

@ -157,7 +157,7 @@ public static class MoveBreed5
{ {
var move = moves[i]; var move = moves[i];
if (baseEgg.IndexOf(move) != -1) if (baseEgg.Contains(move))
possible[i] |= 1 << (int)Base; possible[i] |= 1 << (int)Base;
if (inheritLevelUp && learn.GetIsLearn(move)) if (inheritLevelUp && learn.GetIsLearn(move))

View file

@ -149,7 +149,7 @@ public static class MoveBreed6
{ {
var move = moves[i]; var move = moves[i];
if (baseEgg.IndexOf(move) != -1) if (baseEgg.Contains(move))
possible[i] |= 1 << (int)Base; possible[i] |= 1 << (int)Base;
if (inheritLevelUp && learn.GetIsLearn(move)) if (inheritLevelUp && learn.GetIsLearn(move))

View file

@ -32,7 +32,8 @@ public sealed class EffortValueVerifier : Verifier
var enc = data.EncounterMatch; var enc = data.EncounterMatch;
Span<int> evs = stackalloc int[6]; Span<int> evs = stackalloc int[6];
pk.GetEVs(evs); pk.GetEVs(evs);
if (format >= 6 && evs.ContainsAny(253, 254, 255))
if (format >= 6 && IsAnyAboveHardLimit6(evs))
data.AddLine(GetInvalid(LEffortAbove252)); data.AddLine(GetInvalid(LEffortAbove252));
else if (format < 5) // 3/4 else if (format < 5) // 3/4
VerifyGainedEVs34(data, enc, evs, pk); VerifyGainedEVs34(data, enc, evs, pk);
@ -46,9 +47,9 @@ public sealed class EffortValueVerifier : Verifier
data.AddLine(Get(LEffortAllEqual, Severity.Fishy)); data.AddLine(Get(LEffortAllEqual, Severity.Fishy));
} }
private void VerifyGainedEVs34(LegalityAnalysis data, IEncounterTemplate enc, Span<int> evs, PKM pk) private void VerifyGainedEVs34(LegalityAnalysis data, IEncounterTemplate enc, ReadOnlySpan<int> evs, PKM pk)
{ {
bool anyAbove100 = evs.Find(static ev => ev > EffortValues.MaxVitamins34) != default; bool anyAbove100 = IsAnyAboveVitaminLimit(evs);
if (!anyAbove100) if (!anyAbove100)
return; return;
@ -66,4 +67,18 @@ public sealed class EffortValueVerifier : Verifier
data.AddLine(GetInvalid(string.Format(LEffortUntrainedCap, EffortValues.MaxVitamins34))); data.AddLine(GetInvalid(string.Format(LEffortUntrainedCap, EffortValues.MaxVitamins34)));
} }
} }
// Hard cap at 252 for Gen6+
private static bool IsAnyAboveHardLimit6(ReadOnlySpan<int> evs) => evs.ContainsAny(253, 254, 255);
// Vitamins can only raise to 100 in Gen3/4
private static bool IsAnyAboveVitaminLimit(ReadOnlySpan<int> evs)
{
foreach (var iv in evs)
{
if (iv > EffortValues.MaxVitamins34)
return true;
}
return false;
}
} }

View file

@ -46,8 +46,8 @@ public sealed class IndividualValueVerifier : Verifier
Span<int> IVs = stackalloc int[6]; Span<int> IVs = stackalloc int[6];
g.GetIVs(IVs); g.GetIVs(IVs);
var ivflag = IVs.Find(static iv => (byte)(iv - 0xFC) < 3); var ivflag = IVs.IndexOfAny(0xFC, 0xFD, 0xFE);
if (ivflag == default) // Random IVs if (ivflag == -1) // Random IVs
{ {
bool valid = Legal.GetIsFixedIVSequenceValidSkipRand(IVs, data.Entity); bool valid = Legal.GetIsFixedIVSequenceValidSkipRand(IVs, data.Entity);
if (!valid) if (!valid)
@ -55,7 +55,7 @@ public sealed class IndividualValueVerifier : Verifier
} }
else else
{ {
int IVCount = ivflag - 0xFB; // IV2/IV3 int IVCount = IVs[ivflag] - 0xFB; // IV2/IV3
VerifyIVsFlawless(data, IVCount); VerifyIVsFlawless(data, IVCount);
} }
} }

View file

@ -159,7 +159,7 @@ public sealed class LegendsArceusVerifier : Verifier
if (!mustKnow && currentLearn.GetLevelLearnMove(move) != level) if (!mustKnow && currentLearn.GetLevelLearnMove(move) != level)
continue; continue;
if (current.IndexOf(move) == -1) if (!current.Contains(move))
current[ctr++] = move; current[ctr++] = move;
if (ctr == 4) if (ctr == 4)
return 4; return 4;

View file

@ -598,9 +598,9 @@ public sealed class WA8(byte[] Data) : DataMysteryGift(Data), ILangNick, INature
{ {
Span<int> finalIVs = stackalloc int[6]; Span<int> finalIVs = stackalloc int[6];
GetIVs(finalIVs); GetIVs(finalIVs);
var ivflag = finalIVs.Find(static iv => (byte)(iv - 0xFC) < 3); var ivflag = finalIVs.IndexOfAny(0xFC, 0xFD, 0xFE);
var rng = Util.Rand; var rng = Util.Rand;
if (ivflag == default) // Random IVs if (ivflag == -1) // Random IVs
{ {
for (int i = 0; i < finalIVs.Length; i++) for (int i = 0; i < finalIVs.Length; i++)
{ {
@ -610,7 +610,7 @@ public sealed class WA8(byte[] Data) : DataMysteryGift(Data), ILangNick, INature
} }
else // 1/2/3 perfect IVs else // 1/2/3 perfect IVs
{ {
int IVCount = ivflag - 0xFB; int IVCount = finalIVs[ivflag] - 0xFB;
do { finalIVs[rng.Next(6)] = 31; } do { finalIVs[rng.Next(6)] = 31; }
while (finalIVs.Count(31) < IVCount); while (finalIVs.Count(31) < IVCount);
for (int i = 0; i < finalIVs.Length; i++) for (int i = 0; i < finalIVs.Length; i++)

View file

@ -498,9 +498,9 @@ public sealed class WB7(byte[] Data)
{ {
Span<int> finalIVs = stackalloc int[6]; Span<int> finalIVs = stackalloc int[6];
GetIVs(finalIVs); GetIVs(finalIVs);
var ivflag = finalIVs.Find(static iv => (byte)(iv - 0xFC) < 3); var ivflag = finalIVs.IndexOfAny(0xFC, 0xFD, 0xFE);
var rng = Util.Rand; var rng = Util.Rand;
if (ivflag == default) // Random IVs if (ivflag == -1) // Random IVs
{ {
for (int i = 0; i < finalIVs.Length; i++) for (int i = 0; i < finalIVs.Length; i++)
{ {
@ -510,7 +510,7 @@ public sealed class WB7(byte[] Data)
} }
else // 1/2/3 perfect IVs else // 1/2/3 perfect IVs
{ {
int IVCount = ivflag - 0xFB; int IVCount = finalIVs[ivflag] - 0xFB;
do { finalIVs[rng.Next(6)] = 31; } do { finalIVs[rng.Next(6)] = 31; }
while (finalIVs.Count(31) < IVCount); while (finalIVs.Count(31) < IVCount);
for (int i = 0; i < finalIVs.Length; i++) for (int i = 0; i < finalIVs.Length; i++)

View file

@ -592,9 +592,9 @@ public sealed class WB8(byte[] Data) : DataMysteryGift(Data),
{ {
Span<int> finalIVs = stackalloc int[6]; Span<int> finalIVs = stackalloc int[6];
GetIVs(finalIVs); GetIVs(finalIVs);
var ivflag = finalIVs.Find(static iv => (byte)(iv - 0xFC) < 3); var ivflag = finalIVs.IndexOfAny(0xFC, 0xFD, 0xFE);
var rng = Util.Rand; var rng = Util.Rand;
if (ivflag == default) // Random IVs if (ivflag == -1) // Random IVs
{ {
for (int i = 0; i < finalIVs.Length; i++) for (int i = 0; i < finalIVs.Length; i++)
{ {
@ -604,7 +604,7 @@ public sealed class WB8(byte[] Data) : DataMysteryGift(Data),
} }
else // 1/2/3 perfect IVs else // 1/2/3 perfect IVs
{ {
int IVCount = ivflag - 0xFB; int IVCount = finalIVs[ivflag] - 0xFB;
do { finalIVs[rng.Next(6)] = 31; } do { finalIVs[rng.Next(6)] = 31; }
while (finalIVs.Count(31) < IVCount); while (finalIVs.Count(31) < IVCount);
for (int i = 0; i < finalIVs.Length; i++) for (int i = 0; i < finalIVs.Length; i++)

View file

@ -493,9 +493,9 @@ public sealed class WC6(byte[] Data) : DataMysteryGift(Data), IRibbonSetEvent3,
{ {
Span<int> finalIVs = stackalloc int[6]; Span<int> finalIVs = stackalloc int[6];
GetIVs(finalIVs); GetIVs(finalIVs);
var ivflag = finalIVs.Find(static iv => (byte)(iv - 0xFC) < 3); var ivflag = finalIVs.IndexOfAny(0xFC, 0xFD, 0xFE);
var rng = Util.Rand; var rng = Util.Rand;
if (ivflag == default) // Random IVs if (ivflag == -1) // Random IVs
{ {
for (int i = 0; i < finalIVs.Length; i++) for (int i = 0; i < finalIVs.Length; i++)
{ {
@ -505,7 +505,7 @@ public sealed class WC6(byte[] Data) : DataMysteryGift(Data), IRibbonSetEvent3,
} }
else // 1/2/3 perfect IVs else // 1/2/3 perfect IVs
{ {
int IVCount = ivflag - 0xFB; int IVCount = finalIVs[ivflag] - 0xFB;
do { finalIVs[rng.Next(6)] = 31; } do { finalIVs[rng.Next(6)] = 31; }
while (finalIVs.Count(31) < IVCount); while (finalIVs.Count(31) < IVCount);
for (int i = 0; i < finalIVs.Length; i++) for (int i = 0; i < finalIVs.Length; i++)

View file

@ -533,9 +533,9 @@ public sealed class WC7(byte[] Data) : DataMysteryGift(Data), IRibbonSetEvent3,
{ {
Span<int> finalIVs = stackalloc int[6]; Span<int> finalIVs = stackalloc int[6];
GetIVs(finalIVs); GetIVs(finalIVs);
var ivflag = finalIVs.Find(static iv => (byte)(iv - 0xFC) < 3); var ivflag = finalIVs.IndexOfAny(0xFC, 0xFD, 0xFE);
var rng = Util.Rand; var rng = Util.Rand;
if (ivflag == default) // Random IVs if (ivflag == -1) // Random IVs
{ {
for (int i = 0; i < finalIVs.Length; i++) for (int i = 0; i < finalIVs.Length; i++)
{ {
@ -545,7 +545,7 @@ public sealed class WC7(byte[] Data) : DataMysteryGift(Data), IRibbonSetEvent3,
} }
else // 1/2/3 perfect IVs else // 1/2/3 perfect IVs
{ {
int IVCount = ivflag - 0xFB; int IVCount = finalIVs[ivflag] - 0xFB;
do { finalIVs[rng.Next(6)] = 31; } do { finalIVs[rng.Next(6)] = 31; }
while (finalIVs.Count(31) < IVCount); while (finalIVs.Count(31) < IVCount);
for (int i = 0; i < finalIVs.Length; i++) for (int i = 0; i < finalIVs.Length; i++)

View file

@ -616,9 +616,9 @@ public sealed class WC8(byte[] Data) : DataMysteryGift(Data), ILangNick, INature
{ {
Span<int> finalIVs = stackalloc int[6]; Span<int> finalIVs = stackalloc int[6];
GetIVs(finalIVs); GetIVs(finalIVs);
var ivflag = finalIVs.Find(static iv => (byte)(iv - 0xFC) < 3); var ivflag = finalIVs.IndexOfAny(0xFC, 0xFD, 0xFE);
var rng = Util.Rand; var rng = Util.Rand;
if (ivflag == default) // Random IVs if (ivflag == -1) // Random IVs
{ {
for (int i = 0; i < finalIVs.Length; i++) for (int i = 0; i < finalIVs.Length; i++)
{ {
@ -628,7 +628,7 @@ public sealed class WC8(byte[] Data) : DataMysteryGift(Data), ILangNick, INature
} }
else // 1/2/3 perfect IVs else // 1/2/3 perfect IVs
{ {
int IVCount = ivflag - 0xFB; int IVCount = finalIVs[ivflag] - 0xFB;
do { finalIVs[rng.Next(6)] = 31; } do { finalIVs[rng.Next(6)] = 31; }
while (finalIVs.Count(31) < IVCount); while (finalIVs.Count(31) < IVCount);
for (int i = 0; i < finalIVs.Length; i++) for (int i = 0; i < finalIVs.Length; i++)

View file

@ -643,9 +643,9 @@ public sealed class WC9(byte[] Data) : DataMysteryGift(Data), ILangNick, INature
{ {
Span<int> finalIVs = stackalloc int[6]; Span<int> finalIVs = stackalloc int[6];
GetIVs(finalIVs); GetIVs(finalIVs);
var ivflag = finalIVs.Find(static iv => (byte)(iv - 0xFC) < 3); var ivflag = finalIVs.IndexOfAny(0xFC, 0xFD, 0xFE);
var rng = Util.Rand; var rng = Util.Rand;
if (ivflag == default) // Random IVs if (ivflag == -1) // Random IVs
{ {
for (int i = 0; i < finalIVs.Length; i++) for (int i = 0; i < finalIVs.Length; i++)
{ {
@ -655,7 +655,7 @@ public sealed class WC9(byte[] Data) : DataMysteryGift(Data), ILangNick, INature
} }
else // 1/2/3 perfect IVs else // 1/2/3 perfect IVs
{ {
int IVCount = ivflag - 0xFB; int IVCount = finalIVs[ivflag] - 0xFB;
do { finalIVs[rng.Next(6)] = 31; } do { finalIVs[rng.Next(6)] = 31; }
while (finalIVs.Count(31) < IVCount); while (finalIVs.Count(31) < IVCount);
for (int i = 0; i < finalIVs.Length; i++) for (int i = 0; i < finalIVs.Length; i++)

View file

@ -498,13 +498,17 @@ public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IGeneration, IVe
private bool IsRegionOverwriteProtected(int min, int max) private bool IsRegionOverwriteProtected(int min, int max)
{ {
foreach (var arrays in SlotPointers) var ptrs = SlotPointers;
if (ptrs.Length == 0)
return false;
foreach (var arrays in ptrs)
{ {
foreach (int slotIndex in arrays) foreach (int slotIndex in arrays)
{ {
if (!GetSlotFlags(slotIndex).IsOverwriteProtected()) if (!GetSlotFlags(slotIndex).IsOverwriteProtected())
continue; continue;
if (ArrayUtil.WithinRange(slotIndex, min, max)) if (min <= slotIndex && slotIndex < max)
return true; return true;
} }
} }
@ -514,13 +518,20 @@ public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IGeneration, IVe
public bool IsAnySlotLockedInBox(int BoxStart, int BoxEnd) public bool IsAnySlotLockedInBox(int BoxStart, int BoxEnd)
{ {
foreach (var arrays in SlotPointers) var ptrs = SlotPointers;
if (ptrs.Length == 0)
return false;
var min = BoxStart * BoxSlotCount;
var max = (BoxEnd + 1) * BoxSlotCount;
foreach (var arrays in ptrs)
{ {
foreach (int slotIndex in arrays) foreach (int slotIndex in arrays)
{ {
if (!GetSlotFlags(slotIndex).HasFlag(StorageSlotSource.Locked)) if (!GetSlotFlags(slotIndex).HasFlag(StorageSlotSource.Locked))
continue; continue;
if (ArrayUtil.WithinRange(slotIndex, BoxStart * BoxSlotCount, (BoxEnd + 1) * BoxSlotCount)) if (min <= slotIndex && slotIndex < max)
return true; return true;
} }
} }

View file

@ -8,27 +8,6 @@ namespace PKHeX.Core;
/// </summary> /// </summary>
public static class ArrayUtil public static class ArrayUtil
{ {
public static T Find<T>(this Span<T> data, Func<T, bool> value) where T : unmanaged
{
foreach (var x in data)
{
if (value(x))
return x;
}
return default;
}
/// <summary>
/// Checks the range (exclusive max) if the <see cref="value"/> is inside.
/// </summary>
public static bool WithinRange(int value, int min, int max) => min <= value && value < max;
public static IEnumerable<T[]> EnumerateSplit<T>(T[] bin, int size, int start = 0)
{
for (int i = start; i < bin.Length; i += size)
yield return bin.AsSpan(i, size).ToArray();
}
/// <summary> /// <summary>
/// Copies a <see cref="T"/> list to the destination list, with an option to copy to a starting point. /// Copies a <see cref="T"/> list to the destination list, with an option to copy to a starting point.
/// </summary> /// </summary>

View file

@ -54,8 +54,8 @@ public static class FileUtil
return mc; return mc;
if (TryGetPKM(data, out var pk, ext)) if (TryGetPKM(data, out var pk, ext))
return pk; return pk;
if (TryGetPCBoxBin(data, out IEnumerable<byte[]> pks, reference)) if (TryGetPCBoxBin(data, out var concat, reference))
return pks; return concat;
if (TryGetBattleVideo(data, out var bv)) if (TryGetBattleVideo(data, out var bv))
return bv; return bv;
if (TryGetMysteryGift(data, out var g, ext)) if (TryGetMysteryGift(data, out var g, ext))
@ -228,23 +228,34 @@ public static class FileUtil
/// Tries to get a <see cref="IEnumerable{T}"/> object from the input parameters. /// Tries to get a <see cref="IEnumerable{T}"/> object from the input parameters.
/// </summary> /// </summary>
/// <param name="data">Binary data</param> /// <param name="data">Binary data</param>
/// <param name="pkms">Output result</param> /// <param name="result">Output result</param>
/// <param name="sav">Reference SaveFile used for PC Binary compatibility checks.</param> /// <param name="sav">Reference SaveFile used for PC Binary compatibility checks.</param>
/// <returns>True if file object reference is valid, false if none found.</returns> /// <returns>True if file object reference is valid, false if none found.</returns>
public static bool TryGetPCBoxBin(byte[] data, out IEnumerable<byte[]> pkms, SaveFile? sav) public static bool TryGetPCBoxBin(byte[] data, [NotNullWhen(true)] out ConcatenatedEntitySet? result, SaveFile? sav)
{ {
if (sav == null || IsNoDataPresent(data)) result = null;
{ if (sav is null || IsNoDataPresent(data))
pkms = [];
return false; return false;
}
var length = data.Length; // Only return if the size is one of the save file's data chunk formats.
if (EntityDetection.IsSizePlausible(length / sav.SlotCount) || EntityDetection.IsSizePlausible(length / sav.BoxSlotCount)) var expect = sav.SIZE_BOXSLOT;
// Check if it's the entire PC data.
var countPC = sav.SlotCount;
if (expect * countPC == data.Length)
{ {
pkms = ArrayUtil.EnumerateSplit(data, length); result = new(data, countPC);
return true; return true;
} }
pkms = [];
// Check if it's a single box data.
var countBox = sav.BoxSlotCount;
if (expect * countBox == data.Length)
{
result = new(data, countBox);
return true;
}
return false; return false;
} }
@ -321,3 +332,22 @@ public static class FileUtil
return pk; return pk;
} }
} }
/// <summary>
/// Represents a set of concatenated <see cref="PKM"/> data.
/// </summary>
/// <param name="Data">Object data</param>
/// <param name="Count">Count of objects</param>
public sealed record ConcatenatedEntitySet(Memory<byte> Data, int Count)
{
public int SlotSize => Data.Length / Count;
public Span<byte> GetSlot(int index)
{
var size = SlotSize;
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)size);
var offset = index * size;
return Data.Span.Slice(offset, size);
}
}

View file

@ -618,7 +618,7 @@ public partial class Main : Form
case SaveFile s: return OpenSAV(s, path); case SaveFile s: return OpenSAV(s, path);
case IPokeGroup b: return OpenGroup(b); case IPokeGroup b: return OpenGroup(b);
case MysteryGift g: return OpenMysteryGift(g, path); case MysteryGift g: return OpenMysteryGift(g, path);
case IEnumerable<byte[]> pkms: return OpenPCBoxBin(pkms); case ConcatenatedEntitySet pkms: return OpenPCBoxBin(pkms);
case IEncounterConvertible enc: return OpenPKM(enc.ConvertToPKM(C_SAV.SAV)); case IEncounterConvertible enc: return OpenPKM(enc.ConvertToPKM(C_SAV.SAV));
case SAV3GCMemoryCard gc: case SAV3GCMemoryCard gc:
@ -678,10 +678,9 @@ public partial class Main : Form
return true; return true;
} }
private bool OpenPCBoxBin(IEnumerable<byte[]> pkms) private bool OpenPCBoxBin(ConcatenatedEntitySet pkms)
{ {
var data = pkms.SelectMany(z => z).ToArray(); if (!C_SAV.OpenPCBoxBin(pkms.Data.Span, out string c))
if (!C_SAV.OpenPCBoxBin(data, out string c))
{ {
WinFormsUtil.Alert(MsgFileLoadIncompatible, c); WinFormsUtil.Alert(MsgFileLoadIncompatible, c);
return true; return true;

View file

@ -29,6 +29,6 @@ public static class SMTests
save.ChecksumInfo.Should().BeEquivalentTo(originalChecksumInfo, "because the checksum should have been modified"); save.ChecksumInfo.Should().BeEquivalentTo(originalChecksumInfo, "because the checksum should have been modified");
save.ChecksumsValid.Should().BeTrue("because the checksum should be valid after write"); save.ChecksumsValid.Should().BeTrue("because the checksum should be valid after write");
newSave.ChecksumsValid.Should().BeTrue("because the checksums should be valid after reopening the save"); newSave.ChecksumsValid.Should().BeTrue("because the checksums should be valid after reopening the save");
newSave.ChecksumInfo.Should().BeEquivalentTo(save.ChecksumInfo, "because the checksums should be the same since write and open"); newSave.ChecksumInfo.Should().BeEquivalentTo(originalChecksumInfo, "because the checksums should be the same since write and open");
} }
} }