diff --git a/PKHeX.Core/Editing/Saves/Editors/EventOld/EventWorkspace.cs b/PKHeX.Core/Editing/Saves/Editors/EventOld/EventWorkspace.cs index d0b25fa85..a7955fcb4 100644 --- a/PKHeX.Core/Editing/Saves/Editors/EventOld/EventWorkspace.cs +++ b/PKHeX.Core/Editing/Saves/Editors/EventOld/EventWorkspace.cs @@ -1,50 +1,56 @@ using System; using static PKHeX.Core.GameVersion; -namespace PKHeX.Core +namespace PKHeX.Core; + +public sealed class EventWorkspace where TSave : IEventFlagArray, IEventWorkArray where TWork : unmanaged { - public sealed class EventWorkspace + private readonly TSave SAV; + public readonly bool[] Flags; + public readonly TWork[] Values; + public readonly EventLabelCollection Labels; + + public EventWorkspace(TSave sav) { - private readonly SaveFile SAV; - public readonly bool[] Flags; - public readonly ushort[] Values; - public readonly EventLabelCollection Labels; + SAV = sav; + Flags = sav.GetEventFlags(); + Values = sav.GetAllEventWork(); - public EventWorkspace(SaveFile sav) - { - SAV = sav; - Flags = sav.GetEventFlags(); - Values = sav.GetEventConsts(); + var game = GetResourceSuffix(sav); + Labels = new EventLabelCollection(game, Flags.Length, Values.Length); + } - var game = GetResourceSuffix(sav.Version); - Labels = new EventLabelCollection(game, Flags.Length, Values.Length); - } + public void Save() + { + SAV.SetEventFlags(Flags); + SAV.SetAllEventWork(Values); + if (SAV is SAV7SM s7) // Ensure Magearna event flag has magic constant + s7.UpdateMagearnaConstant(); + } - public void Save() - { - SAV.SetEventFlags(Flags); - SAV.SetEventConsts(Values); - if (SAV is SAV7SM s7) // Ensure Magearna event flag has magic constant - s7.UpdateMagearnaConstant(); - } + private static string GetResourceSuffix(TSave ver) => GetVersion(ver) switch + { + X or Y or XY => "xy", + OR or AS or ORAS => "oras", + SN or MN or SM => "sm", + US or UM or USUM => "usum", + D or P or DP => "dp", + Pt or DPPt => "pt", + HG or SS or HGSS => "hgss", + B or W or BW => "bw", + B2 or W2 or B2W2 => "b2w2", + R or S or RS => "rs", + E => "e", + FR or LG or FRLG => "frlg", + C => "c", + GD or SI or GS => "gs", + _ => throw new ArgumentOutOfRangeException(nameof(GameVersion)), + }; - private static string GetResourceSuffix(GameVersion ver) => ver switch - { - X or Y or XY => "xy", - OR or AS or ORAS => "oras", - SN or MN or SM => "sm", - US or UM or USUM => "usum", - D or P or DP => "dp", - Pt or DPPt => "pt", - HG or SS or HGSS => "hgss", - B or W or BW => "bw", - B2 or W2 or B2W2 => "b2w2", - R or S or RS => "rs", - E => "e", - FR or LG or FRLG => "frlg", - C => "c", - GD or SI or GS => "gs", - _ => throw new ArgumentOutOfRangeException(nameof(GameVersion)), - }; + private static GameVersion GetVersion(TSave ver) + { + if (ver is IVersion v) + return v.Version; + return Invalid; } } diff --git a/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/EventWorkDiff.cs b/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/EventWorkDiff.cs new file mode 100644 index 000000000..6b9c4a230 --- /dev/null +++ b/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/EventWorkDiff.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using static PKHeX.Core.EventWorkDiffCompatibility; +using static PKHeX.Core.EventWorkDiffCompatibilityExtensions; + +namespace PKHeX.Core; + +/// +/// Calculates differences in the Event Blocks between two . +/// +public class EventBlockDiff : IEventWorkDiff where T : IEventFlagArray, IEventWorkArray where T2 : unmanaged, IEquatable +{ + public List SetFlags { get; } = new(); + public List ClearedFlags { get; } = new(); + public List WorkChanged { get; } = new(); + public List WorkDiff { get; } = new(); + public EventWorkDiffCompatibility Message { get; private set; } + + private const int MAX_SAVEFILE_SIZE = 0x10_0000; // 1 MB + + public EventBlockDiff(T s1, T s2) => Diff(s1, s2); + + public EventBlockDiff(string f1, string f2) + { + Message = SanityCheckFiles(f1, f2, MAX_SAVEFILE_SIZE); + if (Message != Valid) + return; + var s1 = SaveUtil.GetVariantSAV(f1); + var s2 = SaveUtil.GetVariantSAV(f2); + if (s1 == null || s2 == null || s1.GetType() != s2.GetType() || s1 is not T t1 || s2 is not T t2) + { + Message = DifferentGameGroup; + return; + } + Diff(t1, t2); + } + + protected EventWorkDiffCompatibility SanityCheckSaveInfo(T s1, T s2) + { + if (s1.GetType() != s2.GetType()) + return DifferentGameGroup; + + if (s1 is not IVersion i1 || s2 is not IVersion i2 || i1.Version != i2.Version) + return DifferentVersion; + + return Valid; + } + + protected void Diff(T s1, T s2) + { + Message = SanityCheckSaveInfo(s1, s2); + if (Message != Valid) + return; + + bool[] oldBits = s1.GetEventFlags(); + bool[] newBits = s2.GetEventFlags(); + var oldConst = s1.GetAllEventWork(); + var newConst = s2.GetAllEventWork(); + + for (int i = 0; i < newBits.Length; i++) + { + if (oldBits[i] != newBits[i]) + (newBits[i] ? SetFlags : ClearedFlags).Add(i); + } + for (int i = 0; i < newConst.Length; i++) + { + if (oldConst[i].Equals(newConst[i])) + continue; + WorkChanged.Add(i); + WorkDiff.Add($"{i}: {oldConst[i]} => {newConst[i]}"); + } + } + + public IReadOnlyList Summarize() + { + var fOn = SetFlags.Select(z => z.ToString()); + var fOff = ClearedFlags.Select(z => z.ToString()); + var wt = WorkChanged.Select((z, _) => z.ToString()); + + var list = new List { "Flags: ON", "=========" }; + list.AddRange(fOn); + if (SetFlags.Count == 0) + list.Add("None."); + + list.Add(""); + list.Add("Flags: OFF"); + list.Add("=========="); + list.AddRange(fOff); + if (ClearedFlags.Count == 0) + list.Add("None."); + + list.Add(""); + list.Add("Work:"); + list.Add("====="); + if (WorkChanged.Count == 0) + list.Add("None."); + list.AddRange(wt); + + return list; + } +} diff --git a/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/EventWorkDiff7b.cs b/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/EventWorkDiff7b.cs new file mode 100644 index 000000000..58d183e5d --- /dev/null +++ b/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/EventWorkDiff7b.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using static PKHeX.Core.EventWorkUtil; +using static PKHeX.Core.EventWorkDiffCompatibility; +using static PKHeX.Core.EventWorkDiffCompatibilityExtensions; + +namespace PKHeX.Core; + +public sealed class EventWorkDiff7b : IEventWorkDiff +{ + private SAV7b? S1; + public List SetFlags { get; } = new(); + public List ClearedFlags { get; } = new(); + public List WorkChanged { get; } = new(); + public List WorkDiff { get; } = new(); + public EventWorkDiffCompatibility Message { get; private set; } + + private const int MAX_SAVEFILE_SIZE = 0x10_0000; // 1 MB + + public EventWorkDiff7b(SAV7b s1, SAV7b s2) => Diff(s1, s2); + + public EventWorkDiff7b(string f1, string f2) + { + Message = SanityCheckFiles(f1, f2, MAX_SAVEFILE_SIZE); + if (Message != Valid) + return; + + var s1 = SaveUtil.GetVariantSAV(f1); + var s2 = SaveUtil.GetVariantSAV(f2); + if (s1 is not SAV7b b1 || s2 is not SAV7b b2) + { + Message = DifferentVersion; + return; + } + + Diff(b1, b2); + } + + private void Diff(SAV7b s1, SAV7b s2) + { + S1 = s1; + if (s1.Version != s2.Version) + { + Message = DifferentVersion; + return; + } + + DiffSavesFlag(s1.Blocks.EventWork, s2.Blocks.EventWork, SetFlags, ClearedFlags); + DiffSavesWork(s1.Blocks.EventWork, s2.Blocks.EventWork, WorkChanged, WorkDiff); + } + + public IReadOnlyList Summarize() + { + if (S1 == null) + return Array.Empty(); + var ew = S1.Blocks.EventWork; + + var fOn = SetFlags.Select(z => FlagSummary.Get(z, ew).ToString()); + var fOff = ClearedFlags.Select(z => FlagSummary.Get(z, ew).ToString()); + var wt = WorkChanged.Select((z, i) => WorkSummary.Get(z, ew, WorkDiff[i]).ToString()); + + var list = new List { "Flags: ON", "=========" }; + list.AddRange(fOn); + if (SetFlags.Count == 0) + list.Add("None."); + + list.Add(""); + list.Add("Flags: OFF"); + list.Add("=========="); + list.AddRange(fOff); + if (ClearedFlags.Count == 0) + list.Add("None."); + + list.Add(""); + list.Add("Work:"); + list.Add("====="); + if (WorkChanged.Count == 0) + list.Add("None."); + list.AddRange(wt); + + return list; + } + + private readonly record struct FlagSummary(EventVarType Type, int Index, int Raw) + { + public override string ToString() => $"{Raw:0000}\t{false}\t{Index:0000}\t{Type}"; + + public static FlagSummary Get(int rawIndex, EventWork7b ew) + { + var type = ew.GetFlagType(rawIndex, out var subIndex); + return new FlagSummary(type, subIndex, rawIndex); + } + } + + private readonly record struct WorkSummary(EventVarType Type, int Index, int Raw, string Text) + { + public override string ToString() => $"{Raw:000}\t{Text}\t{Index:000}\t{Type}"; + + public static WorkSummary Get(int rawIndex, EventWork7b ew, string text) + { + var type = ew.GetFlagType(rawIndex, out var subIndex); + return new WorkSummary(type, subIndex, rawIndex, text); + } + } +} diff --git a/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/EventWorkDiff8b.cs b/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/EventWorkDiff8b.cs new file mode 100644 index 000000000..b31ab0e28 --- /dev/null +++ b/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/EventWorkDiff8b.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using static PKHeX.Core.EventWorkUtil; +using static PKHeX.Core.EventWorkDiffCompatibility; +using static PKHeX.Core.EventWorkDiffCompatibilityExtensions; + +namespace PKHeX.Core; + +public sealed class EventWorkDiff8b : IEventWorkDiff +{ + private SAV8BS? S1; + public List SetSystem { get; } = new(); + public List SetFlags { get; } = new(); + public List ClearedSystem { get; } = new(); + public List ClearedFlags { get; } = new(); + public List WorkChanged { get; } = new(); + public List WorkDiff { get; } = new(); + public EventWorkDiffCompatibility Message { get; private set; } + + private const int MAX_SAVEFILE_SIZE = 0x10_0000; // 1 MB + + public EventWorkDiff8b(SAV8BS s1, SAV8BS s2) => Diff(s1, s2); + + public EventWorkDiff8b(string f1, string f2) + { + Message = SanityCheckFiles(f1, f2, MAX_SAVEFILE_SIZE); + if (Message != Valid) + return; + var s1 = SaveUtil.GetVariantSAV(f1); + var s2 = SaveUtil.GetVariantSAV(f2); + if (s1 is not SAV8BS b1 || s2 is not SAV8BS b2) + { + Message = DifferentGameGroup; + return; + } + Diff(b1, b2); + } + + private void Diff(SAV8BS s1, SAV8BS s2) + { + S1 = s1; + if (s1.Version != s2.Version) + { + Message = DifferentVersion; + return; + } + + DiffSavesFlag(s1.Work, s2.Work, SetFlags, ClearedFlags); + DiffSavesSystem(s1.Work, s2.Work, SetSystem, ClearedSystem); + DiffSavesWork(s1.Work, s2.Work, WorkChanged, WorkDiff); + S1 = s1; + } + + public IReadOnlyList Summarize() + { + if (S1 == null) + return Array.Empty(); + + var fOn = SetFlags.Select(z => new FlagSummary(z).ToString()); + var fOff = ClearedFlags.Select(z => new FlagSummary(z).ToString()); + + var sOn = SetSystem.Select(z => new FlagSummary(z).ToString()); + var sOff = ClearedSystem.Select(z => new FlagSummary(z).ToString()); + + var wt = WorkChanged.Select((z, i) => new WorkSummary(z, WorkDiff[i]).ToString()); + + var list = new List { "Flags: ON", "=========" }; + list.AddRange(fOn); + if (SetFlags.Count == 0) + list.Add("None."); + + list.Add(""); + list.Add("Flags: OFF"); + list.Add("=========="); + list.AddRange(fOff); + if (ClearedFlags.Count == 0) + list.Add("None."); + + list.Add("System: ON"); + list.Add("========="); + list.AddRange(sOn); + if (SetFlags.Count == 0) + list.Add("None."); + + list.Add(""); + list.Add("System: OFF"); + list.Add("=========="); + list.AddRange(sOff); + if (ClearedSystem.Count == 0) + list.Add("None."); + + list.Add(""); + list.Add("Work:"); + list.Add("====="); + if (WorkChanged.Count == 0) + list.Add("None."); + list.AddRange(wt); + + return list; + } + + private readonly record struct FlagSummary(int Raw) + { + public override string ToString() => $"{Raw:0000}\t{false}"; + } + + private readonly record struct WorkSummary(int Raw, string Text) + { + public override string ToString() => $"{Raw:000}\t{Text}"; + } +} diff --git a/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/EventWorkDiffCompatibility.cs b/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/EventWorkDiffCompatibility.cs new file mode 100644 index 000000000..0873f27d0 --- /dev/null +++ b/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/EventWorkDiffCompatibility.cs @@ -0,0 +1,48 @@ +using System; +using System.IO; +using static PKHeX.Core.MessageStrings; +using static PKHeX.Core.EventWorkDiffCompatibility; + +namespace PKHeX.Core; + +public enum EventWorkDiffCompatibility +{ + Valid, + DifferentVersion, + DifferentGameGroup, + FileTooBig1, + FileTooBig2, + FileMissing1, + FileMissing2, +} + +public static class EventWorkDiffCompatibilityExtensions +{ + public static string GetMessage(this EventWorkDiffCompatibility value) => value switch + { + DifferentVersion => MsgSaveDifferentVersions, + DifferentGameGroup => MsgSaveDifferentTypes, + FileTooBig1 => string.Format(MsgSaveNumberInvalid, 1), + FileTooBig2 => string.Format(MsgSaveNumberInvalid, 2), + FileMissing1 => string.Format(MsgSaveNumberInvalid, 1), + FileMissing2 => string.Format(MsgSaveNumberInvalid, 2), + _ => throw new ArgumentOutOfRangeException(nameof(value), value, null) + }; + + public static EventWorkDiffCompatibility SanityCheckFiles(string f1, string f2, int MAX_SAVEFILE_SIZE) + { + if (!File.Exists(f1)) + return FileMissing1; + + if (!File.Exists(f2)) + return FileMissing2; + + if (new FileInfo(f1).Length > MAX_SAVEFILE_SIZE) + return FileTooBig1; + + if (new FileInfo(f2).Length > MAX_SAVEFILE_SIZE) + return FileTooBig2; + + return Valid; + } +} diff --git a/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/IEventWorkDiff.cs b/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/IEventWorkDiff.cs new file mode 100644 index 000000000..61329df74 --- /dev/null +++ b/PKHeX.Core/Editing/Saves/Editors/EventWork/Diff/IEventWorkDiff.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace PKHeX.Core; + +public interface IEventWorkDiff +{ + List SetFlags { get; } + List ClearedFlags { get; } + List WorkChanged { get; } + List WorkDiff { get; } + EventWorkDiffCompatibility Message { get; } + IReadOnlyList Summarize(); +} \ No newline at end of file diff --git a/PKHeX.Core/Editing/Saves/Editors/EventWork/EventWorkDiff.cs b/PKHeX.Core/Editing/Saves/Editors/EventWork/EventWorkDiff.cs deleted file mode 100644 index 89669d4ae..000000000 --- a/PKHeX.Core/Editing/Saves/Editors/EventWork/EventWorkDiff.cs +++ /dev/null @@ -1,267 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using static PKHeX.Core.MessageStrings; - -namespace PKHeX.Core -{ - /// - /// Calculates differences in the Event Blocks between two . - /// - public class EventBlockDiff - { - public string Message { get; protected set; } = string.Empty; - public readonly List SetFlags = new(); - public readonly List ClearedFlags = new(); - public readonly List WorkDiff = new(); - - private const int MAX_SAVEFILE_SIZE = 0x10_0000; // 1 MB - protected EventBlockDiff() { } - - public EventBlockDiff(string f1, string f2) - { - if (!SanityCheckFiles(f1, f2)) - return; - var s1 = SaveUtil.GetVariantSAV(f1); - var s2 = SaveUtil.GetVariantSAV(f2); - if (s1 == null || s2 == null || s1.GetType() != s2.GetType()) - { - Message = MsgSaveDifferentTypes; - return; - } - Diff(s1, s2); - } - - public EventBlockDiff(SaveFile s1, SaveFile s2) => Diff(s1, s2); - - protected bool SanityCheckFiles(string f1, string f2) - { - if (!File.Exists(f1)) - { Message = string.Format(MsgSaveNumberInvalid, 1); return false; } - - if (!File.Exists(f2)) - { Message = string.Format(MsgSaveNumberInvalid, 2); return false; } - - if (new FileInfo(f1).Length > MAX_SAVEFILE_SIZE) - { Message = string.Format(MsgSaveNumberInvalid, 1); return false; } - - if (new FileInfo(f2).Length > MAX_SAVEFILE_SIZE) - { Message = string.Format(MsgSaveNumberInvalid, 2); return false; } - - return true; - } - - protected bool SanityCheckSaveInfo(SaveFile s1, SaveFile s2) - { - if (s1.GetType() != s2.GetType()) - { Message = string.Format(MsgSaveDifferentTypes, $"S1: {s1.GetType().Name}", $"S2: {s2.GetType().Name}"); return false; } - - if (s1.Version != s2.Version) - { Message = string.Format(MsgSaveDifferentVersions, $"S1: {s1.Version}", $"S2: {s2.Version}"); return false; } - - return true; - } - - protected virtual void Diff(SaveFile s1, SaveFile s2) - { - if (!SanityCheckSaveInfo(s1, s2)) - return; - - bool[] oldBits = s1.GetEventFlags(); - bool[] newBits = s2.GetEventFlags(); - var oldConst = s1.GetEventConsts(); - var newConst = s2.GetEventConsts(); - - for (int i = 0; i < newBits.Length; i++) - { - if (oldBits[i] != newBits[i]) - (newBits[i] ? SetFlags : ClearedFlags).Add(i); - } - for (int i = 0; i < newConst.Length; i++) - { - if (oldConst[i] != newConst[i]) - WorkDiff.Add($"{i}: {oldConst[i]}->{newConst[i]}"); - } - } - } - - public sealed class EventWorkDiff7b : EventBlockDiff - { - public readonly List WorkChanged = new(); - private SaveFile? S1; - - public EventWorkDiff7b(string f1, string f2) - { - if (!SanityCheckFiles(f1, f2)) - return; - var s1 = SaveUtil.GetVariantSAV(f1); - var s2 = SaveUtil.GetVariantSAV(f2); - if (s1 == null || s2 == null || s1.GetType() != s2.GetType()) - { - Message = MsgSaveDifferentTypes; - return; - } - Diff(s1, s2); - } - - public EventWorkDiff7b(SaveFile s1, SaveFile s2) => Diff(s1, s2); - - protected override void Diff(SaveFile s1, SaveFile s2) - { - if (!SanityCheckSaveInfo(s1, s2)) - return; - - EventWorkUtil.DiffSavesFlag(((SAV7b)s1).Blocks.EventWork, ((SAV7b)s2).Blocks.EventWork, SetFlags, ClearedFlags); - EventWorkUtil.DiffSavesWork(((SAV7b)s1).Blocks.EventWork, ((SAV7b)s2).Blocks.EventWork, WorkChanged, WorkDiff); - S1 = s1; - } - - public IReadOnlyList Summarize() - { - if (S1 == null) - return Array.Empty(); - var ew = ((SAV7b)S1).Blocks.EventWork; - - var fOn = SetFlags.Select(z => FlagSummary.Get(z, ew).ToString()); - var fOff = ClearedFlags.Select(z => FlagSummary.Get(z, ew).ToString()); - var wt = WorkChanged.Select((z, i) => WorkSummary.Get(z, ew, WorkDiff[i]).ToString()); - - var list = new List { "Flags: ON", "=========" }; - list.AddRange(fOn); - if (SetFlags.Count == 0) - list.Add("None."); - - list.Add(""); - list.Add("Flags: OFF"); - list.Add("=========="); - list.AddRange(fOff); - if (ClearedFlags.Count == 0) - list.Add("None."); - - list.Add(""); - list.Add("Work:"); - list.Add("====="); - if (WorkChanged.Count == 0) - list.Add("None."); - list.AddRange(wt); - - return list; - } - - private readonly record struct FlagSummary(EventVarType Type, int Index, int Raw) - { - public override string ToString() => $"{Raw:0000}\t{false}\t{Index:0000}\t{Type}"; - - public static FlagSummary Get(int rawIndex, EventWork7b ew) - { - var type = ew.GetFlagType(rawIndex, out var subIndex); - return new FlagSummary(type, subIndex, rawIndex); - } - } - - private readonly record struct WorkSummary(EventVarType Type, int Index, int Raw, string Text) - { - public override string ToString() => $"{Raw:000}\t{Text}\t{Index:000}\t{Type}"; - - public static WorkSummary Get(int rawIndex, EventWork7b ew, string text) - { - var type = ew.GetFlagType(rawIndex, out var subIndex); - return new WorkSummary(type, subIndex, rawIndex, text); - } - } - } - - public sealed class EventWorkDiff8b : EventBlockDiff - { - public readonly List WorkChanged = new(); - private SaveFile? S1; - public readonly List SetSystem = new(); - public readonly List ClearedSystem = new(); - - public EventWorkDiff8b(string f1, string f2) - { - if (!SanityCheckFiles(f1, f2)) - return; - var s1 = SaveUtil.GetVariantSAV(f1); - var s2 = SaveUtil.GetVariantSAV(f2); - if (s1 == null || s2 == null || s1.GetType() != s2.GetType()) - { - Message = MsgSaveDifferentTypes; - return; - } - Diff(s1, s2); - } - - public EventWorkDiff8b(SaveFile s1, SaveFile s2) => Diff(s1, s2); - - protected override void Diff(SaveFile s1, SaveFile s2) - { - if (!SanityCheckSaveInfo(s1, s2)) - return; - - EventWorkUtil.DiffSavesFlag(((SAV8BS)s1).Work, ((SAV8BS)s2).Work, SetFlags, ClearedFlags); - EventWorkUtil.DiffSavesSystem(((SAV8BS)s1).Work, ((SAV8BS)s2).Work, SetSystem, ClearedSystem); - EventWorkUtil.DiffSavesWork(((SAV8BS)s1).Work, ((SAV8BS)s2).Work, WorkChanged, WorkDiff); - S1 = s1; - } - - public IReadOnlyList Summarize() - { - if (S1 == null) - return Array.Empty(); - - var fOn = SetFlags.Select(z => new FlagSummary(z).ToString()); - var fOff = ClearedFlags.Select(z => new FlagSummary(z).ToString()); - - var sOn = SetSystem.Select(z => new FlagSummary(z).ToString()); - var sOff = ClearedSystem.Select(z => new FlagSummary(z).ToString()); - - var wt = WorkChanged.Select((z, i) => new WorkSummary(z, WorkDiff[i]).ToString()); - - var list = new List { "Flags: ON", "=========" }; - list.AddRange(fOn); - if (SetFlags.Count == 0) - list.Add("None."); - - list.Add(""); - list.Add("Flags: OFF"); - list.Add("=========="); - list.AddRange(fOff); - if (ClearedFlags.Count == 0) - list.Add("None."); - - list.Add("System: ON"); - list.Add("========="); - list.AddRange(sOn); - if (SetFlags.Count == 0) - list.Add("None."); - - list.Add(""); - list.Add("System: OFF"); - list.Add("=========="); - list.AddRange(sOff); - if (ClearedSystem.Count == 0) - list.Add("None."); - - list.Add(""); - list.Add("Work:"); - list.Add("====="); - if (WorkChanged.Count == 0) - list.Add("None."); - list.AddRange(wt); - - return list; - } - - private readonly record struct FlagSummary(int Raw) - { - public override string ToString() => $"{Raw:0000}\t{false}"; - } - - private readonly record struct WorkSummary(int Raw, string Text) - { - public override string ToString() => $"{Raw:000}\t{Text}"; - } - } -} diff --git a/PKHeX.Core/Editing/Saves/Editors/EventWork/EventWorkUtil.cs b/PKHeX.Core/Editing/Saves/Editors/EventWork/EventWorkUtil.cs index feb34db5a..30a4fa252 100644 --- a/PKHeX.Core/Editing/Saves/Editors/EventWork/EventWorkUtil.cs +++ b/PKHeX.Core/Editing/Saves/Editors/EventWork/EventWorkUtil.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using static PKHeX.Core.MessageStrings; namespace PKHeX.Core { @@ -119,19 +121,31 @@ namespace PKHeX.Core /// Data after the event was triggered /// values that changed /// Summary of the value change - public static void DiffSavesWork(IEventWork before, IEventWork after, List changed, List changes) + public static void DiffSavesWork(IEventWork before, IEventWork after, List changed, List changes) where T : unmanaged, IEquatable { int max = before.CountWork; for (int i = 0; i < max; i++) { var b = before.GetWork(i); var a = after.GetWork(i); - if (b is null || b.Equals(a)) + if (b.Equals(a)) continue; changed.Add(i); changes.Add($"{b} => {a}"); } } + + public static bool SanityCheckSaveInfo(T s1, T s2, [NotNullWhen(false)] out string? Message) where T : SaveFile + { + if (s1.GetType() != s2.GetType()) + { Message = string.Format(MsgSaveDifferentTypes, $"S1: {s1.GetType().Name}", $"S2: {s2.GetType().Name}"); return false; } + + if (s1.Version != s2.Version) + { Message = string.Format(MsgSaveDifferentVersions, $"S1: {s1.Version}", $"S2: {s2.Version}"); return false; } + + Message = null; + return true; + } } } diff --git a/PKHeX.Core/Saves/IEventFlagArray.cs b/PKHeX.Core/Saves/IEventFlagArray.cs new file mode 100644 index 000000000..bfc97b378 --- /dev/null +++ b/PKHeX.Core/Saves/IEventFlagArray.cs @@ -0,0 +1,61 @@ +using System; + +namespace PKHeX.Core; + +public interface IEventFlagArray +{ + int EventFlagCount { get; } + bool GetEventFlag(int flagNumber); + void SetEventFlag(int flagNumber, bool value); +} + +public interface IEventFlag37 : IEventFlagArray, IEventWorkArray { } + +public static class EventFlagArrayExtensions +{ + /// All Event Flag values for the savegame + public static bool[] GetEventFlags(this IEventFlagArray source) + { + var result = new bool[source.EventFlagCount]; + for (int i = 0; i < result.Length; i++) + result[i] = source.GetEventFlag(i); + return result; + } + + /// All Event Flag values for the savegame + public static void SetEventFlags(this IEventFlagArray source, ReadOnlySpan value) + { + if (value.Length != source.EventFlagCount) + return; + for (int i = 0; i < value.Length; i++) + source.SetEventFlag(i, value[i]); + } +} + +public interface IEventWorkArray where T : unmanaged +{ + public int EventWorkCount { get; } + public T GetWork(int index); + public void SetWork(int index, T value = default); +} + +public static class EventWorkArrayExtensions +{ + /// All Event Constant values for the savegame + public static T[] GetAllEventWork(this IEventWorkArray source) where T : unmanaged + { + var result = new T[source.EventWorkCount]; + for (int i = 0; i < result.Length; i++) + result[i] = source.GetWork(i); + return result; + } + + /// All Event Constant values for the savegame + public static void SetAllEventWork(this IEventWorkArray source, ReadOnlySpan value) where T : unmanaged + { + if (value.Length != source.EventWorkCount) + return; + for (int i = 0; i < value.Length; i++) + source.SetWork(i, value[i]); + } +} diff --git a/PKHeX.Core/Saves/SAV1.cs b/PKHeX.Core/Saves/SAV1.cs index a74afdd1b..57577ec5b 100644 --- a/PKHeX.Core/Saves/SAV1.cs +++ b/PKHeX.Core/Saves/SAV1.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core /// /// Generation 1 object. /// - public sealed class SAV1 : SaveFile, ILangDeviantSave + public sealed class SAV1 : SaveFile, ILangDeviantSave, IEventFlagArray { protected internal override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}"; public override string Extension => ".sav"; @@ -115,8 +115,6 @@ namespace PKHeX.Core daycareList.Write().CopyTo(Data, GetPartyOffset(7)); DaycareOffset = GetPartyOffset(7); - EventFlag = Offsets.EventFlag; - // Enable Pokedex editing PokeDex = 0; } @@ -125,8 +123,20 @@ namespace PKHeX.Core private readonly SAV1Offsets Offsets; // Event Flags - protected override int EventFlagMax => 0xA00; // 320 * 8 - protected override int EventConstMax => 0; + public int EventFlagCount => 0xA00; // 320 * 8 + public bool GetEventFlag(int flagNumber) + { + if ((uint)flagNumber >= EventFlagCount) + throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to get ({flagNumber}) is greater than max ({EventFlagCount})."); + return GetFlag(Offsets.EventFlag + (flagNumber >> 3), flagNumber & 7); + } + + public void SetEventFlag(int flagNumber, bool value) + { + if ((uint)flagNumber >= EventFlagCount) + throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to set ({flagNumber}) is greater than max ({EventFlagCount})."); + SetFlag(Offsets.EventFlag + (flagNumber >> 3), flagNumber & 7, value); + } protected override byte[] GetFinalData() { diff --git a/PKHeX.Core/Saves/SAV2.cs b/PKHeX.Core/Saves/SAV2.cs index 055c48fcb..164b08952 100644 --- a/PKHeX.Core/Saves/SAV2.cs +++ b/PKHeX.Core/Saves/SAV2.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core /// /// Generation 2 object. /// - public sealed class SAV2 : SaveFile, ILangDeviantSave + public sealed class SAV2 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWorkArray { protected internal override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}"; public override string Extension => ".sav"; @@ -140,10 +140,11 @@ namespace PKHeX.Core // Enable Pokedex editing PokeDex = 0; - EventFlag = Offsets.EventFlag; - EventConst = Offsets.EventConst; } + private int EventFlag => Offsets.EventFlag; + private int EventWork => Offsets.EventWork; + private PK2 ReadPKMFromOffset(int offset) { var stringLength = StringLength; @@ -272,8 +273,8 @@ namespace PKHeX.Core public override bool IsPKMPresent(ReadOnlySpan data) => PKX.IsPKMPresentGB(data); - protected override int EventConstMax => 0x100; - protected override int EventFlagMax => 2000; + public int EventWorkCount => 0x100; + public int EventFlagCount => 2000; public override int BoxCount => Japanese ? 9 : 14; public override int MaxEV => 65535; @@ -679,25 +680,20 @@ namespace PKHeX.Core SetFlag(region + ofs, bit & 7, value); } - /// All Event Constant values for the save file - /// These are all bytes - public override ushort[] GetEventConsts() + public byte GetWork(int index) => Data[EventWork + index]; + public void SetWork(int index, byte value) => Data[EventWork + index] = value; + public bool GetEventFlag(int flagNumber) { - ushort[] Constants = new ushort[EventConstMax]; - for (int i = 0; i < Constants.Length; i++) - Constants[i] = Data[EventConst + i]; - return Constants; + if ((uint)flagNumber >= EventFlagCount) + throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to get ({flagNumber}) is greater than max ({EventFlagCount})."); + return GetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7); } - /// All Event Constant values for the save file - /// These are all bytes - public override void SetEventConsts(ReadOnlySpan value) + public void SetEventFlag(int flagNumber, bool value) { - if (value.Length != EventConstMax) - return; - - for (int i = 0; i < value.Length; i++) - Data[EventConst + i] = Math.Min(byte.MaxValue, (byte)value[i]); + if ((uint)flagNumber >= EventFlagCount) + throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to set ({flagNumber}) is greater than max ({EventFlagCount})."); + SetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7, value); } // Misc diff --git a/PKHeX.Core/Saves/SAV3.cs b/PKHeX.Core/Saves/SAV3.cs index fc5886016..023521879 100644 --- a/PKHeX.Core/Saves/SAV3.cs +++ b/PKHeX.Core/Saves/SAV3.cs @@ -8,7 +8,7 @@ namespace PKHeX.Core /// /// Generation 3 object. /// - public abstract class SAV3 : SaveFile, ILangDeviantSave + public abstract class SAV3 : SaveFile, ILangDeviantSave, IEventFlag37 { protected internal sealed override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}"; public sealed override string Extension => ".sav"; @@ -148,6 +148,11 @@ namespace PKHeX.Core public sealed override int MaxBallID => Legal.MaxBallID_3; public sealed override int MaxGameID => Legal.MaxGameID_3; + public abstract int EventFlagCount { get; } + public abstract int EventWorkCount { get; } + protected abstract int EventFlag { get; } + protected abstract int EventWork { get; } + /// /// Force loads a new object to the requested . /// @@ -324,47 +329,28 @@ namespace PKHeX.Core set => Small[0x12] = (byte)value; } - public sealed override bool GetEventFlag(int flagNumber) + #region Event Flag/Event Work + public bool GetEventFlag(int flagNumber) { - if (flagNumber >= EventFlagMax) - throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to get ({flagNumber}) is greater than max ({EventFlagMax})."); - - var start = EventFlag; - return GetFlag(start + (flagNumber >> 3), flagNumber & 7); + if ((uint)flagNumber >= EventFlagCount) + throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to get ({flagNumber}) is greater than max ({EventFlagCount})."); + return GetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7); } - public sealed override void SetEventFlag(int flagNumber, bool value) + public void SetEventFlag(int flagNumber, bool value) { - if (flagNumber >= EventFlagMax) - throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to set ({flagNumber}) is greater than max ({EventFlagMax})."); - - var start = EventFlag; - SetFlag(start + (flagNumber >> 3), flagNumber & 7, value); + if ((uint)flagNumber >= EventFlagCount) + throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to set ({flagNumber}) is greater than max ({EventFlagCount})."); + SetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7, value); } + public ushort GetWork(int index) => ReadUInt16LittleEndian(Large.AsSpan(EventWork + (index * 2))); + public void SetWork(int index, ushort value) => WriteUInt16LittleEndian(Large.AsSpan(EventWork)[(index * 2)..], value); + #endregion + public sealed override bool GetFlag(int offset, int bitIndex) => FlagUtil.GetFlag(Large, offset, bitIndex); public sealed override void SetFlag(int offset, int bitIndex, bool value) => FlagUtil.SetFlag(Large, offset, bitIndex, value); - public ushort GetEventConst(int index) => ReadUInt16LittleEndian(Large.AsSpan(EventConst + (index * 2))); - public void SetEventConst(int index, ushort value) => WriteUInt16LittleEndian(Large.AsSpan(EventConst + (index * 2)), value); - - public sealed override ushort[] GetEventConsts() - { - ushort[] Constants = new ushort[EventConstMax]; - for (int i = 0; i < Constants.Length; i++) - Constants[i] = GetEventConst(i); - return Constants; - } - - public sealed override void SetEventConsts(ReadOnlySpan value) - { - if (value.Length != EventConstMax) - return; - - for (int i = 0; i < value.Length; i++) - SetEventConst(i, value[i]); - } - protected abstract int BadgeFlagStart { get; } public abstract uint Coin { get; set; } diff --git a/PKHeX.Core/Saves/SAV3E.cs b/PKHeX.Core/Saves/SAV3E.cs index 42d1efcdb..22c31c288 100644 --- a/PKHeX.Core/Saves/SAV3E.cs +++ b/PKHeX.Core/Saves/SAV3E.cs @@ -15,8 +15,8 @@ namespace PKHeX.Core public override GameVersion Version { get => GameVersion.E; protected set { } } public override PersonalTable Personal => PersonalTable.E; - protected override int EventFlagMax => 8 * 300; - protected override int EventConstMax => 0x100; + public override int EventFlagCount => 8 * 300; + public override int EventWorkCount => 0x100; protected override int DaycareSlotSize => SIZE_STORED + 0x3C; // 0x38 mail + 4 exp public override int DaycareSeedSize => 8; // 32bit protected override int EggEventFlag => 0x86; @@ -25,14 +25,15 @@ namespace PKHeX.Core public SAV3E(byte[] data) : base(data) => Initialize(); public SAV3E(bool japanese = false) : base(japanese) => Initialize(); + protected override int EventFlag => 0x1270; + protected override int EventWork => 0x139C; + private void Initialize() { // small PokeDex = 0x18; // large - EventFlag = 0x1270; - EventConst = 0x139C; DaycareOffset = 0x3030; // storage @@ -48,7 +49,7 @@ namespace PKHeX.Core PokedexMode = value ? (byte)1 : (byte)0; // mode PokedexNationalMagicRSE = value ? PokedexNationalUnlockRSE : (byte)0; // magic SetEventFlag(0x896, value); - SetEventConst(0x46, PokedexNationalUnlockWorkRSE); + SetWork(0x46, PokedexNationalUnlockWorkRSE); } } diff --git a/PKHeX.Core/Saves/SAV3FRLG.cs b/PKHeX.Core/Saves/SAV3FRLG.cs index 7ccc116eb..0234be2cd 100644 --- a/PKHeX.Core/Saves/SAV3FRLG.cs +++ b/PKHeX.Core/Saves/SAV3FRLG.cs @@ -15,8 +15,8 @@ namespace PKHeX.Core private PersonalTable _personal = PersonalTable.FR; public override PersonalTable Personal => _personal; - protected override int EventFlagMax => 8 * 288; - protected override int EventConstMax => 0x100; + public override int EventFlagCount => 8 * 288; + public override int EventWorkCount => 0x100; protected override int DaycareSlotSize => SIZE_STORED + 0x3C; // 0x38 mail + 4 exp public override int DaycareSeedSize => 4; // 16bit protected override int EggEventFlag => 0x266; @@ -34,14 +34,15 @@ namespace PKHeX.Core WriteUInt32LittleEndian(Small.AsSpan(0xAC), 1); } + protected override int EventFlag => 0xEE0; + protected override int EventWork => 0x1000; + private void Initialize() { // small PokeDex = 0x18; // large - EventFlag = 0xEE0; - EventConst = 0x1000; DaycareOffset = 0x2F80; // storage @@ -65,7 +66,7 @@ namespace PKHeX.Core { PokedexNationalMagicFRLG = value ? PokedexNationalUnlockFRLG : (byte)0; // magic SetEventFlag(0x840, value); - SetEventConst(0x4E, PokedexNationalUnlockWorkFRLG); + SetWork(0x4E, PokedexNationalUnlockWorkFRLG); } } diff --git a/PKHeX.Core/Saves/SAV3RS.cs b/PKHeX.Core/Saves/SAV3RS.cs index 4f268a4dd..42e9d1093 100644 --- a/PKHeX.Core/Saves/SAV3RS.cs +++ b/PKHeX.Core/Saves/SAV3RS.cs @@ -15,8 +15,8 @@ namespace PKHeX.Core public override GameVersion Version { get => GameVersion.RS; protected set { } } public override PersonalTable Personal => PersonalTable.RS; - protected override int EventFlagMax => 8 * 288; - protected override int EventConstMax => 0x100; + public override int EventFlagCount => 8 * 288; + public override int EventWorkCount => 0x100; protected override int DaycareSlotSize => SIZE_STORED; public override int DaycareSeedSize => 4; // 16bit protected override int EggEventFlag => 0x86; @@ -25,14 +25,15 @@ namespace PKHeX.Core public SAV3RS(byte[] data) : base(data) => Initialize(); public SAV3RS(bool japanese = false) : base(japanese) => Initialize(); + protected override int EventFlag => 0x1220; + protected override int EventWork => 0x1340; + private void Initialize() { // small PokeDex = 0x18; // large - EventFlag = 0x1220; - EventConst = 0x1340; DaycareOffset = 0x2F9C; // storage @@ -48,7 +49,7 @@ namespace PKHeX.Core PokedexMode = value ? (byte)1 : (byte)0; // mode PokedexNationalMagicRSE = value ? PokedexNationalUnlockRSE : (byte)0; // magic SetEventFlag(0x836, value); - SetEventConst(0x46, PokedexNationalUnlockWorkRSE); + SetWork(0x46, PokedexNationalUnlockWorkRSE); } } diff --git a/PKHeX.Core/Saves/SAV4.cs b/PKHeX.Core/Saves/SAV4.cs index 0fbe57605..dcbf9dd6b 100644 --- a/PKHeX.Core/Saves/SAV4.cs +++ b/PKHeX.Core/Saves/SAV4.cs @@ -10,7 +10,7 @@ namespace PKHeX.Core /// /// Storage data is stored in one contiguous block, and the remaining data is stored in another block. /// - public abstract class SAV4 : SaveFile + public abstract class SAV4 : SaveFile, IEventFlag37 { protected internal override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}"; public sealed override string Extension => ".sav"; @@ -29,6 +29,8 @@ namespace PKHeX.Core protected abstract int StorageStart { get; } public abstract Zukan4 Dex { get; } + protected abstract int EventFlag { get; } + protected abstract int EventWork { get; } public sealed override bool GetFlag(int offset, int bitIndex) => FlagUtil.GetFlag(General, offset, bitIndex); public sealed override void SetFlag(int offset, int bitIndex, bool value) => FlagUtil.SetFlag(General, offset, bitIndex, value); @@ -77,8 +79,8 @@ namespace PKHeX.Core public sealed override int BoxCount => 18; public sealed override int MaxEV => 255; public sealed override int Generation => 4; - protected sealed override int EventFlagMax => 0xB60; // 2912 - protected sealed override int EventConstMax => (EventFlag - EventConst) >> 1; + public int EventFlagCount => 0xB60; // 2912 + public int EventWorkCount => (EventFlag - EventWork) >> 1; protected sealed override int GiftCountMax => 11; public sealed override int OTLength => 7; public sealed override int NickLength => 10; @@ -481,31 +483,25 @@ namespace PKHeX.Core return StringConverter4.SetString(destBuffer, value, maxLength, option); } - /// All Event Constant values for the savegame - public sealed override ushort[] GetEventConsts() + #region Event Flag/Event Work + public bool GetEventFlag(int flagNumber) { - if (EventConstMax <= 0) - return Array.Empty(); - - ushort[] Constants = new ushort[EventConstMax]; - for (int i = 0; i < Constants.Length; i++) - Constants[i] = ReadUInt16LittleEndian(General.AsSpan(EventConst + (i * 2))); - return Constants; + if ((uint)flagNumber >= EventFlagCount) + throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to get ({flagNumber}) is greater than max ({EventFlagCount})."); + return GetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7); } - /// All Event Constant values for the savegame - public sealed override void SetEventConsts(ReadOnlySpan value) + public void SetEventFlag(int flagNumber, bool value) { - if (EventConstMax <= 0) - return; - if (value.Length != EventConstMax) - return; - - var span = General.AsSpan(EventConst); - for (int i = 0; i < value.Length; i++) - WriteUInt16LittleEndian(span[(i * 2)..], value[i]); + if ((uint)flagNumber >= EventFlagCount) + throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to set ({flagNumber}) is greater than max ({EventFlagCount})."); + SetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7, value); } + public ushort GetWork(int index) => ReadUInt16LittleEndian(General.AsSpan(EventWork + (index * 2))); + public void SetWork(int index, ushort value) => WriteUInt16LittleEndian(General.AsSpan(EventWork)[(index * 2)..], value); + #endregion + // Seals private const byte SealMaxCount = 99; diff --git a/PKHeX.Core/Saves/SAV4DP.cs b/PKHeX.Core/Saves/SAV4DP.cs index c8a46cf30..2ed125b76 100644 --- a/PKHeX.Core/Saves/SAV4DP.cs +++ b/PKHeX.Core/Saves/SAV4DP.cs @@ -38,6 +38,9 @@ namespace PKHeX.Core GetSAVOffsets(); } + protected override int EventWork => 0xD9C; + protected override int EventFlag => 0xFDC; + private void GetSAVOffsets() { AdventureInfo = 0; @@ -47,8 +50,6 @@ namespace PKHeX.Core WondercardFlags = 0xA6D0; WondercardData = 0xA7fC; - EventConst = 0xD9C; - EventFlag = 0xFDC; DaycareOffset = 0x141C; OFS_HONEY = 0x72E4; diff --git a/PKHeX.Core/Saves/SAV4HGSS.cs b/PKHeX.Core/Saves/SAV4HGSS.cs index 496144cf6..cd3c77bb6 100644 --- a/PKHeX.Core/Saves/SAV4HGSS.cs +++ b/PKHeX.Core/Saves/SAV4HGSS.cs @@ -40,6 +40,9 @@ namespace PKHeX.Core GetSAVOffsets(); } + protected override int EventWork => 0xDE4; + protected override int EventFlag => 0x10C4; + private void GetSAVOffsets() { AdventureInfo = 0; @@ -49,8 +52,6 @@ namespace PKHeX.Core WondercardFlags = 0x9D3C; WondercardData = 0x9E3C; - EventConst = 0xDE4; - EventFlag = 0x10C4; DaycareOffset = 0x15FC; Seal = 0x4E20; diff --git a/PKHeX.Core/Saves/SAV4Pt.cs b/PKHeX.Core/Saves/SAV4Pt.cs index 28e9aa6da..7842cc18a 100644 --- a/PKHeX.Core/Saves/SAV4Pt.cs +++ b/PKHeX.Core/Saves/SAV4Pt.cs @@ -37,6 +37,9 @@ namespace PKHeX.Core GetSAVOffsets(); } + protected override int EventWork => 0xDAC; + protected override int EventFlag => 0xFEC; + private void GetSAVOffsets() { AdventureInfo = 0; @@ -46,8 +49,6 @@ namespace PKHeX.Core WondercardFlags = 0xB4C0; WondercardData = 0xB5C0; - EventConst = 0xDAC; - EventFlag = 0xFEC; DaycareOffset = 0x1654; OFS_HONEY = 0x7F38; diff --git a/PKHeX.Core/Saves/SAV5.cs b/PKHeX.Core/Saves/SAV5.cs index 8006dc243..cd866eb0c 100644 --- a/PKHeX.Core/Saves/SAV5.cs +++ b/PKHeX.Core/Saves/SAV5.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core /// /// Generation 5 object. /// - public abstract class SAV5 : SaveFile, ISaveBlock5BW + public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37 { protected override PKM GetPKM(byte[] data) => new PK5(data); protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray45(data); @@ -27,6 +27,10 @@ namespace PKHeX.Core public override int OTLength => 7; public override int NickLength => 10; protected override int GiftCountMax => 12; + public abstract int EventFlagCount { get; } + public abstract int EventWorkCount { get; } + protected abstract int EventFlag { get; } + protected abstract int EventWork { get; } public override int MaxMoveID => Legal.MaxMoveID_5; public override int MaxSpeciesID => Legal.MaxSpeciesID_5; @@ -142,6 +146,23 @@ namespace PKHeX.Core return StringConverter5.SetString(destBuffer, value, maxLength, option); } + public bool GetEventFlag(int flagNumber) + { + if ((uint)flagNumber >= EventFlagCount) + throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to get ({flagNumber}) is greater than max ({EventFlagCount})."); + return GetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7); + } + + public void SetEventFlag(int flagNumber, bool value) + { + if ((uint)flagNumber >= EventFlagCount) + throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to set ({flagNumber}) is greater than max ({EventFlagCount})."); + SetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7, value); + } + + public ushort GetWork(int index) => ReadUInt16LittleEndian(Data.AsSpan(EventWork + (index * 2))); + public void SetWork(int index, ushort value) => WriteUInt16LittleEndian(Data.AsSpan(EventWork)[(index * 2)..], value); + // DLC private int CGearSkinInfoOffset => CGearInfoOffset + (this is SAV5B2W2 ? 0x10 : 0) + 0x24; diff --git a/PKHeX.Core/Saves/SAV5B2W2.cs b/PKHeX.Core/Saves/SAV5B2W2.cs index e71fdd8c3..c429186f0 100644 --- a/PKHeX.Core/Saves/SAV5B2W2.cs +++ b/PKHeX.Core/Saves/SAV5B2W2.cs @@ -24,15 +24,15 @@ namespace PKHeX.Core public override PersonalTable Personal => PersonalTable.B2W2; public SaveBlockAccessor5B2W2 Blocks { get; } protected override SaveFile CloneInternal() => new SAV5B2W2((byte[]) Data.Clone()); - protected override int EventConstMax => 0x1AF; // this doesn't seem right? - protected override int EventFlagMax => 0xBF8; + public override int EventWorkCount => 0x1AF; // this doesn't seem right? + public override int EventFlagCount => 0xBF8; + protected override int EventWork => 0x1FF00; + protected override int EventFlag => EventWork + 0x35E; public override int MaxItemID => Legal.MaxItemID_5_B2W2; private void Initialize() { BattleBoxOffset = 0x20900; - EventConst = 0x1FF00; - EventFlag = EventConst + 0x35E; CGearInfoOffset = 0x1C000; CGearDataOffset = 0x52800; EntreeForestOffset = 0x22A00; diff --git a/PKHeX.Core/Saves/SAV5BW.cs b/PKHeX.Core/Saves/SAV5BW.cs index 1a5e21f27..9d13a7cac 100644 --- a/PKHeX.Core/Saves/SAV5BW.cs +++ b/PKHeX.Core/Saves/SAV5BW.cs @@ -23,15 +23,15 @@ namespace PKHeX.Core public override PersonalTable Personal => PersonalTable.BW; public SaveBlockAccessor5BW Blocks { get; } protected override SaveFile CloneInternal() => new SAV5BW((byte[])Data.Clone()); - protected override int EventConstMax => 0x13E; - protected override int EventFlagMax => 0xB60; + public override int EventWorkCount => 0x13E; + public override int EventFlagCount => 0xB60; + protected override int EventWork => 0x20100; + protected override int EventFlag => EventWork + 0x27C; public override int MaxItemID => Legal.MaxItemID_5_BW; private void Initialize() { BattleBoxOffset = 0x20A00; - EventConst = 0x20100; - EventFlag = EventConst + 0x27C; CGearInfoOffset = 0x1C000; CGearDataOffset = 0x52000; EntreeForestOffset = 0x22C00; diff --git a/PKHeX.Core/Saves/SAV6.cs b/PKHeX.Core/Saves/SAV6.cs index b10510189..b8c949266 100644 --- a/PKHeX.Core/Saves/SAV6.cs +++ b/PKHeX.Core/Saves/SAV6.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { /// /// Generation 6 object. /// - public abstract class SAV6 : SAV_BEEF, ITrainerStatRecord, ISaveBlock6Core, IRegionOrigin, IGameSync + public abstract class SAV6 : SAV_BEEF, ITrainerStatRecord, ISaveBlock6Core, IRegionOrigin, IGameSync, IEventFlag37 { // Save Data Attributes protected internal override string ShortSummary => $"{OT} ({Version}) - {Played.LastSavedTime}"; @@ -26,8 +27,10 @@ namespace PKHeX.Core public override int Generation => 6; protected override int GiftCountMax => 24; protected override int GiftFlagMax => 0x100 * 8; - protected override int EventFlagMax => 8 * 0x1A0; - protected override int EventConstMax => (EventFlag - EventConst) / sizeof(ushort); + public int EventFlagCount => 8 * 0x1A0; + public int EventWorkCount => (EventFlag - EventWork) / sizeof(ushort); + protected abstract int EventFlag { get; } + protected abstract int EventWork { get; } public override int OTLength => 12; public override int NickLength => 12; @@ -190,5 +193,22 @@ namespace PKHeX.Core public abstract PlayTime6 Played { get; } public abstract MyStatus6 Status { get; } public abstract RecordBlock6 Records { get; } + + public bool GetEventFlag(int flagNumber) + { + if ((uint)flagNumber >= EventFlagCount) + throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to get ({flagNumber}) is greater than max ({EventFlagCount})."); + return GetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7); + } + + public void SetEventFlag(int flagNumber, bool value) + { + if ((uint)flagNumber >= EventFlagCount) + throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to set ({flagNumber}) is greater than max ({EventFlagCount})."); + SetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7, value); + } + + public ushort GetWork(int index) => ReadUInt16LittleEndian(Data.AsSpan(EventWork + (index * 2))); + public void SetWork(int index, ushort value) => WriteUInt16LittleEndian(Data.AsSpan(EventWork)[(index * 2)..], value); } } diff --git a/PKHeX.Core/Saves/SAV6AO.cs b/PKHeX.Core/Saves/SAV6AO.cs index 6786b4c0f..63ae70d4c 100644 --- a/PKHeX.Core/Saves/SAV6AO.cs +++ b/PKHeX.Core/Saves/SAV6AO.cs @@ -31,13 +31,15 @@ namespace PKHeX.Core public override int MaxItemID => Legal.MaxItemID_6_AO; public override int MaxAbilityID => Legal.MaxAbilityID_6_AO; + protected override int EventWork => 0x14A00; + protected override int EventFlag => EventWork + 0x2F0; + private void Initialize() { PCLayout = 0x04400; BattleBoxOffset = 0x04A00; PSS = 0x05000; Party = 0x14200; - EventConst = 0x14A00; PokeDex = 0x15000; HoF = 0x19E00; DaycareOffset = 0x1BC00; @@ -46,7 +48,6 @@ namespace PKHeX.Core Box = 0x33000; JPEG = 0x67C00; - EventFlag = EventConst + 0x2F0; WondercardData = WondercardFlags + 0x100; } diff --git a/PKHeX.Core/Saves/SAV6AODemo.cs b/PKHeX.Core/Saves/SAV6AODemo.cs index 1b3067781..97ad4a061 100644 --- a/PKHeX.Core/Saves/SAV6AODemo.cs +++ b/PKHeX.Core/Saves/SAV6AODemo.cs @@ -26,14 +26,13 @@ namespace PKHeX.Core public override int MaxMoveID => Legal.MaxMoveID_6_AO; public override int MaxItemID => Legal.MaxItemID_6_AO; public override int MaxAbilityID => Legal.MaxAbilityID_6_AO; + protected override int EventWork => 0x04600; + protected override int EventFlag => EventWork + 0x2F0; public SaveBlockAccessor6AODemo Blocks { get; } private void Initialize() { Party = 0x03E00; - EventConst = 0x04600; - - EventFlag = EventConst + 0x2F0; } public override GameVersion Version => Game switch diff --git a/PKHeX.Core/Saves/SAV6XY.cs b/PKHeX.Core/Saves/SAV6XY.cs index ef72efd35..7f23ddbe2 100644 --- a/PKHeX.Core/Saves/SAV6XY.cs +++ b/PKHeX.Core/Saves/SAV6XY.cs @@ -31,6 +31,9 @@ namespace PKHeX.Core public override int MaxItemID => Legal.MaxItemID_6_XY; public override int MaxAbilityID => Legal.MaxAbilityID_6_XY; + protected override int EventWork => 0x14A00; + protected override int EventFlag => EventWork + 0x2F0; + private void Initialize() { // Enable Features @@ -38,7 +41,6 @@ namespace PKHeX.Core PCLayout = 0x4400; BattleBoxOffset = 0x04A00; PSS = 0x05000; - EventConst = 0x14A00; PokeDex = 0x15000; HoF = 0x19400; DaycareOffset = 0x1B200; @@ -47,7 +49,6 @@ namespace PKHeX.Core Box = 0x22600; JPEG = 0x57200; - EventFlag = EventConst + 0x2F0; WondercardData = WondercardFlags + 0x100; // Extra Viewable Slots diff --git a/PKHeX.Core/Saves/SAV7.cs b/PKHeX.Core/Saves/SAV7.cs index 2e935824e..31343d49e 100644 --- a/PKHeX.Core/Saves/SAV7.cs +++ b/PKHeX.Core/Saves/SAV7.cs @@ -1,13 +1,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { /// /// Generation 7 object. /// - public abstract class SAV7 : SAV_BEEF, ITrainerStatRecord, ISaveBlock7Main, IRegionOrigin, IGameSync + public abstract class SAV7 : SAV_BEEF, ITrainerStatRecord, ISaveBlock7Main, IRegionOrigin, IGameSync, IEventFlag37 { // Save Data Attributes protected internal override string ShortSummary => $"{OT} ({Version}) - {Played.LastSavedTime}"; @@ -74,7 +75,10 @@ namespace PKHeX.Core public override int Generation => 7; protected override int GiftCountMax => 48; protected override int GiftFlagMax => 0x100 * 8; - protected override int EventConstMax => 1000; + public abstract int EventFlagCount { get; } + public int EventWorkCount => 1000; + private int EventWork => AllBlocks[05].Offset; + private int EventFlag => EventWork + (EventWorkCount * 2); // After Event Const (u16)*n public override int OTLength => 12; public override int NickLength => 12; @@ -253,5 +257,21 @@ namespace PKHeX.Core protected override bool[] MysteryGiftReceivedFlags { get => MysteryGift.MysteryGiftReceivedFlags; set => MysteryGift.MysteryGiftReceivedFlags = value; } protected override DataMysteryGift[] MysteryGiftCards { get => MysteryGift.MysteryGiftCards; set => MysteryGift.MysteryGiftCards = value; } + public bool GetEventFlag(int flagNumber) + { + if ((uint)flagNumber >= EventFlagCount) + throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to get ({flagNumber}) is greater than max ({EventFlagCount})."); + return GetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7); + } + + public void SetEventFlag(int flagNumber, bool value) + { + if ((uint)flagNumber >= EventFlagCount) + throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to set ({flagNumber}) is greater than max ({EventFlagCount})."); + SetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7, value); + } + + public ushort GetWork(int index) => ReadUInt16LittleEndian(Data.AsSpan(EventWork + (index * 2))); + public void SetWork(int index, ushort value) => WriteUInt16LittleEndian(Data.AsSpan(EventWork)[(index * 2)..], value); } } diff --git a/PKHeX.Core/Saves/SAV7SM.cs b/PKHeX.Core/Saves/SAV7SM.cs index a0c61c632..b68f69691 100644 --- a/PKHeX.Core/Saves/SAV7SM.cs +++ b/PKHeX.Core/Saves/SAV7SM.cs @@ -23,9 +23,7 @@ namespace PKHeX.Core private void Initialize() { Party = Blocks.BlockInfo[04].Offset; - EventConst = Blocks.BlockInfo[05].Offset; PokeDex = Blocks.BlockInfo[06].Offset; - EventFlag = EventConst + (EventConstMax * 2); // After Event Const (u16)*n TeamSlots = Blocks.BoxLayout.TeamSlots; Box = Blocks.BlockInfo[14].Offset; @@ -64,7 +62,7 @@ namespace PKHeX.Core public override HallOfFame7 Fame => Blocks.Fame; #endregion - protected override int EventFlagMax => 4000; + public override int EventFlagCount => 4000; public override int MaxMoveID => Legal.MaxMoveID_7; public override int MaxSpeciesID => Legal.MaxSpeciesID_7; public override int MaxItemID => Legal.MaxItemID_7; diff --git a/PKHeX.Core/Saves/SAV7USUM.cs b/PKHeX.Core/Saves/SAV7USUM.cs index ea321fad9..7aaf43c6d 100644 --- a/PKHeX.Core/Saves/SAV7USUM.cs +++ b/PKHeX.Core/Saves/SAV7USUM.cs @@ -20,9 +20,7 @@ namespace PKHeX.Core private void Initialize() { Party = Blocks.BlockInfo[04].Offset; - EventConst = Blocks.BlockInfo[05].Offset; PokeDex = Blocks.BlockInfo[06].Offset; - EventFlag = EventConst + (EventConstMax * 2); // After Event Const (u16)*n TeamSlots = Blocks.BoxLayout.TeamSlots; Box = Blocks.BlockInfo[14].Offset; @@ -35,7 +33,7 @@ namespace PKHeX.Core public override PersonalTable Personal => PersonalTable.USUM; public override IReadOnlyList HeldItems => Legal.HeldItems_USUM; protected override SaveFile CloneInternal() => new SAV7USUM((byte[])Data.Clone()); - protected override int EventFlagMax => 4960; + public override int EventFlagCount => 4960; public override int MaxMoveID => Legal.MaxMoveID_7_USUM; public override int MaxSpeciesID => Legal.MaxSpeciesID_7_USUM; public override int MaxItemID => Legal.MaxItemID_7_USUM; diff --git a/PKHeX.Core/Saves/SAV7b.cs b/PKHeX.Core/Saves/SAV7b.cs index 44bba53e6..71c97b8a3 100644 --- a/PKHeX.Core/Saves/SAV7b.cs +++ b/PKHeX.Core/Saves/SAV7b.cs @@ -6,7 +6,7 @@ namespace PKHeX.Core /// /// Generation 7 object for games. /// - public sealed class SAV7b : SAV_BEEF, ISaveBlock7b, IGameSync + public sealed class SAV7b : SAV_BEEF, ISaveBlock7b, IGameSync, IEventFlagArray { protected internal override string ShortSummary => $"{OT} ({Version}) - {Blocks.Played.LastSavedTime}"; public override string Extension => ".bin"; @@ -44,7 +44,6 @@ namespace PKHeX.Core { Box = Blocks.GetBlockOffset(BelugaBlockIndex.PokeListPokemon); Party = Blocks.GetBlockOffset(BelugaBlockIndex.PokeListPokemon); - EventFlag = Blocks.GetBlockOffset(BelugaBlockIndex.EventWork); PokeDex = Blocks.GetBlockOffset(BelugaBlockIndex.Zukan); WondercardData = Blocks.GiftRecords.Offset; @@ -79,11 +78,9 @@ namespace PKHeX.Core public override int NickLength => 12; protected override int GiftCountMax => 48; protected override int GiftFlagMax => 0x100 * 8; - protected override int EventFlagMax => 4160; // 0xDC0 (true max may be up to 0x7F less. 23A8 starts u64 hashvals) - protected override int EventConstMax => 1000; + public int EventFlagCount => 4160; // 0xDC0 (true max may be up to 0x7F less. 23A8 starts u64 hashvals) public override bool HasParty => false; // handled via team slots - public override bool HasEvents => true; // advanced! // BoxSlotCount => 1000 -- why couldn't this be a multiple of 30... public override int BoxSlotCount => 25; @@ -160,7 +157,7 @@ namespace PKHeX.Core /// /// Event Flag to check /// Flag is Set (true) or not Set (false) - public override bool GetEventFlag(int flagNumber) => Blocks.EventWork.GetFlag(flagNumber); + public bool GetEventFlag(int flagNumber) => Blocks.EventWork.GetFlag(flagNumber); /// /// Sets the status of a desired Event Flag @@ -168,7 +165,7 @@ namespace PKHeX.Core /// Event Flag to check /// Event Flag status to set /// Flag is Set (true) or not Set (false) - public override void SetEventFlag(int flagNumber, bool value) => Blocks.EventWork.SetFlag(flagNumber, value); + public void SetEventFlag(int flagNumber, bool value) => Blocks.EventWork.SetFlag(flagNumber, value); protected override bool[] MysteryGiftReceivedFlags { get => Blocks.GiftRecords.GetFlags(); set => Blocks.GiftRecords.SetFlags(value); } protected override DataMysteryGift[] MysteryGiftCards { get => Blocks.GiftRecords.GetRecords(); set => Blocks.GiftRecords.SetRecords((WR7[])value); } diff --git a/PKHeX.Core/Saves/SAV8BS.cs b/PKHeX.Core/Saves/SAV8BS.cs index 6fe50eadb..acf8cba81 100644 --- a/PKHeX.Core/Saves/SAV8BS.cs +++ b/PKHeX.Core/Saves/SAV8BS.cs @@ -8,7 +8,7 @@ namespace PKHeX.Core /// /// Generation 8 object for games. /// - public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord + public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord, IEventFlagArray, IEventWorkArray { // Save Data Attributes protected internal override string ShortSummary => $"{OT} ({Version}) - {System.LastSavedTime}"; @@ -98,8 +98,6 @@ namespace PKHeX.Core TeamSlots = BoxLayout.TeamSlots; } - public override bool HasEvents => true; - // Configuration protected override int SIZE_STORED => PokeCrypto.SIZE_8STORED; protected override int SIZE_PARTY => PokeCrypto.SIZE_8PARTY; @@ -111,7 +109,6 @@ namespace PKHeX.Core public override int MaxEV => 252; public override int Generation => 8; - protected override int EventConstMax => 500; public override PersonalTable Personal => PersonalTable.BDSP; public override int OTLength => 12; public override int NickLength => 12; @@ -252,8 +249,9 @@ namespace PKHeX.Core return StringConverter8.SetString(destBuffer, value, maxLength, option); } - public override bool GetEventFlag(int flagNumber) => Work.GetFlag(flagNumber); - public override void SetEventFlag(int flagNumber, bool value) => Work.SetFlag(flagNumber, value); + public int EventFlagCount => FlagWork8b.COUNT_FLAG; + public bool GetEventFlag(int flagNumber) => Work.GetFlag(flagNumber); + public void SetEventFlag(int flagNumber, bool value) => Work.SetFlag(flagNumber, value); // Player Information public override int TID { get => MyStatus.TID; set => MyStatus.TID = value; } @@ -367,5 +365,9 @@ namespace PKHeX.Core public override string GetDaycareRNGSeed(int loc) => Daycare.DaycareSeed.ToString("X16"); public override void SetDaycareRNGSeed(int loc, string seed) => Daycare.DaycareSeed = Util.GetHexValue64(seed); #endregion + + public int EventWorkCount => FlagWork8b.COUNT_WORK; + public int GetWork(int index) => Work.GetWork(index); + public void SetWork(int index, int value = default) => Work.SetWork(index, value); } } diff --git a/PKHeX.Core/Saves/SAV8SWSH.cs b/PKHeX.Core/Saves/SAV8SWSH.cs index 5b331cacd..251c67d4b 100644 --- a/PKHeX.Core/Saves/SAV8SWSH.cs +++ b/PKHeX.Core/Saves/SAV8SWSH.cs @@ -148,7 +148,6 @@ public sealed class SAV8SWSH : SaveFile, ISaveBlock8SWSH, ITrainerStatRecord, IS public override int BoxCount => BoxLayout8.BoxCount; public override int MaxEV => 252; public override int Generation => 8; - protected override int EventConstMax => 1000; public override int OTLength => 12; public override int NickLength => 12; protected override PKM GetPKM(byte[] data) => new PK8(data); diff --git a/PKHeX.Core/Saves/SaveFile.cs b/PKHeX.Core/Saves/SaveFile.cs index 2db060a97..2c902e294 100644 --- a/PKHeX.Core/Saves/SaveFile.cs +++ b/PKHeX.Core/Saves/SaveFile.cs @@ -1,14 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; -using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { /// /// Base Class for Save Files /// - public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IBoxDetailWallpaper, IBoxDetailName, IGeneration + public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IBoxDetailWallpaper, IBoxDetailName, IGeneration, IVersion { // General Object Properties public byte[] Data; @@ -99,86 +98,6 @@ namespace PKHeX.Core public virtual int MinGameID => 0; #endregion - #region Event Work - public virtual bool HasEvents => GetEventFlags().Length != 0; - protected virtual int EventFlagMax { get; } = int.MinValue; - protected virtual int EventConstMax { get; } = int.MinValue; - protected int EventFlag { get; set; } = int.MinValue; - protected int EventConst { get; set; } = int.MinValue; - - /// All Event Flag values for the savegame - public bool[] GetEventFlags() - { - if (EventFlagMax < 0) - return Array.Empty(); - - bool[] result = new bool[EventFlagMax]; - for (int i = 0; i < result.Length; i++) - result[i] = GetEventFlag(i); - return result; - } - - /// All Event Flag values for the savegame - public void SetEventFlags(bool[] value) - { - if (EventFlagMax < 0) - return; - if (value.Length != EventFlagMax) - return; - for (int i = 0; i < value.Length; i++) - SetEventFlag(i, value[i]); - } - - /// All Event Constant values for the savegame - public virtual ushort[] GetEventConsts() - { - if (EventConstMax <= 0 || Data.Length == 0) - return Array.Empty(); - - ushort[] Constants = new ushort[EventConstMax]; - for (int i = 0; i < Constants.Length; i++) - Constants[i] = ReadUInt16LittleEndian(Data.AsSpan(EventConst + (i * 2))); - return Constants; - } - - /// All Event Constant values for the savegame - public virtual void SetEventConsts(ReadOnlySpan value) - { - if (EventConstMax <= 0) - return; - if (value.Length != EventConstMax) - return; - - var span = Data.AsSpan(EventConst); - for (int i = 0; i < value.Length; i++) - WriteUInt16LittleEndian(span[(i*2)..], value[i]); - } - - /// - /// Gets the status of a desired Event Flag - /// - /// Event Flag to check - /// Flag is Set (true) or not Set (false) - public virtual bool GetEventFlag(int flagNumber) - { - if ((uint)flagNumber >= EventFlagMax) - throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to get ({flagNumber}) is greater than max ({EventFlagMax})."); - return GetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7); - } - - /// - /// Sets the status of a desired Event Flag - /// - /// Event Flag to check - /// Event Flag status to set - /// Flag is Set (true) or not Set (false) - public virtual void SetEventFlag(int flagNumber, bool value) - { - if ((uint)flagNumber >= EventFlagMax) - throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to set ({flagNumber}) is greater than max ({EventFlagMax})."); - SetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7, value); - } - /// /// Gets the status of the Flag at the specified offset and index. /// @@ -195,7 +114,6 @@ namespace PKHeX.Core /// Flag status to set /// Flag is Set (true) or not Set (false) public virtual void SetFlag(int offset, int bitIndex, bool value) => FlagUtil.SetFlag(Data, offset, bitIndex, value); - #endregion public virtual IReadOnlyList Inventory { get => Array.Empty(); set { } } diff --git a/PKHeX.Core/Saves/Substructures/Gen12/SAV2Offsets.cs b/PKHeX.Core/Saves/Substructures/Gen12/SAV2Offsets.cs index d8ff72aa7..ab8ed1788 100644 --- a/PKHeX.Core/Saves/Substructures/Gen12/SAV2Offsets.cs +++ b/PKHeX.Core/Saves/Substructures/Gen12/SAV2Offsets.cs @@ -16,7 +16,7 @@ namespace PKHeX.Core else LoadOffsetsInternational(sav.Version); Daycare = PokedexSeen + 0x1F + 28 + 1; // right after first unown seen - EventConst = EventFlag - 0x100; + EventWork = EventFlag - 0x100; } public int RTCFlags { get; private set; } = -1; @@ -41,7 +41,7 @@ namespace PKHeX.Core public int AccumulatedChecksumEnd { get; private set; } = -1; public int OverallChecksumPosition { get; private set; } = -1; public int EventFlag { get; private set; } = -1; - public int EventConst { get; } + public int EventWork { get; } public int Daycare { get; } public int BlueCardPoints { get; private set; } = -1; diff --git a/PKHeX.Core/Saves/Substructures/Gen5/FestaBlock5.cs b/PKHeX.Core/Saves/Substructures/Gen5/FestaBlock5.cs index 4e99d2392..d555745e2 100644 --- a/PKHeX.Core/Saves/Substructures/Gen5/FestaBlock5.cs +++ b/PKHeX.Core/Saves/Substructures/Gen5/FestaBlock5.cs @@ -3,7 +3,7 @@ using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { - public sealed class FestaBlock5 : SaveBlock + public sealed class FestaBlock5 : SaveBlock { public FestaBlock5(SAV5B2W2 SAV, int offset) : base(SAV) => Offset = offset; diff --git a/PKHeX.Core/Saves/Substructures/Gen7/SaveBlock.cs b/PKHeX.Core/Saves/Substructures/Gen7/SaveBlock.cs index 1630bccb6..f2b69fb73 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/SaveBlock.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/SaveBlock.cs @@ -1,23 +1,38 @@ using System.ComponentModel; -namespace PKHeX.Core +namespace PKHeX.Core; + +/// +/// Base class for a savegame data reader. +/// +public abstract class SaveBlock { - /// - /// Base class for a savegame data reader. - /// - public abstract class SaveBlock + [Browsable(false)] + public int Offset { get; protected init; } + + public readonly byte[] Data; + protected readonly SaveFile SAV; + protected SaveBlock(SaveFile sav) => Data = (SAV = sav).Data; + + protected SaveBlock(SaveFile sav, byte[] data) { - [Browsable(false)] - public int Offset { get; protected init; } - - public readonly byte[] Data; - protected readonly SaveFile SAV; - protected SaveBlock(SaveFile sav) => Data = (SAV = sav).Data; - - protected SaveBlock(SaveFile sav, byte[] data) - { - SAV = sav; - Data = data; - } + SAV = sav; + Data = data; } -} \ No newline at end of file +} + +public abstract class SaveBlock where T : SaveFile +{ + [Browsable(false)] + public int Offset { get; protected init; } + + public readonly byte[] Data; + protected readonly T SAV; + protected SaveBlock(T sav) => Data = (SAV = sav).Data; + + protected SaveBlock(T sav, byte[] data) + { + SAV = sav; + Data = data; + } +} diff --git a/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs b/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs index 50c120745..6c01fdefc 100644 --- a/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs +++ b/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs @@ -524,7 +524,9 @@ namespace PKHeX.WinForms.Controls SAV1 s => (Form) new SAV_EventReset1(s), SAV7b s => new SAV_EventWork(s), SAV8BS s => new SAV_FlagWork8b(s), - _ => new SAV_EventFlags(SAV), + IEventFlag37 g37 => new SAV_EventFlags(g37), + SAV2 s => new SAV_EventFlags2(s), + _ => throw new Exception(), }; form.ShowDialog(); } @@ -1093,7 +1095,7 @@ namespace PKHeX.WinForms.Controls B_OpenPokedex.Visible = sav.HasPokeDex; B_OpenBerryField.Visible = sav is SAV6XY; // oras undocumented B_OpenFriendSafari.Visible = sav is SAV6XY; - B_OpenEventFlags.Visible = sav.HasEvents; + B_OpenEventFlags.Visible = sav is IEventFlag37 or (SAV1 or SAV2 or SAV8BS or SAV7b); B_CGearSkin.Visible = sav.Generation == 5; B_OpenPokeBeans.Visible = B_CellsStickers.Visible = B_FestivalPlaza.Visible = sav is SAV7; diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.cs index 166a4b9d9..349993f52 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.cs @@ -48,7 +48,7 @@ namespace PKHeX.WinForms cba[i].Items.Clear(); cba[i].InitializeBinding(); cba[i].DataSource = new BindingSource(legal, null); - var g3Species = SAV.GetEventConst(0x43 + i); + var g3Species = SAV.GetWork(0x43 + i); var species = SpeciesConverter.GetG4Species(g3Species); cba[i].SelectedValue = species; } @@ -75,7 +75,7 @@ namespace PKHeX.WinForms { var species = (ushort) WinFormsUtil.GetIndex(cba[i]); var g3Species = SpeciesConverter.GetG3Species(species); - SAV.SetEventConst(0x43 + i, (ushort)g3Species); + SAV.SetWork(0x43 + i, (ushort)g3Species); } } @@ -238,9 +238,7 @@ namespace PKHeX.WinForms else { CB_Stats2.Visible = true; - foreach (var t in bft) - CB_Stats2.Items.Add(t); - + CB_Stats2.Items.AddRange(bft); CB_Stats2.SelectedIndex = 0; } @@ -377,8 +375,7 @@ namespace PKHeX.WinForms SetFrontierSymbols(); CB_Stats1.Items.Clear(); - foreach (string t in BFN) - CB_Stats1.Items.Add(t); + CB_Stats1.Items.AddRange(BFN); loading = false; CB_Stats1.SelectedIndex = 0; diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_ZygardeCell.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_ZygardeCell.cs index 59e436589..16bf09d64 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_ZygardeCell.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen7/SAV_ZygardeCell.cs @@ -18,7 +18,7 @@ namespace PKHeX.WinForms // Constants @ 0x1C00 // Cell Data @ 0x1D8C // Use constants 0x18C/2 = 198 thru +95 - ushort[] constants = SAV.GetEventConsts(); + ushort[] constants = SAV.GetAllEventWork(); var cells = constants.AsSpan(celloffset, CellCount); int cellCount = constants[cellstotal]; @@ -28,8 +28,7 @@ namespace PKHeX.WinForms NUD_Collected.Value = cellCollected; var combo = (DataGridViewComboBoxColumn)dgv.Columns[2]; - foreach (string t in states) - combo.Items.Add(t); // add only the Names + combo.Items.AddRange(states); // add only the Names dgv.Columns[0].ValueType = typeof(int); // Populate Grid @@ -54,7 +53,7 @@ namespace PKHeX.WinForms private void B_Save_Click(object sender, EventArgs e) { - ushort[] constants = SAV.GetEventConsts(); + ushort[] constants = SAV.GetAllEventWork(); for (int i = 0; i < CellCount; i++) { string str = (string)dgv.Rows[i].Cells[2].Value; @@ -70,7 +69,7 @@ namespace PKHeX.WinForms if (SAV is SAV7USUM) SAV.SetRecord(72, (int)NUD_Collected.Value); - SAV.SetEventConsts(constants); + SAV.SetAllEventWork(constants); Origin.CopyChangesFrom(SAV); Close(); diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_FlagWork8b.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_FlagWork8b.cs index 3a4cc86a3..80c4672b5 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_FlagWork8b.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen8/SAV_FlagWork8b.cs @@ -257,9 +257,9 @@ namespace PKHeX.WinForms private void DiffSaves() { var diff = new EventWorkDiff8b(TB_OldSAV.Text, TB_NewSAV.Text); - if (diff.Message.Length != 0) + if (diff.Message != EventWorkDiffCompatibility.Valid) { - WinFormsUtil.Alert(diff.Message); + WinFormsUtil.Alert(diff.Message.GetMessage()); return; } diff --git a/PKHeX.WinForms/Subforms/Save Editors/SAV_EventFlags.cs b/PKHeX.WinForms/Subforms/Save Editors/SAV_EventFlags.cs index fdc0ad831..202f69c0c 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/SAV_EventFlags.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/SAV_EventFlags.cs @@ -10,18 +10,18 @@ namespace PKHeX.WinForms { public sealed partial class SAV_EventFlags : Form { - private readonly EventWorkspace Editor; + private readonly EventWorkspace Editor; private readonly Dictionary WorkDict = new(); private readonly Dictionary FlagDict = new(); private bool editing; - public SAV_EventFlags(SaveFile sav) + public SAV_EventFlags(IEventFlag37 sav) { InitializeComponent(); WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage); - var editor = Editor = new EventWorkspace(sav); + var editor = Editor = new EventWorkspace(sav); DragEnter += Main_DragEnter; DragDrop += Main_DragDrop; @@ -40,7 +40,7 @@ namespace PKHeX.WinForms dgv.ResumeLayout(); TLP_Const.ResumeLayout(); - Text = $"{Text} ({sav.Version})"; + Text = $"{Text} ({((IVersion)sav).Version})"; if (CB_Stats.Items.Count > 0) { @@ -285,10 +285,10 @@ namespace PKHeX.WinForms private void DiffSaves() { - var diff = new EventBlockDiff(TB_OldSAV.Text, TB_NewSAV.Text); - if (!string.IsNullOrWhiteSpace(diff.Message)) + var diff = new EventBlockDiff(TB_OldSAV.Text, TB_NewSAV.Text); + if (diff.Message != EventWorkDiffCompatibility.Valid) { - WinFormsUtil.Alert(diff.Message); + WinFormsUtil.Alert(diff.Message.GetMessage()); return; } diff --git a/PKHeX.WinForms/Subforms/Save Editors/SAV_EventFlags2.Designer.cs b/PKHeX.WinForms/Subforms/Save Editors/SAV_EventFlags2.Designer.cs new file mode 100644 index 000000000..2a088e85d --- /dev/null +++ b/PKHeX.WinForms/Subforms/Save Editors/SAV_EventFlags2.Designer.cs @@ -0,0 +1,427 @@ +namespace PKHeX.WinForms +{ + sealed partial class SAV_EventFlags2 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.c_CustomFlag = new System.Windows.Forms.CheckBox(); + this.B_Cancel = new System.Windows.Forms.Button(); + this.GB_FlagStatus = new System.Windows.Forms.GroupBox(); + this.NUD_Flag = new System.Windows.Forms.NumericUpDown(); + this.MT_Stat = new System.Windows.Forms.MaskedTextBox(); + this.CHK_CustomFlag = new System.Windows.Forms.Label(); + this.CB_Stats = new System.Windows.Forms.ComboBox(); + this.L_Stats = new System.Windows.Forms.Label(); + this.B_Save = new System.Windows.Forms.Button(); + this.GB_Researcher = new System.Windows.Forms.GroupBox(); + this.L_UnSet = new System.Windows.Forms.Label(); + this.L_IsSet = new System.Windows.Forms.Label(); + this.TB_NewSAV = new System.Windows.Forms.TextBox(); + this.TB_OldSAV = new System.Windows.Forms.TextBox(); + this.TB_UnSet = new System.Windows.Forms.TextBox(); + this.TB_IsSet = new System.Windows.Forms.TextBox(); + this.B_LoadNew = new System.Windows.Forms.Button(); + this.B_LoadOld = new System.Windows.Forms.Button(); + this.L_EventFlagWarn = new System.Windows.Forms.Label(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.GB_Flags = new System.Windows.Forms.TabPage(); + this.dgv = new System.Windows.Forms.DataGridView(); + this.GB_Constants = new System.Windows.Forms.TabPage(); + this.TLP_Const = new System.Windows.Forms.TableLayoutPanel(); + this.GB_Research = new System.Windows.Forms.TabPage(); + this.GB_FlagStatus.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.NUD_Flag)).BeginInit(); + this.GB_Researcher.SuspendLayout(); + this.tabControl1.SuspendLayout(); + this.GB_Flags.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dgv)).BeginInit(); + this.GB_Constants.SuspendLayout(); + this.GB_Research.SuspendLayout(); + this.SuspendLayout(); + // + // c_CustomFlag + // + this.c_CustomFlag.AutoSize = true; + this.c_CustomFlag.CheckAlign = System.Drawing.ContentAlignment.MiddleRight; + this.c_CustomFlag.Location = new System.Drawing.Point(138, 20); + this.c_CustomFlag.Name = "c_CustomFlag"; + this.c_CustomFlag.Size = new System.Drawing.Size(15, 14); + this.c_CustomFlag.TabIndex = 1; + this.c_CustomFlag.UseVisualStyleBackColor = true; + this.c_CustomFlag.CheckedChanged += new System.EventHandler(this.ChangeCustomBool); + // + // B_Cancel + // + this.B_Cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.B_Cancel.Location = new System.Drawing.Point(289, 330); + this.B_Cancel.Name = "B_Cancel"; + this.B_Cancel.Size = new System.Drawing.Size(75, 23); + this.B_Cancel.TabIndex = 2; + this.B_Cancel.Text = "Cancel"; + this.B_Cancel.UseVisualStyleBackColor = true; + this.B_Cancel.Click += new System.EventHandler(this.B_Cancel_Click); + // + // GB_FlagStatus + // + this.GB_FlagStatus.Controls.Add(this.NUD_Flag); + this.GB_FlagStatus.Controls.Add(this.MT_Stat); + this.GB_FlagStatus.Controls.Add(this.CHK_CustomFlag); + this.GB_FlagStatus.Controls.Add(this.CB_Stats); + this.GB_FlagStatus.Controls.Add(this.L_Stats); + this.GB_FlagStatus.Controls.Add(this.c_CustomFlag); + this.GB_FlagStatus.Location = new System.Drawing.Point(6, 5); + this.GB_FlagStatus.Name = "GB_FlagStatus"; + this.GB_FlagStatus.Size = new System.Drawing.Size(206, 75); + this.GB_FlagStatus.TabIndex = 3; + this.GB_FlagStatus.TabStop = false; + this.GB_FlagStatus.Text = "Check Status"; + // + // NUD_Flag + // + this.NUD_Flag.Location = new System.Drawing.Point(87, 17); + this.NUD_Flag.Maximum = new decimal(new int[] { + 3072, + 0, + 0, + 0}); + this.NUD_Flag.Name = "NUD_Flag"; + this.NUD_Flag.Size = new System.Drawing.Size(45, 20); + this.NUD_Flag.TabIndex = 9; + this.NUD_Flag.ValueChanged += new System.EventHandler(this.ChangeCustomFlag); + this.NUD_Flag.KeyUp += new System.Windows.Forms.KeyEventHandler(this.ChangeCustomFlag); + // + // MT_Stat + // + this.MT_Stat.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.MT_Stat.Location = new System.Drawing.Point(159, 44); + this.MT_Stat.Mask = "00000"; + this.MT_Stat.Name = "MT_Stat"; + this.MT_Stat.Size = new System.Drawing.Size(34, 20); + this.MT_Stat.TabIndex = 34; + this.MT_Stat.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + this.MT_Stat.TextChanged += new System.EventHandler(this.ChangeCustomConst); + // + // CHK_CustomFlag + // + this.CHK_CustomFlag.Location = new System.Drawing.Point(9, 17); + this.CHK_CustomFlag.Name = "CHK_CustomFlag"; + this.CHK_CustomFlag.Size = new System.Drawing.Size(72, 20); + this.CHK_CustomFlag.TabIndex = 2; + this.CHK_CustomFlag.Text = "Flag:"; + this.CHK_CustomFlag.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // CB_Stats + // + this.CB_Stats.DropDownHeight = 156; + this.CB_Stats.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.CB_Stats.DropDownWidth = 180; + this.CB_Stats.FormattingEnabled = true; + this.CB_Stats.IntegralHeight = false; + this.CB_Stats.Location = new System.Drawing.Point(87, 44); + this.CB_Stats.Name = "CB_Stats"; + this.CB_Stats.Size = new System.Drawing.Size(66, 21); + this.CB_Stats.TabIndex = 36; + this.CB_Stats.SelectedIndexChanged += new System.EventHandler(this.ChangeConstantIndex); + // + // L_Stats + // + this.L_Stats.Location = new System.Drawing.Point(9, 45); + this.L_Stats.Name = "L_Stats"; + this.L_Stats.Size = new System.Drawing.Size(72, 20); + this.L_Stats.TabIndex = 37; + this.L_Stats.Text = "Constant:"; + this.L_Stats.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // B_Save + // + this.B_Save.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.B_Save.Location = new System.Drawing.Point(372, 330); + this.B_Save.Name = "B_Save"; + this.B_Save.Size = new System.Drawing.Size(75, 23); + this.B_Save.TabIndex = 9; + this.B_Save.Text = "Save"; + this.B_Save.UseVisualStyleBackColor = true; + this.B_Save.Click += new System.EventHandler(this.B_Save_Click); + // + // GB_Researcher + // + this.GB_Researcher.Controls.Add(this.L_UnSet); + this.GB_Researcher.Controls.Add(this.L_IsSet); + this.GB_Researcher.Controls.Add(this.TB_NewSAV); + this.GB_Researcher.Controls.Add(this.TB_OldSAV); + this.GB_Researcher.Controls.Add(this.TB_UnSet); + this.GB_Researcher.Controls.Add(this.TB_IsSet); + this.GB_Researcher.Controls.Add(this.B_LoadNew); + this.GB_Researcher.Controls.Add(this.B_LoadOld); + this.GB_Researcher.Dock = System.Windows.Forms.DockStyle.Bottom; + this.GB_Researcher.Location = new System.Drawing.Point(3, 160); + this.GB_Researcher.Name = "GB_Researcher"; + this.GB_Researcher.Size = new System.Drawing.Size(416, 120); + this.GB_Researcher.TabIndex = 13; + this.GB_Researcher.TabStop = false; + this.GB_Researcher.Text = "FlagDiff Researcher"; + // + // L_UnSet + // + this.L_UnSet.Location = new System.Drawing.Point(3, 94); + this.L_UnSet.Name = "L_UnSet"; + this.L_UnSet.Size = new System.Drawing.Size(51, 21); + this.L_UnSet.TabIndex = 7; + this.L_UnSet.Text = "UnSet:"; + this.L_UnSet.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // L_IsSet + // + this.L_IsSet.Location = new System.Drawing.Point(6, 73); + this.L_IsSet.Name = "L_IsSet"; + this.L_IsSet.Size = new System.Drawing.Size(48, 20); + this.L_IsSet.TabIndex = 6; + this.L_IsSet.Text = "IsSet:"; + this.L_IsSet.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // TB_NewSAV + // + this.TB_NewSAV.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.TB_NewSAV.Location = new System.Drawing.Point(93, 47); + this.TB_NewSAV.Name = "TB_NewSAV"; + this.TB_NewSAV.ReadOnly = true; + this.TB_NewSAV.Size = new System.Drawing.Size(317, 20); + this.TB_NewSAV.TabIndex = 5; + this.TB_NewSAV.TextChanged += new System.EventHandler(this.ChangeSAV); + // + // TB_OldSAV + // + this.TB_OldSAV.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.TB_OldSAV.Location = new System.Drawing.Point(93, 21); + this.TB_OldSAV.Name = "TB_OldSAV"; + this.TB_OldSAV.ReadOnly = true; + this.TB_OldSAV.Size = new System.Drawing.Size(317, 20); + this.TB_OldSAV.TabIndex = 4; + this.TB_OldSAV.TextChanged += new System.EventHandler(this.ChangeSAV); + // + // TB_UnSet + // + this.TB_UnSet.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.TB_UnSet.Location = new System.Drawing.Point(56, 94); + this.TB_UnSet.Name = "TB_UnSet"; + this.TB_UnSet.ReadOnly = true; + this.TB_UnSet.Size = new System.Drawing.Size(354, 20); + this.TB_UnSet.TabIndex = 3; + // + // TB_IsSet + // + this.TB_IsSet.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.TB_IsSet.Location = new System.Drawing.Point(56, 73); + this.TB_IsSet.Name = "TB_IsSet"; + this.TB_IsSet.ReadOnly = true; + this.TB_IsSet.Size = new System.Drawing.Size(354, 20); + this.TB_IsSet.TabIndex = 2; + // + // B_LoadNew + // + this.B_LoadNew.Location = new System.Drawing.Point(12, 45); + this.B_LoadNew.Name = "B_LoadNew"; + this.B_LoadNew.Size = new System.Drawing.Size(75, 23); + this.B_LoadNew.TabIndex = 1; + this.B_LoadNew.Text = "Load New"; + this.B_LoadNew.UseVisualStyleBackColor = true; + this.B_LoadNew.Click += new System.EventHandler(this.OpenSAV); + // + // B_LoadOld + // + this.B_LoadOld.Location = new System.Drawing.Point(12, 19); + this.B_LoadOld.Name = "B_LoadOld"; + this.B_LoadOld.Size = new System.Drawing.Size(75, 23); + this.B_LoadOld.TabIndex = 0; + this.B_LoadOld.Text = "Load Old"; + this.B_LoadOld.UseVisualStyleBackColor = true; + this.B_LoadOld.Click += new System.EventHandler(this.OpenSAV); + // + // L_EventFlagWarn + // + this.L_EventFlagWarn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.L_EventFlagWarn.ForeColor = System.Drawing.Color.Red; + this.L_EventFlagWarn.Location = new System.Drawing.Point(9, 324); + this.L_EventFlagWarn.Name = "L_EventFlagWarn"; + this.L_EventFlagWarn.Size = new System.Drawing.Size(262, 31); + this.L_EventFlagWarn.TabIndex = 41; + this.L_EventFlagWarn.Text = "Altering Event Flags may impact other story events.\r\nSave file backups are recomm" + + "ended."; + this.L_EventFlagWarn.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // tabControl1 + // + this.tabControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabControl1.Controls.Add(this.GB_Flags); + this.tabControl1.Controls.Add(this.GB_Constants); + this.tabControl1.Controls.Add(this.GB_Research); + this.tabControl1.Location = new System.Drawing.Point(12, 12); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(430, 309); + this.tabControl1.TabIndex = 42; + // + // GB_Flags + // + this.GB_Flags.Controls.Add(this.dgv); + this.GB_Flags.Location = new System.Drawing.Point(4, 22); + this.GB_Flags.Name = "GB_Flags"; + this.GB_Flags.Size = new System.Drawing.Size(422, 283); + this.GB_Flags.TabIndex = 0; + this.GB_Flags.Text = "Event Flags"; + this.GB_Flags.UseVisualStyleBackColor = true; + // + // dgv + // + this.dgv.AllowUserToAddRows = false; + this.dgv.AllowUserToDeleteRows = false; + this.dgv.AllowUserToResizeColumns = false; + this.dgv.AllowUserToResizeRows = false; + this.dgv.BackgroundColor = System.Drawing.SystemColors.ControlLightLight; + this.dgv.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.dgv.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single; + this.dgv.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.dgv.ColumnHeadersVisible = false; + this.dgv.Dock = System.Windows.Forms.DockStyle.Fill; + this.dgv.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter; + this.dgv.Location = new System.Drawing.Point(0, 0); + this.dgv.Margin = new System.Windows.Forms.Padding(0); + this.dgv.MultiSelect = false; + this.dgv.Name = "dgv"; + this.dgv.RowHeadersVisible = false; + this.dgv.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect; + this.dgv.ShowEditingIcon = false; + this.dgv.Size = new System.Drawing.Size(422, 283); + this.dgv.TabIndex = 12; + // + // GB_Constants + // + this.GB_Constants.Controls.Add(this.TLP_Const); + this.GB_Constants.Location = new System.Drawing.Point(4, 22); + this.GB_Constants.Name = "GB_Constants"; + this.GB_Constants.Padding = new System.Windows.Forms.Padding(3); + this.GB_Constants.Size = new System.Drawing.Size(422, 283); + this.GB_Constants.TabIndex = 1; + this.GB_Constants.Text = "Event Constants"; + this.GB_Constants.UseVisualStyleBackColor = true; + // + // TLP_Const + // + this.TLP_Const.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.TLP_Const.AutoScroll = true; + this.TLP_Const.ColumnCount = 3; + this.TLP_Const.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.TLP_Const.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.TLP_Const.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 416F)); + this.TLP_Const.Location = new System.Drawing.Point(3, 3); + this.TLP_Const.Name = "TLP_Const"; + this.TLP_Const.RowCount = 1; + this.TLP_Const.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.TLP_Const.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.TLP_Const.Size = new System.Drawing.Size(416, 277); + this.TLP_Const.TabIndex = 1; + // + // GB_Research + // + this.GB_Research.Controls.Add(this.GB_FlagStatus); + this.GB_Research.Controls.Add(this.GB_Researcher); + this.GB_Research.Location = new System.Drawing.Point(4, 22); + this.GB_Research.Name = "GB_Research"; + this.GB_Research.Padding = new System.Windows.Forms.Padding(3); + this.GB_Research.Size = new System.Drawing.Size(422, 283); + this.GB_Research.TabIndex = 2; + this.GB_Research.Text = "Research"; + this.GB_Research.UseVisualStyleBackColor = true; + // + // SAV_EventFlags + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(454, 361); + this.Controls.Add(this.tabControl1); + this.Controls.Add(this.L_EventFlagWarn); + this.Controls.Add(this.B_Save); + this.Controls.Add(this.B_Cancel); + this.Icon = global::PKHeX.WinForms.Properties.Resources.Icon; + this.MaximizeBox = false; + this.MaximumSize = new System.Drawing.Size(670, 800); + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(470, 400); + this.Name = "SAV_EventFlags"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Event Flag Editor"; + this.GB_FlagStatus.ResumeLayout(false); + this.GB_FlagStatus.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.NUD_Flag)).EndInit(); + this.GB_Researcher.ResumeLayout(false); + this.GB_Researcher.PerformLayout(); + this.tabControl1.ResumeLayout(false); + this.GB_Flags.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.dgv)).EndInit(); + this.GB_Constants.ResumeLayout(false); + this.GB_Research.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.CheckBox c_CustomFlag; + private System.Windows.Forms.Button B_Cancel; + private System.Windows.Forms.GroupBox GB_FlagStatus; + private System.Windows.Forms.Label CHK_CustomFlag; + private System.Windows.Forms.NumericUpDown NUD_Flag; + private System.Windows.Forms.Button B_Save; + private System.Windows.Forms.GroupBox GB_Researcher; + private System.Windows.Forms.Label L_UnSet; + private System.Windows.Forms.Label L_IsSet; + private System.Windows.Forms.TextBox TB_NewSAV; + private System.Windows.Forms.TextBox TB_OldSAV; + private System.Windows.Forms.TextBox TB_UnSet; + private System.Windows.Forms.TextBox TB_IsSet; + private System.Windows.Forms.Button B_LoadNew; + private System.Windows.Forms.Button B_LoadOld; + private System.Windows.Forms.Label L_Stats; + private System.Windows.Forms.ComboBox CB_Stats; + private System.Windows.Forms.MaskedTextBox MT_Stat; + private System.Windows.Forms.Label L_EventFlagWarn; + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage GB_Flags; + private System.Windows.Forms.TabPage GB_Constants; + private System.Windows.Forms.TabPage GB_Research; + private System.Windows.Forms.TableLayoutPanel TLP_Const; + private System.Windows.Forms.DataGridView dgv; + } +} \ No newline at end of file diff --git a/PKHeX.WinForms/Subforms/Save Editors/SAV_EventFlags2.cs b/PKHeX.WinForms/Subforms/Save Editors/SAV_EventFlags2.cs new file mode 100644 index 000000000..f67f17926 --- /dev/null +++ b/PKHeX.WinForms/Subforms/Save Editors/SAV_EventFlags2.cs @@ -0,0 +1,326 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; +using PKHeX.Core; +using static PKHeX.Core.MessageStrings; + +namespace PKHeX.WinForms +{ + public sealed partial class SAV_EventFlags2 : Form + { + private readonly EventWorkspace Editor; + private readonly Dictionary WorkDict = new(); + private readonly Dictionary FlagDict = new(); + + private bool editing; + + public SAV_EventFlags2(SAV2 sav) + { + InitializeComponent(); + WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage); + + var editor = Editor = new EventWorkspace(sav); + DragEnter += Main_DragEnter; + DragDrop += Main_DragDrop; + + editing = true; + CB_Stats.Items.Clear(); + for (int i = 0; i < editor.Values.Length; i++) + CB_Stats.Items.Add(i.ToString()); + + dgv.SuspendLayout(); + TLP_Const.SuspendLayout(); + TLP_Const.Scroll += WinFormsUtil.PanelScroll; + TLP_Const.Controls.Clear(); + AddFlagList(editor.Labels, editor.Flags); + AddConstList(editor.Labels, editor.Values); + + dgv.ResumeLayout(); + TLP_Const.ResumeLayout(); + + Text = $"{Text} ({sav.Version})"; + + if (CB_Stats.Items.Count > 0) + { + CB_Stats.SelectedIndex = 0; + } + else + { + L_Stats.Visible = CB_Stats.Visible = MT_Stat.Visible = false; + tabControl1.TabPages.Remove(GB_Constants); + } + NUD_Flag.Maximum = editor.Flags.Length - 1; + NUD_Flag.Value = 0; + c_CustomFlag.Checked = editor.Flags[0]; + editing = false; + } + + private void B_Cancel_Click(object sender, EventArgs e) + { + Close(); + } + + private void B_Save_Click(object sender, EventArgs e) + { + Editor.Save(); + Close(); + } + + private void AddFlagList(EventLabelCollection list, bool[] values) + { + var labels = list.Flag; + if (labels.Count == 0) + { + dgv.Visible = false; + var research = new Label { Text = MsgResearchRequired, Name = "TLP_Flags_Research", ForeColor = Color.Red, AutoSize = true, Location = new Point(20, 20) }; + GB_Flags.Controls.Add(research); + return; + } + + var cFlag = new DataGridViewCheckBoxColumn + { + DisplayIndex = 0, + Width = 20, + SortMode = DataGridViewColumnSortMode.NotSortable, + }; + + var cLabel = new DataGridViewTextBoxColumn + { + DisplayIndex = 1, + AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill, + ReadOnly = true, + SortMode = DataGridViewColumnSortMode.NotSortable, + }; + + cFlag.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; + cLabel.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft; + + dgv.Columns.Add(cFlag); + dgv.Columns.Add(cLabel); + + var hideBelow = Main.Settings.Advanced.HideEventTypeBelow; + labels = labels.Where(z => z.Type >= hideBelow).OrderByDescending(z => z.Type).ToList(); + dgv.Rows.Add(labels.Count); + + for (int i = 0; i < labels.Count; i++) + FlagDict[labels[i].Index] = i; + + for (int i = 0; i < labels.Count; i++) + { + var (name, index, _) = labels[i]; + var cells = dgv.Rows[i].Cells; + cells[0].Value = values[index]; + cells[1].Value = name; + } + dgv.CellValueChanged += (s, e) => + { + if (e.ColumnIndex != 0 || e.RowIndex == -1) + return; + + bool chk = (bool)dgv.Rows[e.RowIndex].Cells[0].Value; + var index = labels[e.RowIndex].Index; + values[index] = chk; + if (NUD_Flag.Value == index) + c_CustomFlag.Checked = chk; + }; + dgv.CellMouseUp += (s, e) => + { + if (e.RowIndex == -1) + return; + + if (e.ColumnIndex == 0) + { + dgv.EndEdit(); + return; + } + + if (e.ColumnIndex != 1) + return; + + bool chk = (bool)dgv.Rows[e.RowIndex].Cells[0].Value; + dgv.Rows[e.RowIndex].Cells[0].Value = !chk; + var index = labels[e.RowIndex].Index; + values[index] = chk; + if (NUD_Flag.Value == index) + c_CustomFlag.Checked = !chk; + }; + } + + private void AddConstList(EventLabelCollection list, byte[] values) + { + var labels = list.Work; + if (labels.Count == 0) + { + TLP_Const.Controls.Add(new Label { Text = MsgResearchRequired, Name = "TLP_Const_Research", ForeColor = Color.Red, AutoSize = true }, 0, 0); + return; + } + + var hide = Main.Settings.Advanced.HideEventTypeBelow; + labels = labels.OrderByDescending(z => z.Type).ToList(); + for (var i = 0; i < labels.Count; i++) + { + var entry = labels[i]; + if (entry.Type < hide) + break; + var lbl = new Label { Text = entry.Name, Margin = Padding.Empty, AutoSize = true }; + var mtb = new NumericUpDown + { + Maximum = ushort.MaxValue, + Minimum = ushort.MinValue, + Margin = Padding.Empty, + Width = 50, + }; + + var map = entry.PredefinedValues.Select(z => new ComboItem(z.Name, z.Value)).ToList(); + var cb = new ComboBox + { + Margin = Padding.Empty, + Width = 150, + DropDownStyle = ComboBoxStyle.DropDownList, + BindingContext = BindingContext, + DropDownWidth = Width + 100, + }; + cb.InitializeBinding(); + cb.DataSource = map; + + lbl.Click += (sender, e) => mtb.Value = 0; + bool updating = false; + mtb.ValueChanged += ChangeConstValue; + void ChangeConstValue(object? sender, EventArgs e) + { + if (updating) + return; + + updating = true; + var value = (byte)mtb.Value; + var (_, valueID) = map.Find(z => z.Value == value) ?? map[0]; + if (WinFormsUtil.GetIndex(cb) != valueID) + cb.SelectedValue = valueID; + + Editor.Values[entry.Index] = value; + if (CB_Stats.SelectedIndex == entry.Index) + MT_Stat.Text = ((int)mtb.Value).ToString(); + updating = false; + } + cb.SelectedValueChanged += (o, args) => + { + if (editing || updating) + return; + var value = WinFormsUtil.GetIndex(cb); + mtb.Value = value == NamedEventConst.CustomMagicValue ? 0 : value; + }; + + mtb.Value = values[entry.Index]; + if (mtb.Value == 0) + ChangeConstValue(this, EventArgs.Empty); + + TLP_Const.Controls.Add(lbl, 0, i); + TLP_Const.Controls.Add(cb, 1, i); + TLP_Const.Controls.Add(mtb, 2, i); + + WorkDict.Add(entry.Index, mtb); + } + } + + private void ChangeCustomBool(object sender, EventArgs e) + { + if (editing) + return; + editing = true; + var index = (int) NUD_Flag.Value; + Editor.Flags[index] = c_CustomFlag.Checked; + if (FlagDict.TryGetValue(index, out var rowIndex)) + dgv.Rows[rowIndex].Cells[0].Value = c_CustomFlag.Checked; + editing = false; + } + + private void ChangeCustomFlag(object sender, EventArgs e) + { + int flag = (int)NUD_Flag.Value; + c_CustomFlag.Checked = Editor.Flags[flag]; + } + + private void ChangeCustomFlag(object sender, KeyEventArgs e) => ChangeCustomFlag(sender, (EventArgs)e); + + private void ChangeConstantIndex(object sender, EventArgs e) + { + var constants = Editor.Values; + var index = CB_Stats.SelectedIndex; + MT_Stat.Text = constants[index].ToString(); + } + + private void ChangeCustomConst(object sender, EventArgs e) + { + if (editing) + return; + editing = true; + var index = CB_Stats.SelectedIndex; + var parse = byte.TryParse(MT_Stat.Text, out var value) ? value : (byte)0; + Editor.Values[index] = parse; + if (WorkDict.TryGetValue(index, out var mtb)) + mtb.Value = parse; + editing = false; + } + + private void ChangeSAV(object sender, EventArgs e) + { + if (TB_NewSAV.Text.Length > 0 && TB_OldSAV.Text.Length > 0) + DiffSaves(); + } + + private void OpenSAV(object sender, EventArgs e) + { + using var ofd = new OpenFileDialog(); + if (ofd.ShowDialog() == DialogResult.OK) + LoadSAV(sender, ofd.FileName); + } + + private void LoadSAV(object sender, string path) + { + var dest = sender == B_LoadOld ? TB_OldSAV : TB_NewSAV; + dest.Text = path; + } + + private void DiffSaves() + { + var diff = new EventBlockDiff(TB_OldSAV.Text, TB_NewSAV.Text); + if (diff.Message != EventWorkDiffCompatibility.Valid) + { + WinFormsUtil.Alert(diff.Message.GetMessage()); + return; + } + + TB_IsSet.Text = string.Join(", ", diff.SetFlags.Select(z => $"{z:0000}")); + TB_UnSet.Text = string.Join(", ", diff.ClearedFlags.Select(z => $"{z:0000}")); + + if (diff.WorkDiff.Count == 0) + { + WinFormsUtil.Alert("No Event Constant diff found."); + return; + } + + var promptCopy = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, "Copy Event Constant diff to clipboard?"); + if (promptCopy == DialogResult.Yes) + WinFormsUtil.SetClipboardText(string.Join(Environment.NewLine, diff.WorkDiff)); + } + + private static void Main_DragEnter(object? sender, DragEventArgs? e) + { + if (e?.Data is null) + return; + if (e.Data.GetDataPresent(DataFormats.FileDrop)) + e.Effect = DragDropEffects.Copy; + } + + private void Main_DragDrop(object? sender, DragEventArgs? e) + { + if (e?.Data?.GetData(DataFormats.FileDrop) is not string[] { Length: not 0 } files) + return; + var dr = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, Name, "Yes: Old Save" + Environment.NewLine + "No: New Save"); + var button = dr == DialogResult.Yes ? B_LoadOld : B_LoadNew; + LoadSAV(button, files[0]); + } + } +} diff --git a/PKHeX.WinForms/Subforms/Save Editors/SAV_EventWork.cs b/PKHeX.WinForms/Subforms/Save Editors/SAV_EventWork.cs index 8fed87517..a44c0ada4 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/SAV_EventWork.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/SAV_EventWork.cs @@ -270,9 +270,9 @@ namespace PKHeX.WinForms private void DiffSaves() { var diff7b = new EventWorkDiff7b(TB_OldSAV.Text, TB_NewSAV.Text); - if (diff7b.Message.Length != 0) + if (diff7b.Message != EventWorkDiffCompatibility.Valid) { - WinFormsUtil.Alert(diff7b.Message); + WinFormsUtil.Alert(diff7b.Message.GetMessage()); return; } diff --git a/PKHeX.WinForms/Util/WinFormsTranslator.cs b/PKHeX.WinForms/Util/WinFormsTranslator.cs index f0584fc5c..e9bbb4308 100644 --- a/PKHeX.WinForms/Util/WinFormsTranslator.cs +++ b/PKHeX.WinForms/Util/WinFormsTranslator.cs @@ -33,6 +33,7 @@ namespace PKHeX.WinForms // Translate Title var formName = form.Name; + formName = GetSaneFormName(formName); form.Text = context.GetTranslatedText(formName, form.Text); // Translate Controls @@ -43,6 +44,20 @@ namespace PKHeX.WinForms form.ResumeLayout(); } + private static string GetSaneFormName(string formName) + { + // Strip out generic form names + var degen = formName.IndexOf('`'); + if (degen != -1) + formName = formName[..degen]; + + return formName switch + { + nameof(SAV_EventFlags2) => nameof(SAV_EventFlags), + _ => formName, + }; + } + private static void TranslateControl(object c, TranslationContext context, string formname) { if (c is Control r)