Convert all 37 remaining VMs to clone+CopyChangesFrom pattern

All subform ViewModels now use transactional cancel semantics
matching WinForms: edits happen on a clone, Cancel discards them,
Save copies the clone back to the origin via CopyChangesFrom.

Gen 1-2: SAVEventReset1, SAVHallOfFame1, SAVMisc2, SAVRTC2
Gen 3: SAVRoamer3, SAVSecretBase3, SAVHallOfFame3, SAVRTC3,
  SAVMisc3, PokeBlock3CaseEditor, SimplePokedex
Gen 4: PokeGear4, Trainer4BR, BattlePass4, Gear4, Apricorn4,
  HoneyTree4, Underground4, Geonet4, PoffinCase4, Pokedex4
Gen 5: Pokedex5, UnityTower5, DLC5
Gen 6: SAVPokepuff6
Gen 7: SAVHallOfFame7
Gen 8 BDSP: Poffin8b, SealStickers8b, Underground8b
Cross-gen: EventFlags, EventFlags2, EventWork, Inventory,
  MailBox, Wondercard, BoxLayout, TrainerStat
This commit is contained in:
montanon 2026-03-17 15:34:29 -03:00
parent 875f7904ea
commit 8b3da88a13
37 changed files with 192 additions and 71 deletions

View File

@ -31,6 +31,7 @@ public ApricornEntryModel(int index, string name, int count)
/// </summary>
public partial class Apricorn4ViewModel : SaveEditorViewModelBase
{
private readonly SAV4HGSS _origin;
private readonly SAV4HGSS SAV4H;
private const int Count = 7;
private const int ItemNameBase = 485;
@ -41,7 +42,8 @@ public partial class Apricorn4ViewModel : SaveEditorViewModelBase
public Apricorn4ViewModel(SAV4HGSS sav) : base(sav)
{
SAV4H = sav;
_origin = sav;
SAV4H = (SAV4HGSS)sav.Clone();
var itemNames = GameInfo.Strings.itemlist;
for (int i = 0; i < Count; i++)
@ -72,7 +74,7 @@ private void Save()
foreach (var a in Apricorns)
SAV4H.SetApricornCount(a.Index, Math.Min(byte.MaxValue, a.Count));
SAV.State.Edited = true;
_origin.CopyChangesFrom(SAV4H);
Modified = true;
}
}

View File

@ -29,6 +29,7 @@ public BattlePassEntryModel(int index, string label)
/// </summary>
public partial class BattlePass4ViewModel : SaveEditorViewModelBase
{
private readonly SAV4BR _origin;
private readonly SAV4BR SAV4BR;
private BattlePass _currentPass;
private int _currentPassIndex;
@ -72,7 +73,8 @@ public partial class BattlePass4ViewModel : SaveEditorViewModelBase
public BattlePass4ViewModel(SAV4BR sav, int startIndex = 0) : base(sav)
{
SAV4BR = sav;
_origin = sav;
SAV4BR = (SAV4BR)sav.Clone();
LoadPassList();
@ -236,7 +238,7 @@ private void MoveDown()
private void Save()
{
SaveCurrent();
SAV.State.Edited = true;
_origin.CopyChangesFrom(SAV4BR);
Modified = true;
}
}

View File

@ -32,6 +32,7 @@ public BoxDetailModel(int index, string name, int wallpaper)
/// </summary>
public partial class BoxLayoutViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly SaveFile _clone;
private readonly bool _hasNames;
private readonly bool _hasWallpapers;
@ -50,7 +51,8 @@ public partial class BoxLayoutViewModel : SaveEditorViewModelBase
public BoxLayoutViewModel(SaveFile sav) : base(sav)
{
_clone = sav;
_origin = sav;
_clone = sav.Clone();
_hasNames = sav is IBoxDetailNameRead;
_hasWallpapers = sav is IBoxDetailWallpaper;
@ -89,6 +91,7 @@ private void Save()
wpWriter.SetBoxWallpaper(box.Index, box.Wallpaper);
}
_origin.CopyChangesFrom(_clone);
Modified = true;
}

View File

