mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-10 06:34:19 +00:00
Add slot source legality checks
Useful for save files with misplaced data (you really have to be using the program weirdly to get these flagged). Stuff like Eggs deposited in Daycare, non-fuseable species in the Fused slots, etc. Allow HaX to view any slot, & Delete if Set is allowed
This commit is contained in:
parent
0499caa52e
commit
0ffb256052
27 changed files with 148 additions and 97 deletions
|
@ -92,7 +92,7 @@ public static partial class Extensions
|
||||||
};
|
};
|
||||||
|
|
||||||
if (sav is SAV5B2W2 b2w2)
|
if (sav is SAV5B2W2 b2w2)
|
||||||
list.Insert(1, new(b2w2.Forest.Fused, 0) { Type = StorageSlotType.Fused });
|
list.Insert(1, new(b2w2.Forest.Fused, 0) { Type = StorageSlotType.FusedKyurem });
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ public static partial class Extensions
|
||||||
return
|
return
|
||||||
[
|
[
|
||||||
new(sav.GTS.Upload, 0) {Type = StorageSlotType.GTS},
|
new(sav.GTS.Upload, 0) {Type = StorageSlotType.GTS},
|
||||||
new(sav.Fused[0], 0) {Type = StorageSlotType.Fused},
|
new(sav.Fused[0], 0) {Type = StorageSlotType.FusedKyurem},
|
||||||
new(sav.SUBE.GiveSlot, 0) {Type = StorageSlotType.Misc}, // Old Man
|
new(sav.SUBE.GiveSlot, 0) {Type = StorageSlotType.Misc}, // Old Man
|
||||||
|
|
||||||
new(sav.BattleBox[0], 0) {Type = StorageSlotType.BattleBox},
|
new(sav.BattleBox[0], 0) {Type = StorageSlotType.BattleBox},
|
||||||
|
@ -119,7 +119,7 @@ public static partial class Extensions
|
||||||
return
|
return
|
||||||
[
|
[
|
||||||
new(sav.GTS.Upload, 0) { Type = StorageSlotType.GTS },
|
new(sav.GTS.Upload, 0) { Type = StorageSlotType.GTS },
|
||||||
new(sav.Fused[0], 0) { Type = StorageSlotType.Fused },
|
new(sav.Fused[0], 0) { Type = StorageSlotType.FusedKyurem },
|
||||||
new(sav.SUBE.GiveSlot, 0) {Type = StorageSlotType.Misc},
|
new(sav.SUBE.GiveSlot, 0) {Type = StorageSlotType.Misc},
|
||||||
|
|
||||||
new(sav.BattleBox[0], 0) {Type = StorageSlotType.BattleBox},
|
new(sav.BattleBox[0], 0) {Type = StorageSlotType.BattleBox},
|
||||||
|
@ -136,14 +136,14 @@ public static partial class Extensions
|
||||||
var list = new List<SlotInfoMisc>
|
var list = new List<SlotInfoMisc>
|
||||||
{
|
{
|
||||||
new(sav.GTS.Upload, 0) {Type = StorageSlotType.GTS},
|
new(sav.GTS.Upload, 0) {Type = StorageSlotType.GTS},
|
||||||
new(sav.Fused[0], 0, PartyFormat: true) {Type = StorageSlotType.Fused},
|
new(sav.Fused[0], 0, PartyFormat: true) {Type = StorageSlotType.FusedKyurem},
|
||||||
};
|
};
|
||||||
if (sav is SAV7USUM uu)
|
if (sav is SAV7USUM uu)
|
||||||
{
|
{
|
||||||
list.AddRange(
|
list.AddRange(
|
||||||
[
|
[
|
||||||
new SlotInfoMisc(uu.Fused[1], 1, PartyFormat: true) {Type = StorageSlotType.Fused},
|
new SlotInfoMisc(uu.Fused[1], 1, PartyFormat: true) {Type = StorageSlotType.FusedNecrozmaS},
|
||||||
new SlotInfoMisc(uu.Fused[2], 2, PartyFormat: true) {Type = StorageSlotType.Fused},
|
new SlotInfoMisc(uu.Fused[2], 2, PartyFormat: true) {Type = StorageSlotType.FusedNecrozmaM},
|
||||||
]);
|
]);
|
||||||
list.AddRange(
|
list.AddRange(
|
||||||
[
|
[
|
||||||
|
@ -175,9 +175,9 @@ public static partial class Extensions
|
||||||
var dc = sav.Daycare;
|
var dc = sav.Daycare;
|
||||||
var list = new List<SlotInfoMisc>
|
var list = new List<SlotInfoMisc>
|
||||||
{
|
{
|
||||||
new(fused[0], 0, true) {Type = StorageSlotType.Fused},
|
new(fused[0], 0, true) {Type = StorageSlotType.FusedKyurem},
|
||||||
new(fused[1], 1, true) {Type = StorageSlotType.Fused},
|
new(fused[1], 1, true) {Type = StorageSlotType.FusedNecrozmaS},
|
||||||
new(fused[2], 2, true) {Type = StorageSlotType.Fused},
|
new(fused[2], 2, true) {Type = StorageSlotType.FusedNecrozmaM},
|
||||||
|
|
||||||
new(dc[0], 0) {Type = StorageSlotType.Daycare},
|
new(dc[0], 0) {Type = StorageSlotType.Daycare},
|
||||||
new(dc[1], 1) {Type = StorageSlotType.Daycare},
|
new(dc[1], 1) {Type = StorageSlotType.Daycare},
|
||||||
|
@ -188,7 +188,7 @@ public static partial class Extensions
|
||||||
if (sav is SAV8SWSH {SaveRevision: >= 2} s8)
|
if (sav is SAV8SWSH {SaveRevision: >= 2} s8)
|
||||||
{
|
{
|
||||||
var block = s8.Blocks.GetBlockSafe(SaveBlockAccessor8SWSH.KFusedCalyrex);
|
var block = s8.Blocks.GetBlockSafe(SaveBlockAccessor8SWSH.KFusedCalyrex);
|
||||||
var c = new SlotInfoMisc(block.Data, 3, true) {Type = StorageSlotType.Fused};
|
var c = new SlotInfoMisc(block.Data, 3, true) {Type = StorageSlotType.FusedCalyrex};
|
||||||
list.Insert(3, c);
|
list.Insert(3, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,18 +222,18 @@ public static partial class Extensions
|
||||||
var list = new List<SlotInfoMisc>
|
var list = new List<SlotInfoMisc>
|
||||||
{
|
{
|
||||||
// Ride Legend
|
// Ride Legend
|
||||||
new(sav.BoxInfo.Raw.Slice(afterBox, PokeCrypto.SIZE_9PARTY), 0, true, Mutable: true) { Type = StorageSlotType.Party },
|
new(sav.BoxInfo.Raw.Slice(afterBox, PokeCrypto.SIZE_9PARTY), 0, true, Mutable: true) { Type = StorageSlotType.Ride },
|
||||||
};
|
};
|
||||||
|
|
||||||
var block = sav.Blocks.GetBlock(SaveBlockAccessor9SV.KFusedCalyrex);
|
var block = sav.Blocks.GetBlock(SaveBlockAccessor9SV.KFusedCalyrex);
|
||||||
list.Add(new(block.Data, 0, true) { Type = StorageSlotType.Fused });
|
list.Add(new(block.Data, 0, true) { Type = StorageSlotType.FusedCalyrex });
|
||||||
|
|
||||||
if (sav.Blocks.TryGetBlock(SaveBlockAccessor9SV.KFusedKyurem, out var kyurem))
|
if (sav.Blocks.TryGetBlock(SaveBlockAccessor9SV.KFusedKyurem, out var kyurem))
|
||||||
list.Add(new(kyurem.Data, 1, true) { Type = StorageSlotType.Fused });
|
list.Add(new(kyurem.Data, 1, true) { Type = StorageSlotType.FusedKyurem });
|
||||||
if (sav.Blocks.TryGetBlock(SaveBlockAccessor9SV.KFusedNecrozmaS, out var solgaleo))
|
if (sav.Blocks.TryGetBlock(SaveBlockAccessor9SV.KFusedNecrozmaS, out var solgaleo))
|
||||||
list.Add(new(solgaleo.Data, 2, true) { Type = StorageSlotType.Fused });
|
list.Add(new(solgaleo.Data, 2, true) { Type = StorageSlotType.FusedNecrozmaS });
|
||||||
if (sav.Blocks.TryGetBlock(SaveBlockAccessor9SV.KFusedNecrozmaM, out var lunala))
|
if (sav.Blocks.TryGetBlock(SaveBlockAccessor9SV.KFusedNecrozmaM, out var lunala))
|
||||||
list.Add(new(lunala.Data, 3, true) { Type = StorageSlotType.Fused });
|
list.Add(new(lunala.Data, 3, true) { Type = StorageSlotType.FusedNecrozmaM });
|
||||||
|
|
||||||
if (sav.Blocks.TryGetBlock(SaveBlockAccessor9SV.KSurpriseTrade, out var surprise))
|
if (sav.Blocks.TryGetBlock(SaveBlockAccessor9SV.KSurpriseTrade, out var surprise))
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,7 +8,7 @@ public interface ISlotInfo
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates the type of format the slot originates. Useful for legality purposes.
|
/// Indicates the type of format the slot originates. Useful for legality purposes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
SlotOrigin Origin { get; }
|
StorageSlotType Type { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Differentiating slot number from other infos of the same type.
|
/// Differentiating slot number from other infos of the same type.
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace PKHeX.Core;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed record SlotInfoBox(int Box, int Slot) : ISlotInfo
|
public sealed record SlotInfoBox(int Box, int Slot) : ISlotInfo
|
||||||
{
|
{
|
||||||
public SlotOrigin Origin => SlotOrigin.Box;
|
public StorageSlotType Type => StorageSlotType.Box;
|
||||||
public bool CanWriteTo(SaveFile sav) => sav.HasBox && !sav.IsSlotLocked(Box, Slot);
|
public bool CanWriteTo(SaveFile sav) => sav.HasBox && !sav.IsSlotLocked(Box, Slot);
|
||||||
public WriteBlockedMessage CanWriteTo(SaveFile sav, PKM pk) => WriteBlockedMessage.None;
|
public WriteBlockedMessage CanWriteTo(SaveFile sav, PKM pk) => WriteBlockedMessage.None;
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,10 @@ namespace PKHeX.Core;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Records data for <see cref="ISlotInfo"/> that originates from an external file.
|
/// Records data for <see cref="ISlotInfo"/> that originates from an external file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="Path"></param>
|
/// <param name="Path">Path the file was loaded from.</param>
|
||||||
public sealed record SlotInfoFile(string Path) : ISlotInfo
|
public sealed record SlotInfoFile(string Path) : ISlotInfo
|
||||||
{
|
{
|
||||||
public SlotOrigin Origin => SlotOrigin.Party;
|
public StorageSlotType Type => StorageSlotType.Party;
|
||||||
public int Slot => 0;
|
public int Slot => 0;
|
||||||
|
|
||||||
public bool CanWriteTo(SaveFile sav) => false;
|
public bool CanWriteTo(SaveFile sav) => false;
|
||||||
|
|
|
@ -30,8 +30,9 @@ public static class SlotInfoLoader
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var data = File.ReadAllBytes(file);
|
var data = File.ReadAllBytes(file);
|
||||||
_ = FileUtil.TryGetPKM(data, out var pk, fi.Extension, dest);
|
if (!FileUtil.TryGetPKM(data, out var pk, fi.Extension, dest))
|
||||||
if (pk?.Species is not > 0)
|
return;
|
||||||
|
if (pk.Species is 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var info = new SlotInfoFile(file);
|
var info = new SlotInfoFile(file);
|
||||||
|
|
|
@ -7,10 +7,9 @@ namespace PKHeX.Core;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed record SlotInfoMisc(Memory<byte> Data, int Slot, bool PartyFormat = false, bool Mutable = false) : ISlotInfo
|
public sealed record SlotInfoMisc(Memory<byte> Data, int Slot, bool PartyFormat = false, bool Mutable = false) : ISlotInfo
|
||||||
{
|
{
|
||||||
public SlotOrigin Origin => PartyFormat ? SlotOrigin.Party : SlotOrigin.Box;
|
public required StorageSlotType Type { get; init; }
|
||||||
public bool CanWriteTo(SaveFile sav) => Mutable;
|
public bool CanWriteTo(SaveFile sav) => Mutable;
|
||||||
public WriteBlockedMessage CanWriteTo(SaveFile sav, PKM pk) => Mutable ? WriteBlockedMessage.None : WriteBlockedMessage.InvalidDestination;
|
public WriteBlockedMessage CanWriteTo(SaveFile sav, PKM pk) => Mutable ? WriteBlockedMessage.None : WriteBlockedMessage.InvalidDestination;
|
||||||
public StorageSlotType Type { get; init; }
|
|
||||||
|
|
||||||
public bool WriteTo(SaveFile sav, PKM pk, PKMImportSetting setting = PKMImportSetting.UseDefault)
|
public bool WriteTo(SaveFile sav, PKM pk, PKMImportSetting setting = PKMImportSetting.UseDefault)
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace PKHeX.Core;
|
||||||
public sealed record SlotInfoParty(int Slot) : ISlotInfo
|
public sealed record SlotInfoParty(int Slot) : ISlotInfo
|
||||||
{
|
{
|
||||||
public int Slot { get; private set; } = Slot;
|
public int Slot { get; private set; } = Slot;
|
||||||
public SlotOrigin Origin => SlotOrigin.Party;
|
public StorageSlotType Type => StorageSlotType.Party;
|
||||||
public bool CanWriteTo(SaveFile sav) => sav.HasParty;
|
public bool CanWriteTo(SaveFile sav) => sav.HasParty;
|
||||||
|
|
||||||
public WriteBlockedMessage CanWriteTo(SaveFile sav, PKM pk) => pk.IsEgg && sav.IsPartyAllEggs(Slot)
|
public WriteBlockedMessage CanWriteTo(SaveFile sav, PKM pk) => pk.IsEgg && sav.IsPartyAllEggs(Slot)
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
namespace PKHeX.Core;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates where the slot data originated from.
|
|
||||||
/// </summary>
|
|
||||||
public enum SlotOrigin : byte
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Slot data originated from the Party, or follows "party format" data rules.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>Some games do not permit forms to exist outside the party.</remarks>
|
|
||||||
Party = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Slot data originated from the Box, or follows "stored" data rules.
|
|
||||||
/// </summary>
|
|
||||||
Box = 1,
|
|
||||||
}
|
|
|
@ -3,8 +3,10 @@ namespace PKHeX.Core;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extra Slot enumeration to indicate a general type of slot source.
|
/// Extra Slot enumeration to indicate a general type of slot source.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum StorageSlotType
|
public enum StorageSlotType : byte
|
||||||
{
|
{
|
||||||
|
None = 0,
|
||||||
|
|
||||||
Box,
|
Box,
|
||||||
Party,
|
Party,
|
||||||
|
|
||||||
|
@ -14,10 +16,17 @@ public enum StorageSlotType
|
||||||
Daycare,
|
Daycare,
|
||||||
/// <summary> Global Trade Station (GTS) </summary>
|
/// <summary> Global Trade Station (GTS) </summary>
|
||||||
GTS,
|
GTS,
|
||||||
|
|
||||||
/// <summary> Fused Legendary Storage </summary>
|
/// <summary> Fused Legendary Storage </summary>
|
||||||
Fused,
|
FusedKyurem,
|
||||||
|
FusedNecrozmaS,
|
||||||
|
FusedNecrozmaM,
|
||||||
|
FusedCalyrex,
|
||||||
|
|
||||||
/// <summary> Miscellaneous </summary>
|
/// <summary> Miscellaneous </summary>
|
||||||
Misc,
|
Misc,
|
||||||
/// <summary> Poké Pelago (Gen7) </summary>
|
/// <summary> Poké Pelago (Gen7) </summary>
|
||||||
Resort,
|
Resort,
|
||||||
|
/// <summary> Ride Legendary Slot (S/V) </summary>
|
||||||
|
Ride,
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,5 +103,5 @@ public sealed class BulkAnalysis
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LegalityAnalysis Get(SlotCache cache) => new(cache.Entity, cache.SAV.Personal, cache.Source.Origin);
|
private static LegalityAnalysis Get(SlotCache cache) => new(cache.Entity, cache.SAV.Personal, cache.Source.Type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -463,6 +463,9 @@ public static class LegalityCheckStrings
|
||||||
public static string LStatNobleInvalid { get; set; } = "Noble Flag mismatch.";
|
public static string LStatNobleInvalid { get; set; } = "Noble Flag mismatch.";
|
||||||
public static string LStatAlphaInvalid { get; set; } = "Alpha Flag mismatch.";
|
public static string LStatAlphaInvalid { get; set; } = "Alpha Flag mismatch.";
|
||||||
|
|
||||||
|
public static string LStoredSourceEgg { get; set; } = "Egg must be in Box or Party.";
|
||||||
|
public static string LStoredSourceInvalid_0 { get; set; } = "Invalid Stored Source: {0}";
|
||||||
|
|
||||||
public static string LSuperComplete { get; set; } = "Super Training complete flag mismatch.";
|
public static string LSuperComplete { get; set; } = "Super Training complete flag mismatch.";
|
||||||
public static string LSuperDistro { get; set; } = "Distribution Super Training missions are not released.";
|
public static string LSuperDistro { get; set; } = "Distribution Super Training missions are not released.";
|
||||||
public static string LSuperEgg { get; set; } = "Can't Super Train an Egg.";
|
public static string LSuperEgg { get; set; } = "Can't Super Train an Egg.";
|
||||||
|
|
|
@ -43,7 +43,7 @@ public sealed class LegalityAnalysis
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates where the <see cref="Entity"/> originated.
|
/// Indicates where the <see cref="Entity"/> originated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly SlotOrigin SlotOrigin;
|
public readonly StorageSlotType SlotOrigin;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates if all checks ran to completion.
|
/// Indicates if all checks ran to completion.
|
||||||
|
@ -61,20 +61,24 @@ public sealed class LegalityAnalysis
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly LegalInfo Info;
|
public readonly LegalInfo Info;
|
||||||
|
|
||||||
|
private const StorageSlotType Ignore = StorageSlotType.None;
|
||||||
|
|
||||||
|
internal bool IsStoredSlot(StorageSlotType type) => SlotOrigin == type || SlotOrigin is Ignore;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks the input <see cref="PKM"/> data for legality. This is the best method for checking with context, as some games do not have all Alternate Form data available.
|
/// Checks the input <see cref="PKM"/> data for legality. This is the best method for checking with context, as some games do not have all Alternate Form data available.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pk">Input data to check</param>
|
/// <param name="pk">Input data to check</param>
|
||||||
/// <param name="table"><see cref="SaveFile"/> specific personal data</param>
|
/// <param name="table"><see cref="SaveFile"/> specific personal data</param>
|
||||||
/// <param name="source">Details about where the <see cref="Entity"/> originated from.</param>
|
/// <param name="source">Details about where the <see cref="Entity"/> originated from.</param>
|
||||||
public LegalityAnalysis(PKM pk, IPersonalTable table, SlotOrigin source = SlotOrigin.Party) : this(pk, table.GetFormEntry(pk.Species, pk.Form), source) { }
|
public LegalityAnalysis(PKM pk, IPersonalTable table, StorageSlotType source = Ignore) : this(pk, table.GetFormEntry(pk.Species, pk.Form), source) { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks the input <see cref="PKM"/> data for legality.
|
/// Checks the input <see cref="PKM"/> data for legality.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pk">Input data to check</param>
|
/// <param name="pk">Input data to check</param>
|
||||||
/// <param name="source">Details about where the <see cref="Entity"/> originated from.</param>
|
/// <param name="source">Details about where the <see cref="Entity"/> originated from.</param>
|
||||||
public LegalityAnalysis(PKM pk, SlotOrigin source = SlotOrigin.Party) : this(pk, pk.PersonalInfo, source) { }
|
public LegalityAnalysis(PKM pk, StorageSlotType source = Ignore) : this(pk, pk.PersonalInfo, source) { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks the input <see cref="PKM"/> data for legality.
|
/// Checks the input <see cref="PKM"/> data for legality.
|
||||||
|
@ -82,7 +86,7 @@ public sealed class LegalityAnalysis
|
||||||
/// <param name="pk">Input data to check</param>
|
/// <param name="pk">Input data to check</param>
|
||||||
/// <param name="pi">Personal info to parse with</param>
|
/// <param name="pi">Personal info to parse with</param>
|
||||||
/// <param name="source">Details about where the <see cref="Entity"/> originated from.</param>
|
/// <param name="source">Details about where the <see cref="Entity"/> originated from.</param>
|
||||||
public LegalityAnalysis(PKM pk, IPersonalInfo pi, SlotOrigin source = SlotOrigin.Party)
|
public LegalityAnalysis(PKM pk, IPersonalInfo pi, StorageSlotType source = Ignore)
|
||||||
{
|
{
|
||||||
Entity = pk;
|
Entity = pk;
|
||||||
PersonalInfo = pi;
|
PersonalInfo = pi;
|
||||||
|
@ -302,6 +306,7 @@ public sealed class LegalityAnalysis
|
||||||
if (format is 4 or 5 or 6) // Gen 6->7 transfer removes this property.
|
if (format is 4 or 5 or 6) // Gen 6->7 transfer removes this property.
|
||||||
Gen4GroundTile.Verify(this);
|
Gen4GroundTile.Verify(this);
|
||||||
|
|
||||||
|
SlotType.Verify(this);
|
||||||
if (format < 6)
|
if (format < 6)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -36,4 +36,5 @@ internal static class LegalityAnalyzers
|
||||||
public static readonly LegendsArceusVerifier Arceus = new();
|
public static readonly LegendsArceusVerifier Arceus = new();
|
||||||
public static readonly AwakenedValueVerifier Awakening = new();
|
public static readonly AwakenedValueVerifier Awakening = new();
|
||||||
public static readonly TrashByteVerifier Trash = new();
|
public static readonly TrashByteVerifier Trash = new();
|
||||||
|
public static readonly SlotTypeVerifier SlotType = new();
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,4 +163,9 @@ public enum CheckIdentifier : byte
|
||||||
/// The <see cref="CheckResult"/> pertains to string <see cref="TrashBytes"/>.
|
/// The <see cref="CheckResult"/> pertains to string <see cref="TrashBytes"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
TrashBytes,
|
TrashBytes,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="CheckResult"/> pertains to the <see cref="PKM"/> <see cref="StorageSlotType"/>.
|
||||||
|
/// </summary>
|
||||||
|
SlotType,
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,7 @@ public sealed class FormArgumentVerifier : Verifier
|
||||||
EncounterStatic9 { StarterBoxLegend: true } => arg switch
|
EncounterStatic9 { StarterBoxLegend: true } => arg switch
|
||||||
{
|
{
|
||||||
< 1 => GetInvalid(LFormArgumentLow),
|
< 1 => GetInvalid(LFormArgumentLow),
|
||||||
1 => data.SlotOrigin != SlotOrigin.Party ? GetInvalid(LFormParty) : GetValid(LFormArgumentValid),
|
1 => !data.IsStoredSlot(StorageSlotType.Ride) ? GetInvalid(LFormParty) : GetValid(LFormArgumentValid),
|
||||||
> 1 => GetInvalid(LFormArgumentHigh),
|
> 1 => GetInvalid(LFormArgumentHigh),
|
||||||
},
|
},
|
||||||
_ => arg switch
|
_ => arg switch
|
||||||
|
|
|
@ -172,7 +172,7 @@ public sealed class FormVerifier : Verifier
|
||||||
case Shaymin:
|
case Shaymin:
|
||||||
case Furfrou:
|
case Furfrou:
|
||||||
case Hoopa:
|
case Hoopa:
|
||||||
if (form != 0 && data.SlotOrigin is not SlotOrigin.Party && pk.Format <= 6) // has form but stored in box
|
if (form != 0 && !data.IsStoredSlot(StorageSlotType.Party) && pk.Format <= 6) // has form but stored in box
|
||||||
return GetInvalid(LFormParty);
|
return GetInvalid(LFormParty);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
47
PKHeX.Core/Legality/Verifiers/SlotTypeVerifier.cs
Normal file
47
PKHeX.Core/Legality/Verifiers/SlotTypeVerifier.cs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
using static PKHeX.Core.LegalityCheckStrings;
|
||||||
|
|
||||||
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
|
public sealed class SlotTypeVerifier : Verifier
|
||||||
|
{
|
||||||
|
protected override CheckIdentifier Identifier => CheckIdentifier.SlotType;
|
||||||
|
|
||||||
|
public override void Verify(LegalityAnalysis data)
|
||||||
|
{
|
||||||
|
var source = data.SlotOrigin;
|
||||||
|
if (source == 0)
|
||||||
|
return; // not provided, ignore
|
||||||
|
|
||||||
|
var pk = data.Entity;
|
||||||
|
if (pk.IsEgg)
|
||||||
|
{
|
||||||
|
if (!IsSourceValidEgg(pk, source))
|
||||||
|
data.AddLine(GetInvalid(LStoredSourceEgg));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!IsSourceValid(pk, source))
|
||||||
|
data.AddLine(GetInvalid(string.Format(LStoredSourceInvalid_0, source)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsSourceValid(PKM pk, StorageSlotType source) => source switch
|
||||||
|
{
|
||||||
|
StorageSlotType.FusedKyurem => pk.Species is (int)Species.Reshiram or (int)Species.Zekrom,
|
||||||
|
StorageSlotType.FusedNecrozmaS => pk.Species is (int)Species.Solgaleo,
|
||||||
|
StorageSlotType.FusedNecrozmaM => pk.Species is (int)Species.Lunala,
|
||||||
|
StorageSlotType.FusedCalyrex => pk.Species is (int)Species.Glastrier or (int)Species.Spectrier,
|
||||||
|
|
||||||
|
StorageSlotType.Ride => pk.Species is (int)Species.Koraidon or (int)Species.Miraidon && pk is PK9 {FormArgument: 1},
|
||||||
|
_ => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static bool IsSourceValidEgg(PKM pk, StorageSlotType source) => source switch
|
||||||
|
{
|
||||||
|
// Eggs should normally only be in Box or Party.
|
||||||
|
StorageSlotType.Box or StorageSlotType.Party => true,
|
||||||
|
StorageSlotType.Resort => true, // Poké Pelago can incubate eggs
|
||||||
|
StorageSlotType.Daycare when pk.Format == 2 => true, // ignore the "current egg" slot
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
|
@ -94,7 +94,7 @@ public static class SpriteUtil
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Bitmap GetSprite(PKM pk, SaveFile sav, int box, int slot, bool flagIllegal = false)
|
private static Bitmap GetSprite(PKM pk, SaveFile sav, int box, int slot, bool flagIllegal = false, StorageSlotType storage = StorageSlotType.None)
|
||||||
{
|
{
|
||||||
bool inBox = (uint)slot < MaxSlotCount;
|
bool inBox = (uint)slot < MaxSlotCount;
|
||||||
bool empty = pk.Species == 0;
|
bool empty = pk.Species == 0;
|
||||||
|
@ -110,7 +110,7 @@ public static class SpriteUtil
|
||||||
}
|
}
|
||||||
if (flagIllegal)
|
if (flagIllegal)
|
||||||
{
|
{
|
||||||
var la = new LegalityAnalysis(pk, sav.Personal, box != -1 ? SlotOrigin.Box : SlotOrigin.Party);
|
var la = new LegalityAnalysis(pk, sav.Personal, storage);
|
||||||
if (!la.Valid)
|
if (!la.Valid)
|
||||||
sprite = ImageUtil.LayerImage(sprite, Resources.warn, 0, FlagIllegalShiftY);
|
sprite = ImageUtil.LayerImage(sprite, Resources.warn, 0, FlagIllegalShiftY);
|
||||||
else if (pk.Format >= 8 && MoveInfo.IsDummiedMoveAny(pk))
|
else if (pk.Format >= 8 && MoveInfo.IsDummiedMoveAny(pk))
|
||||||
|
@ -286,8 +286,9 @@ public static class SpriteUtil
|
||||||
_ => 0,
|
_ => 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static Bitmap Sprite(this PKM pk, SaveFile sav, int box, int slot, bool flagIllegal = false)
|
public static Bitmap Sprite(this PKM pk, SaveFile sav, int box = -1, int slot = -1,
|
||||||
=> GetSprite(pk, sav, box, slot, flagIllegal);
|
bool flagIllegal = false, StorageSlotType storage = StorageSlotType.None)
|
||||||
|
=> GetSprite(pk, sav, box, slot, flagIllegal, storage);
|
||||||
|
|
||||||
public static Bitmap GetMysteryGiftPreviewPoke(MysteryGift gift)
|
public static Bitmap GetMysteryGiftPreviewPoke(MysteryGift gift)
|
||||||
{
|
{
|
||||||
|
|
|
@ -112,28 +112,25 @@ public partial class ContextMenuSAV : UserControl
|
||||||
var info = GetSenderInfo(sender);
|
var info = GetSenderInfo(sender);
|
||||||
var sav = info.View.SAV;
|
var sav = info.View.SAV;
|
||||||
var pk = info.Slot.Read(sav);
|
var pk = info.Slot.Read(sav);
|
||||||
var type = info.Slot is SlotInfoBox ? SlotOrigin.Box : SlotOrigin.Party;
|
var type = info.Slot.Type;
|
||||||
var la = new LegalityAnalysis(pk, sav.Personal, type);
|
var la = new LegalityAnalysis(pk, sav.Personal, type);
|
||||||
RequestEditorLegality?.Invoke(la);
|
RequestEditorLegality?.Invoke(la);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MenuOpening(object sender, CancelEventArgs e)
|
private void MenuOpening(object sender, CancelEventArgs e)
|
||||||
{
|
{
|
||||||
var items = ((ContextMenuStrip)sender).Items;
|
var info = GetSenderInfo(sender);
|
||||||
|
bool canView = !info.IsEmpty() || Main.HaX;
|
||||||
|
bool canSet = info.CanWriteTo();
|
||||||
|
bool canDelete = canSet && canView;
|
||||||
|
bool canLegality = ModifierKeys == Keys.Control && canView && RequestEditorLegality != null;
|
||||||
|
|
||||||
object? ctrl = ((ContextMenuStrip)sender).SourceControl;
|
ToggleItem(mnuView, canView);
|
||||||
if (ctrl is null)
|
ToggleItem(mnuSet, canSet);
|
||||||
return;
|
ToggleItem(mnuDelete, canDelete);
|
||||||
var info = GetSenderInfo(ctrl);
|
ToggleItem(mnuLegality, canLegality);
|
||||||
bool SlotFull = !info.IsEmpty();
|
|
||||||
bool Editable = info.CanWriteTo();
|
|
||||||
bool legality = ModifierKeys == Keys.Control;
|
|
||||||
ToggleItem(items, mnuSet, Editable);
|
|
||||||
ToggleItem(items, mnuDelete, Editable && SlotFull);
|
|
||||||
ToggleItem(items, mnuLegality, legality && SlotFull && RequestEditorLegality != null);
|
|
||||||
ToggleItem(items, mnuView, SlotFull || !Editable, true);
|
|
||||||
|
|
||||||
if (items.Count == 0)
|
if (!canView && !canSet && !canDelete)
|
||||||
e.Cancel = true;
|
e.Cancel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,18 +144,8 @@ public partial class ContextMenuSAV : UserControl
|
||||||
return new SlotViewInfo<PictureBox>(loc, view);
|
return new SlotViewInfo<PictureBox>(loc, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ToggleItem(ToolStripItemCollection items, ToolStripItem item, bool visible, bool first = false)
|
private static void ToggleItem(ToolStripItem item, bool visible)
|
||||||
{
|
{
|
||||||
if (visible)
|
item.Visible = visible;
|
||||||
{
|
|
||||||
if (first)
|
|
||||||
items.Insert(0, item);
|
|
||||||
else
|
|
||||||
items.Add(item);
|
|
||||||
}
|
|
||||||
else if (items.Contains(item))
|
|
||||||
{
|
|
||||||
items.Remove(item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,7 +209,7 @@ public partial class SAVEditor : UserControl, ISlotViewer<PictureBox>, ISaveFile
|
||||||
{
|
{
|
||||||
if (GetCurrentDaycare() is not { } s)
|
if (GetCurrentDaycare() is not { } s)
|
||||||
throw new Exception();
|
throw new Exception();
|
||||||
return new SlotInfoMisc(s.GetDaycareSlot(index), index);
|
return new SlotInfoMisc(s.GetDaycareSlot(index), index) {Type = StorageSlotType.Daycare};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetPKMBoxes()
|
public void SetPKMBoxes()
|
||||||
|
|
|
@ -8,8 +8,20 @@ namespace PKHeX.WinForms.Controls;
|
||||||
|
|
||||||
public partial class SlotList : UserControl, ISlotViewer<PictureBox>
|
public partial class SlotList : UserControl, ISlotViewer<PictureBox>
|
||||||
{
|
{
|
||||||
private static readonly string[] names = Enum.GetNames<StorageSlotType>();
|
private static readonly string[] names = GetEnumNames();
|
||||||
private readonly Label[] Labels = new Label[names.Length];
|
|
||||||
|
public static string[] GetEnumNames()
|
||||||
|
{
|
||||||
|
var list = Enum.GetNames<StorageSlotType>();
|
||||||
|
foreach (ref var item in list.AsSpan())
|
||||||
|
{
|
||||||
|
if (item.StartsWith("Fused"))
|
||||||
|
item = "Fused";
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly Label[] Labels = new Label[names.Length];
|
||||||
private readonly List<PictureBox> slots = [];
|
private readonly List<PictureBox> slots = [];
|
||||||
private List<SlotInfoMisc> SlotOffsets = [];
|
private List<SlotInfoMisc> SlotOffsets = [];
|
||||||
public int SlotCount { get; private set; }
|
public int SlotCount { get; private set; }
|
||||||
|
@ -99,16 +111,16 @@ public partial class SlotList : UserControl, ISlotViewer<PictureBox>
|
||||||
|
|
||||||
private void AddControls(int countTotal)
|
private void AddControls(int countTotal)
|
||||||
{
|
{
|
||||||
var type = (StorageSlotType)(-1);
|
var type = string.Empty;
|
||||||
int added = -1;
|
int added = -1;
|
||||||
for (int i = 0; i < countTotal; i++)
|
for (int i = 0; i < countTotal; i++)
|
||||||
{
|
{
|
||||||
var info = SlotOffsets[i];
|
var info = SlotOffsets[i];
|
||||||
if (type != info.Type)
|
var label = Labels[(int)info.Type];
|
||||||
|
if (label.Text != type)
|
||||||
{
|
{
|
||||||
added++;
|
added++;
|
||||||
type = info.Type;
|
type = label.Text;
|
||||||
var label = Labels[(int)type];
|
|
||||||
FLP_Slots.Controls.Add(label, 0, added++);
|
FLP_Slots.Controls.Add(label, 0, added++);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,9 +59,9 @@ public static class SlotUtil
|
||||||
|
|
||||||
var img = c switch
|
var img = c switch
|
||||||
{
|
{
|
||||||
SlotInfoBox b => p.Sprite(s, b.Box, b.Slot, flagIllegal),
|
SlotInfoBox b => p.Sprite(s, b.Box, b.Slot, flagIllegal, b.Type),
|
||||||
SlotInfoParty ps => p.Sprite(s, -1, ps.Slot, flagIllegal),
|
SlotInfoParty ps => p.Sprite(s, -1, ps.Slot, flagIllegal, ps.Type),
|
||||||
_ => p.Sprite(s, -1, -1, flagIllegal),
|
_ => p.Sprite(s, -1, -1, flagIllegal, c.Type),
|
||||||
};
|
};
|
||||||
|
|
||||||
pb.BackColor = Color.Transparent;
|
pb.BackColor = Color.Transparent;
|
||||||
|
|
|
@ -1162,7 +1162,7 @@ public partial class Main : Form
|
||||||
if (menu != null)
|
if (menu != null)
|
||||||
menu.Enabled = pk.Species != 0 || HaX; // Species
|
menu.Enabled = pk.Species != 0 || HaX; // Species
|
||||||
|
|
||||||
pb.Image = pk.Sprite(C_SAV.SAV, -1, -1, flagIllegal: false);
|
pb.Image = pk.Sprite(C_SAV.SAV);
|
||||||
if (pb.BackColor == SlotUtil.BadDataColor)
|
if (pb.BackColor == SlotUtil.BadDataColor)
|
||||||
pb.BackColor = SlotUtil.GoodDataColor;
|
pb.BackColor = SlotUtil.GoodDataColor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -652,7 +652,7 @@ public partial class SAV_Database : Form
|
||||||
int begin = start * RES_MIN;
|
int begin = start * RES_MIN;
|
||||||
int end = Math.Min(RES_MAX, Results.Count - begin);
|
int end = Math.Min(RES_MAX, Results.Count - begin);
|
||||||
for (int i = 0; i < end; i++)
|
for (int i = 0; i < end; i++)
|
||||||
PKXBOXES[i].Image = Results[i + begin].Entity.Sprite(SAV, -1, -1, true);
|
PKXBOXES[i].Image = Results[i + begin].Entity.Sprite(SAV, flagIllegal: true, storage: Results[i + begin].Source.Type);
|
||||||
for (int i = end; i < RES_MAX; i++)
|
for (int i = end; i < RES_MAX; i++)
|
||||||
PKXBOXES[i].Image = null;
|
PKXBOXES[i].Image = null;
|
||||||
|
|
||||||
|
|
|
@ -319,7 +319,7 @@ public partial class SAV_FestivalPlaza : Form
|
||||||
private void LoadPictureBox()
|
private void LoadPictureBox()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
PBs[i].Image = p[i].Sprite(SAV, -1, -1, flagIllegal: true);
|
PBs[i].Image = p[i].Sprite(SAV, flagIllegal: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly NumericUpDown[] NUD_Trainers = new NumericUpDown[3];
|
private readonly NumericUpDown[] NUD_Trainers = new NumericUpDown[3];
|
||||||
|
|
|
@ -117,7 +117,7 @@ public sealed partial class SAV_GroupViewer : Form
|
||||||
|
|
||||||
var sav = SAV;
|
var sav = SAV;
|
||||||
for (int i = 0; i < slots.Length; i++)
|
for (int i = 0; i < slots.Length; i++)
|
||||||
Box.Entries[i].Image = slots[i].Sprite(sav, -1, -1, true);
|
Box.Entries[i].Image = slots[i].Sprite(sav, flagIllegal: true);
|
||||||
|
|
||||||
if (slotSelected != -1 && (uint)slotSelected < Box.Entries.Count)
|
if (slotSelected != -1 && (uint)slotSelected < Box.Entries.Count)
|
||||||
Box.Entries[slotSelected].BackgroundImage = groupSelected != index ? null : SpriteUtil.Spriter.View;
|
Box.Entries[slotSelected].BackgroundImage = groupSelected != index ? null : SpriteUtil.Spriter.View;
|
||||||
|
|
|
@ -101,8 +101,7 @@ namespace PKHeX.WinForms
|
||||||
|
|
||||||
private static IEnumerable<Control> GetExtraControls()
|
private static IEnumerable<Control> GetExtraControls()
|
||||||
{
|
{
|
||||||
var slotGroupLabels = Enum.GetNames<StorageSlotType>();
|
foreach (var name in SlotList.GetEnumNames().Distinct())
|
||||||
foreach (var name in slotGroupLabels)
|
|
||||||
yield return new Label { Name = $"{nameof(Main)}.L_{name}", Text = name };
|
yield return new Label { Name = $"{nameof(Main)}.L_{name}", Text = name };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue