Decouple event flag/work from SaveFile

This commit is contained in:
Kurt 2022-04-09 18:12:57 -07:00
parent a601180821
commit a5ee845c3b
45 changed files with 1523 additions and 579 deletions

View file

@ -1,50 +1,56 @@
using System;
using static PKHeX.Core.GameVersion;
namespace PKHeX.Core
namespace PKHeX.Core;
public sealed class EventWorkspace<TSave, TWork> where TSave : IEventFlagArray, IEventWorkArray<TWork> 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;
}
}

View file

@ -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;
/// <summary>
/// Calculates differences in the Event Blocks between two <see cref="SaveFile"/>.
/// </summary>
public class EventBlockDiff<T, T2> : IEventWorkDiff where T : IEventFlagArray, IEventWorkArray<T2> where T2 : unmanaged, IEquatable<T2>
{
public List<int> SetFlags { get; } = new();
public List<int> ClearedFlags { get; } = new();
public List<int> WorkChanged { get; } = new();
public List<string> 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<string> 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<string> { "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;
}
}

View file

@ -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<int> SetFlags { get; } = new();
public List<int> ClearedFlags { get; } = new();
public List<int> WorkChanged { get; } = new();
public List<string> 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<string> Summarize()
{
if (S1 == null)
return Array.Empty<string>();
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<string> { "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);
}
}
}

View file

@ -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<int> SetSystem { get; } = new();
public List<int> SetFlags { get; } = new();
public List<int> ClearedSystem { get; } = new();
public List<int> ClearedFlags { get; } = new();
public List<int> WorkChanged { get; } = new();
public List<string> 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<string> Summarize()
{
if (S1 == null)
return Array.Empty<string>();
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<string> { "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}";
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,13 @@
using System.Collections.Generic;
namespace PKHeX.Core;
public interface IEventWorkDiff
{
List<int> SetFlags { get; }
List<int> ClearedFlags { get; }
List<int> WorkChanged { get; }
List<string> WorkDiff { get; }
EventWorkDiffCompatibility Message { get; }
IReadOnlyList<string> Summarize();
}

View file

@ -1,267 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using static PKHeX.Core.MessageStrings;
namespace PKHeX.Core
{
/// <summary>
/// Calculates differences in the Event Blocks between two <see cref="SaveFile"/>.
/// </summary>
public class EventBlockDiff
{
public string Message { get; protected set; } = string.Empty;
public readonly List<int> SetFlags = new();
public readonly List<int> ClearedFlags = new();
public readonly List<string> 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<int> 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<string> Summarize()
{
if (S1 == null)
return Array.Empty<string>();
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<string> { "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<int> WorkChanged = new();
private SaveFile? S1;
public readonly List<int> SetSystem = new();
public readonly List<int> 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<string> Summarize()
{
if (S1 == null)
return Array.Empty<string>();
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<string> { "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}";
}
}
}

View file

@ -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
/// <param name="after">Data after the event was triggered</param>
/// <param name="changed"><see cref="EventVar.RawIndex"/> values that changed</param>
/// <param name="changes">Summary of the <see cref="EventWork{T}"/> value change</param>
public static void DiffSavesWork<T>(IEventWork<T> before, IEventWork<T> after, List<int> changed, List<string> changes)
public static void DiffSavesWork<T>(IEventWork<T> before, IEventWork<T> after, List<int> changed, List<string> changes) where T : unmanaged, IEquatable<T>
{
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>(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;
}
}
}

View file

@ -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<ushort> { }
public static class EventFlagArrayExtensions
{
/// <summary> All Event Flag values for the savegame </summary>
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;
}
/// <summary> All Event Flag values for the savegame </summary>
public static void SetEventFlags(this IEventFlagArray source, ReadOnlySpan<bool> value)
{
if (value.Length != source.EventFlagCount)
return;
for (int i = 0; i < value.Length; i++)
source.SetEventFlag(i, value[i]);
}
}
public interface IEventWorkArray<T> where T : unmanaged
{
public int EventWorkCount { get; }
public T GetWork(int index);
public void SetWork(int index, T value = default);
}
public static class EventWorkArrayExtensions
{
/// <summary> All Event Constant values for the savegame </summary>
public static T[] GetAllEventWork<T>(this IEventWorkArray<T> 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;
}
/// <summary> All Event Constant values for the savegame </summary>
public static void SetAllEventWork<T>(this IEventWorkArray<T> source, ReadOnlySpan<T> value) where T : unmanaged
{
if (value.Length != source.EventWorkCount)
return;
for (int i = 0; i < value.Length; i++)
source.SetWork(i, value[i]);
}
}

View file

@ -7,7 +7,7 @@ namespace PKHeX.Core
/// <summary>
/// Generation 1 <see cref="SaveFile"/> object.
/// </summary>
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()
{

View file

@ -7,7 +7,7 @@ namespace PKHeX.Core
/// <summary>
/// Generation 2 <see cref="SaveFile"/> object.
/// </summary>
public sealed class SAV2 : SaveFile, ILangDeviantSave
public sealed class SAV2 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWorkArray<byte>
{
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<byte> 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);
}
/// <summary>All Event Constant values for the save file</summary>
/// <remarks>These are all bytes</remarks>
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);
}
/// <summary>All Event Constant values for the save file</summary>
/// <remarks>These are all bytes</remarks>
public override void SetEventConsts(ReadOnlySpan<ushort> 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

View file

@ -8,7 +8,7 @@ namespace PKHeX.Core
/// <summary>
/// Generation 3 <see cref="SaveFile"/> object.
/// </summary>
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; }
/// <summary>
/// Force loads a new <see cref="SAV3"/> object to the requested <see cref="version"/>.
/// </summary>
@ -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<ushort> 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; }

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -10,7 +10,7 @@ namespace PKHeX.Core
/// <remarks>
/// Storage data is stored in one contiguous block, and the remaining data is stored in another block.
/// </remarks>
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);
}
/// <summary> All Event Constant values for the savegame </summary>
public sealed override ushort[] GetEventConsts()
#region Event Flag/Event Work
public bool GetEventFlag(int flagNumber)
{
if (EventConstMax <= 0)
return Array.Empty<ushort>();
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);
}
/// <summary> All Event Constant values for the savegame </summary>
public sealed override void SetEventConsts(ReadOnlySpan<ushort> 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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -7,7 +7,7 @@ namespace PKHeX.Core
/// <summary>
/// Generation 5 <see cref="SaveFile"/> object.
/// </summary>
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;

View file

@ -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;

View file

@ -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;

View file

@ -1,12 +1,13 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core
{
/// <summary>
/// Generation 6 <see cref="SaveFile"/> object.
/// </summary>
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);
}
}

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -1,13 +1,14 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core
{
/// <summary>
/// Generation 7 <see cref="SaveFile"/> object.
/// </summary>
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);
}
}

