using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Logging;
using Ryujinx.Configuration.Hid;
using Ryujinx.Configuration.System;
using Ryujinx.Configuration.Ui;
using System;
using System.Collections.Generic;
namespace Ryujinx.Configuration
{
public class ConfigurationState
{
///
/// UI configuration section
///
public class UiSection
{
public class Columns
{
public ReactiveObject FavColumn { get; private set; }
public ReactiveObject IconColumn { get; private set; }
public ReactiveObject AppColumn { get; private set; }
public ReactiveObject DevColumn { get; private set; }
public ReactiveObject VersionColumn { get; private set; }
public ReactiveObject TimePlayedColumn { get; private set; }
public ReactiveObject LastPlayedColumn { get; private set; }
public ReactiveObject FileExtColumn { get; private set; }
public ReactiveObject FileSizeColumn { get; private set; }
public ReactiveObject PathColumn { get; private set; }
public Columns()
{
FavColumn = new ReactiveObject();
IconColumn = new ReactiveObject();
AppColumn = new ReactiveObject();
DevColumn = new ReactiveObject();
VersionColumn = new ReactiveObject();
TimePlayedColumn = new ReactiveObject();
LastPlayedColumn = new ReactiveObject();
FileExtColumn = new ReactiveObject();
FileSizeColumn = new ReactiveObject();
PathColumn = new ReactiveObject();
}
}
public class ColumnSortSettings
{
public ReactiveObject SortColumnId { get; private set; }
public ReactiveObject SortAscending { get; private set; }
public ColumnSortSettings()
{
SortColumnId = new ReactiveObject();
SortAscending = new ReactiveObject();
}
}
///
/// Used to toggle columns in the GUI
///
public Columns GuiColumns { get; private set; }
///
/// Used to configure column sort settings in the GUI
///
public ColumnSortSettings ColumnSort { get; private set; }
///
/// A list of directories containing games to be used to load games into the games list
///
public ReactiveObject> GameDirs { get; private set; }
///
/// Enable or disable custom themes in the GUI
///
public ReactiveObject EnableCustomTheme { get; private set; }
///
/// Path to custom GUI theme
///
public ReactiveObject CustomThemePath { get; private set; }
public UiSection()
{
GuiColumns = new Columns();
ColumnSort = new ColumnSortSettings();
GameDirs = new ReactiveObject>();
EnableCustomTheme = new ReactiveObject();
CustomThemePath = new ReactiveObject();
}
}
///
/// Logger configuration section
///
public class LoggerSection
{
///
/// Enables printing debug log messages
///
public ReactiveObject EnableDebug { get; private set; }
///
/// Enables printing stub log messages
///
public ReactiveObject EnableStub { get; private set; }
///
/// Enables printing info log messages
///
public ReactiveObject EnableInfo { get; private set; }
///
/// Enables printing warning log messages
///
public ReactiveObject EnableWarn { get; private set; }
///
/// Enables printing error log messages
///
public ReactiveObject EnableError { get; private set; }
///
/// Enables printing guest log messages
///
public ReactiveObject EnableGuest { get; private set; }
///
/// Enables printing FS access log messages
///
public ReactiveObject EnableFsAccessLog { get; private set; }
///
/// Controls which log messages are written to the log targets
///
public ReactiveObject FilteredClasses { get; private set; }
///
/// Enables or disables logging to a file on disk
///
public ReactiveObject EnableFileLog { get; private set; }
///
/// Controls which OpenGL log messages are recorded in the log
///
public ReactiveObject GraphicsDebugLevel { get; private set; }
public LoggerSection()
{
EnableDebug = new ReactiveObject();
EnableStub = new ReactiveObject();
EnableInfo = new ReactiveObject();
EnableWarn = new ReactiveObject();
EnableError = new ReactiveObject();
EnableGuest = new ReactiveObject();
EnableFsAccessLog = new ReactiveObject();
FilteredClasses = new ReactiveObject();
EnableFileLog = new ReactiveObject();
GraphicsDebugLevel = new ReactiveObject();
}
}
///
/// System configuration section
///
public class SystemSection
{
///
/// Change System Language
///
public ReactiveObject Language { get; private set; }
///
/// Change System Region
///
public ReactiveObject Region { get; private set; }
///
/// Change System TimeZone
///
public ReactiveObject TimeZone { get; private set; }
///
/// System Time Offset in Seconds
///
public ReactiveObject SystemTimeOffset { get; private set; }
///
/// Enables or disables Docked Mode
///
public ReactiveObject EnableDockedMode { get; private set; }
///
/// Enables or disables multi-core scheduling of threads
///
public ReactiveObject EnableMulticoreScheduling { get; private set; }
///
/// Enables or disables profiled translation cache persistency
///
public ReactiveObject EnablePtc { get; private set; }
///
/// Enables integrity checks on Game content files
///
public ReactiveObject EnableFsIntegrityChecks { get; private set; }
///
/// Enables FS access log output to the console. Possible modes are 0-3
///
public ReactiveObject FsGlobalAccessLogMode { get; private set; }
///
/// The selected audio backend
///
public ReactiveObject AudioBackend { get; private set; }
///
/// Enable or disable ignoring missing services
///
public ReactiveObject IgnoreMissingServices { get; private set; }
public SystemSection()
{
Language = new ReactiveObject();
Region = new ReactiveObject();
TimeZone = new ReactiveObject();
SystemTimeOffset = new ReactiveObject();
EnableDockedMode = new ReactiveObject();
EnableMulticoreScheduling = new ReactiveObject();
EnablePtc = new ReactiveObject();
EnableFsIntegrityChecks = new ReactiveObject();
FsGlobalAccessLogMode = new ReactiveObject();
AudioBackend = new ReactiveObject();
IgnoreMissingServices = new ReactiveObject();
}
}
///
/// Hid configuration section
///
public class HidSection
{
///
/// Enable or disable keyboard support (Independent from controllers binding)
///
public ReactiveObject EnableKeyboard { get; private set; }
///
/// Hotkey Keyboard Bindings
///
public ReactiveObject Hotkeys { get; private set; }
///
/// Input device configuration.
/// NOTE: This ReactiveObject won't issue an event when the List has elements added or removed.
/// TODO: Implement a ReactiveList class.
///
public ReactiveObject> InputConfig { get; private set; }
public HidSection()
{
EnableKeyboard = new ReactiveObject();
Hotkeys = new ReactiveObject();
InputConfig = new ReactiveObject>();
}
}
///
/// Graphics configuration section
///
public class GraphicsSection
{
///
/// Max Anisotropy. Values range from 0 - 16. Set to -1 to let the game decide.
///
public ReactiveObject MaxAnisotropy { get; private set; }
///
/// Resolution Scale. An integer scale applied to applicable render targets. Values 1-4, or -1 to use a custom floating point scale instead.
///
public ReactiveObject ResScale { get; private set; }
///
/// Custom Resolution Scale. A custom floating point scale applied to applicable render targets. Only active when Resolution Scale is -1.
///
public ReactiveObject ResScaleCustom { get; private set; }
///
/// Dumps shaders in this local directory
///
public ReactiveObject ShadersDumpPath { get; private set; }
///
/// Enables or disables Vertical Sync
///
public ReactiveObject EnableVsync { get; private set; }
public GraphicsSection()
{
ResScale = new ReactiveObject();
ResScaleCustom = new ReactiveObject();
MaxAnisotropy = new ReactiveObject();
ShadersDumpPath = new ReactiveObject();
EnableVsync = new ReactiveObject();
}
}
///
/// The default configuration instance
///
public static ConfigurationState Instance { get; private set; }
///
/// The Ui section
///
public UiSection Ui { get; private set; }
///
/// The Logger section
///
public LoggerSection Logger { get; private set; }
///
/// The System section
///
public SystemSection System { get; private set; }
///
/// The Graphics section
///
public GraphicsSection Graphics { get; private set; }
///
/// The Hid section
///
public HidSection Hid { get; private set; }
///
/// Enables or disables Discord Rich Presence
///
public ReactiveObject EnableDiscordIntegration { get; private set; }
private ConfigurationState()
{
Ui = new UiSection();
Logger = new LoggerSection();
System = new SystemSection();
Graphics = new GraphicsSection();
Hid = new HidSection();
EnableDiscordIntegration = new ReactiveObject();
}
public ConfigurationFileFormat ToFileFormat()
{
List controllerConfigList = new List();
List keyboardConfigList = new List();
foreach (InputConfig inputConfig in Hid.InputConfig.Value)
{
if (inputConfig is ControllerConfig controllerConfig)
{
controllerConfigList.Add(controllerConfig);
}
else if (inputConfig is KeyboardConfig keyboardConfig)
{
keyboardConfigList.Add(keyboardConfig);
}
}
ConfigurationFileFormat configurationFile = new ConfigurationFileFormat
{
Version = ConfigurationFileFormat.CurrentVersion,
ResScale = Graphics.ResScale,
ResScaleCustom = Graphics.ResScaleCustom,
MaxAnisotropy = Graphics.MaxAnisotropy,
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,
LoggingGraphicsDebugLevel = Logger.GraphicsDebugLevel,
EnableFileLog = Logger.EnableFileLog,
SystemLanguage = System.Language,
SystemRegion = System.Region,
SystemTimeZone = System.TimeZone,
SystemTimeOffset = System.SystemTimeOffset,
DockedMode = System.EnableDockedMode,
EnableDiscordIntegration = EnableDiscordIntegration,
EnableVsync = Graphics.EnableVsync,
EnableMulticoreScheduling = System.EnableMulticoreScheduling,
EnablePtc = System.EnablePtc,
EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
FsGlobalAccessLogMode = System.FsGlobalAccessLogMode,
AudioBackend = System.AudioBackend,
IgnoreMissingServices = System.IgnoreMissingServices,
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,
},
ColumnSort = new ColumnSort
{
SortColumnId = Ui.ColumnSort.SortColumnId,
SortAscending = Ui.ColumnSort.SortAscending
},
GameDirs = Ui.GameDirs,
EnableCustomTheme = Ui.EnableCustomTheme,
CustomThemePath = Ui.CustomThemePath,
EnableKeyboard = Hid.EnableKeyboard,
Hotkeys = Hid.Hotkeys,
KeyboardConfig = keyboardConfigList,
ControllerConfig = controllerConfigList
};
return configurationFile;
}
public void LoadDefault()
{
Graphics.ResScale.Value = 1;
Graphics.ResScaleCustom.Value = 1.0f;
Graphics.MaxAnisotropy.Value = -1;
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.GraphicsDebugLevel.Value = GraphicsDebugLevel.None;
Logger.EnableFileLog.Value = true;
System.Language.Value = Language.AmericanEnglish;
System.Region.Value = Region.USA;
System.TimeZone.Value = "UTC";
System.SystemTimeOffset.Value = 0;
System.EnableDockedMode.Value = false;
EnableDiscordIntegration.Value = true;
Graphics.EnableVsync.Value = true;
System.EnableMulticoreScheduling.Value = true;
System.EnablePtc.Value = false;
System.EnableFsIntegrityChecks.Value = true;
System.FsGlobalAccessLogMode.Value = 0;
System.AudioBackend.Value = AudioBackend.OpenAl;
System.IgnoreMissingServices.Value = false;
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.ColumnSort.SortColumnId.Value = 0;
Ui.ColumnSort.SortAscending.Value = false;
Ui.GameDirs.Value = new List();
Ui.EnableCustomTheme.Value = false;
Ui.CustomThemePath.Value = "";
Hid.EnableKeyboard.Value = false;
Hid.Hotkeys.Value = new KeyboardHotkeys
{
ToggleVsync = Key.Tab
};
Hid.InputConfig.Value = new List
{
new KeyboardConfig
{
Index = 0,
ControllerType = ControllerType.JoyconPair,
PlayerIndex = PlayerIndex.Player1,
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,
ButtonSl = Key.Home,
ButtonSr = Key.End
},
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,
ButtonSl = Key.PageUp,
ButtonSr = Key.PageDown
}
}
};
}
public void Load(ConfigurationFileFormat configurationFileFormat, string configurationFilePath)
{
bool configurationFileUpdated = false;
if (configurationFileFormat.Version < 0 || configurationFileFormat.Version > ConfigurationFileFormat.CurrentVersion)
{
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Unsupported configuration version {configurationFileFormat.Version}, loading default.");
LoadDefault();
return;
}
if (configurationFileFormat.Version < 2)
{
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 2.");
configurationFileFormat.SystemRegion = Region.USA;
configurationFileUpdated = true;
}
if (configurationFileFormat.Version < 3)
{
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 3.");
configurationFileFormat.SystemTimeZone = "UTC";
configurationFileUpdated = true;
}
if (configurationFileFormat.Version < 4)
{
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 4.");
configurationFileFormat.MaxAnisotropy = -1;
configurationFileUpdated = true;
}
if (configurationFileFormat.Version < 5)
{
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 5.");
configurationFileFormat.SystemTimeOffset = 0;
configurationFileUpdated = true;
}
if (configurationFileFormat.Version < 6)
{
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 6.");
configurationFileFormat.ControllerConfig = new List();
configurationFileFormat.KeyboardConfig = new List
{
new KeyboardConfig
{
Index = 0,
ControllerType = ControllerType.JoyconPair,
PlayerIndex = PlayerIndex.Player1,
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,
ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound
},
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,
ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound
}
}
};
configurationFileUpdated = true;
}
// Only needed for version 6 configurations.
if (configurationFileFormat.Version == 6)
{
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 7.");
for (int i = 0; i < configurationFileFormat.KeyboardConfig.Count; i++)
{
if (configurationFileFormat.KeyboardConfig[i].Index != KeyboardConfig.AllKeyboardsIndex)
{
configurationFileFormat.KeyboardConfig[i].Index++;
}
}
}
if (configurationFileFormat.Version < 8)
{
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 8.");
configurationFileFormat.EnablePtc = false;
configurationFileUpdated = true;
}
if (configurationFileFormat.Version < 9)
{
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 9.");
configurationFileFormat.ColumnSort = new ColumnSort
{
SortColumnId = 0,
SortAscending = false
};
configurationFileFormat.Hotkeys = new KeyboardHotkeys
{
ToggleVsync = Key.Tab
};
configurationFileUpdated = true;
}
if (configurationFileFormat.Version < 10)
{
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 10.");
configurationFileFormat.AudioBackend = AudioBackend.OpenAl;
configurationFileUpdated = true;
}
if (configurationFileFormat.Version < 11)
{
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 11.");
configurationFileFormat.ResScale = 1;
configurationFileFormat.ResScaleCustom = 1.0f;
configurationFileUpdated = true;
}
if (configurationFileFormat.Version < 12)
{
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 12.");
configurationFileFormat.LoggingGraphicsDebugLevel = GraphicsDebugLevel.None;
configurationFileUpdated = true;
}
List inputConfig = new List();
inputConfig.AddRange(configurationFileFormat.ControllerConfig);
inputConfig.AddRange(configurationFileFormat.KeyboardConfig);
Graphics.ResScale.Value = configurationFileFormat.ResScale;
Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;
Graphics.MaxAnisotropy.Value = configurationFileFormat.MaxAnisotropy;
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.GraphicsDebugLevel.Value = configurationFileFormat.LoggingGraphicsDebugLevel;
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
System.Language.Value = configurationFileFormat.SystemLanguage;
System.Region.Value = configurationFileFormat.SystemRegion;
System.TimeZone.Value = configurationFileFormat.SystemTimeZone;
System.SystemTimeOffset.Value = configurationFileFormat.SystemTimeOffset;
System.EnableDockedMode.Value = configurationFileFormat.DockedMode;
EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration;
Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync;
System.EnableMulticoreScheduling.Value = configurationFileFormat.EnableMulticoreScheduling;
System.EnablePtc.Value = configurationFileFormat.EnablePtc;
System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks;
System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode;
System.AudioBackend.Value = configurationFileFormat.AudioBackend;
System.IgnoreMissingServices.Value = configurationFileFormat.IgnoreMissingServices;
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.ColumnSort.SortColumnId.Value = configurationFileFormat.ColumnSort.SortColumnId;
Ui.ColumnSort.SortAscending.Value = configurationFileFormat.ColumnSort.SortAscending;
Ui.GameDirs.Value = configurationFileFormat.GameDirs;
Ui.EnableCustomTheme.Value = configurationFileFormat.EnableCustomTheme;
Ui.CustomThemePath.Value = configurationFileFormat.CustomThemePath;
Hid.EnableKeyboard.Value = configurationFileFormat.EnableKeyboard;
Hid.Hotkeys.Value = configurationFileFormat.Hotkeys;
Hid.InputConfig.Value = inputConfig;
if (configurationFileUpdated)
{
ToFileFormat().SaveConfig(configurationFilePath);
Common.Logging.Logger.Warning?.Print(LogClass.Application, "Configuration file has been updated!");
}
}
public static void Initialize()
{
if (Instance != null)
{
throw new InvalidOperationException("Configuration is already initialized");
}
Instance = new ConfigurationState();
}
}
}