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"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="Set"><see cref="IBattleTemplate"/> details to copy from.</param>
public static void ApplySetDetails(this PKM pk, IBattleTemplate Set)
/// <param name="set"><see cref="IBattleTemplate"/> details to copy from.</param>
public static void ApplySetDetails(this PKM pk, IBattleTemplate set)
{
pk.Species = Math.Min(pk.MaxSpeciesID, Set.Species);
pk.Form = Set.Form;
if (Set.Moves[0] != 0)
pk.SetMoves(Set.Moves, true);
pk.ApplyHeldItem(Set.HeldItem, Set.Context);
pk.CurrentLevel = Set.Level;
pk.CurrentFriendship = Set.Friendship;
pk.SetIVs(Set.IVs);
pk.Species = Math.Min(pk.MaxSpeciesID, set.Species);
pk.Form = set.Form;
ReadOnlySpan<ushort> moves = set.Moves;
if (moves[0] != 0)
pk.SetMoves(moves, true);
if (Legal.IsPPUpAvailable(pk))
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)
{
// 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.
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))
gb.SetHiddenPower(Set.HiddenPowerType);
if (ivs.ContainsAny(30, 31))
gb.SetHiddenPower(set.HiddenPowerType);
}
// 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).
if (!Set.EVs.AsSpan().ContainsAnyExcept(0))
if (!evs.ContainsAnyExcept(0))
gb.MaxEVs();
else
gb.SetEVs(Set.EVs);
gb.SetEVs(evs);
}
else
{
pk.SetEVs(Set.EVs);
pk.SetEVs(evs);
}
// 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
if (pk.Generation < 8)
pk.SetSuggestedHyperTrainingData(Set.IVs);
pk.SetSuggestedHyperTrainingData(ivs);
if (ShowdownSetIVMarkings)
pk.SetMarkings();
pk.SetNickname(Set.Nickname);
pk.SetSaneGender(Set.Gender);
if (Legal.IsPPUpAvailable(pk))
pk.SetMaximumPPUps(Set.Moves);
pk.SetNickname(set.Nickname);
pk.SetSaneGender(set.Gender);
if (pk.Format >= 3)
{
pk.SetAbility(Set.Ability);
pk.SetNature(Set.Nature);
pk.SetAbility(set.Ability);
pk.SetNature(set.Nature);
}
pk.SetIsShiny(Set.Shiny);
pk.SetIsShiny(set.Shiny);
pk.SetRandomEC();
if (pk is IAwakened a)
@ -229,17 +234,17 @@ public static class CommonEdits
g.SetSuggestedGanbaruValues(pk);
if (pk is IGigantamax c)
c.CanGigantamax = Set.CanGigantamax;
c.CanGigantamax = set.CanGigantamax;
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)
{
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);
}
if (pk is IMoveShop8Mastery s)
s.SetMoveShopFlags(Set.Moves, pk);
s.SetMoveShopFlags(set.Moves, pk);
if (ShowdownSetBehaviorNature && pk.Format >= 8)
pk.Nature = pk.StatNature;
@ -248,7 +253,7 @@ public static class CommonEdits
if (pk is ITechRecord t)
{
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))
pk.SetRelearnMoves(legal);

View file

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

View file

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

View file

@ -86,7 +86,7 @@ public sealed class LearnSource8BDSP : ILearnSource<PersonalInfo8BDSP>, IEggSour
{
var baseSpecies = pi.HatchSpecies;
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)

View file

@ -98,7 +98,7 @@ public sealed class LearnSource8SWSH : ILearnSource<PersonalInfo8SWSH>, IEggSour
{
var baseSpecies = pi.HatchSpecies;
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)

View file

