using LibHac.Fs; 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.Input; using System; using System.IO; using System.Threading.Tasks; using Utf8Json; using Utf8Json.Resolvers; namespace Ryujinx { public class Configuration { /// /// The default configuration instance /// public static Configuration Instance { get; private set; } /// /// Dumps shaders in this local directory /// public string GraphicsShadersDumpPath { get; private set; } /// /// Enables printing debug log messages /// public bool LoggingEnableDebug { get; private set; } /// /// Enables printing stub log messages /// public bool LoggingEnableStub { get; private set; } /// /// Enables printing info log messages /// public bool LoggingEnableInfo { get; private set; } /// /// Enables printing warning log messages /// public bool LoggingEnableWarn { get; private set; } /// /// Enables printing error log messages /// public bool LoggingEnableError { get; private set; } /// /// Enables printing guest log messages /// public bool LoggingEnableGuest { get; private set; } /// /// Enables printing FS access log messages /// public bool LoggingEnableFsAccessLog { get; private set; } /// /// Controls which log messages are written to the log targets /// public LogClass[] LoggingFilteredClasses { get; private set; } /// /// Enables or disables logging to a file on disk /// public bool EnableFileLog { get; private set; } /// /// Change System Language /// public SystemLanguage SystemLanguage { get; private set; } /// /// Enables or disables Docked Mode /// public bool DockedMode { get; private set; } /// /// Enables or disables Discord Rich Presence /// public bool EnableDiscordIntegration { get; private set; } /// /// Enables or disables Vertical Sync /// public bool EnableVsync { get; private set; } /// /// Enables or disables multi-core scheduling of threads /// public bool EnableMulticoreScheduling { get; private set; } /// /// Enables integrity checks on Game content files /// public bool EnableFsIntegrityChecks { get; private set; } /// /// Enables FS access log output to the console. Possible modes are 0-3 /// public int FsGlobalAccessLogMode { get; private set; } /// /// Enable or Disable aggressive CPU optimizations /// public bool EnableAggressiveCpuOpts { get; private set; } /// /// Enable or disable ignoring missing services /// public bool IgnoreMissingServices { get; private set; } /// /// The primary controller's type /// public ControllerStatus ControllerType { get; private set; } /// /// Enable or disable keyboard support (Independent from controllers binding) /// public bool EnableKeyboard { get; private set; } /// /// Keyboard control bindings /// public NpadKeyboard KeyboardControls { get; private set; } /// /// Controller control bindings /// public UI.Input.NpadController JoystickControls { get; private set; } /// /// Loads a configuration file from disk /// /// The path to the JSON configuration file public static void Load(string path) { var resolver = CompositeResolver.Create( new[] { new ConfigurationEnumFormatter() }, new[] { StandardResolver.AllowPrivateSnakeCase } ); using (Stream stream = File.OpenRead(path)) { Instance = JsonSerializer.Deserialize(stream, resolver); } } /// /// Loads a configuration file asynchronously from disk /// /// The path to the JSON configuration file public static async Task LoadAsync(string path) { var resolver = CompositeResolver.Create( new[] { new ConfigurationEnumFormatter() }, new[] { StandardResolver.AllowPrivateSnakeCase } ); using (Stream stream = File.OpenRead(path)) { Instance = await JsonSerializer.DeserializeAsync(stream, resolver); } } /// /// Configures a instance /// /// The instance to configure public static void Configure(Switch device) { if (Instance == null) { throw new InvalidOperationException("Configuration has not been loaded yet."); } GraphicsConfig.ShadersDumpPath = Instance.GraphicsShadersDumpPath; Logger.AddTarget(new AsyncLogTargetWrapper( new ConsoleLogTarget(), 1000, AsyncLogTargetOverflowAction.Block )); if (Instance.EnableFileLog) { Logger.AddTarget(new AsyncLogTargetWrapper( new FileLogTarget(Path.Combine(Program.ApplicationDirectory, "Ryujinx.log")), 1000, AsyncLogTargetOverflowAction.Block )); } Logger.SetEnable(LogLevel.Debug, Instance.LoggingEnableDebug); Logger.SetEnable(LogLevel.Stub, Instance.LoggingEnableStub); Logger.SetEnable(LogLevel.Info, Instance.LoggingEnableInfo); Logger.SetEnable(LogLevel.Warning, Instance.LoggingEnableWarn); Logger.SetEnable(LogLevel.Error, Instance.LoggingEnableError); Logger.SetEnable(LogLevel.Guest, Instance.LoggingEnableGuest); Logger.SetEnable(LogLevel.AccessLog, Instance.LoggingEnableFsAccessLog); if (Instance.LoggingFilteredClasses.Length > 0) { foreach (var logClass in EnumExtensions.GetValues()) { Logger.SetEnable(logClass, false); } foreach (var logClass in Instance.LoggingFilteredClasses) { Logger.SetEnable(logClass, true); } } device.System.State.DiscordIntegrationEnabled = Instance.EnableDiscordIntegration; device.EnableDeviceVsync = Instance.EnableVsync; device.System.State.DockedMode = Instance.DockedMode; device.System.State.SetLanguage(Instance.SystemLanguage); if (Instance.EnableMulticoreScheduling) { device.System.EnableMultiCoreScheduling(); } device.System.FsIntegrityCheckLevel = Instance.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None; device.System.GlobalAccessLogMode = Instance.FsGlobalAccessLogMode; if (Instance.EnableAggressiveCpuOpts) { Optimizations.AssumeStrictAbiCompliance = true; } ServiceConfiguration.IgnoreMissingServices = Instance.IgnoreMissingServices; if (Instance.JoystickControls.Enabled) { if (!Joystick.GetState(Instance.JoystickControls.Index).IsConnected) { Instance.JoystickControls.SetEnabled(false); } } device.Hid.InitializePrimaryController(Instance.ControllerType); device.Hid.InitializeKeyboard(); } private class ConfigurationEnumFormatter : IJsonFormatter where T : struct { public void Serialize(ref JsonWriter writer, T value, IJsonFormatterResolver formatterResolver) { formatterResolver.GetFormatterWithVerify() .Serialize(ref writer, value.ToString(), formatterResolver); } public T Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) { if (reader.ReadIsNull()) { return default(T); } string enumName = formatterResolver.GetFormatterWithVerify() .Deserialize(ref reader, formatterResolver); if (Enum.TryParse(enumName, out T result)) { return result; } return default(T); } } } }