diff --git a/PKHeX.Core/Saves/Storage/Extensions.cs b/PKHeX.Core/Editing/Saves/Slots/Extensions.cs
similarity index 100%
rename from PKHeX.Core/Saves/Storage/Extensions.cs
rename to PKHeX.Core/Editing/Saves/Slots/Extensions.cs
diff --git a/PKHeX.Core/Saves/Storage/SlotChange.cs b/PKHeX.Core/Editing/Saves/Slots/SlotChange.cs
similarity index 90%
rename from PKHeX.Core/Saves/Storage/SlotChange.cs
rename to PKHeX.Core/Editing/Saves/Slots/SlotChange.cs
index 032e4accd..95fd31518 100644
--- a/PKHeX.Core/Saves/Storage/SlotChange.cs
+++ b/PKHeX.Core/Editing/Saves/Slots/SlotChange.cs
@@ -25,5 +25,7 @@ public SlotChange(SlotChange info, SaveFile sav)
Offset = info.Offset;
PKM = sav.GetStoredSlot(info.Offset);
}
+
+ public SlotChange GetInverseData(SaveFile sav) => new SlotChange(this, sav);
}
}
\ No newline at end of file
diff --git a/PKHeX.Core/Saves/Storage/SlotChangeInfo.cs b/PKHeX.Core/Editing/Saves/Slots/SlotChangeInfo.cs
similarity index 100%
rename from PKHeX.Core/Saves/Storage/SlotChangeInfo.cs
rename to PKHeX.Core/Editing/Saves/Slots/SlotChangeInfo.cs
diff --git a/PKHeX.Core/Editing/Saves/Slots/SlotEditor.cs b/PKHeX.Core/Editing/Saves/Slots/SlotEditor.cs
new file mode 100644
index 000000000..7b720a945
--- /dev/null
+++ b/PKHeX.Core/Editing/Saves/Slots/SlotEditor.cs
@@ -0,0 +1,224 @@
+using System.Collections.Generic;
+
+namespace PKHeX.Core
+{
+ ///
+ /// Pushes slot update notifications out to all subscribers.
+ ///
+ public sealed class SlotPublisher
+ {
+ ///
+ /// All instances that provide a view on individual content.
+ ///
+ public List Subscribers { get; } = new List();
+
+ private SlotChange Previous;
+ private SlotTouchType PreviousType = SlotTouchType.None;
+
+ ///
+ /// Notifies all with the latest slot change details.
+ ///
+ /// Last interacted slot
+ /// Last interacted slot interaction type
+ public void NotifySlotChanged(SlotChange slot, SlotTouchType type)
+ {
+ foreach (var sub in Subscribers)
+ ResetView(sub, slot, type);
+ Previous = slot;
+ PreviousType = type;
+ }
+
+ private void ResetView(ISlotViewer sub, SlotChange slot, SlotTouchType type)
+ {
+ if (Previous != null)
+ sub.NotifySlotOld(Previous);
+
+ int index = sub.ViewIndex;
+ if (index == slot.Box)
+ sub.NotifySlotChanged(slot, type);
+ }
+
+ public void ResetView(ISlotViewer sub) => ResetView(sub, Previous, PreviousType);
+ }
+
+ public interface ISlotViewer
+ {
+ ///
+ /// Current index the viewer is viewing.
+ ///
+ int ViewIndex { get; }
+
+ ///
+ /// Notification that the slot is no longer the last interacted slot.
+ ///
+ /// Last interacted slot
+ void NotifySlotOld(SlotChange previous);
+
+ ///
+ /// Notification that the has just been interacted with.
+ ///
+ /// Last interacted slot
+ /// Last interacted slot interaction type
+ void NotifySlotChanged(SlotChange slot, SlotTouchType type);
+ }
+
+ ///
+ /// Facilitates interaction with a or other data location's slot data.
+ ///
+ public sealed class SlotEditor
+ {
+ private readonly SaveFile SAV;
+ public SlotPublisher Publisher { get; } = new SlotPublisher();
+
+ private readonly Stack UndoStack = new Stack();
+ private readonly Stack RedoStack = new Stack();
+
+ public SlotEditor(SaveFile sav) => SAV = sav;
+ private void NotifySlotChanged(SlotChange slot, SlotTouchType type) => Publisher.NotifySlotChanged(slot, type);
+
+ ///
+ /// Gets data from a slot.
+ ///
+ /// Slot to retrieve from.
+ /// Operation succeeded or not via enum value.
+ public PKM Get(SlotChange slot)
+ {
+ // Reading from a slot is always allowed.
+ var pk = ReadSlot(slot);
+ NotifySlotChanged(slot, SlotTouchType.Get);
+ return pk;
+ }
+
+ ///
+ /// Sets data to a slot.
+ ///
+ /// Slot to be set to.
+ /// Data to set.
+ /// Operation succeeded or not via enum value.
+ public SlotTouchResult Set(SlotChange slot, PKM pkm)
+ {
+ if (CantWrite(slot))
+ return SlotTouchResult.FailWrite;
+
+ WriteSlot(slot, pkm);
+ NotifySlotChanged(slot, SlotTouchType.Set);
+
+ return SlotTouchResult.Success;
+ }
+
+ ///
+ /// Deletes a slot.
+ ///
+ /// Slot to be deleted.
+ /// Operation succeeded or not via enum value.
+ public SlotTouchResult Delete(SlotChange slot)
+ {
+ if (CantWrite(slot))
+ return SlotTouchResult.FailDelete;
+
+ DeleteSlot(slot);
+ NotifySlotChanged(slot, SlotTouchType.Delete);
+
+ return SlotTouchResult.Success;
+ }
+
+ ///
+ /// Swaps two slots.
+ ///
+ /// Source slot to be switched with .
+ /// Destination slot to be switched with .
+ /// Operation succeeded or not via enum value.
+ public SlotTouchResult Swap(SlotChange source, SlotChange dest)
+ {
+ if (CantWrite(source))
+ return SlotTouchResult.FailSource;
+ if (CantWrite(dest))
+ return SlotTouchResult.FailDestination;
+
+ NotifySlotChanged(source, SlotTouchType.None);
+ NotifySlotChanged(dest, SlotTouchType.Swap);
+
+ return SlotTouchResult.Success;
+ }
+
+ public bool CantWrite(SlotChange c)
+ {
+ if (c.Type > StorageSlotType.Party)
+ return true;
+ return SAV.IsSlotLocked(c.Box, c.Slot);
+ }
+
+ private PKM ReadSlot(StorageSlotOffset slot) => SAV.GetPKM(slot);
+
+ private void WriteSlot(SlotChange slot, PKM pkm)
+ {
+ if (slot.IsParty)
+ {
+ int count = SAV.PartyCount;
+ if (slot.Slot > count)
+ slot.Slot = count;
+ SAV.SetPartySlot(pkm, slot.Offset);
+ }
+ else
+ {
+ AddUndo(slot);
+ SAV.SetStoredSlot(pkm, slot.Offset);
+ }
+ }
+
+ private void DeleteSlot(SlotChange slot)
+ {
+ var pkm = slot.PKM;
+ if (slot.IsParty)
+ {
+ SAV.SetPartySlot(pkm, slot.Offset);
+ slot.Slot = SAV.PartyCount;
+ }
+ else
+ {
+ AddUndo(slot);
+ SAV.SetStoredSlot(pkm, slot.Offset);
+ }
+ }
+
+ public bool CanUndo => UndoStack.Count != 0;
+ public bool CanRedo => RedoStack.Count != 0;
+
+ public void Undo()
+ {
+ if (UndoStack.Count == 0)
+ return;
+
+ var change = UndoStack.Pop();
+ if (change.Box < 0)
+ return;
+ AddRedo(change);
+ NotifySlotChanged(change, SlotTouchType.Set);
+ }
+
+ public void Redo()
+ {
+ if (RedoStack.Count == 0)
+ return;
+
+ var change = RedoStack.Pop();
+ if (change.Box < 0)
+ return;
+ AddUndo(change);
+ NotifySlotChanged(change, SlotTouchType.Set);
+ }
+
+ private void AddRedo(SlotChange change)
+ {
+ var slotChange = change.GetInverseData(SAV);
+ RedoStack.Push(slotChange);
+ }
+
+ private void AddUndo(SlotChange change)
+ {
+ var slotChange = change.GetInverseData(SAV);
+ UndoStack.Push(slotChange);
+ RedoStack.Clear();
+ }
+ }
+}
diff --git a/PKHeX.Core/Editing/Saves/Slots/SlotTouchResult.cs b/PKHeX.Core/Editing/Saves/Slots/SlotTouchResult.cs
new file mode 100644
index 000000000..b6e1def76
--- /dev/null
+++ b/PKHeX.Core/Editing/Saves/Slots/SlotTouchResult.cs
@@ -0,0 +1,33 @@
+namespace PKHeX.Core
+{
+ ///
+ /// Result indicators for modifying a Slot within a or other data location.
+ ///
+ public enum SlotTouchResult
+ {
+ ///
+ /// Slot interaction was successful.
+ ///
+ Success,
+
+ ///
+ /// Slot interaction failed to apply the data.
+ ///
+ FailWrite,
+
+ ///
+ /// Slot interaction failed to delete the data.
+ ///
+ FailDelete,
+
+ ///
+ /// Slot interaction failed due to a bad/unmodifiable source.
+ ///
+ FailSource,
+
+ ///
+ /// Slot interaction failed due to a bad/unmodifiable destination.
+ ///
+ FailDestination,
+ }
+}
\ No newline at end of file
diff --git a/PKHeX.Core/Editing/Saves/Slots/SlotTouchType.cs b/PKHeX.Core/Editing/Saves/Slots/SlotTouchType.cs
new file mode 100644
index 000000000..ce141fb9b
--- /dev/null
+++ b/PKHeX.Core/Editing/Saves/Slots/SlotTouchType.cs
@@ -0,0 +1,11 @@
+namespace PKHeX.Core
+{
+ public enum SlotTouchType
+ {
+ None,
+ Get,
+ Set,
+ Delete,
+ Swap,
+ }
+}
\ No newline at end of file
diff --git a/PKHeX.Core/Saves/Storage/StorageSlotOffset.cs b/PKHeX.Core/Editing/Saves/Slots/StorageSlotOffset.cs
similarity index 100%
rename from PKHeX.Core/Saves/Storage/StorageSlotOffset.cs
rename to PKHeX.Core/Editing/Saves/Slots/StorageSlotOffset.cs
diff --git a/PKHeX.Core/Saves/Storage/StorageSlotType.cs b/PKHeX.Core/Editing/Saves/Slots/StorageSlotType.cs
similarity index 100%
rename from PKHeX.Core/Saves/Storage/StorageSlotType.cs
rename to PKHeX.Core/Editing/Saves/Slots/StorageSlotType.cs
diff --git a/PKHeX.Core/Saves/SAV7.cs b/PKHeX.Core/Saves/SAV7.cs
index 562d14b8c..1b7423a50 100644
--- a/PKHeX.Core/Saves/SAV7.cs
+++ b/PKHeX.Core/Saves/SAV7.cs
@@ -1230,7 +1230,7 @@ public override int PartyCount
public override bool IsSlotLocked(int box, int slot)
{
- if (slot >= 30 || box >= BoxCount)
+ if ((uint)slot >= 30 || (uint)box >= BoxCount)
return false;
int slotIndex = slot + (BoxSlotCount * box);
@@ -1239,7 +1239,7 @@ public override bool IsSlotLocked(int box, int slot)
public override bool IsSlotInBattleTeam(int box, int slot)
{
- if (slot >= 30 || box >= BoxCount)
+ if ((uint)slot >= 30 || (uint)box >= BoxCount)
return false;
int slotIndex = slot + (BoxSlotCount * box);