@ -35,7 +35,7 @@ public sealed class LearnSource9SV : ILearnSource<PersonalInfo9SV>, IEggSource,
if (index >= EggMoves.Length)
return false;
var moves = EggMoves[index].AsSpan();
return moves.IndexOf(move) != -1;
return moves.Contains(move);
}
public ReadOnlySpan<ushort> GetEggMoves(ushort species, byte form)
@ -52,7 +52,7 @@ public sealed class LearnSource9SV : ILearnSource<PersonalInfo9SV>, IEggSource,
if (index >= Reminder.Length)
return false;
var moves = Reminder[index].AsSpan();
return moves.IndexOf(move) != -1;
return moves.Contains(move);
}
public ReadOnlySpan<ushort> GetReminderMoves(ushort species, byte form)
@ -146,7 +146,7 @@ public sealed class LearnSource9SV : ILearnSource<PersonalInfo9SV>, IEggSource,
{
var baseSpecies = pi.HatchSpecies;
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)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -295,16 +295,16 @@ public static class XDRNG
}
return ctr;
}
/// <summary>
/// Multiplication constants for jumping 2^(index) frames forward.
/// </summary>
private static ReadOnlySpan<uint> JumpMult =>
[
0x000343FD, 0xA9FC6809, 0xDDFF5051, 0xF490B9A1, 0x43BA1741, 0xD290BE81, 0x82E3BD01, 0xBF507A01,
0xF8C4F401, 0x7A19E801, 0x1673D001, 0xB5E7A001, 0x8FCF4001, 0xAF9E8001, 0x9F3D0001, 0x3E7A0001,
0x7CF40001, 0xF9E80001, 0xF3D00001, 0xE7A00001, 0xCF400001, 0x9E800001, 0x3D000001, 0x7A000001,
0xF4000001, 0xE8000001, 0xD0000001, 0xA0000001, 0x40000001, 0x80000001, 0x00000001, 0x00000001,
0x000343FD, 0xA9FC6809, 0xDDFF5051, 0xF490B9A1, 0x43BA1741, 0xD290BE81, 0x82E3BD01, 0xBF507A01,
0xF8C4F401, 0x7A19E801, 0x1673D001, 0xB5E7A001, 0x8FCF4001, 0xAF9E8001, 0x9F3D0001, 0x3E7A0001,
0x7CF40001, 0xF9E80001, 0xF3D00001, 0xE7A00001, 0xCF400001, 0x9E800001, 0x3D000001, 0x7A000001,
0xF4000001, 0xE8000001, 0xD0000001, 0xA0000001, 0x40000001, 0x80000001, 0x00000001, 0x00000001,
];
/// <summary>

View file

@ -9,7 +9,7 @@ namespace PKHeX.Core;
public sealed class EffortValueVerifier : Verifier
{
protected override CheckIdentifier Identifier => CheckIdentifier.EVs;
public override void Verify(LegalityAnalysis data)
{
var pk = data.Entity;
@ -32,7 +32,8 @@ public sealed class EffortValueVerifier : Verifier
var enc = data.EncounterMatch;
Span<int> evs = stackalloc int[6];
pk.GetEVs(evs);
if (format >= 6 && evs.ContainsAny(253, 254, 255))
if (format >= 6 && IsAnyAboveHardLimit6(evs))
data.AddLine(GetInvalid(LEffortAbove252));
else if (format < 5) // 3/4
VerifyGainedEVs34(data, enc, evs, pk);
@ -46,9 +47,9 @@ public sealed class EffortValueVerifier : Verifier
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)
return;
@ -66,4 +67,18 @@ public sealed class EffortValueVerifier : Verifier
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];
g.GetIVs(IVs);
var ivflag = IVs.Find(static iv => (byte)(iv - 0xFC) < 3);
if (ivflag == default) // Random IVs
var ivflag = IVs.IndexOfAny(0xFC, 0xFD, 0xFE);
if (ivflag == -1) // Random IVs
{
bool valid = Legal.GetIsFixedIVSequenceValidSkipRand(IVs, data.Entity);
if (!valid)
@ -55,7 +55,7 @@ public sealed class IndividualValueVerifier : Verifier
}
else
{
int IVCount = ivflag - 0xFB; // IV2/IV3
int IVCount = IVs[ivflag] - 0xFB; // IV2/IV3
VerifyIVsFlawless(data, IVCount);
}
}

View file

