diff --git a/Ryujinx/Configuration.cs b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
similarity index 53%
rename from Ryujinx/Configuration.cs
rename to Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
index c259f9e9d..1a9407cb2 100644
--- a/Ryujinx/Configuration.cs
+++ b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
@@ -1,30 +1,22 @@
 using JsonPrettyPrinterPlus;
-using LibHac.FsSystem;
-using OpenTK.Input;
-using Ryujinx.Common;
 using Ryujinx.Common.Logging;
-using Ryujinx.HLE;
-using Ryujinx.HLE.HOS.SystemState;
-using Ryujinx.HLE.HOS.Services;
-using Ryujinx.HLE.Input;
-using Ryujinx.Ui;
-using Ryujinx.Ui.Input;
 using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Text;
-using System.Threading.Tasks;
 using Utf8Json;
 using Utf8Json.Resolvers;
+using Ryujinx.Configuration.System;
+using Ryujinx.Configuration.Hid;
+using Ryujinx.Common.Configuration.Hid;
+using Ryujinx.UI.Input;
+using Ryujinx.Configuration.Ui;
 
-namespace Ryujinx
+namespace Ryujinx.Configuration
 {
-    public class Configuration
+    public class ConfigurationFileFormat
     {
-        /// <summary>
-        /// The default configuration instance
-        /// </summary>
-        public static Configuration Instance { get; private set; }
+        public int Version { get; set; }
 
         /// <summary>
         /// Dumps shaders in this local directory
@@ -79,7 +71,7 @@ namespace Ryujinx
         /// <summary>
         /// Change System Language
         /// </summary>
-        public SystemLanguage SystemLanguage { get; set; }
+        public Language SystemLanguage { get; set; }
 
         /// <summary>
         /// Enables or disables Docked Mode
@@ -119,7 +111,7 @@ namespace Ryujinx
         /// <summary>
         ///  The primary controller's type
         /// </summary>
-        public ControllerStatus ControllerType { get; set; }
+        public ControllerType ControllerType { get; set; }
 
         /// <summary>
         /// Used to toggle columns in the GUI
@@ -154,13 +146,13 @@ namespace Ryujinx
         /// <summary>
         /// Controller control bindings
         /// </summary>
-        public Ui.Input.NpadController JoystickControls { get; private set; }
+        public NpadController JoystickControls { get; set; }
 
         /// <summary>
         /// Loads a configuration file from disk
         /// </summary>
         /// <param name="path">The path to the JSON configuration file</param>
-        public static void Load(string path)
+        public static ConfigurationFileFormat Load(string path)
         {
             var resolver = CompositeResolver.Create(
                 new[] { new ConfigurationEnumFormatter<Key>() },
@@ -169,24 +161,7 @@ namespace Ryujinx
 
             using (Stream stream = File.OpenRead(path))
             {
-                Instance = JsonSerializer.Deserialize<Configuration>(stream, resolver);
-            }
-        }
-
-        /// <summary>
-        /// Loads a configuration file asynchronously from disk
-        /// </summary>
-        /// <param name="path">The path to the JSON configuration file</param>
-        public static async Task LoadAsync(string path)
-        {
-            IJsonFormatterResolver resolver = CompositeResolver.Create(
-                new[] { new ConfigurationEnumFormatter<Key>()  },
-                new[] { StandardResolver.AllowPrivateSnakeCase }
-            );
-
-            using (Stream stream = File.OpenRead(path))
-            {
-                Instance = await JsonSerializer.DeserializeAsync<Configuration>(stream, resolver);
+                return JsonSerializer.Deserialize<ConfigurationFileFormat>(stream, resolver);
             }
         }
 
@@ -194,108 +169,17 @@ namespace Ryujinx
         /// Save a configuration file to disk
         /// </summary>
         /// <param name="path">The path to the JSON configuration file</param>
-        public static void SaveConfig(Configuration config, string path)
+        public void SaveConfig(string path)
         {
             IJsonFormatterResolver resolver = CompositeResolver.Create(
                 new[] { new ConfigurationEnumFormatter<Key>()  },
                 new[] { StandardResolver.AllowPrivateSnakeCase }
             );
 
-            byte[] data = JsonSerializer.Serialize(config, resolver);
+            byte[] data = JsonSerializer.Serialize(this, resolver);
             File.WriteAllText(path, Encoding.UTF8.GetString(data, 0, data.Length).PrettyPrintJson());
         }
 
-        /// <summary>
-        /// Configures a <see cref="Switch"/> instance
-        /// </summary>
-        /// <param name="device">The instance to configure</param>
-        public static void InitialConfigure(Switch device)
-        {
-            if (Instance == null)
-            {
-                throw new InvalidOperationException("Configuration has not been loaded yet.");
-            }
-
-            SwitchSettings.ConfigureSettings(Instance);
-
-            Logger.AddTarget(new AsyncLogTargetWrapper(
-                new ConsoleLogTarget(),
-                1000,
-                AsyncLogTargetOverflowAction.Block
-            ));
-
-            if (Instance.EnableFileLog)
-            {
-                Logger.AddTarget(new AsyncLogTargetWrapper(
-                    new FileLogTarget(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.log")),
-                    1000,
-                    AsyncLogTargetOverflowAction.Block
-                ));
-            }
-
-            Configure(device, Instance);
-        }
-
-        public static void Configure(Switch device, Configuration SwitchConfig)
-        {
-            GraphicsConfig.ShadersDumpPath = SwitchConfig.GraphicsShadersDumpPath;
-
-            Logger.SetEnable(LogLevel.Debug,     SwitchConfig.LoggingEnableDebug      );
-            Logger.SetEnable(LogLevel.Stub,      SwitchConfig.LoggingEnableStub       );
-            Logger.SetEnable(LogLevel.Info,      SwitchConfig.LoggingEnableInfo       );
-            Logger.SetEnable(LogLevel.Warning,   SwitchConfig.LoggingEnableWarn       );
-            Logger.SetEnable(LogLevel.Error,     SwitchConfig.LoggingEnableError      );
-            Logger.SetEnable(LogLevel.Guest,     SwitchConfig.LoggingEnableGuest      );
-            Logger.SetEnable(LogLevel.AccessLog, SwitchConfig.LoggingEnableFsAccessLog);
-
-            if (SwitchConfig.LoggingFilteredClasses.Length > 0)
-            {
-                foreach (var logClass in EnumExtensions.GetValues<LogClass>())
-                {
-                    Logger.SetEnable(logClass, false);
-                }
-
-                foreach (var logClass in SwitchConfig.LoggingFilteredClasses)
-                {
-                    Logger.SetEnable(logClass, true);
-                }
-            }
-
-            MainWindow.DiscordIntegrationEnabled = SwitchConfig.EnableDiscordIntegration;
-
-            device.EnableDeviceVsync = SwitchConfig.EnableVsync;
-
-            device.System.State.DockedMode = SwitchConfig.DockedMode;
-
-            device.System.State.SetLanguage(SwitchConfig.SystemLanguage);
-
-            if (SwitchConfig.EnableMulticoreScheduling)
-            {
-                device.System.EnableMultiCoreScheduling();
-            }
-
-            device.System.FsIntegrityCheckLevel = SwitchConfig.EnableFsIntegrityChecks
-                ? IntegrityCheckLevel.ErrorOnInvalid
-                : IntegrityCheckLevel.None;
-
-            device.System.GlobalAccessLogMode = SwitchConfig.FsGlobalAccessLogMode;
-
-            ServiceConfiguration.IgnoreMissingServices = SwitchConfig.IgnoreMissingServices;
-        }
-
-        public static void ConfigureHid(Switch device, Configuration SwitchConfig)
-        {
-            if (SwitchConfig.JoystickControls.Enabled)
-            {
-                if (!Joystick.GetState(SwitchConfig.JoystickControls.Index).IsConnected)
-                {
-                    SwitchConfig.JoystickControls.SetEnabled(false);
-                }
-            }
-            device.Hid.InitializePrimaryController(SwitchConfig.ControllerType);
-            device.Hid.InitializeKeyboard();
-        }
-
         private class ConfigurationEnumFormatter<T> : IJsonFormatter<T>
             where T : struct
         {
diff --git a/Ryujinx.Common/Configuration/ConfigurationState.cs b/Ryujinx.Common/Configuration/ConfigurationState.cs
new file mode 100644
index 000000000..050b49738
--- /dev/null
+++ b/Ryujinx.Common/Configuration/ConfigurationState.cs
@@ -0,0 +1,502 @@
+using Ryujinx.Common;
+using Ryujinx.Common.Configuration.Hid;
+using Ryujinx.Common.Logging;
+using Ryujinx.Configuration.Hid;
+using Ryujinx.Configuration.System;
+using Ryujinx.Configuration.Ui;
+using Ryujinx.UI.Input;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Configuration
+{
+    public class ConfigurationState
+    {
+        /// <summary>
+        /// UI configuration section
+        /// </summary>
+        public class UiSection
+        {
+            public class Columns
+            {
+                public ReactiveObject<bool> FavColumn        { get; private set; }
+                public ReactiveObject<bool> IconColumn       { get; private set; }
+                public ReactiveObject<bool> AppColumn        { get; private set; }
+                public ReactiveObject<bool> DevColumn        { get; private set; }
+                public ReactiveObject<bool> VersionColumn    { get; private set; }
+                public ReactiveObject<bool> TimePlayedColumn { get; private set; }
+                public ReactiveObject<bool> LastPlayedColumn { get; private set; } 
+                public ReactiveObject<bool> FileExtColumn    { get; private set; }
+                public ReactiveObject<bool> FileSizeColumn   { get; private set; }
+                public ReactiveObject<bool> PathColumn       { get; private set; }
+
+                public Columns()
+                {
+                    FavColumn        = new ReactiveObject<bool>();
+                    IconColumn       = new ReactiveObject<bool>();
+                    AppColumn        = new ReactiveObject<bool>();
+                    DevColumn        = new ReactiveObject<bool>();
+                    VersionColumn    = new ReactiveObject<bool>();
+                    TimePlayedColumn = new ReactiveObject<bool>();
+                    LastPlayedColumn = new ReactiveObject<bool>();
+                    FileExtColumn    = new ReactiveObject<bool>();
+                    FileSizeColumn   = new ReactiveObject<bool>();
+                    PathColumn       = new ReactiveObject<bool>();
+                }
+            }
+
+            /// <summary>
+            /// Used to toggle columns in the GUI
+            /// </summary>
+            public Columns GuiColumns { get; private set; }
+
+            /// <summary>
+            /// A list of directories containing games to be used to load games into the games list
+            /// </summary>
+            public ReactiveObject<List<string>> GameDirs { get; private set; }
+
+            /// <summary>
+            /// Enable or disable custom themes in the GUI
+            /// </summary>
+            public ReactiveObject<bool> EnableCustomTheme { get; private set; }
+
+            /// <summary>
+            /// Path to custom GUI theme
+            /// </summary>
+            public ReactiveObject<string> CustomThemePath { get; private set; }
+
+            public UiSection()
+            {
+                GuiColumns        = new Columns();
+                GameDirs          = new ReactiveObject<List<string>>();
+                EnableCustomTheme = new ReactiveObject<bool>();
+                CustomThemePath   = new ReactiveObject<string>();
+            }
+        }
+
+        /// <summary>
+        /// Logger configuration section
+        /// </summary>
+        public class LoggerSection
+        {
+            /// <summary>
+            /// Enables printing debug log messages
+            /// </summary>
+            public ReactiveObject<bool> EnableDebug { get; private set; }
+
+            /// <summary>
+            /// Enables printing stub log messages
+            /// </summary>
+            public ReactiveObject<bool> EnableStub { get; private set; }
+
+            /// <summary>
+            /// Enables printing info log messages
+            /// </summary>
+            public ReactiveObject<bool> EnableInfo { get; private set; }
+
+            /// <summary>
+            /// Enables printing warning log messages
+            /// </summary>
+            public ReactiveObject<bool> EnableWarn { get; private set; }
+
+            /// <summary>
+            /// Enables printing error log messages
+            /// </summary>
+            public ReactiveObject<bool> EnableError { get; private set; }
+
+            /// <summary>
+            /// Enables printing guest log messages
+            /// </summary>
+            public ReactiveObject<bool> EnableGuest { get; private set; }
+
+            /// <summary>
+            /// Enables printing FS access log messages
+            /// </summary>
+            public ReactiveObject<bool> EnableFsAccessLog { get; private set; }
+
+            /// <summary>
+            /// Controls which log messages are written to the log targets
+            /// </summary>
+            public ReactiveObject<LogClass[]> FilteredClasses { get; private set; }
+
+            /// <summary>
+            /// Enables or disables logging to a file on disk
+            /// </summary>
+            public ReactiveObject<bool> EnableFileLog { get; private set; }
+
+            public LoggerSection()
+            {
+                EnableDebug       = new ReactiveObject<bool>();
+                EnableStub        = new ReactiveObject<bool>();
+                EnableInfo        = new ReactiveObject<bool>();
+                EnableWarn        = new ReactiveObject<bool>();
+                EnableError       = new ReactiveObject<bool>();
+                EnableGuest       = new ReactiveObject<bool>();
+                EnableFsAccessLog = new ReactiveObject<bool>();
+                FilteredClasses   = new ReactiveObject<LogClass[]>();
+                EnableFileLog     = new ReactiveObject<bool>();
+            }
+        }
+
+        /// <summary>
+        /// System configuration section
+        /// </summary>
+        public class SystemSection
+        {
+            /// <summary>
+            /// Change System Language
+            /// </summary>
+            public ReactiveObject<Language> Language { get; private set; }
+
+            /// <summary>
+            /// Enables or disables Docked Mode
+            /// </summary>
+            public ReactiveObject<bool> EnableDockedMode { get; private set; }
+
+            /// <summary>
+            /// Enables or disables multi-core scheduling of threads
+            /// </summary>
+            public ReactiveObject<bool> EnableMulticoreScheduling { get; private set; }
+
+            /// <summary>
+            /// Enables integrity checks on Game content files
+            /// </summary>
+            public ReactiveObject<bool> EnableFsIntegrityChecks { get; private set; }
+
+            /// <summary>
+            /// Enables FS access log output to the console. Possible modes are 0-3
+            /// </summary>
+            public ReactiveObject<int> FsGlobalAccessLogMode { get; private set; }
+
+            /// <summary>
+            /// Enable or disable ignoring missing services
+            /// </summary>
+            public ReactiveObject<bool> IgnoreMissingServices { get; private set; }
+
+            public SystemSection()
+            {
+                Language                  = new ReactiveObject<Language>();
+                EnableDockedMode          = new ReactiveObject<bool>();
+                EnableMulticoreScheduling = new ReactiveObject<bool>();
+                EnableFsIntegrityChecks   = new ReactiveObject<bool>();
+                FsGlobalAccessLogMode     = new ReactiveObject<int>();
+                IgnoreMissingServices     = new ReactiveObject<bool>();
+            }
+        }
+
+        /// <summary>
+        /// Hid configuration section
+        /// </summary>
+        public class HidSection
+        {
+            /// <summary>
+            ///  The primary controller's type
+            /// </summary>
+            public ReactiveObject<ControllerType> ControllerType { get; private set; }
+
+            /// <summary>
+            /// Enable or disable keyboard support (Independent from controllers binding)
+            /// </summary>
+            public ReactiveObject<bool> EnableKeyboard { get; private set; }
+
+            /// <summary>
+            /// Keyboard control bindings
+            /// </summary>
+            public ReactiveObject<NpadKeyboard> KeyboardControls { get; private set; }
+
+            /// <summary>
+            /// Controller control bindings
+            /// </summary>
+            public ReactiveObject<NpadController> JoystickControls { get; private set; }
+
+            public HidSection()
+            {
+                ControllerType   = new ReactiveObject<ControllerType>();
+                EnableKeyboard   = new ReactiveObject<bool>();
+                KeyboardControls = new ReactiveObject<NpadKeyboard>();
+                JoystickControls = new ReactiveObject<NpadController>();
+            }
+        }
+
+        /// <summary>
+        /// Graphics configuration section
+        /// </summary>
+        public class GraphicsSection
+        {
+            /// <summary>
+            /// Dumps shaders in this local directory
+            /// </summary>
+            public ReactiveObject<string> ShadersDumpPath { get; private set; }
+
+            /// <summary>
+            /// Enables or disables Vertical Sync
+            /// </summary>
+            public ReactiveObject<bool> EnableVsync { get; private set; }
+
+            public GraphicsSection()
+            {
+                ShadersDumpPath = new ReactiveObject<string>();
+                EnableVsync     = new ReactiveObject<bool>();
+            }
+        }
+
+        /// <summary>
+        /// The default configuration instance
+        /// </summary>
+        public static ConfigurationState Instance { get; private set; }
+
+        /// <summary>
+        /// The Ui section
+        /// </summary>
+        public UiSection Ui { get; private set; }
+
+        /// <summary>
+        /// The Logger section
+        /// </summary>
+        public LoggerSection Logger { get; private set; }
+
+        /// <summary>
+        /// The System section
+        /// </summary>
+        public SystemSection System { get; private set; }
+
+        /// <summary>
+        /// The Graphics section
+        /// </summary>
+        public GraphicsSection Graphics { get; private set; }
+
+        /// <summary>
+        /// The Hid section
+        /// </summary>
+        public HidSection Hid { get; private set; }
+
+        /// <summary>
+        /// Enables or disables Discord Rich Presence
+        /// </summary>
+        public ReactiveObject<bool> EnableDiscordIntegration { get; private set; }
+
+        private ConfigurationState()
+        {
+            Ui                       = new UiSection();
+            Logger                   = new LoggerSection();
+            System                   = new SystemSection();
+            Graphics                 = new GraphicsSection();
+            Hid                      = new HidSection();
+            EnableDiscordIntegration = new ReactiveObject<bool>();
+        }
+
+        public ConfigurationFileFormat ToFileFormat()
+        {
+            ConfigurationFileFormat configurationFile = new ConfigurationFileFormat
+            {
+                Version                   = 1,
+                GraphicsShadersDumpPath   = Graphics.ShadersDumpPath,
+                LoggingEnableDebug        = Logger.EnableDebug,
+                LoggingEnableStub         = Logger.EnableStub,
+                LoggingEnableInfo         = Logger.EnableInfo,
+                LoggingEnableWarn         = Logger.EnableWarn,
+                LoggingEnableError        = Logger.EnableError,
+                LoggingEnableGuest        = Logger.EnableGuest,
+                LoggingEnableFsAccessLog  = Logger.EnableFsAccessLog,
+                LoggingFilteredClasses    = Logger.FilteredClasses,
+                EnableFileLog             = Logger.EnableFileLog,
+                SystemLanguage            = System.Language,
+                DockedMode                = System.EnableDockedMode,
+                EnableDiscordIntegration  = EnableDiscordIntegration,
+                EnableVsync               = Graphics.EnableVsync,
+                EnableMulticoreScheduling = System.EnableMulticoreScheduling,
+                EnableFsIntegrityChecks   = System.EnableFsIntegrityChecks,
+                FsGlobalAccessLogMode     = System.FsGlobalAccessLogMode,
+                IgnoreMissingServices     = System.IgnoreMissingServices,
+                ControllerType            = Hid.ControllerType,
+                GuiColumns                = new GuiColumns()
+                {
+                    FavColumn        = Ui.GuiColumns.FavColumn,
+                    IconColumn       = Ui.GuiColumns.IconColumn,
+                    AppColumn        = Ui.GuiColumns.AppColumn,
+                    DevColumn        = Ui.GuiColumns.DevColumn,
+                    VersionColumn    = Ui.GuiColumns.VersionColumn,
+                    TimePlayedColumn = Ui.GuiColumns.TimePlayedColumn,
+                    LastPlayedColumn = Ui.GuiColumns.LastPlayedColumn,
+                    FileExtColumn    = Ui.GuiColumns.FileExtColumn,
+                    FileSizeColumn   = Ui.GuiColumns.FileSizeColumn,
+                    PathColumn       = Ui.GuiColumns.PathColumn,
+                },
+                GameDirs                  = Ui.GameDirs,
+                EnableCustomTheme         = Ui.EnableCustomTheme,
+                CustomThemePath           = Ui.CustomThemePath,
+                EnableKeyboard            = Hid.EnableKeyboard,
+                KeyboardControls          = Hid.KeyboardControls,
+                JoystickControls          = Hid.JoystickControls
+            };
+
+            return configurationFile;
+        }
+
+        public void LoadDefault()
+        {
+            Graphics.ShadersDumpPath.Value         = "";
+            Logger.EnableDebug.Value               = false;
+            Logger.EnableStub.Value                = true;
+            Logger.EnableInfo.Value                = true;
+            Logger.EnableWarn.Value                = true;
+            Logger.EnableError.Value               = true;
+            Logger.EnableGuest.Value               = true;
+            Logger.EnableFsAccessLog.Value         = false;
+            Logger.FilteredClasses.Value           = new LogClass[] { };
+            Logger.EnableFileLog.Value             = true;
+            System.Language.Value                  = Language.AmericanEnglish;
+            System.EnableDockedMode.Value          = false;
+            EnableDiscordIntegration.Value         = true;
+            Graphics.EnableVsync.Value             = true;
+            System.EnableMulticoreScheduling.Value = true;
+            System.EnableFsIntegrityChecks.Value   = true;
+            System.FsGlobalAccessLogMode.Value     = 0;
+            System.IgnoreMissingServices.Value     = false;
+            Hid.ControllerType.Value               = ControllerType.Handheld;
+            Ui.GuiColumns.FavColumn.Value          = true;
+            Ui.GuiColumns.IconColumn.Value         = true;
+            Ui.GuiColumns.AppColumn.Value          = true;
+            Ui.GuiColumns.DevColumn.Value          = true;
+            Ui.GuiColumns.VersionColumn.Value      = true;
+            Ui.GuiColumns.TimePlayedColumn.Value   = true;
+            Ui.GuiColumns.LastPlayedColumn.Value   = true;
+            Ui.GuiColumns.FileExtColumn.Value      = true;
+            Ui.GuiColumns.FileSizeColumn.Value     = true;
+            Ui.GuiColumns.PathColumn.Value         = true;
+            Ui.GameDirs.Value                      = new List<string>();
+            Ui.EnableCustomTheme.Value             = false;
+            Ui.CustomThemePath.Value               = "";
+            Hid.EnableKeyboard.Value               = false;
+
+            Hid.KeyboardControls.Value = new NpadKeyboard
+            {
+                LeftJoycon  = new NpadKeyboardLeft
+                {
+                    StickUp     = Key.W,
+                    StickDown   = Key.S,
+                    StickLeft   = Key.A,
+                    StickRight  = Key.D,
+                    StickButton = Key.F,
+                    DPadUp      = Key.Up,
+                    DPadDown    = Key.Down,
+                    DPadLeft    = Key.Left,
+                    DPadRight   = Key.Right,
+                    ButtonMinus = Key.Minus,
+                    ButtonL     = Key.E,
+                    ButtonZl    = Key.Q,
+                },
+                RightJoycon = new NpadKeyboardRight
+                {
+                    StickUp     = Key.I,
+                    StickDown   = Key.K,
+                    StickLeft   = Key.J,
+                    StickRight  = Key.L,
+                    StickButton = Key.H,
+                    ButtonA     = Key.Z,
+                    ButtonB     = Key.X,
+                    ButtonX     = Key.C,
+                    ButtonY     = Key.V,
+                    ButtonPlus  = Key.Plus,
+                    ButtonR     = Key.U,
+                    ButtonZr    = Key.O,
+                },
+                Hotkeys     = new KeyboardHotkeys
+                {
+                    ToggleVsync = Key.Tab
+                }
+            };
+
+            Hid.JoystickControls.Value = new NpadController
+            {
+                Enabled          = true,
+                Index            = 0,
+                Deadzone         = 0.05f,
+                TriggerThreshold = 0.5f,
+                LeftJoycon       = new NpadControllerLeft
+                {
+                    Stick       = ControllerInputId.Axis0,
+                    StickButton = ControllerInputId.Button8,
+                    DPadUp      = ControllerInputId.Hat0Up,
+                    DPadDown    = ControllerInputId.Hat0Down,
+                    DPadLeft    = ControllerInputId.Hat0Left,
+                    DPadRight   = ControllerInputId.Hat0Right,
+                    ButtonMinus = ControllerInputId.Button6,
+                    ButtonL     = ControllerInputId.Button4,
+                    ButtonZl    = ControllerInputId.Axis2,
+                },
+                RightJoycon      = new NpadControllerRight
+                {
+                    Stick       = ControllerInputId.Axis3,
+                    StickButton = ControllerInputId.Button9,
+                    ButtonA     = ControllerInputId.Button1,
+                    ButtonB     = ControllerInputId.Button0,
+                    ButtonX     = ControllerInputId.Button3,
+                    ButtonY     = ControllerInputId.Button2,
+                    ButtonPlus  = ControllerInputId.Button7,
+                    ButtonR     = ControllerInputId.Button5,
+                    ButtonZr    = ControllerInputId.Axis5,
+                }
+            };
+        }
+
+        public void Load(ConfigurationFileFormat configurationFileFormat)
+        {
+            if (configurationFileFormat.Version != 1 && configurationFileFormat.Version != 0)
+            {
+                Common.Logging.Logger.PrintWarning(LogClass.Application, $"Unsupported configuration version {configurationFileFormat.Version}, loading default.");
+
+                LoadDefault();
+
+                return;
+            }
+
+            Graphics.ShadersDumpPath.Value         = configurationFileFormat.GraphicsShadersDumpPath;
+            Logger.EnableDebug.Value               = configurationFileFormat.LoggingEnableDebug;
+            Logger.EnableStub.Value                = configurationFileFormat.LoggingEnableStub;
+            Logger.EnableInfo.Value                = configurationFileFormat.LoggingEnableInfo;
+            Logger.EnableWarn.Value                = configurationFileFormat.LoggingEnableWarn;
+            Logger.EnableError.Value               = configurationFileFormat.LoggingEnableError;
+            Logger.EnableGuest.Value               = configurationFileFormat.LoggingEnableGuest;
+            Logger.EnableFsAccessLog.Value         = configurationFileFormat.LoggingEnableFsAccessLog;
+            Logger.FilteredClasses.Value           = configurationFileFormat.LoggingFilteredClasses;
+            Logger.EnableFileLog.Value             = configurationFileFormat.EnableFileLog;
+            System.Language.Value                  = configurationFileFormat.SystemLanguage;
+            System.EnableDockedMode.Value          = configurationFileFormat.DockedMode;
+            System.EnableDockedMode.Value          = configurationFileFormat.DockedMode;
+            EnableDiscordIntegration.Value         = configurationFileFormat.EnableDiscordIntegration;
+            Graphics.EnableVsync.Value             = configurationFileFormat.EnableVsync;
+            System.EnableMulticoreScheduling.Value = configurationFileFormat.EnableMulticoreScheduling;
+            System.EnableFsIntegrityChecks.Value   = configurationFileFormat.EnableFsIntegrityChecks;
+            System.FsGlobalAccessLogMode.Value     = configurationFileFormat.FsGlobalAccessLogMode;
+            System.IgnoreMissingServices.Value     = configurationFileFormat.IgnoreMissingServices;
+            Hid.ControllerType.Value               = configurationFileFormat.ControllerType;
+            Ui.GuiColumns.FavColumn.Value          = configurationFileFormat.GuiColumns.FavColumn;
+            Ui.GuiColumns.IconColumn.Value         = configurationFileFormat.GuiColumns.IconColumn;
+            Ui.GuiColumns.AppColumn.Value          = configurationFileFormat.GuiColumns.AppColumn;
+            Ui.GuiColumns.DevColumn.Value          = configurationFileFormat.GuiColumns.DevColumn;
+            Ui.GuiColumns.VersionColumn.Value      = configurationFileFormat.GuiColumns.VersionColumn;
+            Ui.GuiColumns.TimePlayedColumn.Value   = configurationFileFormat.GuiColumns.TimePlayedColumn;
+            Ui.GuiColumns.LastPlayedColumn.Value   = configurationFileFormat.GuiColumns.LastPlayedColumn;
+            Ui.GuiColumns.FileExtColumn.Value      = configurationFileFormat.GuiColumns.FileExtColumn;
+            Ui.GuiColumns.FileSizeColumn.Value     = configurationFileFormat.GuiColumns.FileSizeColumn;
+            Ui.GuiColumns.PathColumn.Value         = configurationFileFormat.GuiColumns.PathColumn;
+            Ui.GameDirs.Value                      = configurationFileFormat.GameDirs;
+            Ui.EnableCustomTheme.Value             = configurationFileFormat.EnableCustomTheme;
+            Ui.CustomThemePath.Value               = configurationFileFormat.CustomThemePath;
+            Hid.EnableKeyboard.Value               = configurationFileFormat.EnableKeyboard;
+            Hid.KeyboardControls.Value             = configurationFileFormat.KeyboardControls;
+            Hid.JoystickControls.Value             = configurationFileFormat.JoystickControls;
+        }
+
+        public static void Initialize()
+        {
+            if (Instance != null)
+            {
+                throw new InvalidOperationException("Configuration is already initialized");
+            }
+
+            Instance = new ConfigurationState();
+        }
+    }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/ControllerInputId.cs b/Ryujinx.Common/Configuration/Hid/ControllerInputId.cs
new file mode 100644
index 000000000..8969b6a4b
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/ControllerInputId.cs
@@ -0,0 +1,45 @@
+namespace Ryujinx.Common.Configuration.Hid
+{
+    public enum ControllerInputId
+    {
+        Button0,
+        Button1,
+        Button2,
+        Button3,
+        Button4,
+        Button5,
+        Button6,
+        Button7,
+        Button8,
+        Button9,
+        Button10,
+        Button11,
+        Button12,
+        Button13,
+        Button14,
+        Button15,
+        Button16,
+        Button17,
+        Button18,
+        Button19,
+        Button20,
+        Axis0,
+        Axis1,
+        Axis2,
+        Axis3,
+        Axis4,
+        Axis5,
+        Hat0Up,
+        Hat0Down,
+        Hat0Left,
+        Hat0Right,
+        Hat1Up,
+        Hat1Down,
+        Hat1Left,
+        Hat1Right,
+        Hat2Up,
+        Hat2Down,
+        Hat2Left,
+        Hat2Right
+    }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/ControllerType.cs b/Ryujinx.Common/Configuration/Hid/ControllerType.cs
new file mode 100644
index 000000000..b0613b2d6
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/ControllerType.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.Configuration.Hid
+{
+    public enum ControllerType
+    {
+        ProController,
+        Handheld,
+        NpadPair,
+        NpadLeft,
+        NpadRight
+    }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/Key.cs b/Ryujinx.Common/Configuration/Hid/Key.cs
new file mode 100644
index 000000000..b658396b9
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/Key.cs
@@ -0,0 +1,153 @@
+namespace Ryujinx.Configuration.Hid
+{
+    public enum Key
+    {
+        Unknown        = 0,
+        ShiftLeft      = 1,
+        LShift         = 1,
+        ShiftRight     = 2,
+        RShift         = 2,
+        ControlLeft    = 3,
+        LControl       = 3,
+        ControlRight   = 4,
+        RControl       = 4,
+        AltLeft        = 5,
+        LAlt           = 5,
+        AltRight       = 6,
+        RAlt           = 6,
+        WinLeft        = 7,
+        LWin           = 7,
+        WinRight       = 8,
+        RWin           = 8,
+        Menu           = 9,
+        F1             = 10,
+        F2             = 11,
+        F3             = 12,
+        F4             = 13,
+        F5             = 14,
+        F6             = 15,
+        F7             = 16,
+        F8             = 17,
+        F9             = 18,
+        F10            = 19,
+        F11            = 20,
+        F12            = 21,
+        F13            = 22,
+        F14            = 23,
+        F15            = 24,
+        F16            = 25,
+        F17            = 26,
+        F18            = 27,
+        F19            = 28,
+        F20            = 29,
+        F21            = 30,
+        F22            = 31,
+        F23            = 32,
+        F24            = 33,
+        F25            = 34,
+        F26            = 35,
+        F27            = 36,
+        F28            = 37,
+        F29            = 38,
+        F30            = 39,
+        F31            = 40,
+        F32            = 41,
+        F33            = 42,
+        F34            = 43,
+        F35            = 44,
+        Up             = 45,
+        Down           = 46,
+        Left           = 47,
+        Right          = 48,
+        Enter          = 49,
+        Escape         = 50,
+        Space          = 51,
+        Tab            = 52,
+        BackSpace      = 53,
+        Back           = 53,
+        Insert         = 54,
+        Delete         = 55,
+        PageUp         = 56,
+        PageDown       = 57,
+        Home           = 58,
+        End            = 59,
+        CapsLock       = 60,
+        ScrollLock     = 61,
+        PrintScreen    = 62,
+        Pause          = 63,
+        NumLock        = 64,
+        Clear          = 65,
+        Sleep          = 66,
+        Keypad0        = 67,
+        Keypad1        = 68,
+        Keypad2        = 69,
+        Keypad3        = 70,
+        Keypad4        = 71,
+        Keypad5        = 72,
+        Keypad6        = 73,
+        Keypad7        = 74,
+        Keypad8        = 75,
+        Keypad9        = 76,
+        KeypadDivide   = 77,
+        KeypadMultiply = 78,
+        KeypadSubtract = 79,
+        KeypadMinus    = 79,
+        KeypadAdd      = 80,
+        KeypadPlus     = 80,
+        KeypadDecimal  = 81,
+        KeypadPeriod   = 81,
+        KeypadEnter    = 82,
+        A              = 83,
+        B              = 84,
+        C              = 85,
+        D              = 86,
+        E              = 87,
+        F              = 88,
+        G              = 89,
+        H              = 90,
+        I              = 91,
+        J              = 92,
+        K              = 93,
+        L              = 94,
+        M              = 95,
+        N              = 96,
+        O              = 97,
+        P              = 98,
+        Q              = 99,
+        R              = 100,
+        S              = 101,
+        T              = 102,
+        U              = 103,
+        V              = 104,
+        W              = 105,
+        X              = 106,
+        Y              = 107,
+        Z              = 108,
+        Number0        = 109,
+        Number1        = 110,
+        Number2        = 111,
+        Number3        = 112,
+        Number4        = 113,
+        Number5        = 114,
+        Number6        = 115,
+        Number7        = 116,
+        Number8        = 117,
+        Number9        = 118,
+        Tilde          = 119,
+        Grave          = 119,
+        Minus          = 120,
+        Plus           = 121,
+        BracketLeft    = 122,
+        LBracket       = 122,
+        BracketRight   = 123,
+        RBracket       = 123,
+        Semicolon      = 124,
+        Quote          = 125,
+        Comma          = 126,
+        Period         = 127,
+        Slash          = 128,
+        BackSlash      = 129,
+        NonUSBackSlash = 130,
+        LastKey        = 131
+    }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
new file mode 100644
index 000000000..1d0b05049
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.Configuration.Hid
+{
+    public struct KeyboardHotkeys
+    {
+        public Key ToggleVsync;
+    }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/NpadController.cs b/Ryujinx.Common/Configuration/Hid/NpadController.cs
new file mode 100644
index 000000000..f00865d55
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/NpadController.cs
@@ -0,0 +1,35 @@
+namespace Ryujinx.Common.Configuration.Hid
+{
+    public class NpadController
+    {
+        /// <summary>
+        /// Enables or disables controller support
+        /// </summary>
+        public bool Enabled;
+
+        /// <summary>
+        /// Controller Device Index
+        /// </summary>
+        public int Index;
+
+        /// <summary>
+        /// Controller Analog Stick Deadzone
+        /// </summary>
+        public float Deadzone;
+
+        /// <summary>
+        /// Controller Trigger Threshold
+        /// </summary>
+        public float TriggerThreshold;
+
+        /// <summary>
+        /// Left JoyCon Controller Bindings
+        /// </summary>
+        public NpadControllerLeft LeftJoycon;
+
+        /// <summary>
+        /// Right JoyCon Controller Bindings
+        /// </summary>
+        public NpadControllerRight RightJoycon;
+    }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs b/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs
new file mode 100644
index 000000000..54ac0f03a
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.Common.Configuration.Hid
+{
+    public struct NpadControllerLeft
+    {
+        public ControllerInputId Stick;
+        public ControllerInputId StickButton;
+        public ControllerInputId ButtonMinus;
+        public ControllerInputId ButtonL;
+        public ControllerInputId ButtonZl;
+        public ControllerInputId DPadUp;
+        public ControllerInputId DPadDown;
+        public ControllerInputId DPadLeft;
+        public ControllerInputId DPadRight;
+    }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs b/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs
new file mode 100644
index 000000000..315136d9f
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.Common.Configuration.Hid
+{
+    public struct NpadControllerRight
+    {
+        public ControllerInputId Stick;
+        public ControllerInputId StickButton;
+        public ControllerInputId ButtonA;
+        public ControllerInputId ButtonB;
+        public ControllerInputId ButtonX;
+        public ControllerInputId ButtonY;
+        public ControllerInputId ButtonPlus;
+        public ControllerInputId ButtonR;
+        public ControllerInputId ButtonZr;
+    }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs b/Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs
new file mode 100644
index 000000000..911f5119e
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs
@@ -0,0 +1,20 @@
+namespace Ryujinx.UI.Input
+{
+    public class NpadKeyboard
+    {
+        /// <summary>
+        /// Left JoyCon Keyboard Bindings
+        /// </summary>
+        public Configuration.Hid.NpadKeyboardLeft LeftJoycon;
+
+        /// <summary>
+        /// Right JoyCon Keyboard Bindings
+        /// </summary>
+        public Configuration.Hid.NpadKeyboardRight RightJoycon;
+
+        /// <summary>
+        /// Hotkey Keyboard Bindings
+        /// </summary>
+        public Configuration.Hid.KeyboardHotkeys Hotkeys;
+    }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs b/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs
new file mode 100644
index 000000000..799cdfdb8
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.Configuration.Hid
+{
+    public struct NpadKeyboardLeft
+    {
+        public Key StickUp;
+        public Key StickDown;
+        public Key StickLeft;
+        public Key StickRight;
+        public Key StickButton;
+        public Key DPadUp;
+        public Key DPadDown;
+        public Key DPadLeft;
+        public Key DPadRight;
+        public Key ButtonMinus;
+        public Key ButtonL;
+        public Key ButtonZl;
+    }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs b/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs
new file mode 100644
index 000000000..311504bb7
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.Configuration.Hid
+{
+    public struct NpadKeyboardRight
+    {
+        public Key StickUp;
+        public Key StickDown;
+        public Key StickLeft;
+        public Key StickRight;
+        public Key StickButton;
+        public Key ButtonA;
+        public Key ButtonB;
+        public Key ButtonX;
+        public Key ButtonY;
+        public Key ButtonPlus;
+        public Key ButtonR;
+        public Key ButtonZr;
+    }
+}
diff --git a/Ryujinx.Common/Configuration/LoggerModule.cs b/Ryujinx.Common/Configuration/LoggerModule.cs
new file mode 100644
index 000000000..504a81418
--- /dev/null
+++ b/Ryujinx.Common/Configuration/LoggerModule.cs
@@ -0,0 +1,109 @@
+using Ryujinx.Common;
+using Ryujinx.Common.Logging;
+using System;
+using System.IO;
+
+namespace Ryujinx.Configuration
+{
+    public static class LoggerModule
+    {
+        public static void Initialize()
+        {
+            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
+            AppDomain.CurrentDomain.ProcessExit        += CurrentDomain_ProcessExit;
+
+            ConfigurationState.Instance.Logger.EnableDebug.Event       += ReloadEnableDebug;
+            ConfigurationState.Instance.Logger.EnableStub.Event        += ReloadEnableStub;
+            ConfigurationState.Instance.Logger.EnableInfo.Event        += ReloadEnableInfo;
+            ConfigurationState.Instance.Logger.EnableWarn.Event        += ReloadEnableWarning;
+            ConfigurationState.Instance.Logger.EnableError.Event       += ReloadEnableError;
+            ConfigurationState.Instance.Logger.EnableGuest.Event       += ReloadEnableGuest;
+            ConfigurationState.Instance.Logger.EnableFsAccessLog.Event += ReloadEnableFsAccessLog;
+            ConfigurationState.Instance.Logger.FilteredClasses.Event   += ReloadFilteredClasses;
+            ConfigurationState.Instance.Logger.EnableFileLog.Event     += ReloadFileLogger;
+        }
+
+        private static void ReloadEnableDebug(object sender, ReactiveEventArgs<bool> e)
+        {
+            Logger.SetEnable(LogLevel.Debug, e.NewValue);
+        }
+
+        private static void ReloadEnableStub(object sender, ReactiveEventArgs<bool> e)
+        {
+            Logger.SetEnable(LogLevel.Stub, e.NewValue);
+        }
+
+        private static void ReloadEnableInfo(object sender, ReactiveEventArgs<bool> e)
+        {
+            Logger.SetEnable(LogLevel.Info, e.NewValue);
+        }
+
+        private static void ReloadEnableWarning(object sender, ReactiveEventArgs<bool> e)
+        {
+            Logger.SetEnable(LogLevel.Warning, e.NewValue);
+        }
+
+        private static void ReloadEnableError(object sender, ReactiveEventArgs<bool> e)
+        {
+            Logger.SetEnable(LogLevel.Error, e.NewValue);
+        }
+
+        private static void ReloadEnableGuest(object sender, ReactiveEventArgs<bool> e)
+        {
+            Logger.SetEnable(LogLevel.Guest, e.NewValue);
+        }
+
+        private static void ReloadEnableFsAccessLog(object sender, ReactiveEventArgs<bool> e)
+        {
+            Logger.SetEnable(LogLevel.AccessLog, e.NewValue);
+        }
+
+        private static void ReloadFilteredClasses(object sender, ReactiveEventArgs<LogClass[]> e)
+        {
+            bool noFilter = e.NewValue.Length == 0;
+
+            foreach (var logClass in EnumExtensions.GetValues<LogClass>())
+            {
+                Logger.SetEnable(logClass, noFilter);
+            }
+
+            foreach (var logClass in e.NewValue)
+            {
+                Logger.SetEnable(logClass, true);
+            }
+        }
+
+        private static void ReloadFileLogger(object sender, ReactiveEventArgs<bool> e)
+        {
+            if (e.NewValue)
+            {
+                Logger.AddTarget(new AsyncLogTargetWrapper(
+                    new FileLogTarget(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.log"), "file"),
+                    1000,
+                    AsyncLogTargetOverflowAction.Block
+                ));
+            }
+            else
+            {
+                Logger.RemoveTarget("file");
+            }
+        }
+
+        private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
+        {
+            Logger.Shutdown();
+        }
+
+        private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
+        {
+            var exception = e.ExceptionObject as Exception;
+
+            Logger.PrintError(LogClass.Emulation, $"Unhandled exception caught: {exception}");
+
+            if (e.IsTerminating)
+            {
+                Logger.Shutdown();
+            }
+        }
+    }
+}
diff --git a/Ryujinx.Common/Configuration/System/Language.cs b/Ryujinx.Common/Configuration/System/Language.cs
new file mode 100644
index 000000000..d3af296ba
--- /dev/null
+++ b/Ryujinx.Common/Configuration/System/Language.cs
@@ -0,0 +1,23 @@
+namespace Ryujinx.Configuration.System
+{
+    public enum Language
+    {
+        Japanese,
+        AmericanEnglish,
+        French,
+        German,
+        Italian,
+        Spanish,
+        Chinese,
+        Korean,
+        Dutch,
+        Portuguese,
+        Russian,
+        Taiwanese,
+        BritishEnglish,
+        CanadianFrench,
+        LatinAmericanSpanish,
+        SimplifiedChinese,
+        TraditionalChinese
+    }
+}
diff --git a/Ryujinx/Ui/GuiColumns.cs b/Ryujinx.Common/Configuration/Ui/GuiColumns.cs
similarity index 90%
rename from Ryujinx/Ui/GuiColumns.cs
rename to Ryujinx.Common/Configuration/Ui/GuiColumns.cs
index b86a273ea..2b3524aa8 100644
--- a/Ryujinx/Ui/GuiColumns.cs
+++ b/Ryujinx.Common/Configuration/Ui/GuiColumns.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.Ui
+namespace Ryujinx.Configuration.Ui
 {
     public struct GuiColumns
     {
diff --git a/Ryujinx.Common/Logging/Logger.cs b/Ryujinx.Common/Logging/Logger.cs
index 10b1d9703..83af97b12 100644
--- a/Ryujinx.Common/Logging/Logger.cs
+++ b/Ryujinx.Common/Logging/Logger.cs
@@ -37,6 +37,12 @@ namespace Ryujinx.Common.Logging
             m_LogTargets = new List<ILogTarget>();
 
             m_Time = Stopwatch.StartNew();
+
+            // Logger should log to console by default
+            AddTarget(new AsyncLogTargetWrapper(
+                new ConsoleLogTarget("console"),
+                1000,
+                AsyncLogTargetOverflowAction.Block));
         }
 
         public static void RestartTime()
@@ -44,6 +50,19 @@ namespace Ryujinx.Common.Logging
             m_Time.Restart();
         }
 
+        private static ILogTarget GetTarget(string targetName)
+        {
+            foreach (var target in m_LogTargets)
+            {
+                if (target.Name.Equals(targetName))
+                {
+                    return target;
+                }
+            }
+
+            return null;
+        }
+
         public static void AddTarget(ILogTarget target)
         {
             m_LogTargets.Add(target);
@@ -51,6 +70,20 @@ namespace Ryujinx.Common.Logging
             Updated += target.Log;
         }
 
+        public static void RemoveTarget(string target)
+        {
+            ILogTarget logTarget = GetTarget(target);
+
+            if (logTarget != null)
+            {
+                Updated -= logTarget.Log;
+
+                m_LogTargets.Remove(logTarget);
+
+                logTarget.Dispose();
+            }
+        }
+
         public static void Shutdown()
         {
             Updated = null;
diff --git a/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs b/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs
index a805a83b6..c946b6788 100644
--- a/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs
+++ b/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs
@@ -27,6 +27,8 @@ namespace Ryujinx.Common.Logging
 
         private readonly int _overflowTimeout;
 
+        string ILogTarget.Name { get => _target.Name; }
+
         public AsyncLogTargetWrapper(ILogTarget target)
             : this(target, -1, AsyncLogTargetOverflowAction.Block)
         { }
diff --git a/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs b/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs
index 871076a41..ff5c6f5ac 100644
--- a/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs
+++ b/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs
@@ -9,6 +9,10 @@ namespace Ryujinx.Common.Logging
 
         private readonly ILogFormatter _formatter;
 
+        private readonly string _name;
+
+        string ILogTarget.Name { get => _name; }
+
         static ConsoleLogTarget()
         {
             _logColors = new ConcurrentDictionary<LogLevel, ConsoleColor> {
@@ -19,9 +23,10 @@ namespace Ryujinx.Common.Logging
             };
         }
 
-        public ConsoleLogTarget()
+        public ConsoleLogTarget(string name)
         {
             _formatter = new DefaultLogFormatter();
+            _name      = name;
         }
 
         public void Log(object sender, LogEventArgs args)
diff --git a/Ryujinx.Common/Logging/Targets/FileLogTarget.cs b/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
index 85dc82497..4db5f7bce 100644
--- a/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
+++ b/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
@@ -1,4 +1,5 @@
-using System.IO;
+using System;
+using System.IO;
 using System.Text;
 
 namespace Ryujinx.Common.Logging
@@ -9,13 +10,17 @@ namespace Ryujinx.Common.Logging
 
         private readonly StreamWriter  _logWriter;
         private readonly ILogFormatter _formatter;
+        private readonly string        _name;
 
-        public FileLogTarget(string path)
-            : this(path, FileShare.Read, FileMode.Append)
+        string ILogTarget.Name { get => _name; }
+
+        public FileLogTarget(string path, string name)
+            : this(path, name, FileShare.Read, FileMode.Append)
         { }
 
-        public FileLogTarget(string path, FileShare fileShare, FileMode fileMode)
+        public FileLogTarget(string path, string name, FileShare fileShare, FileMode fileMode)
         {
+            _name      = name;
             _logWriter = new StreamWriter(File.Open(path, fileMode, FileAccess.Write, fileShare));
             _formatter = new DefaultLogFormatter();
         }
diff --git a/Ryujinx.Common/Logging/Targets/ILogTarget.cs b/Ryujinx.Common/Logging/Targets/ILogTarget.cs
index 261c5e64b..d4d26a936 100644
--- a/Ryujinx.Common/Logging/Targets/ILogTarget.cs
+++ b/Ryujinx.Common/Logging/Targets/ILogTarget.cs
@@ -5,5 +5,7 @@ namespace Ryujinx.Common.Logging
     public interface ILogTarget : IDisposable
     {
         void Log(object sender, LogEventArgs args);
+
+        string Name { get; }
     }
 }
diff --git a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs
index 410394aa2..3729b18d1 100644
--- a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs
+++ b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs
@@ -1,4 +1,5 @@
-using System.IO;
+using System;
+using System.IO;
 using Utf8Json;
 
 namespace Ryujinx.Common.Logging
@@ -7,10 +8,14 @@ namespace Ryujinx.Common.Logging
     {
         private Stream _stream;
         private bool   _leaveOpen;
+        private string _name;
 
-        public JsonLogTarget(Stream stream)
+        string ILogTarget.Name { get => _name; }
+
+        public JsonLogTarget(Stream stream, string name)
         {
             _stream = stream;
+            _name   = name;
         }
 
         public JsonLogTarget(Stream stream, bool leaveOpen)
diff --git a/Ryujinx.Common/ReactiveObject.cs b/Ryujinx.Common/ReactiveObject.cs
new file mode 100644
index 000000000..be30e9b2c
--- /dev/null
+++ b/Ryujinx.Common/ReactiveObject.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Threading;
+
+namespace Ryujinx.Common
+{
+    public class ReactiveObject<T>
+    {
+        private ReaderWriterLock _readerWriterLock = new ReaderWriterLock();
+        private T                _value;
+
+        public event EventHandler<ReactiveEventArgs<T>> Event;
+
+        public T Value
+        {
+            get
+            {
+                _readerWriterLock.AcquireReaderLock(Timeout.Infinite);
+                T value = _value;
+                _readerWriterLock.ReleaseReaderLock();
+
+                return value;
+            }
+            set
+            {
+                _readerWriterLock.AcquireWriterLock(Timeout.Infinite);
+
+                T oldValue = _value;
+                
+                _value = value;
+
+                _readerWriterLock.ReleaseWriterLock();
+
+                if (oldValue == null || !oldValue.Equals(_value))
+                {
+                    Event?.Invoke(this, new ReactiveEventArgs<T>(oldValue, value));
+                }
+            }
+        }
+
+        public static implicit operator T(ReactiveObject<T> obj)
+        {
+            return obj.Value;
+        }
+    }
+
+    public class ReactiveEventArgs<T>
+    {
+        public T OldValue { get; }
+        public T NewValue { get; }
+
+        public ReactiveEventArgs(T oldValue, T newValue)
+        {
+            OldValue = oldValue;
+            NewValue = newValue;
+        }
+    }
+}
diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj
index c777b402c..7f6fa3232 100644
--- a/Ryujinx.Common/Ryujinx.Common.csproj
+++ b/Ryujinx.Common/Ryujinx.Common.csproj
@@ -27,6 +27,7 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <PackageReference Include="JsonPrettyPrinter" Version="1.0.1.1" />
     <PackageReference Include="Utf8Json" Version="1.3.7" />
   </ItemGroup>
 
diff --git a/Ryujinx.HLE/Input/Hid.cs b/Ryujinx.HLE/Input/Hid.cs
index 27e6a3087..5cb7f09de 100644
--- a/Ryujinx.HLE/Input/Hid.cs
+++ b/Ryujinx.HLE/Input/Hid.cs
@@ -1,5 +1,7 @@
 using Ryujinx.Common;
+using Ryujinx.Configuration.Hid;
 using Ryujinx.HLE.HOS;
+using System;
 
 namespace Ryujinx.HLE.Input
 {
@@ -47,18 +49,31 @@ namespace Ryujinx.HLE.Input
             _keyboardOffset     = HidPosition + HidKeyboardOffset;
         }
 
-        public void InitializePrimaryController(ControllerStatus controllerType)
+        private static ControllerStatus ConvertControllerTypeToState(ControllerType controllerType)
         {
-            ControllerId controllerId = controllerType == ControllerStatus.Handheld ?
+            switch (controllerType)
+            {
+                case ControllerType.Handheld:      return ControllerStatus.Handheld;
+                case ControllerType.NpadLeft:      return ControllerStatus.NpadLeft;
+                case ControllerType.NpadRight:     return ControllerStatus.NpadRight;
+                case ControllerType.NpadPair:      return ControllerStatus.NpadPair;
+                case ControllerType.ProController: return ControllerStatus.ProController;
+                default:                           throw new NotImplementedException();
+            }
+        }
+
+        public void InitializePrimaryController(ControllerType controllerType)
+        {
+            ControllerId controllerId = controllerType == ControllerType.Handheld ?
                 ControllerId.ControllerHandheld : ControllerId.ControllerPlayer1;
 
-            if (controllerType == ControllerStatus.ProController)
+            if (controllerType == ControllerType.ProController)
             {
                 PrimaryController = new ProController(_device, NpadColor.Black, NpadColor.Black);
             }
             else
             {
-                PrimaryController = new NpadController(controllerType,
+                PrimaryController = new NpadController(ConvertControllerTypeToState(controllerType),
                      _device,
                      (NpadColor.BodyNeonRed,     NpadColor.BodyNeonRed),
                      (NpadColor.ButtonsNeonBlue, NpadColor.ButtonsNeonBlue));
@@ -67,11 +82,6 @@ namespace Ryujinx.HLE.Input
             PrimaryController.Connect(controllerId);
         }
 
-        public void InitializeKeyboard()
-        {
-            _device.Memory.FillWithZeros(HidPosition + HidKeyboardOffset, HidKeyboardSize);
-        }
-
         public ControllerButtons UpdateStickButtons(
             JoystickPosition leftStick,
             JoystickPosition rightStick)
diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs
index 4a15f616e..a4d07f6ac 100644
--- a/Ryujinx.HLE/Switch.cs
+++ b/Ryujinx.HLE/Switch.cs
@@ -1,8 +1,12 @@
+using LibHac.FsSystem;
 using Ryujinx.Audio;
+using Ryujinx.Configuration;
 using Ryujinx.Graphics;
 using Ryujinx.Graphics.Gal;
 using Ryujinx.HLE.FileSystem;
 using Ryujinx.HLE.HOS;
+using Ryujinx.HLE.HOS.Services;
+using Ryujinx.HLE.HOS.SystemState;
 using Ryujinx.HLE.Input;
 using System;
 using System.Threading;
@@ -60,6 +64,29 @@ namespace Ryujinx.HLE
             VsyncEvent = new AutoResetEvent(true);
         }
 
+        public void Initialize()
+        {
+            System.State.SetLanguage((SystemLanguage)ConfigurationState.Instance.System.Language.Value);
+
+            EnableDeviceVsync = ConfigurationState.Instance.Graphics.EnableVsync;
+
+            // TODO: Make this reloadable and implement Docking/Undocking logic.
+            System.State.DockedMode = ConfigurationState.Instance.System.EnableDockedMode;
+
+            if (ConfigurationState.Instance.System.EnableMulticoreScheduling)
+            {
+                System.EnableMultiCoreScheduling();
+            }
+
+            System.FsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
+                ? IntegrityCheckLevel.ErrorOnInvalid
+                : IntegrityCheckLevel.None;
+
+            System.GlobalAccessLogMode = ConfigurationState.Instance.System.FsGlobalAccessLogMode;
+
+            ServiceConfiguration.IgnoreMissingServices = ConfigurationState.Instance.System.IgnoreMissingServices;
+        }
+
         public void LoadCart(string exeFsDir, string romFsFile = null)
         {
             System.LoadCart(exeFsDir, romFsFile);
diff --git a/Ryujinx/Config.json b/Ryujinx/Config.json
index 8463081fa..e35a67acd 100644
--- a/Ryujinx/Config.json
+++ b/Ryujinx/Config.json
@@ -1,4 +1,5 @@
 {
+    "version": 1,
     "graphics_shaders_dump_path": "",
     "logging_enable_debug": false,
     "logging_enable_stub": true,
@@ -7,9 +8,7 @@
     "logging_enable_error": true,
     "logging_enable_guest": true,
     "logging_enable_fs_access_log": false,
-    "logging_filtered_classes": [
-        
-    ],
+    "logging_filtered_classes": [],
     "enable_file_log": true,
     "system_language": "AmericanEnglish",
     "docked_mode": false,
@@ -32,9 +31,7 @@
         "file_size_column": true,
         "path_column": true
     },
-    "game_dirs": [
-        
-    ],
+    "game_dirs": [],
     "enable_custom_theme": false,
     "custom_theme_path": "",
     "enable_keyboard": false,
diff --git a/Ryujinx/Configuration/DiscordIntegrationModule.cs b/Ryujinx/Configuration/DiscordIntegrationModule.cs
new file mode 100644
index 000000000..15540a1c8
--- /dev/null
+++ b/Ryujinx/Configuration/DiscordIntegrationModule.cs
@@ -0,0 +1,92 @@
+using DiscordRPC;
+using Ryujinx.Common;
+using System;
+using System.IO;
+using System.Linq;
+
+namespace Ryujinx.Configuration
+{
+    static class DiscordIntegrationModule
+    {
+        private static DiscordRpcClient DiscordClient;
+
+        private static string LargeDescription = "Ryujinx is a Nintendo Switch emulator.";
+
+        public static RichPresence DiscordPresence { get; private set; }
+
+        public static void Initialize()
+        {
+            DiscordPresence = new RichPresence
+            {
+                Assets     = new Assets
+                {
+                    LargeImageKey  = "ryujinx",
+                    LargeImageText = LargeDescription
+                },
+                Details    = "Main Menu",
+                State      = "Idling",
+                Timestamps = new Timestamps(DateTime.UtcNow)
+            };
+
+            ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
+        }
+
+        private static void Update(object sender, ReactiveEventArgs<bool> e)
+        {
+            if (e.OldValue != e.NewValue)
+            {
+                // If the integration was active, disable it and unload everything
+                if (e.OldValue)
+                {
+                    DiscordClient?.Dispose();
+
+                    DiscordClient = null;
+                }
+
+                // If we need to activate it and the client isn't active, initialize it
+                if (e.NewValue && DiscordClient == null)
+                {
+                    DiscordClient = new DiscordRpcClient("568815339807309834");
+
+                    DiscordClient.Initialize();
+                    DiscordClient.SetPresence(DiscordPresence);
+                }
+            }
+        }
+
+        public static void SwitchToPlayingState(string titleId, string titleName)
+        {
+            if (File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "RPsupported.dat")).Contains(titleId))
+            {
+                DiscordPresence.Assets.LargeImageKey = titleId;
+            }
+
+            string state = titleId;
+
+            if (state == null)
+            {
+                state = "Ryujinx";
+            }
+            else
+            {
+                state = state.ToUpper();
+            }
+
+            string details = "Idling";
+
+            if (titleName != null)
+            {
+                details = $"Playing {titleName}";
+            }
+
+            DiscordPresence.Details               = details;
+            DiscordPresence.State                 = state;
+            DiscordPresence.Assets.LargeImageText = titleName;
+            DiscordPresence.Assets.SmallImageKey  = "ryujinx";
+            DiscordPresence.Assets.SmallImageText = LargeDescription;
+            DiscordPresence.Timestamps            = new Timestamps(DateTime.UtcNow);
+
+            DiscordClient?.SetPresence(DiscordPresence);
+        }
+    }
+}
diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs
index 96e9e8dec..98b8d692d 100644
--- a/Ryujinx/Program.cs
+++ b/Ryujinx/Program.cs
@@ -1,5 +1,6 @@
 using Gtk;
 using Ryujinx.Common.Logging;
+using Ryujinx.Configuration;
 using Ryujinx.Profiler;
 using Ryujinx.Ui;
 using System;
@@ -16,9 +17,32 @@ namespace Ryujinx
             string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);
             Environment.SetEnvironmentVariable("Path", $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")};{systemPath}");
 
-            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
-            AppDomain.CurrentDomain.ProcessExit        += CurrentDomain_ProcessExit;
-            GLib.ExceptionManager.UnhandledException   += Glib_UnhandledException;
+            GLib.ExceptionManager.UnhandledException += Glib_UnhandledException;
+
+            // Initialize the configuration
+            ConfigurationState.Initialize();
+
+            // Initialize the logger system
+            LoggerModule.Initialize();
+
+            // Initialize Discord integration
+            DiscordIntegrationModule.Initialize();
+
+            string configurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
+
+            // Now load the configuration as the other subsystems are now registered
+            if (File.Exists(configurationPath))
+            {
+                ConfigurationFileFormat configurationFileFormat = ConfigurationFileFormat.Load(configurationPath);
+                ConfigurationState.Instance.Load(configurationFileFormat);
+            }
+            else
+            {
+                // No configuration, we load the default values and save it on disk
+                ConfigurationState.Instance.LoadDefault();
+                ConfigurationState.Instance.ToFileFormat().SaveConfig(configurationPath);
+            }
+
 
             Profile.Initialize();
 
@@ -42,23 +66,6 @@ namespace Ryujinx
             Application.Run();
         }
 
-        private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
-        {
-            Logger.Shutdown();
-        }
-
-        private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
-        {
-            Exception exception = e.ExceptionObject as Exception;
-
-            Logger.PrintError(LogClass.Emulation, $"Unhandled exception caught: {exception}");
-
-            if (e.IsTerminating)
-            {
-                Logger.Shutdown();
-            }
-        }
-
         private static void Glib_UnhandledException(GLib.UnhandledExceptionArgs e)
         {
             Exception exception = e.ExceptionObject as Exception;
diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj
index c54beffe4..b231ddb8d 100644
--- a/Ryujinx/Ryujinx.csproj
+++ b/Ryujinx/Ryujinx.csproj
@@ -71,7 +71,6 @@
     <PackageReference Include="DiscordRichPresence" Version="1.0.121" />
     <PackageReference Include="GtkSharp" Version="3.22.25.24" />
     <PackageReference Include="GtkSharp.Dependencies" Version="1.1.0" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
-    <PackageReference Include="JsonPrettyPrinter" Version="1.0.1.1" />
     <PackageReference Include="OpenTK.NetStandard" Version="1.0.4" />
   </ItemGroup>
 
diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs
index c23a36929..e1994803a 100644
--- a/Ryujinx/Ui/GLScreen.cs
+++ b/Ryujinx/Ui/GLScreen.cs
@@ -1,10 +1,12 @@
 using OpenTK;
 using OpenTK.Graphics;
 using OpenTK.Input;
+using Ryujinx.Configuration;
 using Ryujinx.Graphics.Gal;
 using Ryujinx.HLE;
 using Ryujinx.HLE.Input;
 using Ryujinx.Profiler.UI;
+using Ryujinx.Ui;
 using System;
 using System.Threading;
 
@@ -29,6 +31,8 @@ namespace Ryujinx.Ui
 
         private MouseState? _mouse = null;
 
+        private Input.NpadController _primaryController;
+
         private Thread _renderThread;
 
         private bool _resizeEvent;
@@ -50,6 +54,8 @@ namespace Ryujinx.Ui
             _device   = device;
             _renderer = renderer;
 
+            _primaryController = new Input.NpadController(ConfigurationState.Instance.Hid.JoystickControls);
+
             Location = new Point(
                 (DisplayDevice.Default.Width  / 2) - (Width  / 2),
                 (DisplayDevice.Default.Height / 2) - (Height / 2));
@@ -162,16 +168,16 @@ namespace Ryujinx.Ui
 #endif
 
                 // Normal Input
-                currentHotkeyButtons = Configuration.Instance.KeyboardControls.GetHotkeyButtons(keyboard);
-                currentButton        = Configuration.Instance.KeyboardControls.GetButtons(keyboard);
+                currentHotkeyButtons = KeyboardControls.GetHotkeyButtons(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
+                currentButton        = KeyboardControls.GetButtons(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
 
-                if (Configuration.Instance.EnableKeyboard)
+                if (ConfigurationState.Instance.Hid.EnableKeyboard)
                 {
-                    hidKeyboard = Configuration.Instance.KeyboardControls.GetKeysDown(keyboard);
+                    hidKeyboard = KeyboardControls.GetKeysDown(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
                 }
 
-                (leftJoystickDx, leftJoystickDy)   = Configuration.Instance.KeyboardControls.GetLeftStick(keyboard);
-                (rightJoystickDx, rightJoystickDy) = Configuration.Instance.KeyboardControls.GetRightStick(keyboard);
+                (leftJoystickDx, leftJoystickDy)   = KeyboardControls.GetLeftStick(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
+                (rightJoystickDx, rightJoystickDy) = KeyboardControls.GetRightStick(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
             }
 
             if (!hidKeyboard.HasValue)
@@ -183,17 +189,17 @@ namespace Ryujinx.Ui
                 };
             }
             
-            currentButton |= Configuration.Instance.JoystickControls.GetButtons();
+            currentButton |= _primaryController.GetButtons();
 
             // Keyboard has priority stick-wise
             if (leftJoystickDx == 0 && leftJoystickDy == 0)
             {
-                (leftJoystickDx, leftJoystickDy) = Configuration.Instance.JoystickControls.GetLeftStick();
+                (leftJoystickDx, leftJoystickDy) = _primaryController.GetLeftStick();
             }
 
             if (rightJoystickDx == 0 && rightJoystickDy == 0)
             {
-                (rightJoystickDx, rightJoystickDy) = Configuration.Instance.JoystickControls.GetRightStick();
+                (rightJoystickDx, rightJoystickDy) = _primaryController.GetRightStick();
             }
 
             leftJoystick = new JoystickPosition
@@ -269,7 +275,7 @@ namespace Ryujinx.Ui
                 _device.Hid.SetTouchPoints();
             }
 
-            if (Configuration.Instance.EnableKeyboard && hidKeyboard.HasValue)
+            if (ConfigurationState.Instance.Hid.EnableKeyboard && hidKeyboard.HasValue)
             {
                 _device.Hid.WriteKeyboard(hidKeyboard.Value);
             }
diff --git a/Ryujinx/Ui/NpadKeyboard.cs b/Ryujinx/Ui/KeyboardControls.cs
similarity index 74%
rename from Ryujinx/Ui/NpadKeyboard.cs
rename to Ryujinx/Ui/KeyboardControls.cs
index 95fb22218..db9c0cda8 100644
--- a/Ryujinx/Ui/NpadKeyboard.cs
+++ b/Ryujinx/Ui/KeyboardControls.cs
@@ -1,118 +1,67 @@
-using OpenTK.Input;
+using OpenTK.Input;
 using Ryujinx.HLE.Input;
+using Ryujinx.UI.Input;
 
-namespace Ryujinx.Ui.Input
+namespace Ryujinx.Ui
 {
-    public struct NpadKeyboardLeft
+    public static class KeyboardControls
     {
-        public Key StickUp;
-        public Key StickDown;
-        public Key StickLeft;
-        public Key StickRight;
-        public Key StickButton;
-        public Key DPadUp;
-        public Key DPadDown;
-        public Key DPadLeft;
-        public Key DPadRight;
-        public Key ButtonMinus;
-        public Key ButtonL;
-        public Key ButtonZl;
-    }
-
-    public struct NpadKeyboardRight
-    {
-        public Key StickUp;
-        public Key StickDown;
-        public Key StickLeft;
-        public Key StickRight;
-        public Key StickButton;
-        public Key ButtonA;
-        public Key ButtonB;
-        public Key ButtonX;
-        public Key ButtonY;
-        public Key ButtonPlus;
-        public Key ButtonR;
-        public Key ButtonZr;
-    }
-
-    public struct KeyboardHotkeys
-    {
-        public Key ToggleVsync;
-    }
-
-    public class NpadKeyboard
-    {
-        /// <summary>
-        /// Left JoyCon Keyboard Bindings
-        /// </summary>
-        public NpadKeyboardLeft LeftJoycon { get; set; }
-
-        /// <summary>
-        /// Right JoyCon Keyboard Bindings
-        /// </summary>
-        public NpadKeyboardRight RightJoycon { get; set; }
-
-        /// <summary>
-        /// Hotkey Keyboard Bindings
-        /// </summary>
-        public KeyboardHotkeys Hotkeys { get; private set; }
-
-        public ControllerButtons GetButtons(KeyboardState keyboard)
+        public static ControllerButtons GetButtons(NpadKeyboard npad, KeyboardState keyboard)
         {
             ControllerButtons buttons = 0;
 
-            if (keyboard[(Key)LeftJoycon.StickButton]) buttons |= ControllerButtons.StickLeft;
-            if (keyboard[(Key)LeftJoycon.DPadUp])      buttons |= ControllerButtons.DpadUp;
-            if (keyboard[(Key)LeftJoycon.DPadDown])    buttons |= ControllerButtons.DpadDown;
-            if (keyboard[(Key)LeftJoycon.DPadLeft])    buttons |= ControllerButtons.DpadLeft;
-            if (keyboard[(Key)LeftJoycon.DPadRight])   buttons |= ControllerButtons.DPadRight;
-            if (keyboard[(Key)LeftJoycon.ButtonMinus]) buttons |= ControllerButtons.Minus;
-            if (keyboard[(Key)LeftJoycon.ButtonL])     buttons |= ControllerButtons.L;
-            if (keyboard[(Key)LeftJoycon.ButtonZl])    buttons |= ControllerButtons.Zl;
+            if (keyboard[(Key)npad.LeftJoycon.StickButton]) buttons |= ControllerButtons.StickLeft;
+            if (keyboard[(Key)npad.LeftJoycon.DPadUp])      buttons |= ControllerButtons.DpadUp;
+            if (keyboard[(Key)npad.LeftJoycon.DPadDown])    buttons |= ControllerButtons.DpadDown;
+            if (keyboard[(Key)npad.LeftJoycon.DPadLeft])    buttons |= ControllerButtons.DpadLeft;
+            if (keyboard[(Key)npad.LeftJoycon.DPadRight])   buttons |= ControllerButtons.DPadRight;
+            if (keyboard[(Key)npad.LeftJoycon.ButtonMinus]) buttons |= ControllerButtons.Minus;
+            if (keyboard[(Key)npad.LeftJoycon.ButtonL])     buttons |= ControllerButtons.L;
+            if (keyboard[(Key)npad.LeftJoycon.ButtonZl])    buttons |= ControllerButtons.Zl;
             
-            if (keyboard[(Key)RightJoycon.StickButton]) buttons |= ControllerButtons.StickRight;
-            if (keyboard[(Key)RightJoycon.ButtonA])     buttons |= ControllerButtons.A;
-            if (keyboard[(Key)RightJoycon.ButtonB])     buttons |= ControllerButtons.B;
-            if (keyboard[(Key)RightJoycon.ButtonX])     buttons |= ControllerButtons.X;
-            if (keyboard[(Key)RightJoycon.ButtonY])     buttons |= ControllerButtons.Y;
-            if (keyboard[(Key)RightJoycon.ButtonPlus])  buttons |= ControllerButtons.Plus;
-            if (keyboard[(Key)RightJoycon.ButtonR])     buttons |= ControllerButtons.R;
-            if (keyboard[(Key)RightJoycon.ButtonZr])    buttons |= ControllerButtons.Zr;
+            if (keyboard[(Key)npad.RightJoycon.StickButton]) buttons |= ControllerButtons.StickRight;
+            if (keyboard[(Key)npad.RightJoycon.ButtonA])     buttons |= ControllerButtons.A;
+            if (keyboard[(Key)npad.RightJoycon.ButtonB])     buttons |= ControllerButtons.B;
+            if (keyboard[(Key)npad.RightJoycon.ButtonX])     buttons |= ControllerButtons.X;
+            if (keyboard[(Key)npad.RightJoycon.ButtonY])     buttons |= ControllerButtons.Y;
+            if (keyboard[(Key)npad.RightJoycon.ButtonPlus])  buttons |= ControllerButtons.Plus;
+            if (keyboard[(Key)npad.RightJoycon.ButtonR])     buttons |= ControllerButtons.R;
+            if (keyboard[(Key)npad.RightJoycon.ButtonZr])    buttons |= ControllerButtons.Zr;
 
             return buttons;
         }
 
-        public (short, short) GetLeftStick(KeyboardState keyboard)
+        public static (short, short) GetLeftStick(NpadKeyboard npad, KeyboardState keyboard)
         {
             short dx = 0;
             short dy = 0;
             
-            if (keyboard[(Key)LeftJoycon.StickUp])    dy =  short.MaxValue;
-            if (keyboard[(Key)LeftJoycon.StickDown])  dy = -short.MaxValue;
-            if (keyboard[(Key)LeftJoycon.StickLeft])  dx = -short.MaxValue;
-            if (keyboard[(Key)LeftJoycon.StickRight]) dx =  short.MaxValue;
+            if (keyboard[(Key)npad.LeftJoycon.StickUp])    dy =  short.MaxValue;
+            if (keyboard[(Key)npad.LeftJoycon.StickDown])  dy = -short.MaxValue;
+            if (keyboard[(Key)npad.LeftJoycon.StickLeft])  dx = -short.MaxValue;
+            if (keyboard[(Key)npad.LeftJoycon.StickRight]) dx =  short.MaxValue;
 
             return (dx, dy);
         }
 
-        public (short, short) GetRightStick(KeyboardState keyboard)
+        public static (short, short) GetRightStick(NpadKeyboard npad, KeyboardState keyboard)
         {
             short dx = 0;
             short dy = 0;
 
-            if (keyboard[(Key)RightJoycon.StickUp])    dy =  short.MaxValue;
-            if (keyboard[(Key)RightJoycon.StickDown])  dy = -short.MaxValue;
-            if (keyboard[(Key)RightJoycon.StickLeft])  dx = -short.MaxValue;
-            if (keyboard[(Key)RightJoycon.StickRight]) dx =  short.MaxValue;
+            if (keyboard[(Key)npad.RightJoycon.StickUp])    dy =  short.MaxValue;
+            if (keyboard[(Key)npad.RightJoycon.StickDown])  dy = -short.MaxValue;
+            if (keyboard[(Key)npad.RightJoycon.StickLeft])  dx = -short.MaxValue;
+            if (keyboard[(Key)npad.RightJoycon.StickRight]) dx =  short.MaxValue;
 
             return (dx, dy);
         }
 
-        public HotkeyButtons GetHotkeyButtons(KeyboardState keyboard)
+        public static HotkeyButtons GetHotkeyButtons(NpadKeyboard npad, KeyboardState keyboard)
         {
             HotkeyButtons buttons = 0;
 
-            if (keyboard[(Key)Hotkeys.ToggleVsync]) buttons |= HotkeyButtons.ToggleVSync;
+            if (keyboard[(Key)npad.Hotkeys.ToggleVsync]) buttons |= HotkeyButtons.ToggleVSync;
 
             return buttons;
         }
@@ -267,7 +216,7 @@ namespace Ryujinx.Ui.Input
             new KeyMappingEntry { TargetKey = Key.NumLock,      Target = 10 },
         };
 
-        public HLE.Input.Keyboard GetKeysDown(KeyboardState keyboard)
+        public static HLE.Input.Keyboard GetKeysDown(NpadKeyboard npad, KeyboardState keyboard)
         {
             HLE.Input.Keyboard hidKeyboard = new HLE.Input.Keyboard
             {
diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs
index a24f3ed05..e0bd84947 100644
--- a/Ryujinx/Ui/MainWindow.cs
+++ b/Ryujinx/Ui/MainWindow.cs
@@ -1,22 +1,22 @@
-using DiscordRPC;
 using Gtk;
-using JsonPrettyPrinterPlus;
 using Ryujinx.Audio;
 using Ryujinx.Common.Logging;
-using Ryujinx.Graphics.Gal.OpenGL;
 using Ryujinx.Graphics.Gal;
-using Ryujinx.HLE.FileSystem;
+using Ryujinx.Graphics.Gal.OpenGL;
 using Ryujinx.Profiler;
 using System;
-using System.Diagnostics;
 using System.IO;
-using System.Linq;
 using System.Reflection;
 using System.Text;
-using System.Threading.Tasks;
 using System.Threading;
+using Ryujinx.Configuration;
+using System.Diagnostics;
+using System.Threading.Tasks;
 using Utf8Json;
+using JsonPrettyPrinterPlus;
 using Utf8Json.Resolvers;
+using Ryujinx.HLE.FileSystem;
+
 
 using GUI = Gtk.Builder.ObjectAttribute;
 
@@ -50,12 +50,6 @@ namespace Ryujinx.Ui
 
         private static TreeView _treeView;
 
-        public static bool DiscordIntegrationEnabled { get; set; }
-
-        public static DiscordRpcClient DiscordClient;
-
-        public static RichPresence DiscordPresence;
-
 #pragma warning disable CS0649
 #pragma warning disable IDE0044
         [GUI] Window        _mainWin;
@@ -91,60 +85,39 @@ namespace Ryujinx.Ui
 
             _audioOut = InitializeAudioEngine();
 
-            _device = new HLE.Switch(_renderer, _audioOut);
+            // TODO: Initialization and dispose of HLE.Switch when starting/stoping emulation.
+            _device = InitializeSwitchInstance();
 
             _treeView = _gameTable;
 
-            Configuration.Load(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
-            Configuration.InitialConfigure(_device);
-
             ApplyTheme();
 
-            if (DiscordIntegrationEnabled)
-            {
-                DiscordClient   = new DiscordRpcClient("568815339807309834");
-                DiscordPresence = new RichPresence
-                {
-                    Assets = new Assets
-                    {
-                        LargeImageKey  = "ryujinx",
-                        LargeImageText = "Ryujinx is an emulator for the Nintendo Switch"
-                    },
-                    Details    = "Main Menu",
-                    State      = "Idling",
-                    Timestamps = new Timestamps(DateTime.UtcNow)
-                };
-
-                DiscordClient.Initialize();
-                DiscordClient.SetPresence(DiscordPresence);
-            }
-
             _mainWin.Icon            = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
             _stopEmulation.Sensitive = false;
 
-            if (SwitchSettings.SwitchConfig.GuiColumns.FavColumn)        { _favToggle.Active        = true; }
-            if (SwitchSettings.SwitchConfig.GuiColumns.IconColumn)       { _iconToggle.Active       = true; }
-            if (SwitchSettings.SwitchConfig.GuiColumns.AppColumn)        { _appToggle.Active        = true; }
-            if (SwitchSettings.SwitchConfig.GuiColumns.DevColumn)        { _developerToggle.Active  = true; }
-            if (SwitchSettings.SwitchConfig.GuiColumns.VersionColumn)    { _versionToggle.Active    = true; }
-            if (SwitchSettings.SwitchConfig.GuiColumns.TimePlayedColumn) { _timePlayedToggle.Active = true; }
-            if (SwitchSettings.SwitchConfig.GuiColumns.LastPlayedColumn) { _lastPlayedToggle.Active = true; }
-            if (SwitchSettings.SwitchConfig.GuiColumns.FileExtColumn)    { _fileExtToggle.Active    = true; }
-            if (SwitchSettings.SwitchConfig.GuiColumns.FileSizeColumn)   { _fileSizeToggle.Active   = true; }
-            if (SwitchSettings.SwitchConfig.GuiColumns.PathColumn)       { _pathToggle.Active       = true; }
+            if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn)        _favToggle.Active        = true;
+            if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn)       _iconToggle.Active       = true;
+            if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn)        _appToggle.Active        = true;
+            if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn)        _developerToggle.Active  = true;
+            if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn)    _versionToggle.Active    = true;
+            if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) _timePlayedToggle.Active = true;
+            if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) _lastPlayedToggle.Active = true;
+            if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn)    _fileExtToggle.Active    = true;
+            if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn)   _fileSizeToggle.Active   = true;
+            if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn)       _pathToggle.Active       = true;
 
             _gameTable.Model = _tableStore = new ListStore(
-                typeof(bool), 
-                typeof(Gdk.Pixbuf), 
-                typeof(string), 
-                typeof(string), 
-                typeof(string), 
-                typeof(string), 
-                typeof(string), 
-                typeof(string), 
-                typeof(string), 
+                typeof(bool),
+                typeof(Gdk.Pixbuf),
+                typeof(string),
+                typeof(string),
+                typeof(string),
+                typeof(string),
+                typeof(string),
+                typeof(string),
+                typeof(string),
                 typeof(string));
-            
+
             _tableStore.SetSortFunc(5, TimePlayedSort);
             _tableStore.SetSortFunc(6, LastPlayedSort);
             _tableStore.SetSortFunc(8, FileSizeSort);
@@ -158,22 +131,22 @@ namespace Ryujinx.Ui
 
         internal static void ApplyTheme()
         {
-            if (!SwitchSettings.SwitchConfig.EnableCustomTheme)
+            if (!ConfigurationState.Instance.Ui.EnableCustomTheme)
             {
                 return;
             }
 
-            if (File.Exists(SwitchSettings.SwitchConfig.CustomThemePath) && (System.IO.Path.GetExtension(SwitchSettings.SwitchConfig.CustomThemePath) == ".css"))
+            if (File.Exists(ConfigurationState.Instance.Ui.CustomThemePath) && (System.IO.Path.GetExtension(ConfigurationState.Instance.Ui.CustomThemePath) == ".css"))
             {
                 CssProvider cssProvider = new CssProvider();
 
-                cssProvider.LoadFromPath(SwitchSettings.SwitchConfig.CustomThemePath);
+                cssProvider.LoadFromPath(ConfigurationState.Instance.Ui.CustomThemePath);
 
                 StyleContext.AddProviderForScreen(Gdk.Screen.Default, cssProvider, 800);
             }
             else
             {
-                Logger.PrintWarning(LogClass.Application, $"The \"custom_theme_path\" section in \"Config.json\" contains an invalid path: \"{SwitchSettings.SwitchConfig.CustomThemePath}\".");
+                Logger.PrintWarning(LogClass.Application, $"The \"custom_theme_path\" section in \"Config.json\" contains an invalid path: \"{ConfigurationState.Instance.Ui.CustomThemePath}\".");
             }
         }
 
@@ -187,39 +160,48 @@ namespace Ryujinx.Ui
             CellRendererToggle favToggle = new CellRendererToggle();
             favToggle.Toggled += FavToggle_Toggled;
 
-            if (SwitchSettings.SwitchConfig.GuiColumns.FavColumn)        { _gameTable.AppendColumn("Fav",         favToggle,                "active", 0); }
-            if (SwitchSettings.SwitchConfig.GuiColumns.IconColumn)       { _gameTable.AppendColumn("Icon",        new CellRendererPixbuf(), "pixbuf", 1); }
-            if (SwitchSettings.SwitchConfig.GuiColumns.AppColumn)        { _gameTable.AppendColumn("Application", new CellRendererText(),   "text",   2); }
-            if (SwitchSettings.SwitchConfig.GuiColumns.DevColumn)        { _gameTable.AppendColumn("Developer",   new CellRendererText(),   "text",   3); }
-            if (SwitchSettings.SwitchConfig.GuiColumns.VersionColumn)    { _gameTable.AppendColumn("Version",     new CellRendererText(),   "text",   4); }
-            if (SwitchSettings.SwitchConfig.GuiColumns.TimePlayedColumn) { _gameTable.AppendColumn("Time Played", new CellRendererText(),   "text",   5); }
-            if (SwitchSettings.SwitchConfig.GuiColumns.LastPlayedColumn) { _gameTable.AppendColumn("Last Played", new CellRendererText(),   "text",   6); }
-            if (SwitchSettings.SwitchConfig.GuiColumns.FileExtColumn)    { _gameTable.AppendColumn("File Ext",    new CellRendererText(),   "text",   7); }
-            if (SwitchSettings.SwitchConfig.GuiColumns.FileSizeColumn)   { _gameTable.AppendColumn("File Size",   new CellRendererText(),   "text",   8); }
-            if (SwitchSettings.SwitchConfig.GuiColumns.PathColumn)       { _gameTable.AppendColumn("Path",        new CellRendererText(),   "text",   9); }
+            if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn)        _gameTable.AppendColumn("Fav",         favToggle,                "active", 0);
+            if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn)       _gameTable.AppendColumn("Icon",        new CellRendererPixbuf(), "pixbuf", 1);
+            if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn)        _gameTable.AppendColumn("Application", new CellRendererText(),   "text",   2);
+            if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn)        _gameTable.AppendColumn("Developer",   new CellRendererText(),   "text",   3);
+            if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn)    _gameTable.AppendColumn("Version",     new CellRendererText(),   "text",   4);
+            if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) _gameTable.AppendColumn("Time Played", new CellRendererText(),   "text",   5);
+            if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) _gameTable.AppendColumn("Last Played", new CellRendererText(),   "text",   6);
+            if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn)    _gameTable.AppendColumn("File Ext",    new CellRendererText(),   "text",   7);
+            if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn)   _gameTable.AppendColumn("File Size",   new CellRendererText(),   "text",   8);
+            if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn)       _gameTable.AppendColumn("Path",        new CellRendererText(),   "text",   9);
 
             foreach (TreeViewColumn column in _gameTable.Columns)
             {
-                if (column.Title == "Fav")              { _favColumn        = column; }
-                else if (column.Title == "Application") { _appColumn        = column; }
-                else if (column.Title == "Developer")   { _devColumn        = column; }
-                else if (column.Title == "Version")     { _versionColumn    = column; }
-                else if (column.Title == "Time Played") { _timePlayedColumn = column; }
-                else if (column.Title == "Last Played") { _lastPlayedColumn = column; }
-                else if (column.Title == "File Ext")    { _fileExtColumn    = column; }
-                else if (column.Title == "File Size")   { _fileSizeColumn   = column; }
-                else if (column.Title == "Path")        { _pathColumn       = column; }
+                if (column.Title == "Fav")              _favColumn        = column;
+                else if (column.Title == "Application") _appColumn        = column;
+                else if (column.Title == "Developer")   _devColumn        = column;
+                else if (column.Title == "Version")     _versionColumn    = column;
+                else if (column.Title == "Time Played") _timePlayedColumn = column;
+                else if (column.Title == "Last Played") _lastPlayedColumn = column;
+                else if (column.Title == "File Ext")    _fileExtColumn    = column;
+                else if (column.Title == "File Size")   _fileSizeColumn   = column;
+                else if (column.Title == "Path")        _pathColumn       = column;
             }
 
-            if (SwitchSettings.SwitchConfig.GuiColumns.FavColumn)        { _favColumn.SortColumnId        = 0; }
-            if (SwitchSettings.SwitchConfig.GuiColumns.IconColumn)       { _appColumn.SortColumnId        = 2; }
-            if (SwitchSettings.SwitchConfig.GuiColumns.AppColumn)        { _devColumn.SortColumnId        = 3; }
-            if (SwitchSettings.SwitchConfig.GuiColumns.DevColumn)        { _versionColumn.SortColumnId    = 4; }
-            if (SwitchSettings.SwitchConfig.GuiColumns.TimePlayedColumn) { _timePlayedColumn.SortColumnId = 5; }
-            if (SwitchSettings.SwitchConfig.GuiColumns.LastPlayedColumn) { _lastPlayedColumn.SortColumnId = 6; }
-            if (SwitchSettings.SwitchConfig.GuiColumns.FileExtColumn)    { _fileExtColumn.SortColumnId    = 7; }
-            if (SwitchSettings.SwitchConfig.GuiColumns.FileSizeColumn)   { _fileSizeColumn.SortColumnId   = 8; }
-            if (SwitchSettings.SwitchConfig.GuiColumns.PathColumn)       { _pathColumn.SortColumnId       = 9; }
+            if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn)        _favColumn.SortColumnId        = 0;
+            if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn)        _appColumn.SortColumnId        = 2;
+            if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn)        _devColumn.SortColumnId        = 3;
+            if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn)    _versionColumn.SortColumnId    = 4;
+            if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) _timePlayedColumn.SortColumnId = 5;
+            if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) _lastPlayedColumn.SortColumnId = 6;
+            if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn)    _fileExtColumn.SortColumnId    = 7;
+            if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn)   _fileSizeColumn.SortColumnId   = 8;
+            if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn)       _pathColumn.SortColumnId       = 9;
+        }
+
+        private HLE.Switch InitializeSwitchInstance()
+        {
+            HLE.Switch instance = new HLE.Switch(_renderer, _audioOut);
+
+            instance.Initialize();
+
+            return instance;
         }
 
         internal static async Task UpdateGameTable()
