From 4f910ca841bf2bf18c1ccd3850db80ac0aa92a0b Mon Sep 17 00:00:00 2001 From: Yako Date: Sat, 8 Nov 2025 16:29:28 +0100 Subject: [PATCH] add custom charmap system and an editor to easily add and remove aliases --- DS_Map/DSPRE.csproj | 10 + DS_Map/DSUtils/TextConverter.cs | 116 +----- DS_Map/Main Window.Designer.cs | 38 +- DS_Map/Main Window.cs | 6 + DS_Map/Main Window.resx | 162 ++++---- DS_Map/Resources/CharMaps/CharMapManager.cs | 206 ++++++++++ .../CharMaps/CharMapManagerForm.Designer.cs | 282 +++++++++++++ .../Resources/CharMaps/CharMapManagerForm.cs | 374 ++++++++++++++++++ .../CharMaps/CharMapManagerForm.resx | 126 ++++++ 9 files changed, 1127 insertions(+), 193 deletions(-) create mode 100644 DS_Map/Resources/CharMaps/CharMapManager.cs create mode 100644 DS_Map/Resources/CharMaps/CharMapManagerForm.Designer.cs create mode 100644 DS_Map/Resources/CharMaps/CharMapManagerForm.cs create mode 100644 DS_Map/Resources/CharMaps/CharMapManagerForm.resx diff --git a/DS_Map/DSPRE.csproj b/DS_Map/DSPRE.csproj index c28818e..6bb0208 100644 --- a/DS_Map/DSPRE.csproj +++ b/DS_Map/DSPRE.csproj @@ -344,6 +344,13 @@ ReopenProjectConfirmation.cs + + + Form + + + CharMapManagerForm.cs + Form @@ -626,6 +633,9 @@ ReopenProjectConfirmation.cs + + CharMapManagerForm.cs + CustomScrcmdManager.cs diff --git a/DS_Map/DSUtils/TextConverter.cs b/DS_Map/DSUtils/TextConverter.cs index 3373d8f..0cfd5aa 100644 --- a/DS_Map/DSUtils/TextConverter.cs +++ b/DS_Map/DSUtils/TextConverter.cs @@ -4,93 +4,13 @@ using System.Collections.Generic; using System.IO; using System.Text; using System.Windows.Forms; -using System.Xml.Linq; +using DSPRE.CharMaps; namespace DSPRE { internal class TextConverter { - private static Dictionary decodeMap; - private static Dictionary encodeMap; - private static Dictionary commandMap; - - private static bool mapsInitialized = false; - - public static Dictionary GetDecodingMap() - { - if (!mapsInitialized) - { - InitializeCharMaps(); - } - return decodeMap; - } - - public static Dictionary GetEncodingMap() - { - if (!mapsInitialized) - { - InitializeCharMaps(); - } - return encodeMap; - } - - private static void InitializeCharMaps() - { - decodeMap = new Dictionary(); - encodeMap = new Dictionary(); - commandMap = new Dictionary(); - - string charmapPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Tools", "charmap.xml"); - if (!File.Exists(charmapPath)) - throw new FileNotFoundException("Charmap XML file not found.", charmapPath); - - var xml = XDocument.Load(charmapPath, LoadOptions.PreserveWhitespace); - - foreach (var entry in xml.Descendants("entry")) - { - var code = entry.Attribute("code")?.Value; - var kind = entry.Attribute("kind")?.Value; - var value = entry.Value; - - if (code == null || kind == null || value == null) - { - AppLogger.Error("Found charmap entry with null value."); - continue; - } - - ushort codeValue; - string codeKind = kind.ToLower(); - string chars = value.ToString(); - - if (!ushort.TryParse(code, System.Globalization.NumberStyles.HexNumber, null, out codeValue)) - { - AppLogger.Error($"Invalid code value in charmap: {code}"); - continue; - } - - if (codeKind == "char" || codeKind == "escape") - { - decodeMap[codeValue] = chars; - encodeMap[chars] = codeValue; - } - else if (codeKind == "alias") - { - encodeMap[chars] = codeValue; - } - else if (codeKind == "command") - { - commandMap[codeValue] = chars; - } - else - { - AppLogger.Error($"Unknown kind '{kind}' in charmap entry."); - } - } - - mapsInitialized = true; - } - public static List ReadMessageFromStream(Stream stream, out UInt16 key) { List messages = new List(); @@ -248,9 +168,9 @@ namespace DSPRE { ushort code = message[i]; // Regular characters and escape sequences - if (GetDecodingMap().ContainsKey(code)) + if (CharMapManager.GetDecodingMap().ContainsKey(code)) { - decodedMessage.Append(GetDecodingMap()[code]); + decodedMessage.Append(CharMapManager.GetDecodingMap()[code]); i++; } // Commands @@ -294,9 +214,9 @@ namespace DSPRE while (i < message.Length) { // Regular characters - if (GetEncodingMap().ContainsKey(message[i].ToString())) + if (CharMapManager.GetEncodingMap().ContainsKey(message[i].ToString())) { - encodedMessage.Add(GetEncodingMap()[message[i].ToString()]); + encodedMessage.Add(CharMapManager.GetEncodingMap()[message[i].ToString()]); i++; } // Escape sequences @@ -317,9 +237,9 @@ namespace DSPRE else if (i + 1 < message.Length) { string escapeSeq = message.Substring(i, 2); - if (GetEncodingMap().ContainsKey(escapeSeq)) + if (CharMapManager.GetEncodingMap().ContainsKey(escapeSeq)) { - encodedMessage.Add(GetEncodingMap()[escapeSeq]); + encodedMessage.Add(CharMapManager.GetEncodingMap()[escapeSeq]); i += 2; continue; } @@ -363,9 +283,9 @@ namespace DSPRE if (endIndex != -1) { string multiCharSeq = message.Substring(i, endIndex - i + 1); - if (GetEncodingMap().ContainsKey(multiCharSeq)) + if (CharMapManager.GetEncodingMap().ContainsKey(multiCharSeq)) { - encodedMessage.Add(GetEncodingMap()[multiCharSeq]); + encodedMessage.Add(CharMapManager.GetEncodingMap()[multiCharSeq]); i = endIndex + 1; continue; } @@ -424,7 +344,7 @@ namespace DSPRE // Special case for string buffer vars that have 1 byte command ids int specialByte = 0; - if (!commandMap.ContainsKey(commandID) && commandMap.ContainsKey((ushort)(commandID & 0xFF00))) + if (!CharMapManager.GetCommandMap().ContainsKey(commandID) && CharMapManager.GetCommandMap().ContainsKey((ushort)(commandID & 0xFF00))) { specialByte = (ushort)(commandID & 0x00FF); commandID = (ushort)(commandID & 0xFF00); @@ -434,9 +354,9 @@ namespace DSPRE StringBuilder sb = new StringBuilder(); sb.Append("{"); - if (commandMap.ContainsKey(commandID)) + if (CharMapManager.GetCommandMap().ContainsKey(commandID)) { - sb.Append($"{commandMap[commandID]}"); + sb.Append($"{CharMapManager.GetCommandMap()[commandID]}"); sb.Append($", {specialByte}"); for (int i = 0; i < parameters.Length; i++) @@ -492,9 +412,9 @@ namespace DSPRE encodedCommand.Add(0xFFFE); // Get ID from name or parse hex - if (commandMap.ContainsValue(commandName)) + if (CharMapManager.GetCommandMap().ContainsValue(commandName)) { - encodedCommand.Add(commandMap.Reverse()[commandName]); + encodedCommand.Add(CharMapManager.GetCommandMap().Reverse()[commandName]); } else if (ushort.TryParse(commandName, out ushort commandID)) { @@ -564,9 +484,9 @@ namespace DSPRE if (curChar == 0x1FF) break; - if (GetDecodingMap().ContainsKey(curChar)) + if (CharMapManager.GetDecodingMap().ContainsKey(curChar)) { - decoded.Append(GetDecodingMap()[curChar]); + decoded.Append(CharMapManager.GetDecodingMap()[curChar]); } else { @@ -589,9 +509,9 @@ namespace DSPRE // Get list of characters to encode foreach (char c in nameContent) { - if (GetEncodingMap().ContainsKey(c.ToString())) + if (CharMapManager.GetEncodingMap().ContainsKey(c.ToString())) { - var code = GetEncodingMap()[c.ToString()]; + var code = CharMapManager.GetEncodingMap()[c.ToString()]; // Ensure code fits in 9 bits if (code >> 9 != 0) diff --git a/DS_Map/Main Window.Designer.cs b/DS_Map/Main Window.Designer.cs index 13a8eb9..3a2cb3d 100644 --- a/DS_Map/Main Window.Designer.cs +++ b/DS_Map/Main Window.Designer.cs @@ -132,8 +132,8 @@ this.wildEditorButton = new System.Windows.Forms.ToolStripButton(); this.pokemonEditorButton = new System.Windows.Forms.ToolStripButton(); this.moveEditorButton = new System.Windows.Forms.ToolStripButton(); - this.tradeEditorButton = new System.Windows.Forms.ToolStripButton(); this.itemEditorButton = new System.Windows.Forms.ToolStripButton(); + this.tradeEditorButton = new System.Windows.Forms.ToolStripButton(); this.separator_afterEditors = new System.Windows.Forms.ToolStripSeparator(); this.scriptCommandsButton = new System.Windows.Forms.ToolStripButton(); this.romToolboxToolStripButton = new System.Windows.Forms.ToolStripButton(); @@ -152,6 +152,7 @@ this.weatherMapEditor = new System.Windows.Forms.ToolStripButton(); this.versionLabel = new System.Windows.Forms.Label(); this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.openCharmapManagerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.mainTabControl.SuspendLayout(); this.headerEditorTabPage.SuspendLayout(); this.matrixEditorTabPage.SuspendLayout(); @@ -716,6 +717,7 @@ this.listBasedBatchRenameToolStripMenuItem, this.listBuilderToolStripMenuItem, this.nSBMDUtilityToolStripMenuItem, + this.openCharmapManagerToolStripMenuItem, this.generateCSVToolStripMenuItem}); this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; this.aboutToolStripMenuItem.Size = new System.Drawing.Size(46, 20); @@ -1263,19 +1265,6 @@ this.moveEditorButton.Text = "Move Editor"; this.moveEditorButton.Click += new System.EventHandler(this.moveEditorToolStripMenuItem_Click); // - // tradeEditorButton - // - this.tradeEditorButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; - this.tradeEditorButton.Enabled = false; - this.tradeEditorButton.Image = global::DSPRE.Properties.Resources.trade_editor_icon; - this.tradeEditorButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; - this.tradeEditorButton.ImageTransparentColor = System.Drawing.Color.Magenta; - this.tradeEditorButton.Margin = new System.Windows.Forms.Padding(4, 6, 0, 2); - this.tradeEditorButton.Name = "tradeEditorButton"; - this.tradeEditorButton.Size = new System.Drawing.Size(36, 36); - this.tradeEditorButton.Text = "Trade Editor"; - this.tradeEditorButton.Click += new System.EventHandler(this.tradeEditorToolStripMenuItem_Click); - // // itemEditorButton // this.itemEditorButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; @@ -1289,6 +1278,19 @@ this.itemEditorButton.Text = "Item Editor"; this.itemEditorButton.Click += new System.EventHandler(this.itemEditorToolStripMenuItem_Click); // + // tradeEditorButton + // + this.tradeEditorButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.tradeEditorButton.Enabled = false; + this.tradeEditorButton.Image = global::DSPRE.Properties.Resources.trade_editor_icon; + this.tradeEditorButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.tradeEditorButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.tradeEditorButton.Margin = new System.Windows.Forms.Padding(4, 6, 0, 2); + this.tradeEditorButton.Name = "tradeEditorButton"; + this.tradeEditorButton.Size = new System.Drawing.Size(36, 36); + this.tradeEditorButton.Text = "Trade Editor"; + this.tradeEditorButton.Click += new System.EventHandler(this.tradeEditorToolStripMenuItem_Click); + // // separator_afterEditors // this.separator_afterEditors.Name = "separator_afterEditors"; @@ -1457,6 +1459,13 @@ this.versionLabel.Text = "ROM:"; this.versionLabel.Visible = false; // + // openCharmapManagerToolStripMenuItem + // + this.openCharmapManagerToolStripMenuItem.Name = "openCharmapManagerToolStripMenuItem"; + this.openCharmapManagerToolStripMenuItem.Size = new System.Drawing.Size(220, 22); + this.openCharmapManagerToolStripMenuItem.Text = "Open Charmap Manager"; + this.openCharmapManagerToolStripMenuItem.Click += new System.EventHandler(this.openCharmapManagerToolStripMenuItem_Click); + // // MainProgram // this.AllowDrop = true; @@ -1676,6 +1685,7 @@ private System.Windows.Forms.ToolStripSeparator separator_afterEditors; private System.Windows.Forms.ToolStripButton itemEditorButton; private System.Windows.Forms.ToolStripButton tradeEditorButton; + private System.Windows.Forms.ToolStripMenuItem openCharmapManagerToolStripMenuItem; } } diff --git a/DS_Map/Main Window.cs b/DS_Map/Main Window.cs index 3ab0dec..1ebf10b 100644 --- a/DS_Map/Main Window.cs +++ b/DS_Map/Main Window.cs @@ -1908,6 +1908,12 @@ namespace DSPRE } } + private void openCharmapManagerToolStripMenuItem_Click(object sender, EventArgs e) + { + CharMaps.CharMapManagerForm charMapManager = new CharMaps.CharMapManagerForm(); + charMapManager.ShowDialog(); + } + #endregion } } diff --git a/DS_Map/Main Window.resx b/DS_Map/Main Window.resx index 4658505..1da3da7 100644 --- a/DS_Map/Main Window.resx +++ b/DS_Map/Main Window.resx @@ -124,87 +124,87 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 - ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACM - EgAAAk1TRnQBSQFMAgEBCgEAAfgBHgH4AR4BEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo - AwABQAMAATADAAEBAQABCAYAAQwYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA - AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 - AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA - AWYDAAGZAwABzAIAATMDAAIzAgABMwFmAgABMwGZAgABMwHMAgABMwH/AgABZgMAAWYBMwIAAmYCAAFm - AZkCAAFmAcwCAAFmAf8CAAGZAwABmQEzAgABmQFmAgACmQIAAZkBzAIAAZkB/wIAAcwDAAHMATMCAAHM - AWYCAAHMAZkCAALMAgABzAH/AgAB/wFmAgAB/wGZAgAB/wHMAQABMwH/AgAB/wEAATMBAAEzAQABZgEA - ATMBAAGZAQABMwEAAcwBAAEzAQAB/wEAAf8BMwIAAzMBAAIzAWYBAAIzAZkBAAIzAcwBAAIzAf8BAAEz - AWYCAAEzAWYBMwEAATMCZgEAATMBZgGZAQABMwFmAcwBAAEzAWYB/wEAATMBmQIAATMBmQEzAQABMwGZ - AWYBAAEzApkBAAEzAZkBzAEAATMBmQH/AQABMwHMAgABMwHMATMBAAEzAcwBZgEAATMBzAGZAQABMwLM - AQABMwHMAf8BAAEzAf8BMwEAATMB/wFmAQABMwH/AZkBAAEzAf8BzAEAATMC/wEAAWYDAAFmAQABMwEA - AWYBAAFmAQABZgEAAZkBAAFmAQABzAEAAWYBAAH/AQABZgEzAgABZgIzAQABZgEzAWYBAAFmATMBmQEA - AWYBMwHMAQABZgEzAf8BAAJmAgACZgEzAQADZgEAAmYBmQEAAmYBzAEAAWYBmQIAAWYBmQEzAQABZgGZ - AWYBAAFmApkBAAFmAZkBzAEAAWYBmQH/AQABZgHMAgABZgHMATMBAAFmAcwBmQEAAWYCzAEAAWYBzAH/ - AQABZgH/AgABZgH/ATMBAAFmAf8BmQEAAWYB/wHMAQABzAEAAf8BAAH/AQABzAEAApkCAAGZATMBmQEA - AZkBAAGZAQABmQEAAcwBAAGZAwABmQIzAQABmQEAAWYBAAGZATMBzAEAAZkBAAH/AQABmQFmAgABmQFm - ATMBAAGZATMBZgEAAZkBZgGZAQABmQFmAcwBAAGZATMB/wEAApkBMwEAApkBZgEAA5kBAAKZAcwBAAKZ - Af8BAAGZAcwCAAGZAcwBMwEAAWYBzAFmAQABmQHMAZkBAAGZAswBAAGZAcwB/wEAAZkB/wIAAZkB/wEz - AQABmQHMAWYBAAGZAf8BmQEAAZkB/wHMAQABmQL/AQABzAMAAZkBAAEzAQABzAEAAWYBAAHMAQABmQEA - AcwBAAHMAQABmQEzAgABzAIzAQABzAEzAWYBAAHMATMBmQEAAcwBMwHMAQABzAEzAf8BAAHMAWYCAAHM - AWYBMwEAAZkCZgEAAcwBZgGZAQABzAFmAcwBAAGZAWYB/wEAAcwBmQIAAcwBmQEzAQABzAGZAWYBAAHM - ApkBAAHMAZkBzAEAAcwBmQH/AQACzAIAAswBMwEAAswBZgEAAswBmQEAA8wBAALMAf8BAAHMAf8CAAHM - Af8BMwEAAZkB/wFmAQABzAH/AZkBAAHMAf8BzAEAAcwC/wEAAcwBAAEzAQAB/wEAAWYBAAH/AQABmQEA - AcwBMwIAAf8CMwEAAf8BMwFmAQAB/wEzAZkBAAH/ATMBzAEAAf8BMwH/AQAB/wFmAgAB/wFmATMBAAHM - AmYBAAH/AWYBmQEAAf8BZgHMAQABzAFmAf8BAAH/AZkCAAH/AZkBMwEAAf8BmQFmAQAB/wKZAQAB/wGZ - AcwBAAH/AZkB/wEAAf8BzAIAAf8BzAEzAQAB/wHMAWYBAAH/AcwBmQEAAf8CzAEAAf8BzAH/AQAC/wEz - AQABzAH/AWYBAAL/AZkBAAL/AcwBAAJmAf8BAAFmAf8BZgEAAWYC/wEAAf8CZgEAAf8BZgH/AQAC/wFm - AQABIQEAAaUBAANfAQADdwEAA4YBAAOWAQADywEAA7IBAAPXAQAD3QEAA+MBAAPqAQAD8QEAA/gBAAHw - AfsB/wEAAaQCoAEAA4ADAAH/AgAB/wMAAv8BAAH/AwAB/wEAAf8BAAL/AgAD/xMABLwEAAS8KwAGvAIA - BOkBvAMABOkBvCoAASYBHgQAArwBAATpBAAE6QG8KgABJgEeAgABJgEeAQABvAEAAukBvAIABLwBAALp - AbwtAAG8ASYBHgMAAukBvAEABAsCvALpAbwsAAEmAR4CAAG8AgAC6QG8AgsBvAEAAgsBvALpAbwmAAO8 - AgABJgEeAgABvAMAAukBvAILArwCCwEAAukBvCUAAc0BCgEAAbwBAAEmAR4BAAHsAwABvAEAAukBvAUL - AgAC6QG8JQABzQEKAQACvAEmAR4BAAESArwBAAG8AQAC6QG8AgsCvAMAAukBvCQAAc0BCgMAAbwBAAEm - AR4GAALpAbwBAAILA7wBAALpAbwkAAHNAQoBEgHsAQABEgG8CAAC6QG8AgADCwIAAukBvCQAAc0BCgES - AgABEgG8CAAC6QO8BAABvALpAbwjAAHNAQoBAAG8AwABvAgABOkBvAMABOkBvAEAAbwhAAHNAQoBAAG8 - AwAB7AG8BwAE6QQABOkBGwExAZkhAAFlGwADMT0AARsBMQEbKQAB7AESAe8FAAH0EQAKEhgAAvMC6wHv - AewB6wHzBQAB6gH0BQAC8gH0BgADEgZKAxIUAAHzAfEBvALsAQcBvAEHAewB6wEHBAABDgESAe0EAAHw - ASIBKQHrAfMFAAESAkoGbgJKARIDAAEPBwABDwUAAQ8BAAG8AfcB7QGSAe8BvAEHAfAB9AHyAesB6gQA - AQ4BbQHtAwABBwEjAisBKgHrAfMDAAESAUoBbgFKBm4BSgFuAUoBEgEAAQ8J7wEPAwAB7wIAAW0C9wHw - AbwB8wLyAvQB7QHrAQcDAAEHARUB6gPtASkBKwElAR8BJQEqAesB9AIAARIBSgFuAUoGkwFKAW4BSgES - AgAB7wEBAR8G7wMAAu8CAAG8Ae8B8QH0AfYB8gLwAfQB9gHvAewBbQMAAfQBEAHrAUoBMAEDASUBFwH5 - AiABJQEqAeoCAAESAUoBkwFuBkkBbgGTAUoBEgIAAe8CAQEfBe8BAAIPAu8CAAHyAe8C9gH3AW0B6wHy - AfQB9gHzAesB7AEHAgAB9AEQAXMBRQEkASAB+QEXAfkBIAEfASABMQEiAfQBAAESAZMBbgFJBmwBSQFu - AZMBEgIAAe8BAQEfBu8DAALvAwAB7wHzAbwBkgHwAfcB9gH3Ae0BBwHrAewB7wMAAfIBEQFFAR8CIAH5 - ARcB+QEgAR8BMQEiAfQBAAESAZMBSQJsBHECbAFJAZMBEgEAAQ8J7wEPAwAB7wMAAQcB8AFEASUBGgKS - AfIC8wH0ARIB6wHyAgAB8gFDAewBRQEfASAB+QEXASYBMQE3ATABFAIAAUkBbgFsAXEBlwRxAZcBcQFs - AW4BEgIAAQ8HAAEPBQABDwIAAfQBbQEsASUB6wH0AvEC7wHyAusBvAIAAfMBbQHrAUUBHwIgAfkBMQFR - Am0B8wIAAUkCbAGXAnEClwJxAZcCbAFJAgABFQEPARUDAAEVAQ8BFQgAARwBLAErAeoBBwG8Au8B9AK8 - AfMCbQMAAfABEwFFAR8BJAIrAVEBvAUAAUkBbAJxBpcCcQFsAUkBAAEVAfcBBwH3ARUBAAEVAfcBBwH3 - ARUGAAHvAisBSwHvAQcBvAHxBAcB9AGLAeoDAAHwAREBSwEkASMBAwFKAfAHAAFsAXEDlwJ4A5cBcQFs - AgABDwEHAQABBwEPAQABDwEHAQABBwEPBQAB7wErASwBRQK8AQcB7wEHAbwB8AEZAtsBbAEHAwAB8AEj - AisBDgHwAfEIAAFJAXEClwF4BZcBcQFJAgABFQH3AQcB9wEVAQABFQH3AQcB9wEVBQABEgEsAUUB8gHz - Ae8B8gHxAbsC2gGzAosB8wQAAfABIwE3ATEBDgsAAUkBcQKXAXgDlwFxAUkEAAEVAQ8BFQMAARUBDwEV - BwABEgG8AgABBwGzAdQBswJsAe8IAAHvAg4B8wwAAWwBcQSXAXEBbBkAAQcCEgG8HQAEcRcAAfQL8wH0 - EwAN8xMAAQcL7wEHAgAPEAEAAQcNswEHBwAEQwcAAe8BAAG7BrMBuwIAAe8CAAEQBjgBEAbTARABAAGz - DQABswUAAkME8AJDBQAB7wEAAbMGAAGzAgAB7wIAARACOAMAATgBEALTAgAC0wEQAQABsw0AAbMEAAFD - CPABQwQAAe8BAAGtAQABlwJWAZcBAAGzAgAB7wIAARADOAEAAjgBEAHTAQAC0wEAAdMBEAEAAbMBAAFW - CVABVgEAAbMDAAFDA/ABkgJDAZID8AFDAwAB7wEAAa0BAARWAQABswH0AQAB7wIAARACOAIAAjgBEAHT - AQAC0wEAAdMBEAEAAbMBAAtWAQABswMAAUMB8AGSARIBQwHwAZIBQwESAZIB8AFDAwAB7wEAAa0BAARW - AQABswH0AQAB7wIAARADOAEAAjgBEALTAgAC0wEQAQABswEAC1YBAAGzAgABQwHwARICYwFDAQAB8AFD - AmMBEgHwAUMCAAHvAQABrQEABHgBAAGzAfQBAAHvAgABEAY4ARAG0wEQAQABswEAC1YBAAGzAgABQwES - BEcCQwRHARIBQwIAAe8BAAGtBgABswHzAQAB7wIADxABAAGzAQALVgEAAbMCAAFDDEcBQwIAAe8BAAG7 - Aq0EswG7AfMBAAHvAgABEAZHARAGAAEQAQABswEAC1YBAAGzAgABQwxHAUMCAAHvAQAB8wP0AfMB9AQA - Ae8CAAEQAkcCAAJHARAGAAEQAQABswEAC1YBAAGzAwABQwJHApQGRwFDAwAB7wEAAXQCMgEsAXQBAAG8 - AvcBAAHvAgABEAFHAQACRwEAAUcBEAYAARABAAGzAQABVgl4AVYBAAGzAwABQwFHBJQFRwFDAwAB7wEA - ASwDwwFTAQAB9wIAAfMB8AIAARABRwEAAkcBAAFHARAGAAEQAQABswEAC3gBAAGzBAABQwSUBEcBQwQA - Ae8BAAF0AywBdAEAAfcBAAHzAfADAAEQAkcCAAJHARAGAAEQAQABswEAC3gBAAGzBQACQwGUA0cCQwUA - Ae8IAAHzAfAEAAEQBkcBEAYAARABAAGzDQABswcABEMHAAEHCO8BBwUADxABAAEJDbMBCREAAUIBTQE+ - BwABPgMAASgDAAFAAwABMAMAAQEBAAEBBQABgAEBFgAD/wEAAv8CwwQAAf8BgQKDBAAB/wEAAYcBgwQA - Af8BMAGMASMEAAH/AeEBiAEDBAAB/wHBAYEBAwQAAfEBgwGAASMEAAHhAQ4BgAFjBAAB4AEAAYAB4wQA - AcABgQGIASMEAAHAAX8BjAFjBAABxAF/AYMBwwQAAYQBfwGDAYIEAAGEAT8BhwGABAABjAF/Af8B+AQA - A/8B+AQAAf8BjwG/Af8B4AEHAv8B+AEHAo8BwAEDAv8BwAEHAY8BBwHAAQMBgAE8AYABBwGOAQMBgAEB - AQABGAGAAQMBgAEBAYABAQIAAYABAwGAAQEBgAEBAgABgAEBAYABAAGAAQECAAHAAQEBwAEAAYABAQEA - ARgBwAEAAcABAQGAAQEBgAE8AcABAAHAAQEBgAEBAY4BPwHAAQAB4AEPAYABAQEEAR8BgAEAAeABHwHA - AQMBJAGfAgAB4AE/AcABAwEEAR8BAAEBAeAB/wHgAQcBjgE/AZgBDwHwAf8B8AEPAv8B+AF/Av8B/AE/ - Av8BgAEDAv8BgAEDAv8BgAEDAQABAQEAAQEB/AE/AaABGwEAAQEBfwH9AfABDwGvAdsBAAExAX8B/QHg - AQcBqAFbAQABSQFAAQUBwAEDAagBSwEAAUkBQAEFAcABAwGoAUsBAAExAUABBQGBAQEBqAFLAQABAQFA - AQUBgAEBAa8BywEAAQEBQAEFAYABAQGgAQsBAAH9AUABBQGAAQEBoAF7ARgBxQFAAQUBwAEDAaABiwEk - Ae0BQAEFAcABAwGgAbMBJAHNAUABBQHgAQcBoAGnARgB7QFAAQUB8AEPAb8BzwEAAf0BfwH9AfwBPwGA - AR8BAAEBAQABAQL/Cw== + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACK + EgAAAk1TRnQBSQFMAgEBCgIAAR8BAAEfARABAAEQAQAE/wEJAQAI/wFCAU0BNgEEBgABNgEEAgABKAMA + AUADAAEwAwABAQEAAQgGAAEMGAABgAIAAYADAAKAAQABgAMAAYABAAGAAQACgAIAA8ABAAHAAdwBwAEA + AfABygGmAQABMwUAATMBAAEzAQABMwEAAjMCAAMWAQADHAEAAyIBAAMpAQADVQEAA00BAANCAQADOQEA + AYABfAH/AQACUAH/AQABkwEAAdYBAAH/AewBzAEAAcYB1gHvAQAB1gLnAQABkAGpAa0CAAH/ATMDAAFm + AwABmQMAAcwCAAEzAwACMwIAATMBZgIAATMBmQIAATMBzAIAATMB/wIAAWYDAAFmATMCAAJmAgABZgGZ + AgABZgHMAgABZgH/AgABmQMAAZkBMwIAAZkBZgIAApkCAAGZAcwCAAGZAf8CAAHMAwABzAEzAgABzAFm + AgABzAGZAgACzAIAAcwB/wIAAf8BZgIAAf8BmQIAAf8BzAEAATMB/wIAAf8BAAEzAQABMwEAAWYBAAEz + AQABmQEAATMBAAHMAQABMwEAAf8BAAH/ATMCAAMzAQACMwFmAQACMwGZAQACMwHMAQACMwH/AQABMwFm + AgABMwFmATMBAAEzAmYBAAEzAWYBmQEAATMBZgHMAQABMwFmAf8BAAEzAZkCAAEzAZkBMwEAATMBmQFm + AQABMwKZAQABMwGZAcwBAAEzAZkB/wEAATMBzAIAATMBzAEzAQABMwHMAWYBAAEzAcwBmQEAATMCzAEA + ATMBzAH/AQABMwH/ATMBAAEzAf8BZgEAATMB/wGZAQABMwH/AcwBAAEzAv8BAAFmAwABZgEAATMBAAFm + AQABZgEAAWYBAAGZAQABZgEAAcwBAAFmAQAB/wEAAWYBMwIAAWYCMwEAAWYBMwFmAQABZgEzAZkBAAFm + ATMBzAEAAWYBMwH/AQACZgIAAmYBMwEAA2YBAAJmAZkBAAJmAcwBAAFmAZkCAAFmAZkBMwEAAWYBmQFm + AQABZgKZAQABZgGZAcwBAAFmAZkB/wEAAWYBzAIAAWYBzAEzAQABZgHMAZkBAAFmAswBAAFmAcwB/wEA + AWYB/wIAAWYB/wEzAQABZgH/AZkBAAFmAf8BzAEAAcwBAAH/AQAB/wEAAcwBAAKZAgABmQEzAZkBAAGZ + AQABmQEAAZkBAAHMAQABmQMAAZkCMwEAAZkBAAFmAQABmQEzAcwBAAGZAQAB/wEAAZkBZgIAAZkBZgEz + AQABmQEzAWYBAAGZAWYBmQEAAZkBZgHMAQABmQEzAf8BAAKZATMBAAKZAWYBAAOZAQACmQHMAQACmQH/ + AQABmQHMAgABmQHMATMBAAFmAcwBZgEAAZkBzAGZAQABmQLMAQABmQHMAf8BAAGZAf8CAAGZAf8BMwEA + AZkBzAFmAQABmQH/AZkBAAGZAf8BzAEAAZkC/wEAAcwDAAGZAQABMwEAAcwBAAFmAQABzAEAAZkBAAHM + AQABzAEAAZkBMwIAAcwCMwEAAcwBMwFmAQABzAEzAZkBAAHMATMBzAEAAcwBMwH/AQABzAFmAgABzAFm + ATMBAAGZAmYBAAHMAWYBmQEAAcwBZgHMAQABmQFmAf8BAAHMAZkCAAHMAZkBMwEAAcwBmQFmAQABzAKZ + AQABzAGZAcwBAAHMAZkB/wEAAswCAALMATMBAALMAWYBAALMAZkBAAPMAQACzAH/AQABzAH/AgABzAH/ + ATMBAAGZAf8BZgEAAcwB/wGZAQABzAH/AcwBAAHMAv8BAAHMAQABMwEAAf8BAAFmAQAB/wEAAZkBAAHM + ATMCAAH/AjMBAAH/ATMBZgEAAf8BMwGZAQAB/wEzAcwBAAH/ATMB/wEAAf8BZgIAAf8BZgEzAQABzAJm + AQAB/wFmAZkBAAH/AWYBzAEAAcwBZgH/AQAB/wGZAgAB/wGZATMBAAH/AZkBZgEAAf8CmQEAAf8BmQHM + AQAB/wGZAf8BAAH/AcwCAAH/AcwBMwEAAf8BzAFmAQAB/wHMAZkBAAH/AswBAAH/AcwB/wEAAv8BMwEA + AcwB/wFmAQAC/wGZAQAC/wHMAQACZgH/AQABZgH/AWYBAAFmAv8BAAH/AmYBAAH/AWYB/wEAAv8BZgEA + ASEBAAGlAQADXwEAA3cBAAOGAQADlgEAA8sBAAOyAQAD1wEAA90BAAPjAQAD6gEAA/EBAAP4AQAB8AH7 + Af8BAAGkAqABAAOAAwAB/wIAAf8DAAL/AQAB/wMAAf8BAAH/AQAC/wIAA/8TAAS8BAAEvCsABrwCAATp + AbwDAATpAbwqAAEmAR4EAAK8AQAE6QQABOkBvCoAASYBHgIAASYBHgEAAbwBAALpAbwCAAS8AQAC6QG8 + LQABvAEmAR4DAALpAbwBAAQLArwC6QG8LAABJgEeAgABvAIAAukBvAILAbwBAAILAbwC6QG8JgADvAIA + ASYBHgIAAbwDAALpAbwCCwK8AgsBAALpAbwlAAHNAQoBAAG8AQABJgEeAQAB7AMAAbwBAALpAbwFCwIA + AukBvCUAAc0BCgEAArwBJgEeAQABEgK8AQABvAEAAukBvAILArwDAALpAbwkAAHNAQoDAAG8AQABJgEe + BgAC6QG8AQACCwO8AQAC6QG8JAABzQEKARIB7AEAARIBvAgAAukBvAIAAwsCAALpAbwkAAHNAQoBEgIA + ARIBvAgAAukDvAQAAbwC6QG8IwABzQEKAQABvAMAAbwIAATpAbwDAATpAbwBAAG8IQABzQEKAQABvAMA + AewBvAcABOkEAATpARsBMQGZIQABZRsAAzE9AAEbATEBGykAAewBEgHvBQAB9BEAChIYAALzAusB7wHs + AesB8wUAAeoB9AUAAvIB9AYAAxIGSgMSFAAB8wHxAbwC7AEHAbwBBwHsAesBBwQAAQ4BEgHtBAAB8AEi + ASkB6wHzBQABEgJKBm4CSgESAwABDwcAAQ8FAAEPAQABvAH3Ae0BkgHvAbwBBwHwAfQB8gHrAeoEAAEO + AW0B7QMAAQcBIwIrASoB6wHzAwABEgFKAW4BSgZuAUoBbgFKARIBAAEPCe8BDwMAAe8CAAFtAvcB8AG8 + AfMC8gL0Ae0B6wEHAwABBwEVAeoD7QEpASsBJQEfASUBKgHrAfQCAAESAUoBbgFKBpMBSgFuAUoBEgIA + Ae8BAQEfBu8DAALvAgABvAHvAfEB9AH2AfIC8AH0AfYB7wHsAW0DAAH0ARAB6wFKATABAwElARcB+QIg + ASUBKgHqAgABEgFKAZMBbgZJAW4BkwFKARICAAHvAgEBHwXvAQACDwLvAgAB8gHvAvYB9wFtAesB8gH0 + AfYB8wHrAewBBwIAAfQBEAFzAUUBJAEgAfkBFwH5ASABHwEgATEBIgH0AQABEgGTAW4BSQZsAUkBbgGT + ARICAAHvAQEBHwbvAwAC7wMAAe8B8wG8AZIB8AH3AfYB9wHtAQcB6wHsAe8DAAHyAREBRQEfAiAB+QEX + AfkBIAEfATEBIgH0AQABEgGTAUkCbARxAmwBSQGTARIBAAEPCe8BDwMAAe8DAAEHAfABRAElARoCkgHy + AvMB9AESAesB8gIAAfIBQwHsAUUBHwEgAfkBFwEmATEBNwEwARQCAAFJAW4BbAFxAZcEcQGXAXEBbAFu + ARICAAEPBwABDwUAAQ8CAAH0AW0BLAElAesB9ALxAu8B8gLrAbwCAAHzAW0B6wFFAR8CIAH5ATEBUQJt + AfMCAAFJAmwBlwJxApcCcQGXAmwBSQIAARUBDwEVAwABFQEPARUIAAEcASwBKwHqAQcBvALvAfQCvAHz + Am0DAAHwARMBRQEfASQCKwFRAbwFAAFJAWwCcQaXAnEBbAFJAQABFQH3AQcB9wEVAQABFQH3AQcB9wEV + BgAB7wIrAUsB7wEHAbwB8QQHAfQBiwHqAwAB8AERAUsBJAEjAQMBSgHwBwABbAFxA5cCeAOXAXEBbAIA + AQ8BBwEAAQcBDwEAAQ8BBwEAAQcBDwUAAe8BKwEsAUUCvAEHAe8BBwG8AfABGQLbAWwBBwMAAfABIwIr + AQ4B8AHxCAABSQFxApcBeAWXAXEBSQIAARUB9wEHAfcBFQEAARUB9wEHAfcBFQUAARIBLAFFAfIB8wHv + AfIB8QG7AtoBswKLAfMEAAHwASMBNwExAQ4LAAFJAXEClwF4A5cBcQFJBAABFQEPARUDAAEVAQ8BFQcA + ARIBvAIAAQcBswHUAbMCbAHvCAAB7wIOAfMMAAFsAXEElwFxAWwZAAEHAhIBvB0ABHEXAAH0C/MB9BMA + DfMTAAEHC+8BBwIADxABAAEHDbMBBwcABEMHAAHvAQABuwazAbsCAAHvAgABEAY4ARAG0wEQAQABsw0A + AbMFAAJDBPACQwUAAe8BAAGzBgABswIAAe8CAAEQAjgDAAE4ARAC0wIAAtMBEAEAAbMNAAGzBAABQwjw + AUMEAAHvAQABrQEAAZcCVgGXAQABswIAAe8CAAEQAzgBAAI4ARAB0wEAAtMBAAHTARABAAGzAQABVglQ + AVYBAAGzAwABQwPwAZICQwGSA/ABQwMAAe8BAAGtAQAEVgEAAbMB9AEAAe8CAAEQAjgCAAI4ARAB0wEA + AtMBAAHTARABAAGzAQALVgEAAbMDAAFDAfABkgESAUMB8AGSAUMBEgGSAfABQwMAAe8BAAGtAQAEVgEA + AbMB9AEAAe8CAAEQAzgBAAI4ARAC0wIAAtMBEAEAAbMBAAtWAQABswIAAUMB8AESAmMBQwEAAfABQwJj + ARIB8AFDAgAB7wEAAa0BAAR4AQABswH0AQAB7wIAARAGOAEQBtMBEAEAAbMBAAtWAQABswIAAUMBEgRH + AkMERwESAUMCAAHvAQABrQYAAbMB8wEAAe8CAA8QAQABswEAC1YBAAGzAgABQwxHAUMCAAHvAQABuwKt + BLMBuwHzAQAB7wIAARAGRwEQBgABEAEAAbMBAAtWAQABswIAAUMMRwFDAgAB7wEAAfMD9AHzAfQEAAHv + AgABEAJHAgACRwEQBgABEAEAAbMBAAtWAQABswMAAUMCRwKUBkcBQwMAAe8BAAF0AjIBLAF0AQABvAL3 + AQAB7wIAARABRwEAAkcBAAFHARAGAAEQAQABswEAAVYJeAFWAQABswMAAUMBRwSUBUcBQwMAAe8BAAEs + A8MBUwEAAfcCAAHzAfACAAEQAUcBAAJHAQABRwEQBgABEAEAAbMBAAt4AQABswQAAUMElARHAUMEAAHv + AQABdAMsAXQBAAH3AQAB8wHwAwABEAJHAgACRwEQBgABEAEAAbMBAAt4AQABswUAAkMBlANHAkMFAAHv + CAAB8wHwBAABEAZHARAGAAEQAQABsw0AAbMHAARDBwABBwjvAQcFAA8QAQABCQ2zAQkRAAFCAU0BPgcA + AT4DAAEoAwABQAMAATADAAEBAQABAQUAAYABARYAA/8BAAL/AsMEAAH/AYECgwQAAf8BAAGHAYMEAAH/ + ATABjAEjBAAB/wHhAYgBAwQAAf8BwQGBAQMEAAHxAYMBgAEjBAAB4QEOAYABYwQAAeABAAGAAeMEAAHA + AYEBiAEjBAABwAF/AYwBYwQAAcQBfwGDAcMEAAGEAX8BgwGCBAABhAE/AYcBgAQAAYwBfwH/AfgEAAP/ + AfgEAAH/AY8BvwH/AeABBwL/AfgBBwKPAcABAwL/AcABBwGPAQcBwAEDAYABPAGAAQcBjgEDAYABAQEA + ARgBgAEDAYABAQGAAQECAAGAAQMBgAEBAYABAQIAAYABAQGAAQABgAEBAgABwAEBAcABAAGAAQEBAAEY + AcABAAHAAQEBgAEBAYABPAHAAQABwAEBAYABAQGOAT8BwAEAAeABDwGAAQEBBAEfAYABAAHgAR8BwAED + ASQBnwIAAeABPwHAAQMBBAEfAQABAQHgAf8B4AEHAY4BPwGYAQ8B8AH/AfABDwL/AfgBfwL/AfwBPwL/ + AYABAwL/AYABAwL/AYABAwEAAQEBAAEBAfwBPwGgARsBAAEBAX8B/QHwAQ8BrwHbAQABMQF/Af0B4AEH + AagBWwEAAUkBQAEFAcABAwGoAUsBAAFJAUABBQHAAQMBqAFLAQABMQFAAQUBgQEBAagBSwEAAQEBQAEF + AYABAQGvAcsBAAEBAUABBQGAAQEBoAELAQAB/QFAAQUBgAEBAaABewEYAcUBQAEFAcABAwGgAYsBJAHt + AUABBQHAAQMBoAGzASQBzQFAAQUB4AEHAaABpwEYAe0BQAEFAfABDwG/Ac8BAAH9AX8B/QH8AT8BgAEf + AQABAQEAAQEC/ws= diff --git a/DS_Map/Resources/CharMaps/CharMapManager.cs b/DS_Map/Resources/CharMaps/CharMapManager.cs new file mode 100644 index 0000000..d2287f2 --- /dev/null +++ b/DS_Map/Resources/CharMaps/CharMapManager.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Linq; + +namespace DSPRE.CharMaps +{ + internal static class CharMapManager + { + + private static Dictionary decodeMap; + private static Dictionary encodeMap; + private static Dictionary commandMap; + + public static readonly string charmapFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Tools", "charmap.xml"); + public static readonly string customCharmapFilePath = Path.Combine(Program.DspreDataPath, "charmap.xml"); + + private static bool mapsInitialized = false; + + public static Dictionary GetDecodingMap() + { + if (!mapsInitialized) + { + InitializeCharMaps(); + } + return decodeMap; + } + + public static Dictionary GetEncodingMap() + { + if (!mapsInitialized) + { + InitializeCharMaps(); + } + return encodeMap; + } + + public static Dictionary GetCommandMap() + { + if (!mapsInitialized) + { + InitializeCharMaps(); + } + return commandMap; + } + + public static void InvalidateMaps() + { + mapsInitialized = false; + } + + /// + /// Initializes the character maps by loading data from the charmap XML file. + /// + /// + /// Loads the custom charmap file if it exists; otherwise, loads the default charmap file. + /// If neither file is found, a FileNotFoundException is thrown. + /// + /// + private static void InitializeCharMaps() + { + decodeMap = new Dictionary(); + encodeMap = new Dictionary(); + commandMap = new Dictionary(); + + string charmapPath = ""; + + if (File.Exists(customCharmapFilePath)) + { + AppLogger.Info("Loading custom charmap from user data directory."); + charmapPath = customCharmapFilePath; + } + else if (File.Exists(charmapFilePath)) + { + AppLogger.Info("Loading default charmap from application directory."); + charmapPath = charmapFilePath; + } + else + { + throw new FileNotFoundException("No charmap XML file found in application or user data directory."); + } + + var xml = XDocument.Load(charmapPath, LoadOptions.PreserveWhitespace); + + foreach (var entry in xml.Descendants("entry")) + { + var code = entry.Attribute("code")?.Value; + var kind = entry.Attribute("kind")?.Value; + var value = entry.Value; + + if (code == null || kind == null || value == null) + { + AppLogger.Error("Found charmap entry with null value."); + continue; + } + + ushort codeValue; + string codeKind = kind.ToLower(); + string chars = value.ToString(); + + if (!ushort.TryParse(code, System.Globalization.NumberStyles.HexNumber, null, out codeValue)) + { + AppLogger.Error($"Invalid code value in charmap: {code}"); + continue; + } + + if (codeKind == "char" || codeKind == "escape") + { + decodeMap[codeValue] = chars; + encodeMap[chars] = codeValue; + } + else if (codeKind == "alias") + { + encodeMap[chars] = codeValue; + } + else if (codeKind == "command") + { + commandMap[codeValue] = chars; + } + else + { + AppLogger.Error($"Unknown kind '{kind}' in charmap entry."); + } + } + + mapsInitialized = true; + } + + /// + /// Creates a custom character map file by copying the default character map file to a specified location. + /// + /// This method ensures that the directory for the custom character map file exists + /// before attempting to copy the file. If the operation succeeds, the custom character map file will overwrite + /// any existing file at the target location. + /// if the custom character map file is successfully created; otherwise, . + public static bool CreateCustomCharMapFile() + { + try + { + if (!Directory.Exists(Path.GetDirectoryName(customCharmapFilePath))) + { + Directory.CreateDirectory(Path.GetDirectoryName(customCharmapFilePath)); + } + File.Copy(charmapFilePath, customCharmapFilePath, overwrite: true); + AppLogger.Info("Custom charmap file created successfully."); + return true; + } + catch (Exception ex) + { + AppLogger.Error($"Failed to create custom charmap file: {ex.Message}"); + return false; + } + } + + /// + /// Deletes the custom character map file if it exists. + /// + /// This method checks for the existence of the custom character map file at the + /// predefined path and deletes it if found. + /// if the operation completes successfully, regardless of whether the file existed; + /// otherwise, if an error occurs during the deletion process. + public static bool DeleteCustomCharMapFile() + { + try + { + if (File.Exists(customCharmapFilePath)) + { + File.Delete(customCharmapFilePath); + AppLogger.Info("Custom charmap file deleted successfully."); + } + else + { + AppLogger.Info("No custom charmap file to delete."); + } + return true; + } + catch (Exception ex) + { + AppLogger.Error($"Failed to delete custom charmap file: {ex.Message}"); + return false; + } + } + + public static bool SaveCustomCharMap(XmlDocument xml) + { + try + { + xml.Save(customCharmapFilePath); + AppLogger.Info("Custom charmap XML saved successfully."); + InvalidateMaps(); + return true; + } + catch (Exception ex) + { + AppLogger.Error($"Failed to save custom charmap XML: {ex.Message}"); + return false; + } + } + + } +} diff --git a/DS_Map/Resources/CharMaps/CharMapManagerForm.Designer.cs b/DS_Map/Resources/CharMaps/CharMapManagerForm.Designer.cs new file mode 100644 index 0000000..d2221cd --- /dev/null +++ b/DS_Map/Resources/CharMaps/CharMapManagerForm.Designer.cs @@ -0,0 +1,282 @@ +namespace DSPRE.CharMaps +{ + partial class CharMapManagerForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.aliasLabel = new System.Windows.Forms.Label(); + this.aliasListBox = new System.Windows.Forms.ListBox(); + this.reloadButton = new System.Windows.Forms.Button(); + this.addAliasButton = new System.Windows.Forms.Button(); + this.newAliasTextBox = new System.Windows.Forms.TextBox(); + this.newAliasLabel = new System.Windows.Forms.Label(); + this.charLabel = new System.Windows.Forms.Label(); + this.codeComboBox = new System.Windows.Forms.ComboBox(); + this.aliasGroupBox = new System.Windows.Forms.GroupBox(); + this.removeAliasButton = new System.Windows.Forms.Button(); + this.charmapGroupBox = new System.Windows.Forms.GroupBox(); + this.charmapLabel = new System.Windows.Forms.Label(); + this.charMapListBox = new System.Windows.Forms.ListBox(); + this.saveButton = new System.Windows.Forms.Button(); + this.createCustomMapButton = new System.Windows.Forms.Button(); + this.deleteCustomMapButton = new System.Windows.Forms.Button(); + this.toolTip = new System.Windows.Forms.ToolTip(this.components); + this.aliasGroupBox.SuspendLayout(); + this.charmapGroupBox.SuspendLayout(); + this.SuspendLayout(); + // + // aliasLabel + // + this.aliasLabel.AutoSize = true; + this.aliasLabel.Location = new System.Drawing.Point(3, 29); + this.aliasLabel.Name = "aliasLabel"; + this.aliasLabel.Size = new System.Drawing.Size(84, 13); + this.aliasLabel.TabIndex = 0; + this.aliasLabel.Text = "Avalable Aliases"; + // + // aliasListBox + // + this.aliasListBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.aliasListBox.FormattingEnabled = true; + this.aliasListBox.ItemHeight = 20; + this.aliasListBox.Location = new System.Drawing.Point(6, 46); + this.aliasListBox.Name = "aliasListBox"; + this.aliasListBox.Size = new System.Drawing.Size(151, 224); + this.aliasListBox.TabIndex = 1; + // + // reloadButton + // + this.reloadButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.reloadButton.Image = global::DSPRE.Properties.Resources.resetIcon; + this.reloadButton.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.reloadButton.Location = new System.Drawing.Point(214, 163); + this.reloadButton.Name = "reloadButton"; + this.reloadButton.Size = new System.Drawing.Size(100, 50); + this.reloadButton.TabIndex = 2; + this.reloadButton.Text = "Reload \r\nMappings"; + this.reloadButton.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.toolTip.SetToolTip(this.reloadButton, "Reload mappings from file. Unsaved edits will be lost!"); + this.reloadButton.UseVisualStyleBackColor = true; + this.reloadButton.Click += new System.EventHandler(this.reloadButton_Click); + // + // addAliasButton + // + this.addAliasButton.Image = global::DSPRE.Properties.Resources.addIcon; + this.addAliasButton.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.addAliasButton.Location = new System.Drawing.Point(163, 132); + this.addAliasButton.Name = "addAliasButton"; + this.addAliasButton.Size = new System.Drawing.Size(151, 23); + this.addAliasButton.TabIndex = 3; + this.addAliasButton.Text = "Add New Alias"; + this.addAliasButton.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.toolTip.SetToolTip(this.addAliasButton, "Add new alias"); + this.addAliasButton.UseVisualStyleBackColor = true; + this.addAliasButton.Click += new System.EventHandler(this.addAliasButton_Click); + // + // newAliasTextBox + // + this.newAliasTextBox.Location = new System.Drawing.Point(163, 62); + this.newAliasTextBox.Name = "newAliasTextBox"; + this.newAliasTextBox.Size = new System.Drawing.Size(151, 20); + this.newAliasTextBox.TabIndex = 4; + // + // newAliasLabel + // + this.newAliasLabel.AutoSize = true; + this.newAliasLabel.Location = new System.Drawing.Point(160, 46); + this.newAliasLabel.Name = "newAliasLabel"; + this.newAliasLabel.Size = new System.Drawing.Size(29, 13); + this.newAliasLabel.TabIndex = 5; + this.newAliasLabel.Text = "Alias"; + // + // charLabel + // + this.charLabel.AutoSize = true; + this.charLabel.Location = new System.Drawing.Point(160, 86); + this.charLabel.Name = "charLabel"; + this.charLabel.Size = new System.Drawing.Size(53, 13); + this.charLabel.TabIndex = 6; + this.charLabel.Text = "Character"; + // + // codeComboBox + // + this.codeComboBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.codeComboBox.FormattingEnabled = true; + this.codeComboBox.Location = new System.Drawing.Point(163, 102); + this.codeComboBox.Name = "codeComboBox"; + this.codeComboBox.Size = new System.Drawing.Size(151, 24); + this.codeComboBox.TabIndex = 7; + // + // aliasGroupBox + // + this.aliasGroupBox.Controls.Add(this.removeAliasButton); + this.aliasGroupBox.Controls.Add(this.aliasListBox); + this.aliasGroupBox.Controls.Add(this.newAliasLabel); + this.aliasGroupBox.Controls.Add(this.charLabel); + this.aliasGroupBox.Controls.Add(this.codeComboBox); + this.aliasGroupBox.Controls.Add(this.aliasLabel); + this.aliasGroupBox.Controls.Add(this.addAliasButton); + this.aliasGroupBox.Controls.Add(this.newAliasTextBox); + this.aliasGroupBox.Location = new System.Drawing.Point(12, 12); + this.aliasGroupBox.Name = "aliasGroupBox"; + this.aliasGroupBox.Size = new System.Drawing.Size(320, 280); + this.aliasGroupBox.TabIndex = 8; + this.aliasGroupBox.TabStop = false; + this.aliasGroupBox.Text = "Manage Aliases"; + // + // removeAliasButton + // + this.removeAliasButton.Image = global::DSPRE.Properties.Resources.deleteIcon; + this.removeAliasButton.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.removeAliasButton.Location = new System.Drawing.Point(163, 161); + this.removeAliasButton.Name = "removeAliasButton"; + this.removeAliasButton.Size = new System.Drawing.Size(151, 23); + this.removeAliasButton.TabIndex = 8; + this.removeAliasButton.Text = "Remove Alias"; + this.removeAliasButton.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.toolTip.SetToolTip(this.removeAliasButton, "Remove selected alias"); + this.removeAliasButton.UseVisualStyleBackColor = true; + this.removeAliasButton.Click += new System.EventHandler(this.removeAliasButton_Click); + // + // charmapGroupBox + // + this.charmapGroupBox.Controls.Add(this.charmapLabel); + this.charmapGroupBox.Controls.Add(this.charMapListBox); + this.charmapGroupBox.Controls.Add(this.saveButton); + this.charmapGroupBox.Controls.Add(this.createCustomMapButton); + this.charmapGroupBox.Controls.Add(this.deleteCustomMapButton); + this.charmapGroupBox.Controls.Add(this.reloadButton); + this.charmapGroupBox.Location = new System.Drawing.Point(338, 12); + this.charmapGroupBox.Name = "charmapGroupBox"; + this.charmapGroupBox.Size = new System.Drawing.Size(320, 280); + this.charmapGroupBox.TabIndex = 9; + this.charmapGroupBox.TabStop = false; + this.charmapGroupBox.Text = "Manage Custom Charmap"; + // + // charmapLabel + // + this.charmapLabel.AutoSize = true; + this.charmapLabel.Location = new System.Drawing.Point(6, 28); + this.charmapLabel.Name = "charmapLabel"; + this.charmapLabel.Size = new System.Drawing.Size(68, 13); + this.charmapLabel.TabIndex = 13; + this.charmapLabel.Text = "Full Charmap"; + // + // charMapListBox + // + this.charMapListBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.charMapListBox.FormattingEnabled = true; + this.charMapListBox.ItemHeight = 20; + this.charMapListBox.Location = new System.Drawing.Point(6, 45); + this.charMapListBox.Name = "charMapListBox"; + this.charMapListBox.Size = new System.Drawing.Size(202, 224); + this.charMapListBox.TabIndex = 12; + this.toolTip.SetToolTip(this.charMapListBox, "List of all mappings. Double-click to select character.\r\n"); + this.charMapListBox.DoubleClick += new System.EventHandler(this.charMapListBox_DoubleClick); + // + // saveButton + // + this.saveButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.saveButton.Image = global::DSPRE.Properties.Resources.save_rom; + this.saveButton.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.saveButton.Location = new System.Drawing.Point(214, 219); + this.saveButton.Name = "saveButton"; + this.saveButton.Size = new System.Drawing.Size(100, 50); + this.saveButton.TabIndex = 11; + this.saveButton.Text = "Save\r\nMappings"; + this.saveButton.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.toolTip.SetToolTip(this.saveButton, "Save edits to file."); + this.saveButton.UseVisualStyleBackColor = true; + this.saveButton.Click += new System.EventHandler(this.saveButton_Click); + // + // createCustomMapButton + // + this.createCustomMapButton.Image = global::DSPRE.Properties.Resources.addIcon; + this.createCustomMapButton.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.createCustomMapButton.Location = new System.Drawing.Point(214, 51); + this.createCustomMapButton.Name = "createCustomMapButton"; + this.createCustomMapButton.Size = new System.Drawing.Size(100, 50); + this.createCustomMapButton.TabIndex = 10; + this.createCustomMapButton.Text = "Create Custom Charmap"; + this.createCustomMapButton.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.toolTip.SetToolTip(this.createCustomMapButton, "Create new custom charmap. Will overwrite existing map."); + this.createCustomMapButton.UseVisualStyleBackColor = true; + this.createCustomMapButton.Click += new System.EventHandler(this.createCustomMapButton_Click); + // + // deleteCustomMapButton + // + this.deleteCustomMapButton.Image = global::DSPRE.Properties.Resources.deleteIcon; + this.deleteCustomMapButton.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.deleteCustomMapButton.Location = new System.Drawing.Point(214, 107); + this.deleteCustomMapButton.Name = "deleteCustomMapButton"; + this.deleteCustomMapButton.Size = new System.Drawing.Size(100, 50); + this.deleteCustomMapButton.TabIndex = 9; + this.deleteCustomMapButton.Text = "Delete Custom Charmap"; + this.deleteCustomMapButton.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.toolTip.SetToolTip(this.deleteCustomMapButton, "Delete charmap file. Once you close the editor this can not be undone."); + this.deleteCustomMapButton.UseVisualStyleBackColor = true; + this.deleteCustomMapButton.Click += new System.EventHandler(this.deleteCustomMapButton_Click); + // + // CharMapManagerForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(669, 303); + this.Controls.Add(this.charmapGroupBox); + this.Controls.Add(this.aliasGroupBox); + this.Name = "CharMapManagerForm"; + this.Text = "Character Map Manager"; + this.aliasGroupBox.ResumeLayout(false); + this.aliasGroupBox.PerformLayout(); + this.charmapGroupBox.ResumeLayout(false); + this.charmapGroupBox.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Label aliasLabel; + private System.Windows.Forms.ListBox aliasListBox; + private System.Windows.Forms.Button reloadButton; + private System.Windows.Forms.Button addAliasButton; + private System.Windows.Forms.TextBox newAliasTextBox; + private System.Windows.Forms.Label newAliasLabel; + private System.Windows.Forms.Label charLabel; + private System.Windows.Forms.ComboBox codeComboBox; + private System.Windows.Forms.GroupBox aliasGroupBox; + private System.Windows.Forms.Button removeAliasButton; + private System.Windows.Forms.GroupBox charmapGroupBox; + private System.Windows.Forms.Button deleteCustomMapButton; + private System.Windows.Forms.Button createCustomMapButton; + private System.Windows.Forms.Button saveButton; + private System.Windows.Forms.Label charmapLabel; + private System.Windows.Forms.ListBox charMapListBox; + private System.Windows.Forms.ToolTip toolTip; + } +} \ No newline at end of file diff --git a/DS_Map/Resources/CharMaps/CharMapManagerForm.cs b/DS_Map/Resources/CharMaps/CharMapManagerForm.cs new file mode 100644 index 0000000..f599021 --- /dev/null +++ b/DS_Map/Resources/CharMaps/CharMapManagerForm.cs @@ -0,0 +1,374 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Windows.Forms; +using System.Xml; +using System.Xml.Linq; + +namespace DSPRE.CharMaps +{ + public partial class CharMapManagerForm : Form + { + + private XmlDocument currentMap; + Dictionary decodeDict = new Dictionary(); + Dictionary encodeDict = new Dictionary(); + + private bool dirty = false; + + public CharMapManagerForm() + { + InitializeComponent(); + LoadCustomMap(); + ReadCurrentMap(); + } + + private void SetDirty(bool isDirty) + { + dirty = isDirty; + + if (dirty) + { + this.Text = "Character Map Manager*"; + } + else + { + this.Text = "Character Map Manager"; + } + } + + private void LoadCustomMap() + { + // If file exists, try loading it + if (!File.Exists(CharMapManager.customCharmapFilePath)) + { + // File does not exist, this is okay and a valid state + currentMap = null; + EnableDisableControls(false); + return; + } + + try + { + currentMap = new XmlDocument(); + currentMap.PreserveWhitespace = true; + currentMap.Load(CharMapManager.customCharmapFilePath); + EnableDisableControls(true); + return; + } + catch (Exception ex) + { + // File is somehow invalid, this should be considered a valid state + currentMap = null; + EnableDisableControls(false); + AppLogger.Error("Failed to load custom charmap: " + ex.ToString()); + MessageBox.Show("Failed to load custom charmap: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + + private void ReadCurrentMap() + { + charMapListBox.Items.Clear(); + aliasListBox.Items.Clear(); + codeComboBox.Items.Clear(); + codeComboBox.SelectedIndex = -1; + decodeDict.Clear(); + + SetDirty(false); + + if (currentMap == null) + { + return; + } + + foreach (XmlNode entry in currentMap.SelectNodes("//entry")) + { + string codeString = entry.Attributes["code"]?.Value; + string kind = entry.Attributes["kind"]?.Value; + string text = entry.InnerText; + + if (codeString == null || kind == null || text == null) + { + AppLogger.Warn("Found charmap entry with null value in custom map. Skipping."); + continue; + } + + ushort code; + + if (!ushort.TryParse(codeString, System.Globalization.NumberStyles.HexNumber, null, out code)) + { + AppLogger.Error($"Invalid code value in charmap: {codeString}"); + continue; + } + + if (kind == "command") + { + // We don't display commands in this manager + continue; + } + + charMapListBox.Items.Add($"0x{code:X4} <-> {text} ({kind})"); + + if (kind == "char") + { + decodeDict[code] = text; + codeComboBox.Items.Add($"0x{code:X4} <-> {text}"); + } + else if (kind == "alias") + { + encodeDict[text] = code; + // Get the original character this alias points to + if (decodeDict.TryGetValue(code, out string originalValue)) + { + // Add alias to listbox + aliasListBox.Items.Add($"{text} -> {originalValue} <-> 0x{code:X4}"); + } + } + } + } + + private void EnableDisableControls(bool enableControls) + { + addAliasButton.Enabled = enableControls; + removeAliasButton.Enabled = enableControls; + saveButton.Enabled = enableControls; + deleteCustomMapButton.Enabled = enableControls; + } + + private void addAliasButton_Click(object sender, EventArgs e) + { + if (currentMap == null) + { + return; + } + + string alias = newAliasTextBox.Text.Trim(); + + if (string.IsNullOrEmpty(alias)) + { + MessageBox.Show("Alias name cannot be empty.", "Invalid Alias", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + // Warn if single character alias without brackets is used + if (alias.Length == 1) + { + var result = MessageBox.Show("Unbracketed single character aliases are not recommended and may lead to encoding issues. " + + "Do you want to enclose the character in brackets?", "Single Character Alias", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning); + if (result == DialogResult.Yes) + { + alias = "[" + alias + "]"; + } + else if (result == DialogResult.Cancel) + { + return; + } + } + // Ensure that multi character aliased are enclosed in [] + else if (alias.Length > 1 && !(alias.StartsWith("[") && alias.EndsWith("]"))) + { + alias = "[" + alias + "]"; + } + + // Check if alias already exists + if (encodeDict.ContainsKey(alias)) + { + MessageBox.Show("Alias already exists in the charmap.", "Duplicate Alias", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + // Get selected code + if (codeComboBox.SelectedItem == null) + { + MessageBox.Show("Please select a character code to alias.", "No Code Selected", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + string selectedCodeStr = codeComboBox.SelectedItem.ToString().Split(' ')[0]; + ushort selectedCode = ushort.Parse(selectedCodeStr.Substring(2), System.Globalization.NumberStyles.HexNumber); + + // Add new alias entry to XML + try + { + var root = currentMap.DocumentElement; + XmlElement newEntry = currentMap.CreateElement("entry"); + newEntry.SetAttribute("code", selectedCode.ToString("X4")); + newEntry.SetAttribute("kind", "alias"); + newEntry.InnerText = alias; + + // Find the last entry element + XmlNode lastEntry = root.SelectSingleNode("entry[last()]"); + + if (lastEntry != null) + { + // Get the whitespace (newline + indent) before the last entry + XmlNode whitespaceBeforeLast = lastEntry.PreviousSibling; + + // Insert: whitespace, then new entry + if (whitespaceBeforeLast != null && whitespaceBeforeLast.NodeType == XmlNodeType.Whitespace) + { + // Clone the whitespace pattern + XmlNode newWhitespace = currentMap.CreateTextNode(whitespaceBeforeLast.Value); + root.InsertAfter(newWhitespace, lastEntry); + root.InsertAfter(newEntry, newWhitespace); + } + else + { + // Fallback: add newline + 2 spaces + root.InsertAfter(currentMap.CreateTextNode("\n "), lastEntry); + root.InsertAfter(newEntry, lastEntry.NextSibling); + } + } + else + { + root.AppendChild(newEntry); + } + } + catch (Exception ex) + { + AppLogger.Error("Failed to add alias to charmap: " + ex.ToString()); + MessageBox.Show("Failed to add alias to charmap: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + // Add alias to listbox and dictionary + encodeDict[alias] = selectedCode; + aliasListBox.Items.Add($"{alias} <-> 0x{selectedCode:X4}"); + aliasListBox.SelectedIndex = aliasListBox.Items.Count - 1; + + SetDirty(true); + } + + private void removeAliasButton_Click(object sender, EventArgs e) + { + if (currentMap == null) + { + return; + } + + if (aliasListBox.SelectedItem == null) + { + MessageBox.Show("Please select an alias to remove.", "No Alias Selected", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + string selectedAliasStr = aliasListBox.SelectedItem.ToString(); + string aliasName = selectedAliasStr.Split(' ')[0]; + + // Find and remove the alias entry from XML + try + { + var entryToRemove = currentMap.DocumentElement.SelectSingleNode($"entry[@kind='alias' and text()='{aliasName}']"); + if (entryToRemove != null) + { + // Remove the whitespace after the entry (the newline following it) + // this is just to keep the XML tidy + XmlNode nextNode = entryToRemove.NextSibling; + + entryToRemove.ParentNode.RemoveChild(entryToRemove); + + // If next node was whitespace (newline + indent for next entry), remove it + if (nextNode != null && nextNode.NodeType == XmlNodeType.Whitespace) + { + nextNode.ParentNode.RemoveChild(nextNode); + } + } + else + { + MessageBox.Show("Selected alias not found in charmap XML. " + + "This indicates some mismatch between display and the internal data.", "Alias Not Found", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + catch (Exception ex) + { + AppLogger.Error("Failed to remove alias from charmap: " + ex.ToString()); + MessageBox.Show("Failed to remove alias from charmap: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + // Remove alias from listbox and dictionary + aliasListBox.Items.Remove(aliasListBox.SelectedItem); + aliasListBox.SelectedIndex = aliasListBox.Items.Count - 1; + + encodeDict.Remove(aliasName); + + SetDirty(true); + } + + private void createCustomMapButton_Click(object sender, EventArgs e) + { + if (File.Exists(CharMapManager.customCharmapFilePath)) + { + var result = MessageBox.Show("A custom charmap already exists. Do you want to overwrite it?", "Custom Charmap Exists", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); + if (result != DialogResult.Yes) + { + return; + } + } + + CharMapManager.CreateCustomCharMapFile(); + LoadCustomMap(); + ReadCurrentMap(); + + } + + private void deleteCustomMapButton_Click(object sender, EventArgs e) + { + var result = MessageBox.Show("Are you sure you want to delete the custom charmap? This action cannot be undone.", "Delete Custom Charmap", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); + if (result == DialogResult.Yes) + { + CharMapManager.DeleteCustomCharMapFile(); + } + } + + private void reloadButton_Click(object sender, EventArgs e) + { + var result = MessageBox.Show("Are you sure you want to reload the custom charmap? All unsaved changes will be lost.", "Reload Custom Charmap", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); + + if (result == DialogResult.Yes) + { + LoadCustomMap(); + ReadCurrentMap(); + } + } + + private void saveButton_Click(object sender, EventArgs e) + { + if (currentMap == null) + { + return; + } + + CharMapManager.SaveCustomCharMap(currentMap); + SetDirty(false); + } + + private void charMapListBox_DoubleClick(object sender, EventArgs e) + { + // Get clicked item + if (charMapListBox.SelectedItem == null) + { + return; + } + + string selectedItemStr = charMapListBox.SelectedItem.ToString(); + + // Try to select corresponding code in combo box + string codeStr = selectedItemStr.Split(' ')[0]; + + for (int i = 0; i < codeComboBox.Items.Count; i++) + { + string comboItemStr = codeComboBox.Items[i].ToString(); + if (comboItemStr.StartsWith(codeStr)) + { + codeComboBox.SelectedIndex = i; + break; + } + } + + } + } +} diff --git a/DS_Map/Resources/CharMaps/CharMapManagerForm.resx b/DS_Map/Resources/CharMaps/CharMapManagerForm.resx new file mode 100644 index 0000000..4909af1 --- /dev/null +++ b/DS_Map/Resources/CharMaps/CharMapManagerForm.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 17, 17 + + \ No newline at end of file