@ -57,6 +57,7 @@ public Pokestar5Entry(int index, string label)
/// </summary>
public partial class DLC5ViewModel : SaveEditorViewModelBase
{
private readonly SAV5 _origin;
private readonly SAV5 SAV5;
// C-Gear
@ -92,7 +93,8 @@ public partial class DLC5ViewModel : SaveEditorViewModelBase
public DLC5ViewModel(SAV5 sav) : base(sav)
{
SAV5 = sav;
_origin = sav;
SAV5 = (SAV5)sav.Clone();
ShowPWT = sav is SAV5B2W2;
ShowPokestar = sav is SAV5B2W2;
@ -158,7 +160,7 @@ private void LoadMusical()
private void Save()
{
SAV5.Musical.MusicalName = MusicalName;
SAV.State.Edited = true;
_origin.CopyChangesFrom(SAV5);
Modified = true;
}
}

View File

@ -59,6 +59,8 @@ public EventWork8Model(int index, int value, string label, IReadOnlyList<NamedEv
/// </summary>
public partial class EventFlags2ViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly SaveFile _clone;
private readonly IEventFlag? _eventFlags;
private readonly ISystemFlag? _systemFlags;
private readonly IEventWork<int>? _eventWork;
@ -99,9 +101,11 @@ public partial class EventFlags2ViewModel : SaveEditorViewModelBase
/// </summary>
public EventFlags2ViewModel(SaveFile sav, IEventFlag eventFlags, ISystemFlag? systemFlags, IEventWork<int>? eventWork, GameVersion version) : base(sav)
{
_eventFlags = eventFlags;
_systemFlags = systemFlags;
_eventWork = eventWork;
_origin = sav;
_clone = sav.Clone();
_eventFlags = (IEventFlag)_clone;
_systemFlags = _clone as ISystemFlag;
_eventWork = _clone as IEventWork<int>;
_isGen2 = false;
WindowTitle = $"Event Flags ({version})";
@ -123,8 +127,10 @@ public EventFlags2ViewModel(SaveFile sav, IEventFlag eventFlags, ISystemFlag? sy
/// </summary>
public EventFlags2ViewModel(SaveFile sav, IEventFlagArray flagArray, IEventWorkArray<byte> workArray, GameVersion version) : base(sav)
{
_flagArray = flagArray;
_workArray = workArray;
_origin = sav;
_clone = sav.Clone();
_flagArray = (IEventFlagArray)_clone;
_workArray = (IEventWorkArray<byte>)_clone;
_isGen2 = true;
WindowTitle = $"Event Flags ({version})";
@ -253,6 +259,7 @@ private void Save()
{
SaveGen8();
}
_origin.CopyChangesFrom(_clone);
Modified = true;
}

View File

@ -51,6 +51,8 @@ public EventWorkModel(int index, ushort value, string label)
/// </summary>
public partial class EventFlagsViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly SaveFile _clone;
private readonly IEventFlag37 _eventWork;
private readonly bool[] _flags;
private readonly ushort[] _works;
@ -84,7 +86,9 @@ public partial class EventFlagsViewModel : SaveEditorViewModelBase
public EventFlagsViewModel(SaveFile sav, IEventFlag37 eventWork, GameVersion version) : base(sav)
{
_eventWork = eventWork;
_origin = sav;
_clone = sav.Clone();
_eventWork = _clone is IEventFlagProvider37 provider ? provider.EventWork : (IEventFlag37)_clone;
_flags = eventWork.GetEventFlags();
_works = eventWork.GetAllEventWork();
@ -174,6 +178,7 @@ private void Save()
_eventWork.SetEventFlags(_flags);
_eventWork.SetAllEventWork(_works);
_origin.CopyChangesFrom(_clone);
Modified = true;
}

View File

@ -51,6 +51,8 @@ public EventWorkConstModel(int index, int value)
/// </summary>
public partial class EventWorkViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly SaveFile _clone;
private readonly EventWork7b _eventWork;
[ObservableProperty]
@ -74,7 +76,9 @@ public partial class EventWorkViewModel : SaveEditorViewModelBase
public EventWorkViewModel(SaveFile sav, EventWork7b eventWork) : base(sav)
{
_eventWork = eventWork;
_origin = sav;
_clone = sav.Clone();
_eventWork = ((SAV7b)_clone).EventWork;
WindowTitle = $"Event Work ({sav.Version})";
LoadFlags();
@ -125,6 +129,7 @@ private void Save()
foreach (var work in AllWorks)
_eventWork.SetWork(work.Index, work.Value);
_origin.CopyChangesFrom(_clone);
Modified = true;
}
}

View File

@ -34,6 +34,7 @@ public GearEntryModel(int index, string characterStyle, string category, string
/// </summary>
public partial class Gear4ViewModel : SaveEditorViewModelBase
{
private readonly SAV4BR _origin;
private readonly SAV4BR SAV4BR;
public ObservableCollection<GearEntryModel> GearItems { get; } = [];
@ -47,7 +48,8 @@ public partial class Gear4ViewModel : SaveEditorViewModelBase
public Gear4ViewModel(SAV4BR sav) : base(sav)
{
SAV4BR = sav;
_origin = sav;
SAV4BR = (SAV4BR)sav.Clone();
_shinyGroudon = sav.GearShinyGroudonOutfit;
_shinyLucario = sav.GearShinyLucarioOutfit;
@ -117,7 +119,7 @@ private void Save()
SAV4BR.GearShinyRoseradeOutfit = ShinyRoserade;
SAV4BR.GearShinyPachirisuOutfit = ShinyPachirisu;
SAV.State.Edited = true;
_origin.CopyChangesFrom(SAV4BR);
Modified = true;
}
}

View File

@ -34,6 +34,7 @@ public GeonetEntryModel(int country, string countryName, int subregion, string s
/// </summary>
public partial class Geonet4ViewModel : SaveEditorViewModelBase
{
private readonly SAV4 _origin;
private readonly SAV4 SAV4;
private readonly Geonet4 Geonet;
@ -46,7 +47,8 @@ public partial class Geonet4ViewModel : SaveEditorViewModelBase
public Geonet4ViewModel(SAV4 sav) : base(sav)
{
SAV4 = sav;
_origin = sav;
SAV4 = (SAV4)sav.Clone();
Geonet = new Geonet4(sav);
_globalFlag = Geonet.GlobalFlag;
LoadEntries();
@ -118,7 +120,7 @@ private void Save()
Geonet.GlobalFlag = GlobalFlag;
Geonet.Save();
SAV.State.Edited = true;
_origin.CopyChangesFrom(SAV4);
Modified = true;
}
}

View File

@ -11,6 +11,7 @@ namespace PKHeX.Avalonia.ViewModels.Subforms;
/// </summary>
public partial class HoneyTree4ViewModel : SaveEditorViewModelBase
{
private readonly SAV4Sinnoh _origin;
private readonly SAV4Sinnoh SAV4S;
private HoneyTreeValue? _tree;
private int _treeIndex;
@ -38,7 +39,8 @@ public partial class HoneyTree4ViewModel : SaveEditorViewModelBase
public HoneyTree4ViewModel(SAV4Sinnoh sav) : base(sav)
{
SAV4S = sav;
_origin = sav;
SAV4S = (SAV4Sinnoh)sav.Clone();
// Build tree name list
TreeNames =
@ -107,7 +109,7 @@ private void SaveTree()
private void Save()
{
SaveTree();
SAV.State.Edited = true;
_origin.CopyChangesFrom(SAV4S);
Modified = true;
}
}

View File

@ -14,6 +14,8 @@ namespace PKHeX.Avalonia.ViewModels.Subforms;
/// </summary>
public partial class InventoryViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly SaveFile _clone;
private readonly PlayerBag _bag;
private readonly string[] _itemNames;
@ -30,8 +32,10 @@ public partial class InventoryViewModel : SaveEditorViewModelBase
public InventoryViewModel(SaveFile sav) : base(sav)
{
_bag = sav.Inventory;
_itemNames = [.. GameInfo.Strings.GetItemStrings(sav.Context, sav.Version)];
_origin = sav;
_clone = sav.Clone();
_bag = _clone.Inventory;
_itemNames = [.. GameInfo.Strings.GetItemStrings(_clone.Context, _clone.Version)];
// Fill in blanks for unnamed items
for (int i = 0; i < _itemNames.Length; i++)
@ -91,7 +95,8 @@ private List<string> GetValidItemNames(ReadOnlySpan<ushort> validItemIds)
private void Save()
{
WriteBackAllPouches();
_bag.CopyTo(SAV);
_bag.CopyTo(_clone);
_origin.CopyChangesFrom(_clone);
Modified = true;
}

View File

@ -46,6 +46,8 @@ public MailEntryModel(int index, MailDetail mail, bool isParty)
/// </summary>
public partial class MailBoxViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly SaveFile _clone;
private readonly MailDetail[] _mails;
private readonly int _partyCount;
@ -66,6 +68,8 @@ public partial class MailBoxViewModel : SaveEditorViewModelBase
public MailBoxViewModel(SaveFile sav, MailDetail[] mails, int partyCount) : base(sav)
{
_origin = sav;
_clone = sav.Clone();
_mails = mails;
_partyCount = partyCount;
WindowTitle = $"Mail Box ({sav.Version})";
@ -129,10 +133,11 @@ private void Save()
_mails[SelectedIndex].MailType = SelectedMailType;
}
// Copy all mails back to save
// Copy all mails back to clone, then commit
foreach (var mail in _mails)
mail.CopyTo(SAV);
mail.CopyTo(_clone);
_origin.CopyChangesFrom(_clone);
Modified = true;
}
}

View File

@ -36,6 +36,7 @@ public Poffin8bModel(int index)
/// </summary>
public partial class Poffin8bViewModel : SaveEditorViewModelBase
{
private readonly SAV8BS _origin;
private readonly SAV8BS _sav;
private readonly IReadOnlyList<Poffin8b> _allItems;
private readonly string[] _itemNames;
@ -44,7 +45,7 @@ public partial class Poffin8bViewModel : SaveEditorViewModelBase
public Poffin8bViewModel(SAV8BS sav) : base(sav)
{
_sav = sav;
_sav = (SAV8BS)(_origin = sav).Clone();
var names = new string[256];
for (int i = 0; i < names.Length; i++)
names[i] = $"Poffin {i}";
@ -126,6 +127,7 @@ private void Save()
item.FlavorSour = (byte)Math.Clamp(model.Sour, 0, 255);
}
_sav.Poffins.SetPoffins(_allItems);
_origin.CopyChangesFrom(_sav);
Modified = true;
}
}

View File

@ -68,6 +68,8 @@ public void ApplyTo(Poffin4 poffin)
/// </summary>
public partial class PoffinCase4ViewModel : SaveEditorViewModelBase
{
private readonly SAV4Sinnoh _origin;
private readonly SAV4Sinnoh SAV4S;
private readonly PoffinCase4 Case;
public ObservableCollection<PoffinEntryModel> Poffins { get; } = [];
@ -77,7 +79,9 @@ public partial class PoffinCase4ViewModel : SaveEditorViewModelBase
public PoffinCase4ViewModel(SAV4Sinnoh sav) : base(sav)
{
Case = new PoffinCase4(sav);
_origin = sav;
SAV4S = (SAV4Sinnoh)sav.Clone();
Case = new PoffinCase4(SAV4S);
LoadPoffins();
}
@ -120,7 +124,7 @@ private void Save()
Poffins[i].ApplyTo(Case.Poffins[i]);
Case.Save();
SAV.State.Edited = true;
_origin.CopyChangesFrom(SAV4S);
Modified = true;
}
}

View File

@ -80,6 +80,8 @@ public void LoadFromBlock()
/// </summary>
public partial class PokeBlock3CaseEditorViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly SAV3 _sav;
private readonly ISaveBlock3LargeHoenn _hoenn;
private readonly PokeBlock3Case _case;
@ -94,8 +96,10 @@ public partial class PokeBlock3CaseEditorViewModel : SaveEditorViewModelBase
public PokeBlock3CaseEditorViewModel(SAV3 sav, ISaveBlock3LargeHoenn hoenn) : base(sav)
{
_hoenn = hoenn;
_case = hoenn.PokeBlocks;
_origin = sav;
_sav = (SAV3)sav.Clone();
_hoenn = (ISaveBlock3LargeHoenn)_sav.LargeBlock;
_case = _hoenn.PokeBlocks;
for (int i = 0; i < _case.Blocks.Length; i++)
Blocks.Add(new PokeBlock3Model(_case.Blocks[i], i));
@ -132,6 +136,7 @@ private void Save()
foreach (var block in Blocks)
block.SaveToBlock();
_hoenn.PokeBlocks = _case;
_origin.CopyChangesFrom(_sav);
Modified = true;
}
}

View File

@ -31,13 +31,15 @@ public PokeGearEntryModel(int index, string name, int callerId)
/// </summary>
public partial class PokeGear4ViewModel : SaveEditorViewModelBase
{
private readonly SAV4HGSS _origin;
private readonly SAV4HGSS SAV4H;
public ObservableCollection<PokeGearEntryModel> Contacts { get; } = [];
public PokeGear4ViewModel(SAV4HGSS sav) : base(sav)
{
SAV4H = sav;
_origin = sav;
SAV4H = (SAV4HGSS)sav.Clone();
LoadContacts();
}
@ -81,7 +83,7 @@ private void Save()
for (int i = 0; i < Contacts.Count; i++)
rolodex[i] = (PokegearNumber)Contacts[i].CallerId;
SAV4H.SetPokeGearRoloDex(rolodex);
SAV.State.Edited = true;
_origin.CopyChangesFrom(SAV4H);
Modified = true;
}
}

View File

@ -41,6 +41,7 @@ public Pokedex4EntryModel(ushort species, string label, bool seen, bool caught,
/// </summary>
public partial class Pokedex4ViewModel : SaveEditorViewModelBase
{
private readonly SAV4 _origin;
private readonly SAV4 SAV4;
private const int LangCount = 6;
@ -59,7 +60,8 @@ public partial class Pokedex4ViewModel : SaveEditorViewModelBase
public Pokedex4ViewModel(SAV4 sav) : base(sav)
{
SAV4 = sav;
_origin = sav;
SAV4 = (SAV4)sav.Clone();
var speciesNames = GameInfo.Strings.specieslist;
for (ushort i = 1; i <= sav.MaxSpeciesID; i++)
@ -141,7 +143,7 @@ private void Save()
if (DexUpgraded >= 0)
SAV4.DexUpgraded = DexUpgraded;
SAV.State.Edited = true;
_origin.CopyChangesFrom(SAV4);
Modified = true;
}
}

View File

@ -50,6 +50,7 @@ public Pokedex5EntryModel(ushort species, string label, bool hasLanguages)
/// </summary>
public partial class Pokedex5ViewModel : SaveEditorViewModelBase
{
private readonly SAV5 _origin;
private readonly SAV5 SAV5;
private readonly Zukan5 Dex;
private const int LangCount = 7;
@ -66,8 +67,9 @@ public partial class Pokedex5ViewModel : SaveEditorViewModelBase
public Pokedex5ViewModel(SAV5 sav) : base(sav)
{
SAV5 = sav;
Dex = sav.Zukan;
_origin = sav;
SAV5 = (SAV5)sav.Clone();
Dex = SAV5.Zukan;
_nationalDexUnlocked = Dex.IsNationalDexUnlocked;
_nationalDexActive = Dex.IsNationalDexMode;
@ -202,7 +204,7 @@ private void Save()
if (uint.TryParse(SpindaPid, System.Globalization.NumberStyles.HexNumber, null, out var spinda))
Dex.Spinda = spinda;
SAV.State.Edited = true;
_origin.CopyChangesFrom(SAV5);
Modified = true;
}
}

View File

@ -39,13 +39,17 @@ private void Reset()
/// </summary>
public partial class SAVEventReset1ViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly SAV1 _sav;
private readonly G1OverworldSpawner _overworld;
public ObservableCollection<FlagPairModel> FlagPairs { get; } = [];
public SAVEventReset1ViewModel(SAV1 sav) : base(sav)
{
_overworld = new G1OverworldSpawner(sav);
_origin = sav;
_sav = (SAV1)sav.Clone();
_overworld = new G1OverworldSpawner(_sav);
var pairs = _overworld.GetFlagPairs().OrderBy(z => z.Name);
foreach (var pair in pairs)
@ -67,6 +71,7 @@ public SAVEventReset1ViewModel(SAV1 sav) : base(sav)
private void Save()
{
_overworld.Save();
_origin.CopyChangesFrom(_sav);
Modified = true;
}
}

View File

@ -14,6 +14,7 @@ namespace PKHeX.Avalonia.ViewModels.Subforms;
/// </summary>
public partial class SAVHallOfFame1ViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly SAV1 _sav;
private readonly HallOfFameReader1 _fame;
private int _currentTeam = -1;
@ -58,8 +59,9 @@ public partial class SAVHallOfFame1ViewModel : SaveEditorViewModelBase
public SAVHallOfFame1ViewModel(SAV1 sav) : base(sav)
{
_sav = sav;
_fame = sav.HallOfFame;
_origin = sav;
_sav = (SAV1)sav.Clone();
_fame = _sav.HallOfFame;
MaxNicknameLength = sav.Japanese ? 5 : 10;
SpeciesList = GameInfo.FilteredSources.Species.ToList();
@ -205,6 +207,7 @@ private void ClearAll()
_fame.Clear();
HallOfFameClears = 0;
_sav.HallOfFameCount = 0;
_origin.CopyChangesFrom(_sav);
Modified = true;
}
@ -213,6 +216,7 @@ private void Save()
{
SaveEntity();
_sav.HallOfFameCount = HallOfFameClears;
_origin.CopyChangesFrom(_sav);
Modified = true;
}
}

View File

@ -13,6 +13,7 @@ namespace PKHeX.Avalonia.ViewModels.Subforms;
/// </summary>
public partial class SAVHallOfFame3ViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly SAV3 _sav;
private readonly HallFame3Entry[] _fame;
private int _prevEntry;
@ -51,8 +52,9 @@ public partial class SAVHallOfFame3ViewModel : SaveEditorViewModelBase
public SAVHallOfFame3ViewModel(SAV3 sav) : base(sav)
{
_sav = sav;
_fame = HallFame3Entry.GetEntries(sav);
_origin = sav;
_sav = (SAV3)sav.Clone();
_fame = HallFame3Entry.GetEntries(_sav);
EntryIndices = Enumerable.Range(0, 50).ToList();
SpeciesList = GameInfo.FilteredSources.Species.ToList();
@ -132,6 +134,7 @@ private void Save()
var pkm = _fame[SelectedEntryIndex].Team[MemberIndex];
SaveEntry(pkm);
HallFame3Entry.SetEntries(_sav, _fame);
_origin.CopyChangesFrom(_sav);
Modified = true;
}
}