@@ -233,7 +215,7 @@ namespace Ryujinx.Ui
 
             _tableStore.Clear();
 
-            await Task.Run(() => ApplicationLibrary.LoadApplications(SwitchSettings.SwitchConfig.GameDirs, _device.System.KeySet, _device.System.State.DesiredTitleLanguage));
+            await Task.Run(() => ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs, _device.System.KeySet, _device.System.State.DesiredTitleLanguage));
 
             _updatingGameTable = false;
         }
@@ -248,6 +230,9 @@ namespace Ryujinx.Ui
             {
                 Logger.RestartTime();
 
+                // TODO: Move this somewhere else + reloadable?
+                GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
+
                 if (Directory.Exists(path))
                 {
                     string[] romFsFiles = Directory.GetFiles(path, "*.istorage");
@@ -313,40 +298,7 @@ namespace Ryujinx.Ui
                 _gameLoaded              = true;
                 _stopEmulation.Sensitive = true;
 
-                if (DiscordIntegrationEnabled)
-                {
-                    if (File.ReadAllLines(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "RPsupported.dat")).Contains(_device.System.TitleId))
-                    {
-                        DiscordPresence.Assets.LargeImageKey = _device.System.TitleId;
-                    }
-
-                    string state = _device.System.TitleId;
-
-                    if (state == null)
-                    {
-                        state = "Ryujinx";
-                    }
-                    else
-                    {
-                        state = state.ToUpper();
-                    }
-
-                    string details = "Idling";
-
-                    if (_device.System.TitleName != null)
-                    {
-                        details = $"Playing {_device.System.TitleName}";
-                    }
-
-                    DiscordPresence.Details               = details;
-                    DiscordPresence.State                 = state;
-                    DiscordPresence.Assets.LargeImageText = _device.System.TitleName;
-                    DiscordPresence.Assets.SmallImageKey  = "ryujinx";
-                    DiscordPresence.Assets.SmallImageText = "Ryujinx is an emulator for the Nintendo Switch";
-                    DiscordPresence.Timestamps            = new Timestamps(DateTime.UtcNow);
-
-                    DiscordClient.SetPresence(DiscordPresence);
-                }
+                DiscordIntegrationModule.SwitchToPlayingState(_device.System.TitleId, _device.System.TitleName);
 
                 string metadataFolder = System.IO.Path.Combine(new VirtualFileSystem().GetBasePath(), "games", _device.System.TitleId, "gui");
                 string metadataFile   = System.IO.Path.Combine(metadataFolder, "metadata.json");
@@ -384,8 +336,8 @@ namespace Ryujinx.Ui
 
         private static void CreateGameWindow()
         {
-            Configuration.ConfigureHid(_device, SwitchSettings.SwitchConfig);
-            
+            _device.Hid.InitializePrimaryController(ConfigurationState.Instance.Hid.ControllerType);
+
             using (_screen = new GlScreen(_device, _renderer))
             {
                 _screen.MainLoop();
@@ -444,7 +396,6 @@ namespace Ryujinx.Ui
             Profile.FinishProfiling();
             _device.Dispose();
             _audioOut.Dispose();
-            DiscordClient?.Dispose();
             Logger.Shutdown();
             Environment.Exit(0);
         }
@@ -607,7 +558,7 @@ namespace Ryujinx.Ui
 
         private void Settings_Pressed(object sender, EventArgs args)
         {
-            SwitchSettings settingsWin = new SwitchSettings(_device);
+            SwitchSettings settingsWin = new SwitchSettings();
             settingsWin.Show();
         }
 
@@ -633,121 +584,81 @@ namespace Ryujinx.Ui
 
         private void Fav_Toggled(object sender, EventArgs args)
         {
-            GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
-            updatedColumns.FavColumn               = _favToggle.Active;
-            SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
-            Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+            ConfigurationState.Instance.Ui.GuiColumns.FavColumn.Value = _favToggle.Active;
 
+            SaveConfig();
             UpdateColumns();
         }
 
         private void Icon_Toggled(object sender, EventArgs args)
         {
-            GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
-            updatedColumns.IconColumn              = _iconToggle.Active;
-            SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
-            Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+            ConfigurationState.Instance.Ui.GuiColumns.IconColumn.Value = _iconToggle.Active;
 
+            SaveConfig();
             UpdateColumns();
         }
 
         private void Title_Toggled(object sender, EventArgs args)
         {
-            GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
-            updatedColumns.AppColumn               = _appToggle.Active;
-            SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
-            Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+            ConfigurationState.Instance.Ui.GuiColumns.AppColumn.Value = _appToggle.Active;
 
+            SaveConfig();
             UpdateColumns();
         }
 
         private void Developer_Toggled(object sender, EventArgs args)
         {
-            GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
-            updatedColumns.DevColumn               = _developerToggle.Active;
-            SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
-            Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+            ConfigurationState.Instance.Ui.GuiColumns.DevColumn.Value = _developerToggle.Active;
 
+            SaveConfig();
             UpdateColumns();
         }
 
         private void Version_Toggled(object sender, EventArgs args)
         {
-            GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
-            updatedColumns.VersionColumn           = _versionToggle.Active;
-            SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
-            Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+            ConfigurationState.Instance.Ui.GuiColumns.VersionColumn.Value = _versionToggle.Active;
 
+            SaveConfig();
             UpdateColumns();
         }
 
         private void TimePlayed_Toggled(object sender, EventArgs args)
         {
-            GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
-            updatedColumns.TimePlayedColumn        = _timePlayedToggle.Active;
-            SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
-            Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+            ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn.Value = _timePlayedToggle.Active;
 
+            SaveConfig();
             UpdateColumns();
         }
 
         private void LastPlayed_Toggled(object sender, EventArgs args)
         {
-            GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
-            updatedColumns.LastPlayedColumn        = _lastPlayedToggle.Active;
-            SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
-            Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+            ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn.Value = _lastPlayedToggle.Active;
 
+            SaveConfig();
             UpdateColumns();
         }
 
         private void FileExt_Toggled(object sender, EventArgs args)
         {
-            GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
-            updatedColumns.FileExtColumn           = _fileExtToggle.Active;
-            SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
-            Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+            ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn.Value = _fileExtToggle.Active;
 
+            SaveConfig();
             UpdateColumns();
         }
 
         private void FileSize_Toggled(object sender, EventArgs args)
         {
-            GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
-            updatedColumns.FileSizeColumn          = _fileSizeToggle.Active;
-            SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
-            Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+            ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn.Value = _fileSizeToggle.Active;
 
+            SaveConfig();
             UpdateColumns();
         }
 
         private void Path_Toggled(object sender, EventArgs args)
         {
-            GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
-            updatedColumns.PathColumn              = _pathToggle.Active;
-            SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
-            Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+            ConfigurationState.Instance.Ui.GuiColumns.PathColumn.Value = _pathToggle.Active;
 
+            SaveConfig();
             UpdateColumns();
         }
 
@@ -865,5 +776,10 @@ namespace Ryujinx.Ui
                 return 0;
             }
         }