View file

@ -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;

View file

@ -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<ushort> 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;

View file

@ -6,7 +6,7 @@ namespace PKHeX.Core
/// <summary>
/// Generation 7 <see cref="SaveFile"/> object for <see cref="GameVersion.GG"/> games.
/// </summary>
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
/// </summary>
/// <param name="flagNumber">Event Flag to check</param>
/// <returns>Flag is Set (true) or not Set (false)</returns>
public override bool GetEventFlag(int flagNumber) => Blocks.EventWork.GetFlag(flagNumber);
public bool GetEventFlag(int flagNumber) => Blocks.EventWork.GetFlag(flagNumber);
/// <summary>
/// Sets the <see cref="bool"/> status of a desired Event Flag
@ -168,7 +165,7 @@ namespace PKHeX.Core
/// <param name="flagNumber">Event Flag to check</param>
/// <param name="value">Event Flag status to set</param>
/// <remarks>Flag is Set (true) or not Set (false)</remarks>
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); }

View file

@ -8,7 +8,7 @@ namespace PKHeX.Core
/// <summary>
/// Generation 8 <see cref="SaveFile"/> object for <see cref="GameVersion.BDSP"/> games.
/// </summary>
public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord
public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord, IEventFlagArray, IEventWorkArray<int>
{
// 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);
}
}

View file

@ -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);

View file