View File

@ -14,6 +14,7 @@ namespace PKHeX.Avalonia.ViewModels.Subforms;
/// </summary>
public partial class SAVHallOfFame7ViewModel : SaveEditorViewModelBase
{
private readonly SAV7 _origin;
private readonly SAV7 _sav;
private readonly HallOfFame7 _fame;
@ -41,8 +42,9 @@ public partial class SAVHallOfFame7ViewModel : SaveEditorViewModelBase
public SAVHallOfFame7ViewModel(SAV7 sav) : base(sav)
{
_sav = sav;
_fame = sav.EventWork.Fame;
_origin = sav;
_sav = (SAV7)sav.Clone();
_fame = _sav.EventWork.Fame;
SpeciesList = GameInfo.FilteredSources.Species.ToList();
ShowStarterEc = sav is SAV7USUM;
@ -92,6 +94,7 @@ private void Save()
uu.Misc.StarterEncryptionConstant = ec;
}
_origin.CopyChangesFrom(_sav);
Modified = true;
}
}

View File

@ -10,6 +10,7 @@ namespace PKHeX.Avalonia.ViewModels.Subforms;
/// </summary>
public partial class SAVMisc2ViewModel : SaveEditorViewModelBase
{
private readonly SAV2 _origin;
private readonly SAV2 _sav;
[ObservableProperty]
@ -20,9 +21,10 @@ public partial class SAVMisc2ViewModel : SaveEditorViewModelBase
public SAVMisc2ViewModel(SAV2 sav) : base(sav)
{
_sav = sav;
_showGsBallButton = sav.Version is GameVersion.C;
_gsBallEnabled = !sav.IsEnabledGSBallMobileEvent;
_origin = sav;
_sav = (SAV2)sav.Clone();
_showGsBallButton = _sav.Version is GameVersion.C;
_gsBallEnabled = !_sav.IsEnabledGSBallMobileEvent;
}
[RelayCommand]
@ -35,6 +37,7 @@ private void EnableGsBall()
[RelayCommand]
private void Save()
{
_origin.CopyChangesFrom(_sav);
Modified = true;
}
}