+
+        public static void SaveConfig()
+        {
+            ConfigurationState.Instance.ToFileFormat().SaveConfig(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+        }
     }
 }
diff --git a/Ryujinx/Ui/NpadController.cs b/Ryujinx/Ui/NpadController.cs
index f72c40755..67961b491 100644
--- a/Ryujinx/Ui/NpadController.cs
+++ b/Ryujinx/Ui/NpadController.cs
@@ -1,160 +1,57 @@
 using OpenTK;
 using OpenTK.Input;
+using Ryujinx.Common.Configuration.Hid;
 using Ryujinx.HLE.Input;
 using System;
 
+using InnerNpadController = Ryujinx.Common.Configuration.Hid.NpadController;
+
 namespace Ryujinx.Ui.Input
 {
-    public enum ControllerInputId
-    {
-        Button0,
-        Button1,
-        Button2,
-        Button3,
-        Button4,
-        Button5,
-        Button6,
-        Button7,
-        Button8,
-        Button9,
-        Button10,
-        Button11,
-        Button12,
-        Button13,
-        Button14,
-        Button15,
-        Button16,
-        Button17,
-        Button18,
-        Button19,
-        Button20,
-        Axis0,
-        Axis1,
-        Axis2,
-        Axis3,
-        Axis4,
-        Axis5,
-        Hat0Up,
-        Hat0Down,
-        Hat0Left,
-        Hat0Right,
-        Hat1Up,
-        Hat1Down,
-        Hat1Left,
-        Hat1Right,
-        Hat2Up,
-        Hat2Down,
-        Hat2Left,
-        Hat2Right,
-    }
-
-    public struct NpadControllerLeft
-    {
-        public ControllerInputId Stick;
-        public ControllerInputId StickButton;
-        public ControllerInputId ButtonMinus;
-        public ControllerInputId ButtonL;
-        public ControllerInputId ButtonZl;
-        public ControllerInputId DPadUp;
-        public ControllerInputId DPadDown;
-        public ControllerInputId DPadLeft;
-        public ControllerInputId DPadRight;
-    }
-
-    public struct NpadControllerRight
-    {
-        public ControllerInputId Stick;
-        public ControllerInputId StickButton;
-        public ControllerInputId ButtonA;
-        public ControllerInputId ButtonB;
-        public ControllerInputId ButtonX;
-        public ControllerInputId ButtonY;
-        public ControllerInputId ButtonPlus;
-        public ControllerInputId ButtonR;
-        public ControllerInputId ButtonZr;
-    }
-
     public class NpadController
     {
-        /// <summary>
-        /// Enables or disables controller support
-        /// </summary>
-        public bool Enabled { get; private set; }
+        private InnerNpadController _inner;
 
-        /// <summary>
-        /// Controller Device Index
-        /// </summary>
-        public int Index { get; private set; }
-
-        /// <summary>
-        /// Controller Analog Stick Deadzone
-        /// </summary>
-        public float Deadzone { get; private set; }
-
-        /// <summary>
-        /// Controller Trigger Threshold
-        /// </summary>
-        public float TriggerThreshold { get; private set; }
-
-        /// <summary>
-        /// Left JoyCon Controller Bindings
-        /// </summary>
-        public NpadControllerLeft LeftJoycon { get; private set; }
-
-        /// <summary>
-        /// Right JoyCon Controller Bindings
-        /// </summary>
-        public NpadControllerRight RightJoycon { get; private set; }
-
-        public NpadController(
-            bool                enabled,
-            int                 index,
-            float               deadzone,
-            float               triggerThreshold,
-            NpadControllerLeft  leftJoycon,
-            NpadControllerRight rightJoycon)
+        // NOTE: This should be initialized AFTER GTK for compat reasons with OpenTK SDL2 backend and GTK on Linux.
+        // BODY: Usage of Joystick.GetState must be defer to after GTK full initialization. Otherwise, GTK will segfault because SDL2 was already init *sighs*
+        public NpadController(InnerNpadController inner)
         {
-            Enabled          = enabled;
-            Index            = index;
-            Deadzone         = deadzone;
-            TriggerThreshold = triggerThreshold;
-            LeftJoycon       = leftJoycon;
-            RightJoycon      = rightJoycon;
+            _inner = inner;
         }
 
-        public void SetEnabled(bool enabled)
+        private bool IsEnabled()
         {
-            Enabled = enabled;
+            return _inner.Enabled && Joystick.GetState(_inner.Index).IsConnected;
         }
 
         public ControllerButtons GetButtons()
         {
-            if (!Enabled)
+            if (!IsEnabled())
             {
                 return 0;
             }
 
-            JoystickState joystickState = Joystick.GetState(Index);
+            JoystickState joystickState = Joystick.GetState(_inner.Index);
 
             ControllerButtons buttons = 0;
 
-            if (IsActivated(joystickState, LeftJoycon.DPadUp))       buttons |= ControllerButtons.DpadUp;
-            if (IsActivated(joystickState, LeftJoycon.DPadDown))     buttons |= ControllerButtons.DpadDown;
-            if (IsActivated(joystickState, LeftJoycon.DPadLeft))     buttons |= ControllerButtons.DpadLeft;
-            if (IsActivated(joystickState, LeftJoycon.DPadRight))    buttons |= ControllerButtons.DPadRight;
-            if (IsActivated(joystickState, LeftJoycon.StickButton))  buttons |= ControllerButtons.StickLeft;
-            if (IsActivated(joystickState, LeftJoycon.ButtonMinus))  buttons |= ControllerButtons.Minus;
-            if (IsActivated(joystickState, LeftJoycon.ButtonL))      buttons |= ControllerButtons.L;
-            if (IsActivated(joystickState, LeftJoycon.ButtonZl))     buttons |= ControllerButtons.Zl;
+            if (IsActivated(joystickState, _inner.LeftJoycon.DPadUp))       buttons |= ControllerButtons.DpadUp;
+            if (IsActivated(joystickState, _inner.LeftJoycon.DPadDown))     buttons |= ControllerButtons.DpadDown;
+            if (IsActivated(joystickState, _inner.LeftJoycon.DPadLeft))     buttons |= ControllerButtons.DpadLeft;
+            if (IsActivated(joystickState, _inner.LeftJoycon.DPadRight))    buttons |= ControllerButtons.DPadRight;
+            if (IsActivated(joystickState, _inner.LeftJoycon.StickButton))  buttons |= ControllerButtons.StickLeft;
+            if (IsActivated(joystickState, _inner.LeftJoycon.ButtonMinus))  buttons |= ControllerButtons.Minus;
+            if (IsActivated(joystickState, _inner.LeftJoycon.ButtonL))      buttons |= ControllerButtons.L;
+            if (IsActivated(joystickState, _inner.LeftJoycon.ButtonZl))     buttons |= ControllerButtons.Zl;
 
-            if (IsActivated(joystickState, RightJoycon.ButtonA))     buttons |= ControllerButtons.A;
-            if (IsActivated(joystickState, RightJoycon.ButtonB))     buttons |= ControllerButtons.B;
-            if (IsActivated(joystickState, RightJoycon.ButtonX))     buttons |= ControllerButtons.X;
-            if (IsActivated(joystickState, RightJoycon.ButtonY))     buttons |= ControllerButtons.Y;
-            if (IsActivated(joystickState, RightJoycon.StickButton)) buttons |= ControllerButtons.StickRight;
-            if (IsActivated(joystickState, RightJoycon.ButtonPlus))  buttons |= ControllerButtons.Plus;
-            if (IsActivated(joystickState, RightJoycon.ButtonR))     buttons |= ControllerButtons.R;
-            if (IsActivated(joystickState, RightJoycon.ButtonZr))    buttons |= ControllerButtons.Zr;
+            if (IsActivated(joystickState, _inner.RightJoycon.ButtonA))     buttons |= ControllerButtons.A;
+            if (IsActivated(joystickState, _inner.RightJoycon.ButtonB))     buttons |= ControllerButtons.B;
+            if (IsActivated(joystickState, _inner.RightJoycon.ButtonX))     buttons |= ControllerButtons.X;
+            if (IsActivated(joystickState, _inner.RightJoycon.ButtonY))     buttons |= ControllerButtons.Y;
+            if (IsActivated(joystickState, _inner.RightJoycon.StickButton)) buttons |= ControllerButtons.StickRight;
+            if (IsActivated(joystickState, _inner.RightJoycon.ButtonPlus))  buttons |= ControllerButtons.Plus;
+            if (IsActivated(joystickState, _inner.RightJoycon.ButtonR))     buttons |= ControllerButtons.R;
+            if (IsActivated(joystickState, _inner.RightJoycon.ButtonZr))    buttons |= ControllerButtons.Zr;
 
             return buttons;
         }
@@ -169,7 +66,7 @@ namespace Ryujinx.Ui.Input
             {
                 int axis = controllerInputId - ControllerInputId.Axis0;
 
-                return joystickState.GetAxis(axis) > TriggerThreshold;
+                return joystickState.GetAxis(axis) > _inner.TriggerThreshold;
             }
             else if (controllerInputId <= ControllerInputId.Hat2Right)
             {
@@ -190,22 +87,22 @@ namespace Ryujinx.Ui.Input
 
         public (short, short) GetLeftStick()
         {
-            if (!Enabled)
+            if (!IsEnabled())
             {
                 return (0, 0);
             }
 
-            return GetStick(LeftJoycon.Stick);
+            return GetStick(_inner.LeftJoycon.Stick);
         }
 
         public (short, short) GetRightStick()
         {
-            if (!Enabled)
+            if (!IsEnabled())
             {
                 return (0, 0);
             }
 
-            return GetStick(RightJoycon.Stick);
+            return GetStick(_inner.RightJoycon.Stick);
         }
 
         private (short, short) GetStick(ControllerInputId stickInputId)
@@ -215,7 +112,7 @@ namespace Ryujinx.Ui.Input
                 return (0, 0);
             }
 
-            JoystickState jsState = Joystick.GetState(Index);
+            JoystickState jsState = Joystick.GetState(_inner.Index);
 
             int xAxis = stickInputId - ControllerInputId.Axis0;
 
@@ -227,8 +124,8 @@ namespace Ryujinx.Ui.Input
 
         private (short, short) ApplyDeadzone(Vector2 axis)
         {
-            return (ClampAxis(MathF.Abs(axis.X) > Deadzone ? axis.X : 0f),
-                    ClampAxis(MathF.Abs(axis.Y) > Deadzone ? axis.Y : 0f));
+            return (ClampAxis(MathF.Abs(axis.X) > _inner.Deadzone ? axis.X : 0f),
+                    ClampAxis(MathF.Abs(axis.Y) > _inner.Deadzone ? axis.Y : 0f));
         }
 
         private static short ClampAxis(float value)
diff --git a/Ryujinx/Ui/SwitchSettings.cs b/Ryujinx/Ui/SwitchSettings.cs
index 955c6b0b6..5c56cf7ea 100644
--- a/Ryujinx/Ui/SwitchSettings.cs
+++ b/Ryujinx/Ui/SwitchSettings.cs
@@ -1,12 +1,12 @@
 using Gtk;
-using Ryujinx.HLE.HOS.SystemState;
-using Ryujinx.HLE.Input;
-using Ryujinx.Ui.Input;
 using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Reflection;
+using Ryujinx.Configuration;
+using Ryujinx.Configuration.System;
+using Ryujinx.Configuration.Hid;
 
 using GUI = Gtk.Builder.ObjectAttribute;
 
@@ -14,10 +14,6 @@ namespace Ryujinx.Ui
 {
     public class SwitchSettings : Window
     {
-        internal static Configuration SwitchConfig { get; set; }
-
-        private readonly HLE.Switch _device;
-
         private static ListStore _gameDirsBoxStore;
 
         private static bool _listeningForKeypress;
@@ -83,16 +79,12 @@ namespace Ryujinx.Ui
 #pragma warning restore CS0649
 #pragma warning restore IDE0044
 
-        public static void ConfigureSettings(Configuration instance) { SwitchConfig = instance; }
+        public SwitchSettings() : this(new Builder("Ryujinx.Ui.SwitchSettings.glade")) { }
 
-        public SwitchSettings(HLE.Switch device) : this(new Builder("Ryujinx.Ui.SwitchSettings.glade"), device) { }
-
-        private SwitchSettings(Builder builder, HLE.Switch device) : base(builder.GetObject("_settingsWin").Handle)
+        private SwitchSettings(Builder builder) : base(builder.GetObject("_settingsWin").Handle)
         {
             builder.Autoconnect(this);
 
-            _device = device;
-
             _settingsWin.Icon        = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
             _controller1Image.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.JoyCon.png", 500, 500);
 
@@ -124,60 +116,123 @@ namespace Ryujinx.Ui
             _controller1Type.Changed += (sender, args) => Controller_Changed(sender, args, _controller1Type.ActiveId, _controller1Image);
 
             //Setup Currents
-            if (SwitchConfig.EnableFileLog)             _fileLogToggle.Click();
-            if (SwitchConfig.LoggingEnableError)        _errorLogToggle.Click();
-            if (SwitchConfig.LoggingEnableWarn)         _warningLogToggle.Click();
-            if (SwitchConfig.LoggingEnableInfo)         _infoLogToggle.Click();
-            if (SwitchConfig.LoggingEnableStub)         _stubLogToggle.Click();
-            if (SwitchConfig.LoggingEnableDebug)        _debugLogToggle.Click();
-            if (SwitchConfig.LoggingEnableGuest)        _guestLogToggle.Click();
-            if (SwitchConfig.LoggingEnableFsAccessLog)  _fsAccessLogToggle.Click();
-            if (SwitchConfig.DockedMode)                _dockedModeToggle.Click();
-            if (SwitchConfig.EnableDiscordIntegration)  _discordToggle.Click();
-            if (SwitchConfig.EnableVsync)               _vSyncToggle.Click();
-            if (SwitchConfig.EnableMulticoreScheduling) _multiSchedToggle.Click();
-            if (SwitchConfig.EnableFsIntegrityChecks)   _fsicToggle.Click();
-            if (SwitchConfig.IgnoreMissingServices)     _ignoreToggle.Click();
-            if (SwitchConfig.EnableKeyboard)            _directKeyboardAccess.Click();
-            if (SwitchConfig.EnableCustomTheme)         _custThemeToggle.Click();
+            if (ConfigurationState.Instance.Logger.EnableFileLog)
+            {
+                _fileLogToggle.Click();
+            }
 
-            _systemLanguageSelect.SetActiveId(SwitchConfig.SystemLanguage.ToString());
-            _controller1Type     .SetActiveId(SwitchConfig.ControllerType.ToString());
+            if (ConfigurationState.Instance.Logger.EnableError)
+            {
+                _errorLogToggle.Click();
+            }
+
+            if (ConfigurationState.Instance.Logger.EnableWarn)
+            {
+                _warningLogToggle.Click();
+            }
+
+            if (ConfigurationState.Instance.Logger.EnableInfo)
+            {
+                _infoLogToggle.Click();
+            }
+
+            if (ConfigurationState.Instance.Logger.EnableStub)
+            {
+                _stubLogToggle.Click();
+            }
+
+            if (ConfigurationState.Instance.Logger.EnableDebug)
+            {
+                _debugLogToggle.Click();
+            }
+
+            if (ConfigurationState.Instance.Logger.EnableGuest)
+            {
+                _guestLogToggle.Click();
+            }
+
+            if (ConfigurationState.Instance.Logger.EnableFsAccessLog)
+            {
+                _fsAccessLogToggle.Click();
+            }
+
+            if (ConfigurationState.Instance.System.EnableDockedMode)
+            {
+                _dockedModeToggle.Click();
+            }
+
+            if (ConfigurationState.Instance.EnableDiscordIntegration)
+            {
+                _discordToggle.Click();
+            }
+
+            if (ConfigurationState.Instance.Graphics.EnableVsync)
+            {
+                _vSyncToggle.Click();
+            }
+
+            if (ConfigurationState.Instance.System.EnableMulticoreScheduling)
+            {
+                _multiSchedToggle.Click();
+            }
+
+            if (ConfigurationState.Instance.System.EnableFsIntegrityChecks)
+            {
+                _fsicToggle.Click();
+            }
+
+            if (ConfigurationState.Instance.System.IgnoreMissingServices)
+            {
+                _ignoreToggle.Click();
+            }
+
+            if (ConfigurationState.Instance.Hid.EnableKeyboard)
+            {
+                _directKeyboardAccess.Click();
+            }
+
+            if (ConfigurationState.Instance.Ui.EnableCustomTheme)
+            {
+                _custThemeToggle.Click();
+            }
+
+            _systemLanguageSelect.SetActiveId(ConfigurationState.Instance.System.Language.Value.ToString());
+            _controller1Type     .SetActiveId(ConfigurationState.Instance.Hid.ControllerType.Value.ToString());
             Controller_Changed(null, null, _controller1Type.ActiveId, _controller1Image);
 
-            _lStickUp1.Label     = SwitchConfig.KeyboardControls.LeftJoycon.StickUp.ToString();
-            _lStickDown1.Label   = SwitchConfig.KeyboardControls.LeftJoycon.StickDown.ToString();
-            _lStickLeft1.Label   = SwitchConfig.KeyboardControls.LeftJoycon.StickLeft.ToString();
-            _lStickRight1.Label  = SwitchConfig.KeyboardControls.LeftJoycon.StickRight.ToString();
-            _lStickButton1.Label = SwitchConfig.KeyboardControls.LeftJoycon.StickButton.ToString();
-            _dpadUp1.Label       = SwitchConfig.KeyboardControls.LeftJoycon.DPadUp.ToString();
-            _dpadDown1.Label     = SwitchConfig.KeyboardControls.LeftJoycon.DPadDown.ToString();
-            _dpadLeft1.Label     = SwitchConfig.KeyboardControls.LeftJoycon.DPadLeft.ToString();
-            _dpadRight1.Label    = SwitchConfig.KeyboardControls.LeftJoycon.DPadRight.ToString();
-            _minus1.Label        = SwitchConfig.KeyboardControls.LeftJoycon.ButtonMinus.ToString();
-            _l1.Label            = SwitchConfig.KeyboardControls.LeftJoycon.ButtonL.ToString();
-            _zL1.Label           = SwitchConfig.KeyboardControls.LeftJoycon.ButtonZl.ToString();
-            _rStickUp1.Label     = SwitchConfig.KeyboardControls.RightJoycon.StickUp.ToString();
-            _rStickDown1.Label   = SwitchConfig.KeyboardControls.RightJoycon.StickDown.ToString();
-            _rStickLeft1.Label   = SwitchConfig.KeyboardControls.RightJoycon.StickLeft.ToString();
-            _rStickRight1.Label  = SwitchConfig.KeyboardControls.RightJoycon.StickRight.ToString();
-            _rStickButton1.Label = SwitchConfig.KeyboardControls.RightJoycon.StickButton.ToString();
-            _a1.Label            = SwitchConfig.KeyboardControls.RightJoycon.ButtonA.ToString();
-            _b1.Label            = SwitchConfig.KeyboardControls.RightJoycon.ButtonB.ToString();
-            _x1.Label            = SwitchConfig.KeyboardControls.RightJoycon.ButtonX.ToString();
-            _y1.Label            = SwitchConfig.KeyboardControls.RightJoycon.ButtonY.ToString();
-            _plus1.Label         = SwitchConfig.KeyboardControls.RightJoycon.ButtonPlus.ToString();
-            _r1.Label            = SwitchConfig.KeyboardControls.RightJoycon.ButtonR.ToString();
-            _zR1.Label           = SwitchConfig.KeyboardControls.RightJoycon.ButtonZr.ToString();
+            _lStickUp1.Label     = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickUp.ToString();
+            _lStickDown1.Label   = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickDown.ToString();
+            _lStickLeft1.Label   = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickLeft.ToString();
+            _lStickRight1.Label  = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickRight.ToString();
+            _lStickButton1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickButton.ToString();
+            _dpadUp1.Label       = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadUp.ToString();
+            _dpadDown1.Label     = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadDown.ToString();
+            _dpadLeft1.Label     = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadLeft.ToString();
+            _dpadRight1.Label    = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadRight.ToString();
+            _minus1.Label        = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.ButtonMinus.ToString();
+            _l1.Label            = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.ButtonL.ToString();
+            _zL1.Label           = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.ButtonZl.ToString();
+            _rStickUp1.Label     = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickUp.ToString();
+            _rStickDown1.Label   = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickDown.ToString();
+            _rStickLeft1.Label   = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickLeft.ToString();
+            _rStickRight1.Label  = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickRight.ToString();
+            _rStickButton1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickButton.ToString();
+            _a1.Label            = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonA.ToString();
+            _b1.Label            = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonB.ToString();
+            _x1.Label            = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonX.ToString();
+            _y1.Label            = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonY.ToString();
+            _plus1.Label         = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonPlus.ToString();
+            _r1.Label            = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonR.ToString();
+            _zR1.Label           = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonZr.ToString();
 
-            _custThemePath.Buffer.Text           = SwitchConfig.CustomThemePath;
-            _graphicsShadersDumpPath.Buffer.Text = SwitchConfig.GraphicsShadersDumpPath;
-            _fsLogSpinAdjustment.Value           = SwitchConfig.FsGlobalAccessLogMode;
+            _custThemePath.Buffer.Text           = ConfigurationState.Instance.Ui.CustomThemePath;
+            _graphicsShadersDumpPath.Buffer.Text = ConfigurationState.Instance.Graphics.ShadersDumpPath;
+            _fsLogSpinAdjustment.Value           = ConfigurationState.Instance.System.FsGlobalAccessLogMode;
 
             _gameDirsBox.AppendColumn("", new CellRendererText(), "text", 0);
             _gameDirsBoxStore  = new ListStore(typeof(string));
             _gameDirsBox.Model = _gameDirsBoxStore;
-            foreach (string gameDir in SwitchConfig.GameDirs)
+            foreach (string gameDir in ConfigurationState.Instance.Ui.GameDirs.Value)
             {
                 _gameDirsBoxStore.AppendValues(gameDir);
             }
@@ -208,7 +263,7 @@ namespace Ryujinx.Ui
                     string key    = keyPressed.Event.Key.ToString();
                     string capKey = key.First().ToString().ToUpper() + key.Substring(1);
 
-                    if (Enum.IsDefined(typeof(OpenTK.Input.Key), capKey))
+                    if (Enum.IsDefined(typeof(Configuration.Hid.Key), capKey))
                     {
                         button.Label = capKey;
                     }
@@ -325,65 +380,63 @@ namespace Ryujinx.Ui
                 _gameDirsBoxStore.IterNext(ref treeIter);
             }
 
-            SwitchConfig.LoggingEnableError        = _errorLogToggle.Active;
-            SwitchConfig.LoggingEnableWarn         = _warningLogToggle.Active;
-            SwitchConfig.LoggingEnableInfo         = _infoLogToggle.Active;
-            SwitchConfig.LoggingEnableStub         = _stubLogToggle.Active;
-            SwitchConfig.LoggingEnableDebug        = _debugLogToggle.Active;
-            SwitchConfig.LoggingEnableGuest        = _guestLogToggle.Active;
-            SwitchConfig.LoggingEnableFsAccessLog  = _fsAccessLogToggle.Active;
-            SwitchConfig.EnableFileLog             = _fileLogToggle.Active;
-            SwitchConfig.DockedMode                = _dockedModeToggle.Active;
-            SwitchConfig.EnableDiscordIntegration  = _discordToggle.Active;
-            SwitchConfig.EnableVsync               = _vSyncToggle.Active;
-            SwitchConfig.EnableMulticoreScheduling = _multiSchedToggle.Active;
-            SwitchConfig.EnableFsIntegrityChecks   = _fsicToggle.Active;
-            SwitchConfig.IgnoreMissingServices     = _ignoreToggle.Active;
-            SwitchConfig.EnableKeyboard            = _directKeyboardAccess.Active;
-            SwitchConfig.EnableCustomTheme         = _custThemeToggle.Active;
+            ConfigurationState.Instance.Logger.EnableError.Value               = _errorLogToggle.Active;
+            ConfigurationState.Instance.Logger.EnableWarn.Value                = _warningLogToggle.Active;
+            ConfigurationState.Instance.Logger.EnableInfo.Value                = _infoLogToggle.Active;
+            ConfigurationState.Instance.Logger.EnableStub.Value                = _stubLogToggle.Active;
+            ConfigurationState.Instance.Logger.EnableDebug.Value               = _debugLogToggle.Active;
+            ConfigurationState.Instance.Logger.EnableGuest.Value               = _guestLogToggle.Active;
+            ConfigurationState.Instance.Logger.EnableFsAccessLog.Value         = _fsAccessLogToggle.Active;
+            ConfigurationState.Instance.Logger.EnableFileLog.Value             = _fileLogToggle.Active;
+            ConfigurationState.Instance.System.EnableDockedMode.Value          = _dockedModeToggle.Active;
+            ConfigurationState.Instance.EnableDiscordIntegration.Value         = _discordToggle.Active;
+            ConfigurationState.Instance.Graphics.EnableVsync.Value             = _vSyncToggle.Active;
+            ConfigurationState.Instance.System.EnableMulticoreScheduling.Value = _multiSchedToggle.Active;
+            ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value   = _fsicToggle.Active;
+            ConfigurationState.Instance.System.IgnoreMissingServices.Value     = _ignoreToggle.Active;
+            ConfigurationState.Instance.Hid.EnableKeyboard.Value               = _directKeyboardAccess.Active;
+            ConfigurationState.Instance.Ui.EnableCustomTheme.Value             = _custThemeToggle.Active;
 
-            SwitchConfig.KeyboardControls.LeftJoycon = new NpadKeyboardLeft()
+            ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon = new NpadKeyboardLeft()
             {
-                StickUp     = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickUp1.Label),
-                StickDown   = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickDown1.Label),
-                StickLeft   = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickLeft1.Label),
-                StickRight  = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickRight1.Label),
-                StickButton = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickButton1.Label),
-                DPadUp      = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _dpadUp1.Label),
-                DPadDown    = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _dpadDown1.Label),
-                DPadLeft    = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _dpadLeft1.Label),
-                DPadRight   = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _dpadRight1.Label),
-                ButtonMinus = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _minus1.Label),
-                ButtonL     = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _l1.Label),
-                ButtonZl    = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _zL1.Label),
+                StickUp     = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickUp1.Label),
+                StickDown   = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickDown1.Label),
+                StickLeft   = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickLeft1.Label),
+                StickRight  = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickRight1.Label),
+                StickButton = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickButton1.Label),
+                DPadUp      = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadUp1.Label),
+                DPadDown    = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadDown1.Label),
+                DPadLeft    = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadLeft1.Label),
+                DPadRight   = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadRight1.Label),
+                ButtonMinus = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _minus1.Label),
+                ButtonL     = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _l1.Label),
+                ButtonZl    = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _zL1.Label),
             };
 
