PKHeX/PKHeX.WinForms/Subforms/Save Editors/SAV_EventFlags.cs
2020-12-23 17:14:38 -08:00

458 lines
No EOL
16 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
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_EventFlags : Form
{
private readonly SaveFile Origin;
private readonly SaveFile SAV;
public SAV_EventFlags(SaveFile sav)
{
InitializeComponent();
WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage);
SAV = (Origin = sav).Clone();
DragEnter += Main_DragEnter;
DragDrop += Main_DragDrop;
flags = SAV.GetEventFlags();
Constants = SAV.GetEventConsts();
CB_Stats.Items.Clear();
for (int i = 0; i < Constants.Length; i++)
CB_Stats.Items.Add(i.ToString());
TLP_Flags.SuspendLayout();
TLP_Const.SuspendLayout();
TLP_Flags.Scroll += WinFormsUtil.PanelScroll;
TLP_Const.Scroll += WinFormsUtil.PanelScroll;
TLP_Flags.Controls.Clear();
TLP_Const.Controls.Clear();
AddFlagList(GetStringList("flags"));
AddConstList(GetStringList("const"));
TLP_Flags.ResumeLayout();
TLP_Const.ResumeLayout();
WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage);
Text = $"{Text} ({gamePrefix.ToUpper()})";
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 = flags.Length - 1;
NUD_Flag.Text = "0";
c_CustomFlag.Checked = flags[0];
}
private readonly bool[] flags;
private readonly ushort[] Constants;
private const string flagTag = "bool_";
private const string constTag = "const_";
private const string constCBTag = "cbconst_";
private const string flagLabelTag = "flag_";
private const string constLabelTag = "L_";
private bool editing;
private int constEntry = -1;
private string gamePrefix = "unk";
private void B_Cancel_Click(object sender, EventArgs e)
{
Close();
}
private void B_Save_Click(object sender, EventArgs e)
{
// Gather Updated Flags
foreach (CheckBox flag in TLP_Flags.Controls.OfType<CheckBox>())
flags[GetControlNum(flag)] = flag.Checked;
SAV.SetEventFlags(flags);
// Copy back Constants
ChangeConstantIndex(sender, EventArgs.Empty); // Trigger Saving
SAV.SetEventConsts(Constants);
HandleSpecialFlags();
Origin.CopyChangesFrom(SAV);
Close();
}
private void HandleSpecialFlags()
{
if (SAV is SAV7SM s7) // Ensure magearna event flag has magic constant
s7.UpdateMagearnaConstant();
}
private string[] GetStringList(string type)
{
gamePrefix = GetResourceSuffix(SAV.Version);
return GameLanguage.GetStrings(gamePrefix, GameInfo.CurrentLanguage, type);
}
private static string GetResourceSuffix(GameVersion ver)
{
return ver switch
{
GameVersion.X or GameVersion.Y or GameVersion.XY => "xy",
GameVersion.OR or GameVersion.AS or GameVersion.ORAS => "oras",
GameVersion.SN or GameVersion.MN or GameVersion.SM => "sm",
GameVersion.US or GameVersion.UM or GameVersion.USUM => "usum",
GameVersion.D or GameVersion.P or GameVersion.DP => "dp",
GameVersion.Pt or GameVersion.DPPt => "pt",
GameVersion.HG or GameVersion.SS or GameVersion.HGSS => "hgss",
GameVersion.B or GameVersion.W or GameVersion.BW => "bw",
GameVersion.B2 or GameVersion.W2 or GameVersion.B2W2 => "b2w2",
GameVersion.R or GameVersion.S or GameVersion.RS => "rs",
GameVersion.E => "e",
GameVersion.FR or GameVersion.LG or GameVersion.FRLG => "frlg",
GameVersion.C => "c",
GameVersion.GD or GameVersion.SV or GameVersion.GS => "gs",
_ => throw new ArgumentException(nameof(GameVersion))
};
}
private void AddFlagList(string[] list)
{
if (list.Length == 0)
{
TLP_Flags.Controls.Add(new Label { Text = MsgResearchRequired, Name = "TLP_Flags_Research", ForeColor = Color.Red, AutoSize = true }, 0, 0);
return;
}
// Get list
List<int> num = new();
List<string> desc = new();
foreach (string[] split in list.Select(s => s.Split('\t')).Where(split => split.Length == 2))
{
try
{
var flagIndex = split[0];
int n = TryParseHexDec(flagIndex);
if (num.Contains(n))
continue;
num.Add(n);
desc.Add(split[1]);
}
#pragma warning disable CA1031 // Do not catch general exception types
catch
#pragma warning restore CA1031 // Do not catch general exception types
{
// Ignore bad user values
Debug.WriteLine(string.Concat(split));
}
}
if (num.Count == 0)
{
TLP_Flags.Controls.Add(new Label { Text = MsgResearchRequired, Name = "TLP_Flags_Research", ForeColor = Color.Red, AutoSize = true }, 0, 0);
return;
}
for (int i = 0; i < num.Count; i++)
{
var lbl = new Label
{
Text = desc[i],
Name = gamePrefix + flagLabelTag + num[i].ToString("0000"),
Margin = Padding.Empty,
AutoSize = true
};
var chk = new CheckBox
{
Name = flagTag + num[i].ToString("0000"),
CheckAlign = ContentAlignment.MiddleLeft,
Margin = Padding.Empty,
Checked = flags[num[i]],
AutoSize = true
};
chk.CheckStateChanged += (o, args) => ToggleFlag(chk, args);
lbl.Click += (sender, e) => chk.Checked ^= true;
TLP_Flags.Controls.Add(chk, 0, i);
TLP_Flags.Controls.Add(lbl, 1, i);
}
}
private static int TryParseHexDec(string flag)
{
if (!flag.StartsWith("0x"))
return Convert.ToInt16(flag);
flag = flag.Substring(2);
return Convert.ToInt16(flag, 16);
}
private void AddConstList(string[] list)
{
if (list.Length == 0)
{
TLP_Const.Controls.Add(new Label { Text = MsgResearchRequired, Name = "TLP_Const_Research", ForeColor = Color.Red, AutoSize = true }, 0, 0);
return;
}
// Get list
List<int> num = new();
List<string> desc = new();
List<string> enums = new();
foreach (string[] split in list.Select(s => s.Split('\t')).Where(split => split.Length == 2 || split.Length == 3))
{
try
{
var c = split[0];
int n = TryParseHexDecConst(c);
if (num.Contains(n))
continue;
num.Add(n);
desc.Add(split[1]);
enums.Add(split.Length == 3 ? split[2] : string.Empty);
}
#pragma warning disable CA1031 // Do not catch general exception types
catch
{
// Ignore bad user values
Debug.WriteLine(string.Concat(split));
}
#pragma warning restore CA1031 // Do not catch general exception types
}
if (num.Count == 0)
{
TLP_Const.Controls.Add(new Label { Text = MsgResearchRequired, Name = "TLP_Const_Research", ForeColor = Color.Red, AutoSize = true }, 0, 0);
return;
}
for (int i = 0; i < num.Count; i++)
{
var lbl = new Label
{
Text = desc[i],
Name = gamePrefix + constLabelTag + num[i].ToString("0000"),
Margin = Padding.Empty,
AutoSize = true
};
var mtb = new NumericUpDown
{
Maximum = ushort.MaxValue,
Minimum = ushort.MinValue,
Value = Constants[num[i]],
Name = constTag + num[i].ToString("0000"),
Margin = Padding.Empty,
Width = 50,
};
var map = new[] { new ComboItem("Custom", -1 ) }.ToList();
if (!string.IsNullOrWhiteSpace(enums[i]))
{
foreach (var entry in enums[i].Split(','))
{
var spl = entry.Split(':');
map.Add(new ComboItem(spl[1], Convert.ToInt32(spl[0])));
}
}
var cb = new ComboBox
{
Margin = Padding.Empty,
Width = 150,
Name = constCBTag + num[i].ToString("0000"),
DropDownStyle = ComboBoxStyle.DropDownList,
BindingContext = BindingContext,
DropDownWidth = Width + 100
};
cb.InitializeBinding();
cb.DataSource = map;
cb.SelectedIndex = 0;
cb.SelectedValueChanged += (o, args) => ToggleConst(cb, args);
mtb.TextChanged += (o, args) => ToggleConst(mtb, args);
TLP_Const.Controls.Add(lbl, 0, i);
TLP_Const.Controls.Add(cb, 1, i);
TLP_Const.Controls.Add(mtb, 2, i);
int cid = Constants[num[i]];
if (map.Any(val => val.Value == cid))
cb.SelectedValue = cid;
}
}
private static int TryParseHexDecConst(string c)
{
if (!c.StartsWith("0x40"))
return Convert.ToInt16(c);
c = c.Substring(4);
return Convert.ToInt16(c, 16);
}
private static int GetControlNum(Control c)
{
string source = c.Name.Split('_')[1];
return int.TryParse(source, out var val) ? val : 0;
}
private void ChangeCustomBool(object sender, EventArgs e)
{
if (editing)
return;
editing = true;
flags[(int)NUD_Flag.Value] = c_CustomFlag.Checked;
var name = flagTag + NUD_Flag.Value.ToString("0000");
if (TLP_Flags.Controls[name] is CheckBox c)
c.Checked = c_CustomFlag.Checked;
editing = false;
}
private void ChangeCustomFlag(object sender, EventArgs e)
{
int flag = (int)NUD_Flag.Value;
if (flag >= flags.Length)
{
c_CustomFlag.Checked = false;
c_CustomFlag.Enabled = false;
NUD_Flag.BackColor = Color.Red;
}
else
{
c_CustomFlag.Enabled = true;
NUD_Flag.ResetBackColor();
c_CustomFlag.Checked = flags[flag];
}
}
private void ChangeCustomFlag(object sender, KeyEventArgs e) => ChangeCustomFlag(sender, (EventArgs)e);
private void ToggleFlag(object sender, EventArgs e)
{
if (editing)
return;
editing = true;
int flagnum = GetControlNum((CheckBox) sender);
flags[flagnum] = ((CheckBox)sender).Checked;
if (NUD_Flag.Value == flagnum)
c_CustomFlag.Checked = flags[flagnum];
editing = false;
}
private void ChangeCustomConst(object sender, EventArgs e)
{
if (editing)
return;
editing = true;
Constants[CB_Stats.SelectedIndex] = (ushort)(Util.ToUInt32(((MaskedTextBox)sender).Text) & 0xFFFF);
var name = constTag + CB_Stats.SelectedIndex.ToString("0000");
if (TLP_Flags.Controls[name] is MaskedTextBox m)
m.Text = MT_Stat.Text;
editing = false;
}
private void ChangeConstantIndex(object sender, EventArgs e)
{
if (Constants.Length == 0)
return;
if (constEntry > -1) // Set Entry
Constants[constEntry] = (ushort)Math.Min(Util.ToUInt32(MT_Stat.Text), 0xFFFF);
constEntry = CB_Stats.SelectedIndex; // Get Entry
MT_Stat.Text = Constants[constEntry].ToString();
}
private void ToggleConst(object sender, EventArgs e)
{
if (editing)
return;
int constnum = GetControlNum((Control)sender);
if (sender is ComboBox cb)
{
var nud = (NumericUpDown)TLP_Const.GetControlFromPosition(2, TLP_Const.GetRow(cb));
var sel_val = (int)cb.SelectedValue;
editing = true;
nud.Enabled = sel_val == -1;
if (sel_val != -1)
nud.Value = (ushort)sel_val;
Constants[constnum] = (ushort)Util.ToUInt32(nud.Text);
editing = false;
}
else if (sender is NumericUpDown nud)
{
editing = true;
Constants[constnum] = (ushort)Util.ToUInt32(nud.Text);
if (constnum == CB_Stats.SelectedIndex)
MT_Stat.Text = Constants[constnum].ToString();
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)
{
if (sender == B_LoadOld)
TB_OldSAV.Text = path;
else
TB_NewSAV.Text = path;
}
private void DiffSaves()
{
var diff = new EventBlockDiff(TB_OldSAV.Text, TB_NewSAV.Text);
if (!string.IsNullOrWhiteSpace(diff.Message))
{
WinFormsUtil.Alert(diff.Message);
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;
}
if (DialogResult.Yes == WinFormsUtil.Prompt(MessageBoxButtons.YesNo, "Copy Event Constant diff to clipboard?"))
WinFormsUtil.SetClipboardText(string.Join(Environment.NewLine, diff.WorkDiff));
}
private static void Main_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
e.Effect = DragDropEffects.Copy;
}
private void Main_DragDrop(object sender, DragEventArgs e)
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
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]);
}
}
}