View File

@ -13,6 +13,7 @@ namespace PKHeX.Avalonia.ViewModels.Subforms;
/// </summary>
public partial class SAVMisc3ViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly SAV3 _sav;
// Records
@ -121,7 +122,8 @@ public partial class SAVMisc3ViewModel : SaveEditorViewModelBase
public SAVMisc3ViewModel(SAV3 sav) : base(sav)
{
_sav = sav;
_origin = sav;
_sav = (SAV3)sav.Clone();
// Records
RecordItems = Record3.GetItems(sav);
@ -266,6 +268,7 @@ private void Save()
if (_sav is SAV3FRLG frlg)
frlg.RivalName = RivalName;
_origin.CopyChangesFrom(_sav);
Modified = true;
}
}

View File

@ -32,6 +32,7 @@ public PokepuffSlotModel(int index, int puffIndex)
/// </summary>
public partial class SAVPokepuff6ViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly ISaveBlock6Main _sav;
public ObservableCollection<PokepuffSlotModel> Slots { get; } = [];
@ -39,7 +40,9 @@ public partial class SAVPokepuff6ViewModel : SaveEditorViewModelBase
public SAVPokepuff6ViewModel(ISaveBlock6Main sav) : base((SaveFile)sav)
{
_sav = sav;
_origin = (SaveFile)sav;
var clone = _origin.Clone();
_sav = (ISaveBlock6Main)clone;
PuffNames = [.. GameInfo.Strings.puffs];
LoadPuffs();
@ -108,6 +111,7 @@ private void SavePuffs()
private void Save()
{
SavePuffs();
_origin.CopyChangesFrom((SaveFile)_sav);
Modified = true;
}
}

