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