-            SwitchConfig.KeyboardControls.RightJoycon = new NpadKeyboardRight()
+            ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon = new NpadKeyboardRight()
             {
-                StickUp     = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickUp1.Label),
-                StickDown   = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickDown1.Label),
-                StickLeft   = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickLeft1.Label),
-                StickRight  = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickRight1.Label),
-                StickButton = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickButton1.Label),
-                ButtonA     = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _a1.Label),
-                ButtonB     = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _b1.Label),
-                ButtonX     = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _x1.Label),
-                ButtonY     = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _y1.Label),
-                ButtonPlus  = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _plus1.Label),
-                ButtonR     = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _r1.Label),
-                ButtonZr    = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _zR1.Label),
+                StickUp     = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickUp1.Label),
+                StickDown   = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickDown1.Label),
+                StickLeft   = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickLeft1.Label),
+                StickRight  = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickRight1.Label),
+                StickButton = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickButton1.Label),
+                ButtonA     = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _a1.Label),
+                ButtonB     = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _b1.Label),
+                ButtonX     = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _x1.Label),
+                ButtonY     = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _y1.Label),
+                ButtonPlus  = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _plus1.Label),
+                ButtonR     = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _r1.Label),
+                ButtonZr    = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _zR1.Label),
             };
 