View File

@ -10,6 +10,7 @@ namespace PKHeX.Avalonia.ViewModels.Subforms;
/// </summary>
public partial class SAVRTC2ViewModel : SaveEditorViewModelBase
{
private readonly SAV2 _origin;
private readonly SAV2 _sav;
[ObservableProperty]
@ -17,7 +18,8 @@ public partial class SAVRTC2ViewModel : SaveEditorViewModelBase
public SAVRTC2ViewModel(SAV2 sav) : base(sav)
{
_sav = sav;
_origin = sav;
_sav = (SAV2)sav.Clone();
}
[RelayCommand]
@ -30,6 +32,7 @@ private void ResetRtc()
[RelayCommand]
private void Save()
{
_origin.CopyChangesFrom(_sav);
Modified = true;
}
}

View File

@ -11,6 +11,7 @@ namespace PKHeX.Avalonia.ViewModels.Subforms;
/// </summary>
public partial class SAVRTC3ViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly SAV3 _sav;
private readonly ISaveBlock3SmallHoenn _small;
private readonly RTC3 _clockInitial;
@ -44,8 +45,9 @@ public partial class SAVRTC3ViewModel : SaveEditorViewModelBase
public SAVRTC3ViewModel(SAV3 sav) : base(sav)
{
_sav = sav;
_small = (ISaveBlock3SmallHoenn)sav.SmallBlock;
_origin = sav;
_sav = (SAV3)sav.Clone();
_small = (ISaveBlock3SmallHoenn)_sav.SmallBlock;
_clockInitial = _small.ClockInitial;
_clockElapsed = _small.ClockElapsed;
@ -94,6 +96,7 @@ private void Save()
_small.ClockInitial = _clockInitial;
_small.ClockElapsed = _clockElapsed;
_origin.CopyChangesFrom(_sav);
Modified = true;
}
}

