diff --git a/ACViewer/Config/BackgroundColors.cs b/ACViewer/Config/BackgroundColors.cs
new file mode 100644
index 0000000..1604572
--- /dev/null
+++ b/ACViewer/Config/BackgroundColors.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Globalization;
+using Microsoft.Xna.Framework;
+using Newtonsoft.Json;
+
+namespace ACViewer.Config
+{
+ public class BackgroundColors
+ {
+ [JsonConverter(typeof(JsonConverter_Color))]
+ public Color ModelViewer { get; set; }
+
+ [JsonConverter(typeof(JsonConverter_Color))]
+ public Color ParticleViewer { get; set; }
+
+ [JsonConverter(typeof(JsonConverter_Color))]
+ public Color TextureViewer { get; set; }
+
+ [JsonConverter(typeof(JsonConverter_Color))]
+ public Color WorldViewer { get; set; }
+
+ public BackgroundColors()
+ {
+ // defaults
+ ModelViewer = Color.Black;
+ ParticleViewer = Color.Black;
+ TextureViewer = Color.Black;
+ WorldViewer = new Color(32, 32, 32);
+ }
+ }
+
+ public sealed class JsonConverter_Color : JsonConverter
+ {
+ public override bool CanConvert(Type objectType)
+ {
+ return typeof(Color).Equals(objectType);
+ }
+
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ var color = (Color)value;
+
+ writer.WriteValue($"#{color.R:X2}{color.G:X2}{color.B:X2}");
+ }
+
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ var str = reader.Value.ToString();
+
+ if (str == null || str.Length != 7 || str[0] != '#')
+ return Color.Black;
+
+ if (!byte.TryParse(str.Substring(1, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var r) ||
+ !byte.TryParse(str.Substring(3, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var g) ||
+ !byte.TryParse(str.Substring(5, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var b))
+ {
+ return Color.Black;
+ }
+ return new Color(r, g, b);
+ }
+ }
+}
diff --git a/ACViewer/Config/Config.cs b/ACViewer/Config/Config.cs
index 3fa4f21..c58c608 100644
--- a/ACViewer/Config/Config.cs
+++ b/ACViewer/Config/Config.cs
@@ -7,5 +7,6 @@ public class Config
public Database Database { get; set; } = new Database();
public Toggles Toggles { get; set; } = new Toggles();
public MapViewerOptions MapViewer { get; set; } = new MapViewerOptions();
+ public BackgroundColors BackgroundColors { get; set; } = new BackgroundColors();
}
}
diff --git a/ACViewer/Extensions/ColorDialogEx.cs b/ACViewer/Extensions/ColorDialogEx.cs
new file mode 100644
index 0000000..8dbbd5e
--- /dev/null
+++ b/ACViewer/Extensions/ColorDialogEx.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace ACViewer.Extensions
+{
+ public class ColorDialogEx : ColorDialog
+ {
+ #region private const
+ //Windows Message Constants
+ private const int WM_INITDIALOG = 0x0110;
+ private const int WM_CTLCOLOREDIT = 0x0133;
+ private const int WM_CTLCOLORSTATIC = 0x0138;
+
+ //Window Controls
+ private const int COLOR_RED = 706;
+ private const int COLOR_GREEN = 707;
+ private const int COLOR_BLUE = 708;
+
+ //uFlag Constants
+ private const uint SWP_NOSIZE = 0x0001;
+ private const uint SWP_SHOWWINDOW = 0x0040;
+ private const uint SWP_NOZORDER = 0x0004;
+ private const uint UFLAGS = SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW;
+ #endregion
+
+ #region private readonly
+ //Windows Handle Constants
+ private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
+ private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
+ private static readonly IntPtr HWND_TOP = new IntPtr(0);
+ private static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
+ #endregion
+
+ #region private vars
+ //Module vars
+ private int _x;
+ private int _y;
+ private string _title = null;
+ #endregion
+
+ #region private static methods imports
+ //WinAPI definitions
+
+ ///
+ /// Sets the window text.
+ ///
+ /// The h WND.
+ /// The text.
+ ///
+ [DllImport("user32.dll", CharSet = CharSet.Auto)]
+ private static extern bool SetWindowText(IntPtr hWnd, string text);
+
+ ///
+ /// Sets the window pos.
+ ///
+ /// The h WND.
+ /// The h WND insert after.
+ /// The x.
+ /// The y.
+ /// The cx.
+ /// The cy.
+ /// The u flags.
+ ///
+ [DllImport("user32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
+
+ [DllImport("user32.dll")]
+ private static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);
+
+ [DllImport("user32.dll")]
+ private static extern int GetDlgItemInt(IntPtr hDlg, int nIDDlgItem, IntPtr lpTranslated, bool bSigned);
+ #endregion
+
+ #region public constructor
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The X position
+ /// The Y position
+ /// The title of the windows. If set to null(by default), the title will not be changed
+ public ColorDialogEx(int x, int y, String title = null)
+ {
+ _x = x;
+ _y = y;
+ _title = title;
+ }
+ #endregion
+
+ #region protected override methods
+ ///
+ /// Defines the common dialog box hook procedure that is overridden to add specific functionality to a common dialog box.
+ ///
+ /// The handle to the dialog box window.
+ /// The message being received.
+ /// Additional information about the message.
+ /// Additional information about the message.
+ ///
+ /// A zero value if the default dialog box procedure processes the message; a nonzero value if the default dialog box procedure ignores the message.
+ ///
+ protected override IntPtr HookProc(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam)
+ {
+ //We do the base initialization
+ IntPtr hookProc = base.HookProc(hWnd, msg, wparam, lparam);
+ //When we init the dialog
+ if (msg == WM_INITDIALOG)
+ {
+ //We change the title
+ if (!string.IsNullOrEmpty(_title))
+ {
+ SetWindowText(hWnd, _title);
+ }
+ //We move the position
+ SetWindowPos(hWnd, HWND_TOP, _x, _y, 0, 0, UFLAGS);
+ }
+ else if (msg == WM_CTLCOLOREDIT)
+ {
+ if (ColorEditCallback != null)
+ {
+ var r = GetDlgItemInt(hWnd, COLOR_RED, IntPtr.Zero, false);
+ var g = GetDlgItemInt(hWnd, COLOR_GREEN, IntPtr.Zero, false);
+ var b = GetDlgItemInt(hWnd, COLOR_BLUE, IntPtr.Zero, false);
+ ColorEditCallback(r, g, b);
+ }
+ }
+ return hookProc;
+ }
+ #endregion
+
+ public Action ColorEditCallback { get; set; }
+ }
+}
diff --git a/ACViewer/Extensions/ColorExtensions.cs b/ACViewer/Extensions/ColorExtensions.cs
new file mode 100644
index 0000000..0c36cf9
--- /dev/null
+++ b/ACViewer/Extensions/ColorExtensions.cs
@@ -0,0 +1,27 @@
+using System.Windows.Media;
+
+namespace ACViewer.Extensions
+{
+ public static class ColorExtensions
+ {
+ public static SolidColorBrush ToSolidColorBrush(this System.Drawing.Color c)
+ {
+ return new SolidColorBrush(Color.FromArgb(c.A, c.R, c.G, c.B));
+ }
+
+ public static SolidColorBrush ToSolidColorBrush(this Microsoft.Xna.Framework.Color c)
+ {
+ return new SolidColorBrush(Color.FromArgb(c.A, c.R, c.G, c.B));
+ }
+
+ public static System.Drawing.Color ToColor(this SolidColorBrush brush)
+ {
+ return System.Drawing.Color.FromArgb(brush.Color.A, brush.Color.R, brush.Color.G, brush.Color.B);
+ }
+
+ public static Microsoft.Xna.Framework.Color ToXNAColor(this SolidColorBrush brush)
+ {
+ return new Microsoft.Xna.Framework.Color(brush.Color.R, brush.Color.G, brush.Color.B, brush.Color.A);
+ }
+ }
+}
diff --git a/ACViewer/Extensions/CommandHandler.cs b/ACViewer/Extensions/CommandHandler.cs
new file mode 100644
index 0000000..2ab31e4
--- /dev/null
+++ b/ACViewer/Extensions/CommandHandler.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Windows.Input;
+
+namespace ACViewer.Extensions
+{
+ public class CommandHandler : ICommand
+ {
+ private Action