@ -159,7 +159,7 @@ public sealed class LegendsArceusVerifier : Verifier
if (!mustKnow && currentLearn.GetLevelLearnMove(move) != level)
continue;
if (current.IndexOf(move) == -1)
if (!current.Contains(move))
current[ctr++] = move;
if (ctr == 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];
GetIVs(finalIVs);
var ivflag = finalIVs.Find(static iv => (byte)(iv - 0xFC) < 3);
var ivflag = finalIVs.IndexOfAny(0xFC, 0xFD, 0xFE);
var rng = Util.Rand;
if (ivflag == default) // Random IVs
if (ivflag == -1) // Random IVs
{
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
{
int IVCount = ivflag - 0xFB;
int IVCount = finalIVs[ivflag] - 0xFB;
do { finalIVs[rng.Next(6)] = 31; }
while (finalIVs.Count(31) < IVCount);
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];
GetIVs(finalIVs);
var ivflag = finalIVs.Find(static iv => (byte)(iv - 0xFC) < 3);
var ivflag = finalIVs.IndexOfAny(0xFC, 0xFD, 0xFE);
var rng = Util.Rand;
if (ivflag == default) // Random IVs
if (ivflag == -1) // Random IVs
{
for (int i = 0; i < finalIVs.Length; i++)
{
@ -510,7 +510,7 @@ public sealed class WB7(byte[] Data)
}
else // 1/2/3 perfect IVs
{
int IVCount = ivflag - 0xFB;
int IVCount = finalIVs[ivflag] - 0xFB;
do { finalIVs[rng.Next(6)] = 31; }
while (finalIVs.Count(31) < IVCount);
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];
GetIVs(finalIVs);
var ivflag = finalIVs.Find(static iv => (byte)(iv - 0xFC) < 3);
var ivflag = finalIVs.IndexOfAny(0xFC, 0xFD, 0xFE);
var rng = Util.Rand;
if (ivflag == default) // Random IVs
if (ivflag == -1) // Random IVs
{
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
{
int IVCount = ivflag - 0xFB;
int IVCount = finalIVs[ivflag] - 0xFB;
do { finalIVs[rng.Next(6)] = 31; }
while (finalIVs.Count(31) < IVCount);
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];
GetIVs(finalIVs);
var ivflag = finalIVs.Find(static iv => (byte)(iv - 0xFC) < 3);
var ivflag = finalIVs.IndexOfAny(0xFC, 0xFD, 0xFE);
var rng = Util.Rand;
if (ivflag == default) // Random IVs
if (ivflag == -1) // Random IVs
{
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
{
int IVCount = ivflag - 0xFB;
int IVCount = finalIVs[ivflag] - 0xFB;
do { finalIVs[rng.Next(6)] = 31; }
while (finalIVs.Count(31) < IVCount);
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];
GetIVs(finalIVs);
var ivflag = finalIVs.Find(static iv => (byte)(iv - 0xFC) < 3);
var ivflag = finalIVs.IndexOfAny(0xFC, 0xFD, 0xFE);
var rng = Util.Rand;
if (ivflag == default) // Random IVs
if (ivflag == -1) // Random IVs
{
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
{
int IVCount = ivflag - 0xFB;
int IVCount = finalIVs[ivflag] - 0xFB;
do { finalIVs[rng.Next(6)] = 31; }
while (finalIVs.Count(31) < IVCount);
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];
GetIVs(finalIVs);
var ivflag = finalIVs.Find(static iv => (byte)(iv - 0xFC) < 3);
var ivflag = finalIVs.IndexOfAny(0xFC, 0xFD, 0xFE);
var rng = Util.Rand;
if (ivflag == default) // Random IVs
if (ivflag == -1) // Random IVs
{
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
{
int IVCount = ivflag - 0xFB;
int IVCount = finalIVs[ivflag] - 0xFB;
do { finalIVs[rng.Next(6)] = 31; }
while (finalIVs.Count(31) < IVCount);
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];
GetIVs(finalIVs);
var ivflag = finalIVs.Find(static iv => (byte)(iv - 0xFC) < 3);
var ivflag = finalIVs.IndexOfAny(0xFC, 0xFD, 0xFE);
var rng = Util.Rand;
if (ivflag == default) // Random IVs
if (ivflag == -1) // Random IVs
{
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
{
int IVCount = ivflag - 0xFB;
int IVCount = finalIVs[ivflag] - 0xFB;
do { finalIVs[rng.Next(6)] = 31; }
while (finalIVs.Count(31) < IVCount);
for (int i = 0; i < finalIVs.Length; i++)

View file

@ -12,7 +12,7 @@ public static class StringFont8b
// BD/SP: StreamingAssets/AssetAssistant/Dpr/font
// S/V: arc/appli/font/bin
// For KOR/CHS/CHT, BD/SP uses a bundled copy of the Switch system font
public static ReadOnlySpan<char> DefinedLiberationSans =>
[
'\u0020', '\u0021', '\u0022', '\u0023', '\u0024', '\u0025', '\u0026', '\u0027', '\u0028', '\u0029', '\u002A', '\u002B', '\u002C', '\u002D', '\u002E', '\u002F',

View file

@ -498,13 +498,17 @@ public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IGeneration, IVe
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)
{
if (!GetSlotFlags(slotIndex).IsOverwriteProtected())
continue;
if (ArrayUtil.WithinRange(slotIndex, min, max))
if (min <= slotIndex && slotIndex < max)
return true;
}
}
@ -514,13 +518,20 @@ public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IGeneration, IVe
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)
{
if (!GetSlotFlags(slotIndex).HasFlag(StorageSlotSource.Locked))
continue;
if (ArrayUtil.WithinRange(slotIndex, BoxStart * BoxSlotCount, (BoxEnd + 1) * BoxSlotCount))
if (min <= slotIndex && slotIndex < max)
return true;
}
}

View file

@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace PKHeX.Core;
public sealed class MyItem6XY(SAV6XY SAV, Memory<byte> raw) : MyItem(SAV, raw)
public sealed class MyItem6XY(SAV6XY SAV, Memory<byte> raw) : MyItem(SAV, raw)
{
private const int HeldItem = 0; // 0
private const int KeyItem = 0x640; // 1

View file

@ -56,7 +56,7 @@ public sealed class SevenStarRaidDetail(SevenStarRaidCapturedDetail captured, Se
defeated.Defeated = value;
else
captured.Defeated = value;
}
}
}
}

View file

@ -13,7 +13,7 @@ public sealed class Epoch0000DateTime(Memory<byte> Data): EpochDateTime(Data)
public override int Year { get => RawYear; set => RawYear = value; }
public override int Month { get => RawMonth; set => RawMonth = value; }
public override DateTime Timestamp
{
get => new(Year, Month, Day, Hour, Minute, 0);

View file

@ -40,14 +40,14 @@ public static class DexFormUtil
private static ReadOnlySpan<ushort> DexSpeciesWithForm_SM =>
[
003, 006, 009, 015, 018, 019, 020, 025, 026, 027, 028, 037, 038, 050, 051, 052,
053, 065, 074, 075, 076, 080, 088, 089, 094, 103, 105, 115, 127, 130, 142, 150,
181, 201, 208, 212, 214, 229, 248, 254, 257, 260, 282, 302, 303, 306, 308, 310,
319, 323, 334, 351, 354, 359, 362, 373, 376, 380, 381, 382, 383, 384, 386, 412,
413, 421, 422, 423, 428, 445, 448, 460, 475, 479, 487, 492, 493, 531, 550, 555,
585, 586, 641, 642, 645, 646, 647, 648, 649, 658, 666, 669, 670, 671, 676, 678,
681, 710, 711, 716, 718, 719, 720, 735, 738, 741, 745, 746, 754, 758, 773, 774,
778, 784, 801,
003, 006, 009, 015, 018, 019, 020, 025, 026, 027, 028, 037, 038, 050, 051, 052,
053, 065, 074, 075, 076, 080, 088, 089, 094, 103, 105, 115, 127, 130, 142, 150,
181, 201, 208, 212, 214, 229, 248, 254, 257, 260, 282, 302, 303, 306, 308, 310,
319, 323, 334, 351, 354, 359, 362, 373, 376, 380, 381, 382, 383, 384, 386, 412,
413, 421, 422, 423, 428, 445, 448, 460, 475, 479, 487, 492, 493, 531, 550, 555,
585, 586, 641, 642, 645, 646, 647, 648, 649, 658, 666, 669, 670, 671, 676, 678,
681, 710, 711, 716, 718, 719, 720, 735, 738, 741, 745, 746, 754, 758, 773, 774,
778, 784, 801,
];
private static ReadOnlySpan<byte> DexSpeciesCount_SM =>
@ -64,14 +64,14 @@ public static class DexFormUtil
private static ReadOnlySpan<ushort> DexSpeciesWithForm_USUM =>
[
003, 006, 009, 015, 018, 019, 020, 025, 026, 027, 028, 037, 038, 050, 051, 052,
053, 065, 074, 075, 076, 080, 088, 089, 094, 103, 105, 115, 127, 130, 142, 150,
181, 201, 208, 212, 214, 229, 248, 254, 257, 260, 282, 302, 303, 306, 308, 310,
319, 323, 334, 351, 354, 359, 362, 373, 376, 380, 381, 382, 383, 384, 386, 412,
413, 414, 421, 422, 423, 428, 445, 448, 460, 475, 479, 487, 492, 493, 531, 550,
555, 585, 586, 641, 642, 645, 646, 647, 648, 649, 658, 664, 665, 666, 669, 670,
671, 676, 678, 681, 710, 711, 716, 718, 719, 720, 735, 738, 741, 743, 744, 745,
746, 752, 754, 758, 773, 774, 777, 778, 784, 800, 801,
003, 006, 009, 015, 018, 019, 020, 025, 026, 027, 028, 037, 038, 050, 051, 052,
053, 065, 074, 075, 076, 080, 088, 089, 094, 103, 105, 115, 127, 130, 142, 150,
181, 201, 208, 212, 214, 229, 248, 254, 257, 260, 282, 302, 303, 306, 308, 310,
319, 323, 334, 351, 354, 359, 362, 373, 376, 380, 381, 382, 383, 384, 386, 412,
413, 414, 421, 422, 423, 428, 445, 448, 460, 475, 479, 487, 492, 493, 531, 550,
555, 585, 586, 641, 642, 645, 646, 647, 648, 649, 658, 664, 665, 666, 669, 670,
671, 676, 678, 681, 710, 711, 716, 718, 719, 720, 735, 738, 741, 743, 744, 745,
746, 752, 754, 758, 773, 774, 777, 778, 784, 800, 801,
];
private static ReadOnlySpan<byte> DexSpeciesCount_USUM =>
@ -88,8 +88,8 @@ public static class DexFormUtil
private static ReadOnlySpan<ushort> DexSpeciesWithForm_GG =>
[
003, 006, 009, 015, 018, 019, 020, 025, 026, 027, 028, 037, 038, 050, 051, 052,
053, 065, 074, 075, 076, 080, 088, 089, 094, 103, 105, 115, 127, 130, 142, 150,
003, 006, 009, 015, 018, 019, 020, 025, 026, 027, 028, 037, 038, 050, 051, 052,
053, 065, 074, 075, 076, 080, 088, 089, 094, 103, 105, 115, 127, 130, 142, 150,
];
private static ReadOnlySpan<byte> DexSpeciesCount_GG =>

View file

@ -8,27 +8,6 @@ namespace PKHeX.Core;
/// </summary>
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>
/// Copies a <see cref="T"/> list to the destination list, with an option to copy to a starting point.
/// </summary>

View file

@ -54,8 +54,8 @@ public static class FileUtil
return mc;
if (TryGetPKM(data, out var pk, ext))
return pk;
if (TryGetPCBoxBin(data, out IEnumerable<byte[]> pks, reference))
return pks;
if (TryGetPCBoxBin(data, out var concat, reference))
return concat;
if (TryGetBattleVideo(data, out var bv))
return bv;
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.
/// </summary>
/// <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>
/// <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))
{
pkms = [];
result = null;
if (sav is null || IsNoDataPresent(data))
return false;
}
var length = data.Length;
if (EntityDetection.IsSizePlausible(length / sav.SlotCount) || EntityDetection.IsSizePlausible(length / sav.BoxSlotCount))
// Only return if the size is one of the save file's data chunk formats.
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;
}
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;
}
@ -321,3 +332,22 @@ public static class FileUtil
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 IPokeGroup b: return OpenGroup(b);
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 SAV3GCMemoryCard gc:
@ -678,10 +678,9 @@ public partial class Main : Form
return true;
}
private bool OpenPCBoxBin(IEnumerable<byte[]> pkms)
private bool OpenPCBoxBin(ConcatenatedEntitySet pkms)
{
var data = pkms.SelectMany(z => z).ToArray();
if (!C_SAV.OpenPCBoxBin(data, out string c))
if (!C_SAV.OpenPCBoxBin(pkms.Data.Span, out string c))
{
WinFormsUtil.Alert(MsgFileLoadIncompatible, c);
return true;

View file

@ -226,7 +226,7 @@ public partial class SAV_SecretBase : Form
CB_Species.SelectedValue = (int)pk.Species;
CB_HeldItem.SelectedValue = pk.HeldItem;
CB_Form.SelectedIndex = pk.Form;
CB_Nature.SelectedValue = (int)pk.Nature;
CB_Ball.SelectedValue = (int)pk.Ball;

View file

@ -29,6 +29,6 @@ public static class SMTests
save.ChecksumInfo.Should().BeEquivalentTo(originalChecksumInfo, "because the checksum should have been modified");
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.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");
}
}