View File

@ -13,6 +13,7 @@ namespace PKHeX.Avalonia.ViewModels.Subforms;
/// </summary>
public partial class SAVRoamer3ViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly Roamer3 _roamer;
private readonly SAV3 _sav;
@ -56,8 +57,9 @@ public partial class SAVRoamer3ViewModel : SaveEditorViewModelBase
public SAVRoamer3ViewModel(SAV3 sav) : base(sav)
{
_sav = sav;
_roamer = new Roamer3(sav.LargeBlock);
_origin = sav;
_sav = (SAV3)sav.Clone();
_roamer = new Roamer3(_sav.LargeBlock);
SpeciesList = GameInfo.FilteredSources.Species.ToList();
LoadData();
@ -97,6 +99,7 @@ private void Save()
_roamer.Active = Active;
_roamer.CurrentLevel = Level;
_roamer.HP_Current = HpCurrent;
_origin.CopyChangesFrom(_sav);
Modified = true;
}
}

View File

@ -14,6 +14,7 @@ namespace PKHeX.Avalonia.ViewModels.Subforms;
/// </summary>
public partial class SAVSecretBase3ViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly SAV3 _sav;
private readonly SecretBaseManager3 _manager;
@ -88,8 +89,9 @@ public partial class SAVSecretBase3ViewModel : SaveEditorViewModelBase
public SAVSecretBase3ViewModel(SAV3 sav) : base(sav)
{
_sav = sav;
var large = (ISaveBlock3LargeHoenn)sav.LargeBlock;
_origin = sav;
_sav = (SAV3)sav.Clone();
var large = (ISaveBlock3LargeHoenn)_sav.LargeBlock;
_manager = large.SecretBases;
var filtered = GameInfo.FilteredSources;
@ -193,6 +195,7 @@ private void UpdatePkm()
private void Save()
{
_manager.Save();
_origin.CopyChangesFrom(_sav);
Modified = true;
}
}