@ -1,14 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core
{
/// <summary>
/// Base Class for Save Files
/// </summary>
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;
/// <summary> All Event Flag values for the savegame </summary>
public bool[] GetEventFlags()
{
if (EventFlagMax < 0)
return Array.Empty<bool>();
bool[] result = new bool[EventFlagMax];
for (int i = 0; i < result.Length; i++)
result[i] = GetEventFlag(i);
return result;
}
/// <summary> All Event Flag values for the savegame </summary>
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]);
}
/// <summary> All Event Constant values for the savegame </summary>
public virtual ushort[] GetEventConsts()
{
if (EventConstMax <= 0 || Data.Length == 0)
return Array.Empty<ushort>();
ushort[] Constants = new ushort[EventConstMax];
for (int i = 0; i < Constants.Length; i++)
Constants[i] = ReadUInt16LittleEndian(Data.AsSpan(EventConst + (i * 2)));
return Constants;
}
/// <summary> All Event Constant values for the savegame </summary>
public virtual void SetEventConsts(ReadOnlySpan<ushort> 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]);
}
/// <summary>
/// Gets the <see cref="bool"/> status of a desired Event Flag
/// </summary>
/// <param name="flagNumber">Event Flag to check</param>
/// <returns>Flag is Set (true) or not Set (false)</returns>
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);
}
/// <summary>
/// Sets the <see cref="bool"/> status of a desired Event Flag
/// </summary>
/// <param name="flagNumber">Event Flag to check</param>
/// <param name="value">Event Flag status to set</param>
/// <remarks>Flag is Set (true) or not Set (false)</remarks>
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);
}
/// <summary>
/// Gets the <see cref="bool"/> status of the Flag at the specified offset and index.
/// </summary>
@ -195,7 +114,6 @@ namespace PKHeX.Core
/// <param name="value">Flag status to set</param>
/// <remarks>Flag is Set (true) or not Set (false)</remarks>
public virtual void SetFlag(int offset, int bitIndex, bool value) => FlagUtil.SetFlag(Data, offset, bitIndex, value);
#endregion
public virtual IReadOnlyList<InventoryPouch> Inventory { get => Array.Empty<InventoryPouch>(); set { } }

View file

@ -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;

View file

@ -3,7 +3,7 @@ using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core
{
public sealed class FestaBlock5 : SaveBlock
public sealed class FestaBlock5 : SaveBlock<SAV5B2W2>
{
public FestaBlock5(SAV5B2W2 SAV, int offset) : base(SAV) => Offset = offset;

View file

@ -1,23 +1,38 @@
using System.ComponentModel;
namespace PKHeX.Core
namespace PKHeX.Core;
/// <summary>
/// Base class for a savegame data reader.
/// </summary>
public abstract class SaveBlock
{
/// <summary>
/// Base class for a savegame data reader.
/// </summary>
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;
}
}
public abstract class SaveBlock<T> 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;
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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();

View file

@ -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;
}

View file

@ -10,18 +10,18 @@ namespace PKHeX.WinForms
{
public sealed partial class SAV_EventFlags : Form
{
private readonly EventWorkspace Editor;
private readonly EventWorkspace<IEventFlag37, ushort> Editor;
private readonly Dictionary<int, NumericUpDown> WorkDict = new();
private readonly Dictionary<int, int> 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<IEventFlag37, ushort>(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<IEventFlag37, ushort>(TB_OldSAV.Text, TB_NewSAV.Text);
if (diff.Message != EventWorkDiffCompatibility.Valid)
{
WinFormsUtil.Alert(diff.Message);
WinFormsUtil.Alert(diff.Message.GetMessage());
return;
}

View file

@ -0,0 +1,427 @@
namespace PKHeX.WinForms
{
sealed partial class SAV_EventFlags2
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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;
}
}

View file

@ -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<SAV2, byte> Editor;
private readonly Dictionary<int, NumericUpDown> WorkDict = new();
private readonly Dictionary<int, int> FlagDict = new();
private bool editing;
public SAV_EventFlags2(SAV2 sav)
{
InitializeComponent();
WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage);
var editor = Editor = new EventWorkspace<SAV2, byte>(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<SAV2, byte>(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]);
}
}
}

View file

@ -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;
}

View file

@ -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)