diff --git a/OpenKh.Tools.Kh2TextEditor/OpenKh.Tools.Kh2TextEditor.csproj b/OpenKh.Tools.Kh2TextEditor/OpenKh.Tools.Kh2TextEditor.csproj
index b3cc2d242..cdf91855a 100644
--- a/OpenKh.Tools.Kh2TextEditor/OpenKh.Tools.Kh2TextEditor.csproj
+++ b/OpenKh.Tools.Kh2TextEditor/OpenKh.Tools.Kh2TextEditor.csproj
@@ -10,6 +10,10 @@
..\bin\$(Configuration)\
true
+
+
+
+
diff --git a/OpenKh.Tools.Kh2TextEditor/Services/CsvTextExporter.cs b/OpenKh.Tools.Kh2TextEditor/Services/CsvTextExporter.cs
new file mode 100644
index 000000000..f6ef5749b
--- /dev/null
+++ b/OpenKh.Tools.Kh2TextEditor/Services/CsvTextExporter.cs
@@ -0,0 +1,27 @@
+using CsvHelper;
+using CsvHelper.Configuration;
+using OpenKh.Tools.Kh2TextEditor.Models;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenKh.Tools.Kh2TextEditor.Services
+{
+ class CsvTextExporter : ITextExporter
+ {
+ void ITextExporter.Export(IEnumerable messages, TextWriter writer)
+ {
+ new CsvWriter(writer, new CsvConfiguration(CultureInfo.InvariantCulture)
+ {
+ Delimiter = ","
+ })
+ .WriteRecords(messages);
+ }
+
+ (string, string[]) ITextExporter.Filter() => ("CSV", "csv".Split(';'));
+ }
+}
diff --git a/OpenKh.Tools.Kh2TextEditor/Services/CsvTextImporter.cs b/OpenKh.Tools.Kh2TextEditor/Services/CsvTextImporter.cs
new file mode 100644
index 000000000..5e04be073
--- /dev/null
+++ b/OpenKh.Tools.Kh2TextEditor/Services/CsvTextImporter.cs
@@ -0,0 +1,27 @@
+using CsvHelper;
+using CsvHelper.Configuration;
+using OpenKh.Tools.Kh2TextEditor.Models;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenKh.Tools.Kh2TextEditor.Services
+{
+ class CsvTextImporter : ITextImporter
+ {
+ (string, string[]) ITextImporter.Filter() => ("CSV", "csv".Split(';'));
+
+ IEnumerable ITextImporter.Import(TextReader reader)
+ {
+ return new CsvReader(reader, new CsvConfiguration(CultureInfo.InvariantCulture)
+ {
+ Delimiter = ",",
+ })
+ .GetRecords();
+ }
+ }
+}
diff --git a/OpenKh.Tools.Kh2TextEditor/Services/ExchangeableMessage.cs b/OpenKh.Tools.Kh2TextEditor/Services/ExchangeableMessage.cs
new file mode 100644
index 000000000..f28ec2848
--- /dev/null
+++ b/OpenKh.Tools.Kh2TextEditor/Services/ExchangeableMessage.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Serialization;
+
+namespace OpenKh.Tools.Kh2TextEditor.Services
+{
+ public class ExchangeableMessage
+ {
+ [XmlElement]
+ public int Id { get; set; }
+ [XmlElement]
+ public string Text { get; set; }
+ }
+}
diff --git a/OpenKh.Tools.Kh2TextEditor/Services/ITextExporter.cs b/OpenKh.Tools.Kh2TextEditor/Services/ITextExporter.cs
new file mode 100644
index 000000000..e9f3c13d7
--- /dev/null
+++ b/OpenKh.Tools.Kh2TextEditor/Services/ITextExporter.cs
@@ -0,0 +1,16 @@
+using OpenKh.Tools.Kh2TextEditor.Models;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenKh.Tools.Kh2TextEditor.Services
+{
+ public interface ITextExporter
+ {
+ void Export(IEnumerable messages, TextWriter writer);
+ (string, string[]) Filter();
+ }
+}
diff --git a/OpenKh.Tools.Kh2TextEditor/Services/ITextImporter.cs b/OpenKh.Tools.Kh2TextEditor/Services/ITextImporter.cs
new file mode 100644
index 000000000..7a113c737
--- /dev/null
+++ b/OpenKh.Tools.Kh2TextEditor/Services/ITextImporter.cs
@@ -0,0 +1,16 @@
+using OpenKh.Tools.Kh2TextEditor.Models;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenKh.Tools.Kh2TextEditor.Services
+{
+ public interface ITextImporter
+ {
+ IEnumerable Import(TextReader reader);
+ (string, string[]) Filter();
+ }
+}
diff --git a/OpenKh.Tools.Kh2TextEditor/Services/PlainTextExporter.cs b/OpenKh.Tools.Kh2TextEditor/Services/PlainTextExporter.cs
new file mode 100644
index 000000000..43475ae5c
--- /dev/null
+++ b/OpenKh.Tools.Kh2TextEditor/Services/PlainTextExporter.cs
@@ -0,0 +1,24 @@
+using OpenKh.Tools.Kh2TextEditor.Models;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenKh.Tools.Kh2TextEditor.Services
+{
+ class PlainTextExporter : ITextExporter
+ {
+ void ITextExporter.Export(IEnumerable messages, TextWriter writer)
+ {
+ foreach (var one in messages)
+ {
+ writer.WriteLine($"{one.Id}: {one.Text}");
+ writer.WriteLine("---");
+ }
+ }
+
+ (string, string[]) ITextExporter.Filter() => ("Plain text", "txt".Split(';'));
+ }
+}
diff --git a/OpenKh.Tools.Kh2TextEditor/Services/TextExporters.cs b/OpenKh.Tools.Kh2TextEditor/Services/TextExporters.cs
new file mode 100644
index 000000000..2ee173bc4
--- /dev/null
+++ b/OpenKh.Tools.Kh2TextEditor/Services/TextExporters.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenKh.Tools.Kh2TextEditor.Services
+{
+ public class TextExporters
+ {
+ public static IEnumerable GetAll() => new ITextExporter[]
+ {
+ new PlainTextExporter(),
+ new CsvTextExporter(),
+ new XmlTextExporter(),
+ new YamlTextExporter(),
+ };
+
+ public static ITextExporter FindFromFile(string fileName)
+ {
+ var selectedExtension = Path.GetExtension(fileName).TrimStart('.');
+ var textExporters = GetAll();
+
+ return textExporters
+ .Where(
+ exporter => exporter.Filter().Item2
+ .Any(
+ it => string.Compare(it, selectedExtension, true) == 0
+ )
+ )
+ .FirstOrDefault() ?? textExporters.First(); // fallback
+ }
+ }
+}
diff --git a/OpenKh.Tools.Kh2TextEditor/Services/TextImporters.cs b/OpenKh.Tools.Kh2TextEditor/Services/TextImporters.cs
new file mode 100644
index 000000000..874d00dce
--- /dev/null
+++ b/OpenKh.Tools.Kh2TextEditor/Services/TextImporters.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenKh.Tools.Kh2TextEditor.Services
+{
+ public class TextImporters
+ {
+ public static IEnumerable GetAll() => new ITextImporter[]
+ {
+ new CsvTextImporter(),
+ new XmlTextImporter(),
+ new YamlTextImporter(),
+ };
+
+ public static ITextImporter FindFromFile(string fileName)
+ {
+ var selectedExtension = Path.GetExtension(fileName).TrimStart('.');
+ var textExporters = GetAll();
+
+ return textExporters
+ .Where(
+ exporter => exporter.Filter().Item2
+ .Any(
+ it => string.Compare(it, selectedExtension, true) == 0
+ )
+ )
+ .FirstOrDefault();
+ }
+ }
+}
diff --git a/OpenKh.Tools.Kh2TextEditor/Services/XmlTextExporter.cs b/OpenKh.Tools.Kh2TextEditor/Services/XmlTextExporter.cs
new file mode 100644
index 000000000..5e2189bb9
--- /dev/null
+++ b/OpenKh.Tools.Kh2TextEditor/Services/XmlTextExporter.cs
@@ -0,0 +1,34 @@
+using OpenKh.Tools.Kh2TextEditor.Models;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Serialization;
+
+namespace OpenKh.Tools.Kh2TextEditor.Services
+{
+ public class XmlTextExporter : ITextExporter
+ {
+ void ITextExporter.Export(IEnumerable messages, TextWriter writer)
+ {
+ new XmlSerializer(typeof(RootModel)).Serialize(
+ writer,
+ new RootModel
+ {
+ Message = messages.ToArray()
+ }
+ );
+ }
+
+ (string, string[]) ITextExporter.Filter() => ("XML", "xml".Split(';'));
+
+ [XmlRoot("Messages")]
+ public class RootModel
+ {
+ [XmlElement]
+ public ExchangeableMessage[] Message { get; set; }
+ }
+ }
+}
diff --git a/OpenKh.Tools.Kh2TextEditor/Services/XmlTextImporter.cs b/OpenKh.Tools.Kh2TextEditor/Services/XmlTextImporter.cs
new file mode 100644
index 000000000..236ceb8bf
--- /dev/null
+++ b/OpenKh.Tools.Kh2TextEditor/Services/XmlTextImporter.cs
@@ -0,0 +1,29 @@
+using OpenKh.Tools.Kh2TextEditor.Models;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Serialization;
+
+namespace OpenKh.Tools.Kh2TextEditor.Services
+{
+ public class XmlTextImporter : ITextImporter
+ {
+ (string, string[]) ITextImporter.Filter() => ("XML", "xml".Split(';'));
+
+ IEnumerable ITextImporter.Import(TextReader reader)
+ {
+ var model = (RootModel)new XmlSerializer(typeof(RootModel)).Deserialize(reader);
+ return model.Message;
+ }
+
+ [XmlRoot("Messages")]
+ public class RootModel
+ {
+ [XmlElement]
+ public ExchangeableMessage[] Message { get; set; }
+ }
+ }
+}
diff --git a/OpenKh.Tools.Kh2TextEditor/Services/YamlTextExporter.cs b/OpenKh.Tools.Kh2TextEditor/Services/YamlTextExporter.cs
new file mode 100644
index 000000000..11ac79b00
--- /dev/null
+++ b/OpenKh.Tools.Kh2TextEditor/Services/YamlTextExporter.cs
@@ -0,0 +1,28 @@
+using OpenKh.Tools.Kh2TextEditor.Models;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenKh.Tools.Kh2TextEditor.Services
+{
+ class YamlTextExporter : ITextExporter
+ {
+ void ITextExporter.Export(IEnumerable messages, TextWriter writer)
+ {
+ new YamlDotNet.Serialization.SerializerBuilder()
+ .Build()
+ .Serialize(
+ writer,
+ new
+ {
+ Messages = messages
+ }
+ );
+ }
+
+ (string, string[]) ITextExporter.Filter() => ("YAML", "yml".Split(';'));
+ }
+}
diff --git a/OpenKh.Tools.Kh2TextEditor/Services/YamlTextImporter.cs b/OpenKh.Tools.Kh2TextEditor/Services/YamlTextImporter.cs
new file mode 100644
index 000000000..862fef399
--- /dev/null
+++ b/OpenKh.Tools.Kh2TextEditor/Services/YamlTextImporter.cs
@@ -0,0 +1,29 @@
+using OpenKh.Tools.Kh2TextEditor.Models;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenKh.Tools.Kh2TextEditor.Services
+{
+ class YamlTextImporter : ITextImporter
+ {
+ (string, string[]) ITextImporter.Filter() => ("YAML", "yml".Split(';'));
+
+ IEnumerable ITextImporter.Import(TextReader reader)
+ {
+ var model = new YamlDotNet.Serialization.DeserializerBuilder()
+ .Build()
+ .Deserialize(reader);
+
+ return model.Messages;
+ }
+
+ public class RootModel
+ {
+ public ExchangeableMessage[] Messages { get; set; }
+ }
+ }
+}
diff --git a/OpenKh.Tools.Kh2TextEditor/ViewModels/MainViewModel.cs b/OpenKh.Tools.Kh2TextEditor/ViewModels/MainViewModel.cs
index 14382948b..b429867e0 100644
--- a/OpenKh.Tools.Kh2TextEditor/ViewModels/MainViewModel.cs
+++ b/OpenKh.Tools.Kh2TextEditor/ViewModels/MainViewModel.cs
@@ -16,7 +16,9 @@
using Xe.Tools;
using Xe.Tools.Wpf.Commands;
using Xe.Tools.Wpf.Dialogs;
-
+using OpenKh.Tools.Kh2TextEditor.Services;
+using System.Text;
+
namespace OpenKh.Tools.Kh2TextEditor.ViewModels
{
public class MainViewModel : BaseNotifyPropertyChanged
@@ -46,6 +48,9 @@ private string FileName
public RelayCommand OpenCommand { get; }
public RelayCommand SaveCommand { get; }
public RelayCommand SaveAsCommand { get; }
+ public RelayCommand ExportMessageAsCommand { get; }
+ public RelayCommand ImportMessageFromCommand { get; }
+
public RelayCommand ExitCommand { get; }
public RelayCommand GuideCommand { get; }
public RelayCommand AboutCommand { get; }
@@ -118,6 +123,48 @@ public MainViewModel()
}
}, x => true);
+ ExportMessageAsCommand = new RelayCommand(x =>
+ {
+ var fd = FileDialog.Factory(Window, FileDialog.Behavior.Save,
+ TextExporters.GetAll().Select(exporter => exporter.Filter())
+ );
+
+ if (fd.ShowDialog() == true)
+ {
+ var selectedExtension = $"{Path.GetExtension(fd.FileName).TrimStart('.')}";
+
+ ExportMessageAsFile(
+ fileName: fd.FileName,
+ textExporter: TextExporters.FindFromFile(fd.FileName)
+ );
+ }
+ }, x => true);
+
+ ImportMessageFromCommand = new RelayCommand(x =>
+ {
+ var fd = FileDialog.Factory(Window, FileDialog.Behavior.Open,
+ TextImporters.GetAll().Select(importer => importer.Filter())
+ );
+
+ if (fd.ShowDialog() == true)
+ {
+ var selectedExtension = $"{Path.GetExtension(fd.FileName).TrimStart('.')}";
+
+ var textImporter = TextImporters.FindFromFile(fd.FileName);
+ if (textImporter != null)
+ {
+ ImportMessageFromFile(
+ fileName: fd.FileName,
+ textImporter
+ );
+ }
+ else
+ {
+ MessageBox.Show($"Failed to match text decoder for your file:\n{fd.FileName}");
+ }
+ }
+ }, x => true);
+
ExitCommand = new RelayCommand(x =>
{
Window.Close();
@@ -185,7 +232,7 @@ public MainViewModel()
}, x => true);
AboutCommand = new RelayCommand(x =>
- {
+ {
new AboutDialog(Assembly.GetExecutingAssembly()).ShowDialog();
}, x => true);
@@ -239,6 +286,40 @@ public void SaveFile(string previousFileName, string fileName)
}
}
+ public void ExportMessageAsFile(string fileName, ITextExporter textExporter)
+ {
+ new StreamWriter(fileName, false, Encoding.UTF8).Using(
+ writer => textExporter.Export(
+ TextEditor.Messages
+ .Select(
+ source => new ExchangeableMessage
+ {
+ Id = source.Id,
+ Text = source.Text,
+ }
+ ),
+ writer
+ )
+ );
+ }
+
+ public void ImportMessageFromFile(string fileName, ITextImporter textImporter)
+ {
+ var importedMessages = new StreamReader(fileName, Encoding.UTF8).Using(
+ reader => textImporter.Import(reader)
+ .ToArray() // make sure to import all messages from file before closing StreamReader!
+ );
+
+ foreach (var importMessage in importedMessages)
+ {
+ var found = TextEditor.Messages.SingleOrDefault(it => it.Id == importMessage.Id);
+ if (found != null)
+ {
+ found.Text = importMessage.Text;
+ }
+ }
+ }
+
private void OpenFontImageFile(string fileName) => File.OpenRead(fileName).Using(stream =>
{
if (Bar.IsValid(stream))
diff --git a/OpenKh.Tools.Kh2TextEditor/Views/MainWindow.xaml b/OpenKh.Tools.Kh2TextEditor/Views/MainWindow.xaml
index aaa2c1f21..0724023a6 100644
--- a/OpenKh.Tools.Kh2TextEditor/Views/MainWindow.xaml
+++ b/OpenKh.Tools.Kh2TextEditor/Views/MainWindow.xaml
@@ -26,6 +26,11 @@
+
+
+