View File

@ -31,6 +31,7 @@ public SealSticker8bModel(int index, string name)
/// </summary>
public partial class SealStickers8bViewModel : SaveEditorViewModelBase
{
private readonly SAV8BS _origin;
private readonly SAV8BS _sav;
private readonly IReadOnlyList<SealSticker8b> _allItems;
private readonly string[] _itemNames;
@ -39,7 +40,7 @@ public partial class SealStickers8bViewModel : SaveEditorViewModelBase
public SealStickers8bViewModel(SAV8BS sav) : base(sav)
{
_sav = sav;
_sav = (SAV8BS)(_origin = sav).Clone();
var names = new string[200];
for (int i = 0; i < names.Length; i++)
names[i] = $"Sticker {i}";
@ -103,6 +104,7 @@ private void Save()
item.IsGet = model.IsObtained;
}
_sav.SealList.WriteItems(_allItems);
_origin.CopyChangesFrom(_sav);
Modified = true;
}
}

View File

@ -36,6 +36,9 @@ public PokedexEntryModel(ushort species, string label, bool seen, bool caught)
/// </summary>
public partial class SimplePokedexViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly SaveFile _clone;
[ObservableProperty]
private string _searchText = string.Empty;
@ -46,6 +49,8 @@ public partial class SimplePokedexViewModel : SaveEditorViewModelBase
public SimplePokedexViewModel(SaveFile sav) : base(sav)
{
_origin = sav;
_clone = (SaveFile)sav.Clone();
var count = sav.MaxSpeciesID;
var speciesNames = GameInfo.Strings.specieslist;
@ -110,15 +115,15 @@ private void Save()
{
foreach (var entry in AllEntries)
{
SAV.SetSeen(entry.Species, entry.Seen);
SAV.SetCaught(entry.Species, entry.Caught);
_clone.SetSeen(entry.Species, entry.Seen);
_clone.SetCaught(entry.Species, entry.Caught);
}
// Sanity checks for gen 3
if (SAV is SAV3 s3)
if (_clone is SAV3 s3)
s3.MirrorSeenFlags();
SAV.State.Edited = true;
_origin.CopyChangesFrom(_clone);
Modified = true;
}
}

View File

@ -11,6 +11,7 @@ namespace PKHeX.Avalonia.ViewModels.Subforms;
/// </summary>
public partial class Trainer4BRViewModel : SaveEditorViewModelBase
{
private readonly SAV4BR _origin;
private readonly SAV4BR SAV4BR;
[ObservableProperty] private string _otName = string.Empty;
@ -56,7 +57,8 @@ public partial class Trainer4BRViewModel : SaveEditorViewModelBase
public Trainer4BRViewModel(SAV4BR sav) : base(sav)
{
SAV4BR = sav;
_origin = sav;
SAV4BR = (SAV4BR)sav.Clone();
_otName = sav.CurrentOT;
_birthMonth = sav.BirthMonth;
@ -145,7 +147,7 @@ private void Save()
SAV4BR.UnlockedStargazerColosseum = UnlockedStargazerColosseum;
SAV4BR.UnlockedPostGame = UnlockedPostGame;
SAV.State.Edited = true;
_origin.CopyChangesFrom(SAV4BR);
Modified = true;
}
}

View File