-            SwitchConfig.SystemLanguage          = (SystemLanguage)Enum.Parse(typeof(SystemLanguage), _systemLanguageSelect.ActiveId);
-            SwitchConfig.ControllerType          = (ControllerStatus)Enum.Parse(typeof(ControllerStatus), _controller1Type.ActiveId);
-            SwitchConfig.CustomThemePath         = _custThemePath.Buffer.Text;
-            SwitchConfig.GraphicsShadersDumpPath = _graphicsShadersDumpPath.Buffer.Text;
-            SwitchConfig.GameDirs                = gameDirs;
-            SwitchConfig.FsGlobalAccessLogMode   = (int)_fsLogSpinAdjustment.Value;
-
-            Configuration.SaveConfig(SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
-            Configuration.Configure(_device, SwitchConfig);
+            ConfigurationState.Instance.System.Language.Value              = (Language)Enum.Parse(typeof(Language), _systemLanguageSelect.ActiveId);
+            ConfigurationState.Instance.Hid.ControllerType.Value           = (ControllerType)Enum.Parse(typeof(ControllerType), _controller1Type.ActiveId);
+            ConfigurationState.Instance.Ui.CustomThemePath.Value           = _custThemePath.Buffer.Text;
+            ConfigurationState.Instance.Graphics.ShadersDumpPath.Value     = _graphicsShadersDumpPath.Buffer.Text;
+            ConfigurationState.Instance.Ui.GameDirs.Value                  = gameDirs;
+            ConfigurationState.Instance.System.FsGlobalAccessLogMode.Value = (int)_fsLogSpinAdjustment.Value;
 
+            MainWindow.SaveConfig();
             MainWindow.ApplyTheme();
 #pragma warning disable CS4014
             MainWindow.UpdateGameTable();