@ -36,6 +36,8 @@ public TrainerStatModel(int index, string label, int value, int offset, int max)
/// </summary>
public partial class TrainerStatViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly SaveFile _clone;
private readonly ITrainerStatRecord _record;
[ObservableProperty]
@ -50,7 +52,9 @@ public partial class TrainerStatViewModel : SaveEditorViewModelBase
public TrainerStatViewModel(SaveFile sav, ITrainerStatRecord record, Dictionary<int, string> recordNames) : base(sav)
{
_record = record;
_origin = sav;
_clone = sav.Clone();
_record = (ITrainerStatRecord)_clone;
WindowTitle = $"Trainer Stats ({sav.Version})";
for (int i = 0; i < record.RecordCount; i++)
@ -93,6 +97,7 @@ private void Save()
_record.SetRecord(rec.Index, clamped);
}
_origin.CopyChangesFrom(_clone);
Modified = true;
}
}

View File

@ -32,13 +32,15 @@ public UGScoreModel(string label, uint value, uint max)
/// </summary>
public partial class Underground4ViewModel : SaveEditorViewModelBase
{
private readonly SAV4Sinnoh _origin;
private readonly SAV4Sinnoh SAV4S;
public ObservableCollection<UGScoreModel> Scores { get; } = [];
public Underground4ViewModel(SAV4Sinnoh sav) : base(sav)
{
SAV4S = sav;
_origin = sav;
SAV4S = (SAV4Sinnoh)sav.Clone();
var max = SAV4Sinnoh.UG_MAX;
Scores.Add(new UGScoreModel("Players Met", sav.UG_PeopleMet, max));
@ -73,7 +75,7 @@ private void Save()
SAV4S.UG_FlagsCaptured = Scores[11].Value;
SAV4S.UG_HelpedOthers = Scores[12].Value;
SAV.State.Edited = true;
_origin.CopyChangesFrom(SAV4S);
Modified = true;
}
}

View File

@ -35,6 +35,7 @@ public UndergroundItem8bModel(int index, string typeName, string name, int maxVa
/// </summary>
public partial class Underground8bViewModel : SaveEditorViewModelBase
{
private readonly SAV8BS _origin;
private readonly SAV8BS _sav;
private readonly IReadOnlyList<UndergroundItem8b> _allItems;
private readonly string[] _itemNames;
@ -43,7 +44,7 @@ public partial class Underground8bViewModel : SaveEditorViewModelBase
public Underground8bViewModel(SAV8BS sav) : base(sav)
{
_sav = sav;
_sav = (SAV8BS)(_origin = sav).Clone();
var names = new string[300];
for (int i = 0; i < names.Length; i++)
names[i] = $"Item {i}";
@ -101,6 +102,7 @@ private void Save()
item.IsFavoriteFlag = model.IsFavorite;
}
_sav.Underground.WriteItems(_allItems);
_origin.CopyChangesFrom(_sav);
Modified = true;
}
}

View File

@ -53,6 +53,7 @@ public UnityTowerFloorModel(int country, string countryName, bool unlocked)
/// </summary>
public partial class UnityTower5ViewModel : SaveEditorViewModelBase
{
private readonly SAV5 _origin;
private readonly SAV5 SAV5;
private readonly UnityTower5 Tower;
@ -66,8 +67,9 @@ public partial class UnityTower5ViewModel : SaveEditorViewModelBase
public UnityTower5ViewModel(SAV5 sav) : base(sav)
{
SAV5 = sav;
Tower = sav.UnityTower;
_origin = sav;
SAV5 = (SAV5)sav.Clone();
Tower = SAV5.UnityTower;
_globalFlag = Tower.GlobalFlag;
_unityTowerFlag = Tower.UnityTowerFlag;
@ -160,7 +162,7 @@ private void Save()
Tower.GlobalFlag = GlobalFlag;
Tower.UnityTowerFlag = UnityTowerFlag;
SAV.State.Edited = true;
_origin.CopyChangesFrom(SAV5);
Modified = true;
}
}

View File

@ -54,6 +54,8 @@ public void SetGift(DataMysteryGift gift)
/// </summary>
public partial class WondercardViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly SaveFile _clone;
private readonly IMysteryGiftStorage _cards;
[ObservableProperty]
@ -67,7 +69,9 @@ public partial class WondercardViewModel : SaveEditorViewModelBase
public WondercardViewModel(SaveFile sav) : base(sav)
{
_cards = GetMysteryGiftProvider(sav);
_origin = sav;
_clone = sav.Clone();
_cards = GetMysteryGiftProvider(_clone);
LoadSlots();
if (Slots.Count > 0)
@ -125,6 +129,7 @@ private void Save()
for (int i = 0; i < Slots.Count && i < _cards.GiftCountMax; i++)
_cards.SetMysteryGift(i, Slots[i].Gift);
_origin.CopyChangesFrom(_clone);
Modified = true;
}