diff --git a/CONFIG.md b/CONFIG.md index eef7b68d9..b63f5e832 100644 --- a/CONFIG.md +++ b/CONFIG.md @@ -1,123 +1,124 @@ ## Config File -`Ryujinx.conf` should be present in executable folder (It's an *.ini file) following this format: +`Config.jsonc` should be present in executable folder. The available settings follow: -- `Logging_Enable_Info` *(bool)* +- `graphics_shaders_dump_path` *(string)* + + Dump shaders in local directory (e.g. `C:\ShaderDumps`) + +- `logging_enable_debug` *(bool)* + + Enable the Debug Logging. + +- `logging_enable_stub` *(bool)* + + Enable the Trace Logging. + +- `logging_enable_info` *(bool)* Enable the Informations Logging. -- `Logging_Enable_Trace` *(bool)* +- `logging_enable_warn` *(bool)* - Enable the Trace Logging (Enabled in Debug recommended). - -- `Logging_Enable_Debug` *(bool)* + Enable the Warning Logging. - Enable the Debug Logging (Enabled in Debug recommended). +- `logging_enable_error` *(bool)* -- `Logging_Enable_Warn` *(bool)* + Enable the Error Logging. - Enable the Warning Logging (Enabled in Debug recommended). - -- `Logging_Enable_Error` *(bool)* - - Enable the Error Logging (Enabled in Debug recommended). - -- `Logging_Enable_Fatal` *(bool)* - - Enable the Fatal Logging (Enabled in Debug recommended). - -- `Logging_Enable_Ipc` *(bool)* - - Enable the Ipc Message Logging. - -- `Logging_Enable_LogFile` *(bool)* +- `enable_file_log` *(bool)* Enable writing the logging inside a Ryujinx.log file. - -- `GamePad_Index` *(int)* - The index of the Controller Device. - -- `GamePad_Deadzone` *(float)* +- `system_language` *(string)* - The deadzone of both analog sticks on the Controller. + Change System Language, [System Language list](https://gist.github.com/HorrorTroll/b6e4a88d774c3c9b3bdf54d79a7ca43b) -- `GamePad_Enable` *(bool)* - - Whether or not to enable Controller Support. - -- `Controls_Left_JoyConKeyboard_XX` *(int)* - ``` - Controls_Left_JoyConKeyboard_Stick_Up (int) - Controls_Left_JoyConKeyboard_Stick_Down (int) - Controls_Left_JoyConKeyboard_Stick_Left (int) - Controls_Left_JoyConKeyboard_Stick_Right (int) - Controls_Left_JoyConKeyboard_Stick_Button (int) - Controls_Left_JoyConKeyboard_DPad_Up (int) - Controls_Left_JoyConKeyboard_DPad_Down (int) - Controls_Left_JoyConKeyboard_DPad_Left (int) - Controls_Left_JoyConKeyboard_DPad_Right (int) - Controls_Left_JoyConKeyboard_Button_Minus (int) - Controls_Left_JoyConKeyboard_Button_L (int) - Controls_Left_JoyConKeyboard_Button_ZL (int) - ``` - - Keys of the Left Emulated Joycon, the values depend of the [OpenTK Enum Keys](https://github.com/opentk/opentk/blob/develop/src/OpenTK/Input/Key.cs). - - OpenTK use a QWERTY layout, so pay attention if you use another Keyboard Layout. - - Ex: `Controls_Left_JoyConKeyboard_Button_Minus = 52` > Tab key (All Layout). +- `docked_mode` *(bool)* -- `Controls_Right_JoyConKeyboard_XX` *(int)* - ``` - Controls_Right_JoyConKeyboard_Stick_Up (int) - Controls_Right_JoyConKeyboard_Stick_Down (int) - Controls_Right_JoyConKeyboard_Stick_Left (int) - Controls_Right_JoyConKeyboard_Stick_Right (int) - Controls_Right_JoyConKeyboard_Stick_Button (int) - Controls_Right_JoyConKeyboard_Button_A (int) - Controls_Right_JoyConKeyboard_Button_B (int) - Controls_Right_JoyConKeyboard_Button_X (int) - Controls_Right_JoyConKeyboard_Button_Y (int) - Controls_Right_JoyConKeyboard_Button_Plus (int) - Controls_Right_JoyConKeyboard_Button_R (int) - Controls_Right_JoyConKeyboard_Button_ZR (int) - ``` + Enable or Disable Docked Mode - Keys of the right Emulated Joycon, the values depend of the [OpenTK Enum Keys](https://github.com/opentk/opentk/blob/develop/src/OpenTK/Input/Key.cs). - - OpenTK use a QWERTY layout, so pay attention if you use another Keyboard Layout. - - Ex: `Controls_Right_JoyConKeyboard_Button_A = 83` > A key (QWERTY Layout) / Q key (AZERTY Layout). - -- `Controls_Left_JoyConController_XX` *(String)* - ``` - Controls_Left_JoyConController_Stick (String) - Controls_Left_JoyConController_Stick_Button (String) - Controls_Left_JoyConController_DPad_Up (String) - Controls_Left_JoyConController_DPad_Down (String) - Controls_Left_JoyConController_DPad_Left (String) - Controls_Left_JoyConController_DPad_Right (String) - Controls_Left_JoyConController_Button_Minus (String) - Controls_Left_JoyConController_Button_L (String) - Controls_Left_JoyConController_Button_ZL (String) - ``` - -- `Controls_Right_JoyConController_XX` *(String)* - ``` - Controls_Right_JoyConController_Stick (String) - Controls_Right_JoyConController_Stick_Button (String) - Controls_Right_JoyConController_Button_A (String) - Controls_Right_JoyConController_Button_B (String) - Controls_Right_JoyConController_Button_X (String) - Controls_Right_JoyConController_Button_Y (String) - Controls_Right_JoyConController_Button_Plus (String) - Controls_Right_JoyConController_Button_R (String) - Controls_Right_JoyConController_Button_ZR (String) - ``` +- `enable_vsync` *(bool)* -- Default Mapping - - Controller + Enable or Disable Game Vsync + +- `enable_multicore_scheduling` *(bool)* + + Enable or Disable Multi-core scheduling of threads + +- `enable_fs_integrity_checks` *(bool)* + + Enable integrity checks on Switch content files + +- `controller_type` *(string)* + + The primary controller's type. + Supported Values: `Handheld`, `ProController`, `NpadPair`, `NpadLeft`, `NpadRight` + +- `keyboard_controls` *(object)* : + - `left_joycon` *(object)* : + Left JoyCon Keyboard Bindings + - `stick_up` *(string)* + - `stick_down` *(string)* + - `stick_left` *(string)* + - `stick_right` *(string)* + - `stick_button` *(string)* + - `dpad_up` *(string)* + - `dpad_down` *(string)* + - `dpad_left` *(string)* + - `dpad_right` *(string)* + - `button_minus` *(string)* + - `button_l` *(string)* + - `button_zl` *(string)* + - `right_joycon` *(object)* : + Right JoyCon Keyboard Bindings + - `stick_up` *(string)* + - `stick_down` *(string)* + - `stick_left` *(string)* + - `stick_right` *(string)* + - `stick_button` *(string)* + - `button_a` *(string)* + - `button_b` *(string)* + - `button_x` *(string)* + - `button_y` *(string)* + - `button_plus` *(string)* + - `button_r` *(string)* + - `button_zr` *(string)* + +- `gamepad_controls` *(object)* : + - `enabled` *(bool)* + Whether or not to enable Controller Support. + - `index` *(int)* + The index of the Controller Device. + - `deadzone` *(number)* + The deadzone of both analog sticks on the Controller. + - `trigger_threshold` *(number)* + The value of how pressed down each trigger has to be in order to register a button press + - `left_joycon` *(object)* : + Left JoyCon Controller Bindings + - `stick` *(string)* + - `stick_button` *(string)* + - `dpad_up` *(string)* + - `dpad_down` *(string)* + - `dpad_left` *(string)* + - `dpad_right` *(string)* + - `button_minus` *(string)* + - `button_l` *(string)* + - `button_zl` *(string)* + - `right_joycon` *(object)* : + Right JoyCon Controller Bindings + - `stick` *(string)* + - `stick_button` *(string)* + - `button_a` *(string)* + - `button_b` *(string)* + - `button_x` *(string)* + - `button_y` *(string)* + - `button_plus` *(string)* + - `button_r` *(string)* + - `button_zr` *(string)* + +### Default Mapping + #### Controller - Left Joycon: - Analog Stick = Left Analog Stick - DPad Up = DPad Up @@ -137,7 +138,8 @@ - Plus = Start / Options - R = Right Shoulder Button - ZR = Right Trigger - - Keyboard + + #### Keyboard - Left Joycon: - Stick Up = W - Stick Down = S @@ -166,7 +168,7 @@ - R = U - ZR = O -- Valid Button Mappings +### Valid Button Mappings - A = The A / Cross Button - B = The B / Circle Button - X = The X / Square Button diff --git a/README.md b/README.md index bdf8588ae..10279b505 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ or just drag'n'drop the homebrew *.NRO / *.NSO or the game *.NSP / *.XCI on the - Controller Input is supported, see [CONFIG.md](CONFIG.md) - - Config File: `Ryujinx.conf` should be present in executable folder. + - Config File: `Config.jsonc` should be present in executable folder. For more information [you can go here](CONFIG.md). **Help** diff --git a/Ryujinx.Common/StructIOExtension.cs b/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs similarity index 83% rename from Ryujinx.Common/StructIOExtension.cs rename to Ryujinx.Common/Extensions/BinaryReaderExtensions.cs index 8671b1920..49af946fd 100644 --- a/Ryujinx.Common/StructIOExtension.cs +++ b/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs @@ -1,14 +1,13 @@ using System; -using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; -using System.Text; namespace Ryujinx.Common { - public static class StructIOExtension + public static class BinaryReaderExtensions { - public unsafe static T ReadStruct(this BinaryReader reader) where T : struct + public unsafe static T ReadStruct(this BinaryReader reader) + where T : struct { int size = Marshal.SizeOf(); @@ -20,7 +19,8 @@ namespace Ryujinx.Common } } - public unsafe static void WriteStruct(this BinaryWriter writer, T value) where T : struct + public unsafe static void WriteStruct(this BinaryWriter writer, T value) + where T : struct { long size = Marshal.SizeOf(); diff --git a/Ryujinx.Common/Extensions/EnumExtensions.cs b/Ryujinx.Common/Extensions/EnumExtensions.cs new file mode 100644 index 000000000..560af8829 --- /dev/null +++ b/Ryujinx.Common/Extensions/EnumExtensions.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ryujinx.Common +{ + public static class EnumExtensions + { + public static T[] GetValues() + { + return (T[])Enum.GetValues(typeof(T)); + } + } +} diff --git a/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs b/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs new file mode 100644 index 000000000..0c4396e7f --- /dev/null +++ b/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs @@ -0,0 +1,53 @@ +using System.Reflection; +using System.Text; + +namespace Ryujinx.Common.Logging +{ + internal class DefaultLogFormatter : ILogFormatter + { + private static readonly ObjectPool _stringBuilderPool = SharedPools.Default(); + + public string Format(LogEventArgs args) + { + StringBuilder sb = _stringBuilderPool.Allocate(); + + try + { + sb.Clear(); + + sb.AppendFormat(@"{0:hh\:mm\:ss\.fff}", args.Time); + sb.Append(" | "); + sb.AppendFormat("{0:d4}", args.ThreadId); + sb.Append(' '); + sb.Append(args.Message); + + if (args.Data != null) + { + PropertyInfo[] props = args.Data.GetType().GetProperties(); + + sb.Append(' '); + + foreach (var prop in props) + { + sb.Append(prop.Name); + sb.Append(": "); + sb.Append(prop.GetValue(args.Data)); + sb.Append(" - "); + } + + // We remove the final '-' from the string + if (props.Length > 0) + { + sb.Remove(sb.Length - 3, 3); + } + } + + return sb.ToString(); + } + finally + { + _stringBuilderPool.Release(sb); + } + } + } +} diff --git a/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs b/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs new file mode 100644 index 000000000..9a55bc6b0 --- /dev/null +++ b/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Common.Logging +{ + interface ILogFormatter + { + string Format(LogEventArgs args); + } +} diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index f20347b6b..66a83b376 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -2,6 +2,7 @@ namespace Ryujinx.Common.Logging { public enum LogClass { + Application, Audio, Cpu, Font, diff --git a/Ryujinx.Common/Logging/Logger.cs b/Ryujinx.Common/Logging/Logger.cs index 35ca416bc..88ebe4731 100644 --- a/Ryujinx.Common/Logging/Logger.cs +++ b/Ryujinx.Common/Logging/Logger.cs @@ -1,8 +1,7 @@ using System; +using System.Collections.Generic; using System.Diagnostics; -using System.Reflection; using System.Runtime.CompilerServices; -using System.Text; using System.Threading; namespace Ryujinx.Common.Logging @@ -14,9 +13,9 @@ namespace Ryujinx.Common.Logging private static readonly bool[] m_EnabledLevels; private static readonly bool[] m_EnabledClasses; - public static event EventHandler Updated; + private static readonly List m_LogTargets; - public static bool EnableFileLog { get; set; } + public static event EventHandler Updated; static Logger() { @@ -33,9 +32,30 @@ namespace Ryujinx.Common.Logging m_EnabledClasses[index] = true; } + m_LogTargets = new List(); + m_Time = Stopwatch.StartNew(); } + public static void AddTarget(ILogTarget target) + { + m_LogTargets.Add(target); + + Updated += target.Log; + } + + public static void Shutdown() + { + Updated = null; + + foreach(var target in m_LogTargets) + { + target.Dispose(); + } + + m_LogTargets.Clear(); + } + public static void SetEnable(LogLevel logLevel, bool enabled) { m_EnabledLevels[(int)logLevel] = enabled; diff --git a/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs b/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs new file mode 100644 index 000000000..a805a83b6 --- /dev/null +++ b/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; + +namespace Ryujinx.Common.Logging +{ + public enum AsyncLogTargetOverflowAction + { + /// + /// Block until there's more room in the queue + /// + Block = 0, + + /// + /// Discard the overflowing item + /// + Discard = 1 + } + + public class AsyncLogTargetWrapper : ILogTarget + { + private ILogTarget _target; + + private Thread _messageThread; + + private BlockingCollection _messageQueue; + + private readonly int _overflowTimeout; + + public AsyncLogTargetWrapper(ILogTarget target) + : this(target, -1, AsyncLogTargetOverflowAction.Block) + { } + + public AsyncLogTargetWrapper(ILogTarget target, int queueLimit, AsyncLogTargetOverflowAction overflowAction) + { + _target = target; + _messageQueue = new BlockingCollection(queueLimit); + _overflowTimeout = overflowAction == AsyncLogTargetOverflowAction.Block ? -1 : 0; + + _messageThread = new Thread(() => { + while (!_messageQueue.IsCompleted) + { + try + { + _target.Log(this, _messageQueue.Take()); + } + catch (InvalidOperationException) + { + // IOE means that Take() was called on a completed collection. + // Some other thread can call CompleteAdding after we pass the + // IsCompleted check but before we call Take. + // We can simply catch the exception since the loop will break + // on the next iteration. + } + } + }); + + _messageThread.IsBackground = true; + _messageThread.Start(); + } + + public void Log(object sender, LogEventArgs e) + { + if (!_messageQueue.IsAddingCompleted) + { + _messageQueue.TryAdd(e, _overflowTimeout); + } + } + + public void Dispose() + { + _messageQueue.CompleteAdding(); + _messageThread.Join(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs b/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs new file mode 100644 index 000000000..871076a41 --- /dev/null +++ b/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Concurrent; + +namespace Ryujinx.Common.Logging +{ + public class ConsoleLogTarget : ILogTarget + { + private static readonly ConcurrentDictionary _logColors; + + private readonly ILogFormatter _formatter; + + static ConsoleLogTarget() + { + _logColors = new ConcurrentDictionary { + [ LogLevel.Stub ] = ConsoleColor.DarkGray, + [ LogLevel.Info ] = ConsoleColor.White, + [ LogLevel.Warning ] = ConsoleColor.Yellow, + [ LogLevel.Error ] = ConsoleColor.Red + }; + } + + public ConsoleLogTarget() + { + _formatter = new DefaultLogFormatter(); + } + + public void Log(object sender, LogEventArgs args) + { + if (_logColors.TryGetValue(args.Level, out ConsoleColor color)) + { + Console.ForegroundColor = color; + + Console.WriteLine(_formatter.Format(args)); + + Console.ResetColor(); + } + else + { + Console.WriteLine(_formatter.Format(args)); + } + } + + public void Dispose() + { + Console.ResetColor(); + } + } +} diff --git a/Ryujinx.Common/Logging/Targets/FileLogTarget.cs b/Ryujinx.Common/Logging/Targets/FileLogTarget.cs new file mode 100644 index 000000000..85dc82497 --- /dev/null +++ b/Ryujinx.Common/Logging/Targets/FileLogTarget.cs @@ -0,0 +1,36 @@ +using System.IO; +using System.Text; + +namespace Ryujinx.Common.Logging +{ + public class FileLogTarget : ILogTarget + { + private static readonly ObjectPool _stringBuilderPool = SharedPools.Default(); + + private readonly StreamWriter _logWriter; + private readonly ILogFormatter _formatter; + + public FileLogTarget(string path) + : this(path, FileShare.Read, FileMode.Append) + { } + + public FileLogTarget(string path, FileShare fileShare, FileMode fileMode) + { + _logWriter = new StreamWriter(File.Open(path, fileMode, FileAccess.Write, fileShare)); + _formatter = new DefaultLogFormatter(); + } + + public void Log(object sender, LogEventArgs args) + { + _logWriter.WriteLine(_formatter.Format(args)); + _logWriter.Flush(); + } + + public void Dispose() + { + _logWriter.WriteLine("---- End of Log ----"); + _logWriter.Flush(); + _logWriter.Dispose(); + } + } +} diff --git a/Ryujinx.Common/Logging/Targets/ILogTarget.cs b/Ryujinx.Common/Logging/Targets/ILogTarget.cs new file mode 100644 index 000000000..261c5e64b --- /dev/null +++ b/Ryujinx.Common/Logging/Targets/ILogTarget.cs @@ -0,0 +1,9 @@ +using System; + +namespace Ryujinx.Common.Logging +{ + public interface ILogTarget : IDisposable + { + void Log(object sender, LogEventArgs args); + } +} diff --git a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs new file mode 100644 index 000000000..410394aa2 --- /dev/null +++ b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs @@ -0,0 +1,35 @@ +using System.IO; +using Utf8Json; + +namespace Ryujinx.Common.Logging +{ + public class JsonLogTarget : ILogTarget + { + private Stream _stream; + private bool _leaveOpen; + + public JsonLogTarget(Stream stream) + { + _stream = stream; + } + + public JsonLogTarget(Stream stream, bool leaveOpen) + { + _stream = stream; + _leaveOpen = leaveOpen; + } + + public void Log(object sender, LogEventArgs e) + { + JsonSerializer.Serialize(_stream, e); + } + + public void Dispose() + { + if (!_leaveOpen) + { + _stream.Dispose(); + } + } + } +} diff --git a/Ryujinx.Common/Pools/ObjectPool.cs b/Ryujinx.Common/Pools/ObjectPool.cs new file mode 100644 index 000000000..dba671bb7 --- /dev/null +++ b/Ryujinx.Common/Pools/ObjectPool.cs @@ -0,0 +1,75 @@ +using System; +using System.Threading; + +namespace Ryujinx.Common +{ + public class ObjectPool + where T : class + { + private T _firstItem; + private readonly T[] _items; + + private readonly Func _factory; + + public ObjectPool(Func factory, int size) + { + _items = new T[size - 1]; + _factory = factory; + } + + public T Allocate() + { + var instance = _firstItem; + + if (instance == null || instance != Interlocked.CompareExchange(ref _firstItem, null, instance)) + { + instance = AllocateInternal(); + } + + return instance; + } + + private T AllocateInternal() + { + var items = _items; + + for (int i = 0; i < items.Length; i++) + { + var instance = items[i]; + + if (instance != null && instance == Interlocked.CompareExchange(ref items[i], null, instance)) + { + return instance; + } + } + + return _factory(); + } + + public void Release(T obj) + { + if (_firstItem == null) + { + _firstItem = obj; + } + else + { + ReleaseInternal(obj); + } + } + + private void ReleaseInternal(T obj) + { + var items = _items; + + for (int i = 0; i < items.Length; i++) + { + if (items[i] == null) + { + items[i] = obj; + break; + } + } + } + } +} diff --git a/Ryujinx.Common/Pools/SharedPools.cs b/Ryujinx.Common/Pools/SharedPools.cs new file mode 100644 index 000000000..b4860b85b --- /dev/null +++ b/Ryujinx.Common/Pools/SharedPools.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Common +{ + public static class SharedPools + { + private static class DefaultPool + where T : class, new() + { + public static readonly ObjectPool Instance = new ObjectPool(() => new T(), 20); + } + + public static ObjectPool Default() + where T : class, new() + { + return DefaultPool.Instance; + } + } +} diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj index 5c9293b70..bba481e6d 100644 --- a/Ryujinx.Common/Ryujinx.Common.csproj +++ b/Ryujinx.Common/Ryujinx.Common.csproj @@ -13,4 +13,8 @@ true + + + + diff --git a/Ryujinx.Common/BitUtils.cs b/Ryujinx.Common/Utilities/BitUtils.cs similarity index 100% rename from Ryujinx.Common/BitUtils.cs rename to Ryujinx.Common/Utilities/BitUtils.cs diff --git a/Ryujinx.Common/HexUtils.cs b/Ryujinx.Common/Utilities/HexUtils.cs similarity index 100% rename from Ryujinx.Common/HexUtils.cs rename to Ryujinx.Common/Utilities/HexUtils.cs diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index d661b273a..4a15f616e 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -112,7 +112,6 @@ namespace Ryujinx.HLE if (disposing) { System.Dispose(); - VsyncEvent.Dispose(); } } diff --git a/Ryujinx.sln b/Ryujinx.sln index 148224faa..990a89a2e 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -23,6 +23,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luea", "Ryujinx.LLE\Luea.cs EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Common", "Ryujinx.Common\Ryujinx.Common.csproj", "{5FD4E4F6-8928-4B3C-BE07-28A675C17226}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{464D8AB7-B056-4A99-B207-B8DCFB47AAA9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -73,6 +75,10 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9} = {464D8AB7-B056-4A99-B207-B8DCFB47AAA9} + {D8F72938-78EF-4E8C-BAFE-531C9C3C8F15} = {464D8AB7-B056-4A99-B207-B8DCFB47AAA9} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {110169B3-3328-4730-8AB0-BA05BEF75C1A} EndGlobalSection diff --git a/Ryujinx/Config.cs b/Ryujinx/Config.cs deleted file mode 100644 index a1d8cddf4..000000000 --- a/Ryujinx/Config.cs +++ /dev/null @@ -1,203 +0,0 @@ -using LibHac.IO; -using Ryujinx.Common.Logging; -using Ryujinx.HLE; -using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.HLE.Input; -using Ryujinx.UI.Input; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Reflection; - -namespace Ryujinx -{ - public static class Config - { - public static NpadKeyboard NpadKeyboard { get; private set; } - public static NpadController NpadController { get; private set; } - - public static void Read(Switch device) - { - string iniFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); - - string iniPath = Path.Combine(iniFolder, "Ryujinx.conf"); - - IniParser parser = new IniParser(iniPath); - - GraphicsConfig.ShadersDumpPath = parser.Value("Graphics_Shaders_Dump_Path"); - - Logger.SetEnable(LogLevel.Debug, Convert.ToBoolean(parser.Value("Logging_Enable_Debug"))); - Logger.SetEnable(LogLevel.Stub, Convert.ToBoolean(parser.Value("Logging_Enable_Stub"))); - Logger.SetEnable(LogLevel.Info, Convert.ToBoolean(parser.Value("Logging_Enable_Info"))); - Logger.SetEnable(LogLevel.Warning, Convert.ToBoolean(parser.Value("Logging_Enable_Warn"))); - Logger.SetEnable(LogLevel.Error, Convert.ToBoolean(parser.Value("Logging_Enable_Error"))); - - string[] filteredLogClasses = parser.Value("Logging_Filtered_Classes").Split(',', StringSplitOptions.RemoveEmptyEntries); - - //When the classes are specified on the list, we only - //enable the classes that are on the list. - //So, first disable everything, then enable - //the classes that the user added to the list. - if (filteredLogClasses.Length > 0) - { - foreach (LogClass Class in Enum.GetValues(typeof(LogClass))) - { - Logger.SetEnable(Class, false); - } - } - - foreach (string logClass in filteredLogClasses) - { - if (!string.IsNullOrEmpty(logClass.Trim())) - { - foreach (LogClass Class in Enum.GetValues(typeof(LogClass))) - { - if (Class.ToString().ToLower().Contains(logClass.Trim().ToLower())) - { - Logger.SetEnable(Class, true); - } - } - } - } - - Logger.EnableFileLog = Convert.ToBoolean(parser.Value("Enable_File_Log")); - - SystemLanguage SetLanguage = Enum.Parse(parser.Value("System_Language")); - - device.System.State.SetLanguage(SetLanguage); - - device.System.State.DockedMode = Convert.ToBoolean(parser.Value("Docked_Mode")); - - device.EnableDeviceVsync = Convert.ToBoolean(parser.Value("Enable_Vsync")); - - if (Convert.ToBoolean(parser.Value("Enable_MultiCore_Scheduling"))) - { - device.System.EnableMultiCoreScheduling(); - } - - device.System.FsIntegrityCheckLevel = Convert.ToBoolean(parser.Value("Enable_FS_Integrity_Checks")) - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - - HidControllerType ControllerType = Enum.Parse(parser.Value("Controller_Type")); - - device.Hid.InitilizePrimaryController(ControllerType); - - NpadKeyboard = new NpadKeyboard( - - new NpadKeyboardLeft - { - StickUp = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Stick_Up")), - StickDown = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Stick_Down")), - StickLeft = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Stick_Left")), - StickRight = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Stick_Right")), - StickButton = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Stick_Button")), - DPadUp = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_DPad_Up")), - DPadDown = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_DPad_Down")), - DPadLeft = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_DPad_Left")), - DPadRight = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_DPad_Right")), - ButtonMinus = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Button_Minus")), - ButtonL = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Button_L")), - ButtonZl = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Button_ZL")) - }, - - new NpadKeyboardRight - { - StickUp = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Stick_Up")), - StickDown = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Stick_Down")), - StickLeft = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Stick_Left")), - StickRight = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Stick_Right")), - StickButton = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Stick_Button")), - ButtonA = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_A")), - ButtonB = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_B")), - ButtonX = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_X")), - ButtonY = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_Y")), - ButtonPlus = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_Plus")), - ButtonR = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_R")), - ButtonZr = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_ZR")) - }); - - NpadController = new NpadController( - Convert.ToBoolean(parser.Value("GamePad_Enable")), - Convert.ToInt32 (parser.Value("GamePad_Index")), - (float)Convert.ToDouble (parser.Value("GamePad_Deadzone"), CultureInfo.InvariantCulture), - (float)Convert.ToDouble (parser.Value("GamePad_Trigger_Threshold"), CultureInfo.InvariantCulture), - - new NpadControllerLeft - { - Stick = ToId(parser.Value("Controls_Left_JoyConController_Stick")), - StickButton = ToId(parser.Value("Controls_Left_JoyConController_Stick_Button")), - DPadUp = ToId(parser.Value("Controls_Left_JoyConController_DPad_Up")), - DPadDown = ToId(parser.Value("Controls_Left_JoyConController_DPad_Down")), - DPadLeft = ToId(parser.Value("Controls_Left_JoyConController_DPad_Left")), - DPadRight = ToId(parser.Value("Controls_Left_JoyConController_DPad_Right")), - ButtonMinus = ToId(parser.Value("Controls_Left_JoyConController_Button_Minus")), - ButtonL = ToId(parser.Value("Controls_Left_JoyConController_Button_L")), - ButtonZl = ToId(parser.Value("Controls_Left_JoyConController_Button_ZL")) - }, - - new NpadControllerRight - { - Stick = ToId(parser.Value("Controls_Right_JoyConController_Stick")), - StickButton = ToId(parser.Value("Controls_Right_JoyConController_Stick_Button")), - ButtonA = ToId(parser.Value("Controls_Right_JoyConController_Button_A")), - ButtonB = ToId(parser.Value("Controls_Right_JoyConController_Button_B")), - ButtonX = ToId(parser.Value("Controls_Right_JoyConController_Button_X")), - ButtonY = ToId(parser.Value("Controls_Right_JoyConController_Button_Y")), - ButtonPlus = ToId(parser.Value("Controls_Right_JoyConController_Button_Plus")), - ButtonR = ToId(parser.Value("Controls_Right_JoyConController_Button_R")), - ButtonZr = ToId(parser.Value("Controls_Right_JoyConController_Button_ZR")) - }); - } - - private static ControllerInputId ToId(string key) - { - switch (key.ToUpper()) - { - case "LSTICK": return ControllerInputId.LStick; - case "DPADUP": return ControllerInputId.DPadUp; - case "DPADDOWN": return ControllerInputId.DPadDown; - case "DPADLEFT": return ControllerInputId.DPadLeft; - case "DPADRIGHT": return ControllerInputId.DPadRight; - case "BACK": return ControllerInputId.Back; - case "LSHOULDER": return ControllerInputId.LShoulder; - case "LTRIGGER": return ControllerInputId.LTrigger; - - case "RSTICK": return ControllerInputId.RStick; - case "A": return ControllerInputId.A; - case "B": return ControllerInputId.B; - case "X": return ControllerInputId.X; - case "Y": return ControllerInputId.Y; - case "START": return ControllerInputId.Start; - case "RSHOULDER": return ControllerInputId.RShoulder; - case "RTRIGGER": return ControllerInputId.RTrigger; - - case "LJOYSTICK": return ControllerInputId.LJoystick; - case "RJOYSTICK": return ControllerInputId.RJoystick; - - default: return ControllerInputId.Invalid; - } - } - } - - //https://stackoverflow.com/a/37772571 - public class IniParser - { - private readonly Dictionary _values; - - public IniParser(string path) - { - _values = File.ReadLines(path) - .Where(line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith('#')) - .Select(line => line.Split('=', 2)) - .ToDictionary(parts => parts[0].Trim(), parts => parts.Length > 1 ? parts[1].Trim() : null); - } - - public string Value(string name) - { - return _values.TryGetValue(name, out string value) ? value : null; - } - } -} \ No newline at end of file diff --git a/Ryujinx/Config.jsonc b/Ryujinx/Config.jsonc new file mode 100644 index 000000000..1ba601647 --- /dev/null +++ b/Ryujinx/Config.jsonc @@ -0,0 +1,124 @@ +{ + "$schema": "./_schema.json", + + // Dump shaders in local directory (e.g. `C:\ShaderDumps`) + "graphics_shaders_dump_path": "", + + // Enable print debug logs + "logging_enable_debug": false, + + // Enable print stubbed calls logs + "logging_enable_stub": true, + + // Enable print informations logs + "logging_enable_info": true, + + // Enable print warning logs + "logging_enable_warn": true, + + // Enable print error logs + "logging_enable_error": true, + + // Filtered log classes, in a JSON array, eg. `[ "Loader", "ServiceFs" ]` + "logging_filtered_classes": [ ], + + // Enable file logging + "enable_file_log": true, + + // Change System Language + // System Language list: https://gist.github.com/HorrorTroll/b6e4a88d774c3c9b3bdf54d79a7ca43b + "system_language": "AmericanEnglish", + + // Enable or Disable Docked Mode + "docked_mode": false, + + // Enable or Disable Game Vsync + "enable_vsync": true, + + // Enable or Disable Multi-core scheduling of threads + "enable_multicore_scheduling": false, + + // Enable integrity checks on Switch content files + "enable_fs_integrity_checks": true, + + // The primary controller's type + // Supported Values: Handheld, ProController, NpadPair, NpadLeft, NpadRight + "controller_type": "Handheld", + + // Keyboard Controls + // https://github.com/opentk/opentk/blob/master/src/OpenTK/Input/Key.cs + "keyboard_controls": { + // Left JoyCon Keyboard Bindings + "left_joycon": { + "stick_up": "W", + "stick_down": "S", + "stick_left": "A", + "stick_right": "D", + "stick_button": "F", + "dpad_up": "Up", + "dpad_down": "Down", + "dpad_left": "Left", + "dpad_right": "Right", + "button_minus": "Minus", + "button_l": "E", + "button_zl": "Q" + }, + + // Right JoyCon Keyboard Bindings + "right_joycon": { + "stick_up": "I", + "stick_down": "K", + "stick_left": "J", + "stick_right": "L", + "stick_button": "H", + "button_a": "Z", + "button_b": "X", + "button_x": "C", + "button_y": "V", + "button_plus": "Plus", + "button_r": "U", + "button_zr": "O" + } + }, + + // Controller Controls + "gamepad_controls": { + // Whether or not to enable Controller support + "enabled": true, + + // Controller Device Index + "index": 0, + + // Controller Analog Stick Deadzone + "deadzone": 0.05, + + // The value of how pressed down each trigger has to be in order to register a button press + "trigger_threshold": 0.5, + + // Left JoyCon Controller Bindings + "left_joycon": { + "stick": "LJoystick", + "stick_button": "LStick", + "dpad_up": "DPadUp", + "dpad_down": "DPadDown", + "dpad_left": "DPadLeft", + "dpad_right": "DPadRight", + "button_minus": "Back", + "button_l": "LShoulder", + "button_zl": "LTrigger" + }, + + // Right JoyCon Controller Bindings + "right_joycon": { + "stick": "RJoystick", + "stick_button": "RStick", + "button_a": "B", + "button_b": "A", + "button_x": "Y", + "button_y": "X", + "button_plus": "Start", + "button_r": "RShoulder", + "button_zr": "RTrigger" + } + } +} \ No newline at end of file diff --git a/Ryujinx/Configuration.cs b/Ryujinx/Configuration.cs new file mode 100644 index 000000000..5f1f86789 --- /dev/null +++ b/Ryujinx/Configuration.cs @@ -0,0 +1,239 @@ +using LibHac.IO; +using OpenTK.Input; +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using Ryujinx.HLE; +using Ryujinx.HLE.HOS.SystemState; +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; } + + /// + /// 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 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; } + + /// + /// The primary controller's type + /// + public HidControllerType ControllerType { get; private set; } + + /// + /// Keyboard control bindings + /// + public NpadKeyboard KeyboardControls { get; private set; } + + /// + /// Controller control bindings + /// + public NpadController GamepadControls { 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("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); + + 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.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; + + if(Instance.GamepadControls.Enabled) + { + if (GamePad.GetName(Instance.GamepadControls.Index) == "Unmapped Controller") + { + Instance.GamepadControls.SetEnabled(false); + } + } + + device.Hid.InitilizePrimaryController(Instance.ControllerType); + } + + 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); + } + + var enumName = formatterResolver.GetFormatterWithVerify() + .Deserialize(ref reader, formatterResolver); + + if(Enum.TryParse(enumName, out T result)) + { + return result; + } + + return default(T); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index 335aa0ea8..19916fe9f 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -20,9 +20,8 @@ namespace Ryujinx Switch device = new Switch(renderer, audioOut); - Config.Read(device); - - Logger.Updated += Log.LogMessage; + Configuration.Load("Config.jsonc"); + Configuration.Configure(device); AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; @@ -40,13 +39,13 @@ namespace Ryujinx if (romFsFiles.Length > 0) { - Console.WriteLine("Loading as cart with RomFS."); + Logger.PrintInfo(LogClass.Application, "Loading as cart with RomFS."); device.LoadCart(args[0], romFsFiles[0]); } else { - Console.WriteLine("Loading as cart WITHOUT RomFS."); + Logger.PrintInfo(LogClass.Application, "Loading as cart WITHOUT RomFS."); device.LoadCart(args[0]); } @@ -56,20 +55,20 @@ namespace Ryujinx switch (Path.GetExtension(args[0]).ToLowerInvariant()) { case ".xci": - Console.WriteLine("Loading as XCI."); + Logger.PrintInfo(LogClass.Application, "Loading as XCI."); device.LoadXci(args[0]); break; case ".nca": - Console.WriteLine("Loading as NCA."); + Logger.PrintInfo(LogClass.Application, "Loading as NCA."); device.LoadNca(args[0]); break; case ".nsp": case ".pfs0": - Console.WriteLine("Loading as NSP."); + Logger.PrintInfo(LogClass.Application, "Loading as NSP."); device.LoadNsp(args[0]); break; default: - Console.WriteLine("Loading as homebrew."); + Logger.PrintInfo(LogClass.Application, "Loading as homebrew."); device.LoadProgram(args[0]); break; } @@ -77,7 +76,7 @@ namespace Ryujinx } else { - Console.WriteLine("Please specify the folder with the NSOs/IStorage or a NSO/NRO."); + Logger.PrintInfo(LogClass.Application, "Please specify the folder with the NSOs/IStorage or a NSO/NRO."); } using (GlScreen screen = new GlScreen(device, renderer)) @@ -88,11 +87,13 @@ namespace Ryujinx } audioOut.Dispose(); + + Logger.Shutdown(); } private static void CurrentDomain_ProcessExit(object sender, EventArgs e) { - Log.Close(); + Logger.Shutdown(); } private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) @@ -103,7 +104,7 @@ namespace Ryujinx if (e.IsTerminating) { - Log.Close(); + Logger.Shutdown(); } } diff --git a/Ryujinx/Ryujinx.conf b/Ryujinx/Ryujinx.conf deleted file mode 100644 index 604b14ed6..000000000 --- a/Ryujinx/Ryujinx.conf +++ /dev/null @@ -1,107 +0,0 @@ -#Enable cpu memory checks (slow) -Enable_Memory_Checks = false - -#Dump shaders in local directory (e.g. `C:\ShaderDumps`) -Graphics_Shaders_Dump_Path = - -#Enable print debug logs -Logging_Enable_Debug = false - -#Enable print stubbed calls logs -Logging_Enable_Stub = true - -#Enable print informations logs -Logging_Enable_Info = true - -#Enable print warning logs -Logging_Enable_Warn = true - -#Enable print error logs -Logging_Enable_Error = true - -#Filtered log classes, seperated by ", ", eg. `Logging_Filtered_Classes = Loader, ServiceFS` -Logging_Filtered_Classes = - -#Enable file logging -Enable_File_Log = false - -#System Language list: https://gist.github.com/HorrorTroll/b6e4a88d774c3c9b3bdf54d79a7ca43b -#Change System Language -System_Language = AmericanEnglish - -#Enable or Disable Docked Mode -Docked_Mode = false - -#Enable Game Vsync -Enable_Vsync = true - -#Enable or Disable Multi-core scheduling of threads -Enable_MultiCore_Scheduling = false - -#Enable integrity checks on Switch content files -Enable_FS_Integrity_Checks = true - -#Controller Device Index -GamePad_Index = 0 - -#Controller Analog Stick Deadzone -GamePad_Deadzone = 0.05 - -#The value of how pressed down each trigger has to be in order to register a button press -GamePad_Trigger_Threshold = 0.5 - -#Whether or not to enable Controller support -GamePad_Enable = true - -#The primary controller's type. Supported Values: ProController, Handheld, NpadPair, NpadLeft, NpadRight -Controller_Type = Handheld - -#https://github.com/opentk/opentk/blob/develop/src/OpenTK/Input/Key.cs -Controls_Left_JoyConKeyboard_Stick_Up = 105 -Controls_Left_JoyConKeyboard_Stick_Down = 101 -Controls_Left_JoyConKeyboard_Stick_Left = 83 -Controls_Left_JoyConKeyboard_Stick_Right = 86 -Controls_Left_JoyConKeyboard_Stick_Button = 88 -Controls_Left_JoyConKeyboard_DPad_Up = 45 -Controls_Left_JoyConKeyboard_DPad_Down = 46 -Controls_Left_JoyConKeyboard_DPad_Left = 47 -Controls_Left_JoyConKeyboard_DPad_Right = 48 -Controls_Left_JoyConKeyboard_Button_Minus = 120 -Controls_Left_JoyConKeyboard_Button_L = 87 -Controls_Left_JoyConKeyboard_Button_ZL = 99 - -Controls_Right_JoyConKeyboard_Stick_Up = 91 -Controls_Right_JoyConKeyboard_Stick_Down = 93 -Controls_Right_JoyConKeyboard_Stick_Left = 92 -Controls_Right_JoyConKeyboard_Stick_Right = 94 -Controls_Right_JoyConKeyboard_Stick_Button = 90 -Controls_Right_JoyConKeyboard_Button_A = 108 -Controls_Right_JoyConKeyboard_Button_B = 106 -Controls_Right_JoyConKeyboard_Button_X = 85 -Controls_Right_JoyConKeyboard_Button_Y = 104 -Controls_Right_JoyConKeyboard_Button_Plus = 121 -Controls_Right_JoyConKeyboard_Button_R = 103 -Controls_Right_JoyConKeyboard_Button_ZR = 97 - -#Controller Controls - -Controls_Left_JoyConController_Stick_Button = LStick -Controls_Left_JoyConController_DPad_Up = DPadUp -Controls_Left_JoyConController_DPad_Down = DPadDown -Controls_Left_JoyConController_DPad_Left = DPadLeft -Controls_Left_JoyConController_DPad_Right = DPadRight -Controls_Left_JoyConController_Button_Minus = Back -Controls_Left_JoyConController_Button_L = LShoulder -Controls_Left_JoyConController_Button_ZL = LTrigger - -Controls_Right_JoyConController_Stick_Button = RStick -Controls_Right_JoyConController_Button_A = B -Controls_Right_JoyConController_Button_B = A -Controls_Right_JoyConController_Button_X = Y -Controls_Right_JoyConController_Button_Y = X -Controls_Right_JoyConController_Button_Plus = Start -Controls_Right_JoyConController_Button_R = RShoulder -Controls_Right_JoyConController_Button_ZR = RTrigger - -Controls_Left_JoyConController_Stick = LJoystick -Controls_Right_JoyConController_Stick = RJoystick diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index 1789ef2e9..087258464 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -20,7 +20,7 @@ - + PreserveNewest diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index e3a6d299b..c7aaea239 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -142,24 +142,24 @@ namespace Ryujinx { KeyboardState keyboard = _keyboard.Value; - currentButton = Config.NpadKeyboard.GetButtons(keyboard); + currentButton = Configuration.Instance.KeyboardControls.GetButtons(keyboard); - (leftJoystickDx, leftJoystickDy) = Config.NpadKeyboard.GetLeftStick(keyboard); + (leftJoystickDx, leftJoystickDy) = Configuration.Instance.KeyboardControls.GetLeftStick(keyboard); - (rightJoystickDx, rightJoystickDy) = Config.NpadKeyboard.GetRightStick(keyboard); + (rightJoystickDx, rightJoystickDy) = Configuration.Instance.KeyboardControls.GetRightStick(keyboard); } - currentButton |= Config.NpadController.GetButtons(); + currentButton |= Configuration.Instance.GamepadControls.GetButtons(); //Keyboard has priority stick-wise if (leftJoystickDx == 0 && leftJoystickDy == 0) { - (leftJoystickDx, leftJoystickDy) = Config.NpadController.GetLeftStick(); + (leftJoystickDx, leftJoystickDy) = Configuration.Instance.GamepadControls.GetLeftStick(); } if (rightJoystickDx == 0 && rightJoystickDy == 0) { - (rightJoystickDx, rightJoystickDy) = Config.NpadController.GetRightStick(); + (rightJoystickDx, rightJoystickDy) = Configuration.Instance.GamepadControls.GetRightStick(); } leftJoystick = new HidJoystickPosition diff --git a/Ryujinx/Ui/Log.cs b/Ryujinx/Ui/Log.cs deleted file mode 100644 index 5daae1402..000000000 --- a/Ryujinx/Ui/Log.cs +++ /dev/null @@ -1,140 +0,0 @@ -using Ryujinx.Common.Logging; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Text; -using System.Threading; - -namespace Ryujinx -{ - static class Log - { - private static readonly string _path; - - private static StreamWriter _logWriter; - - private static Thread _messageThread; - - private static BlockingCollection _messageQueue; - - private static Dictionary _logColors; - - static Log() - { - _logColors = new Dictionary() - { - { LogLevel.Stub, ConsoleColor.DarkGray }, - { LogLevel.Info, ConsoleColor.White }, - { LogLevel.Warning, ConsoleColor.Yellow }, - { LogLevel.Error, ConsoleColor.Red } - }; - - _messageQueue = new BlockingCollection(10); - - _messageThread = new Thread(() => - { - while (!_messageQueue.IsCompleted) - { - try - { - PrintLog(_messageQueue.Take()); - } - catch (InvalidOperationException) - { - // IOE means that Take() was called on a completed collection. - // Some other thread can call CompleteAdding after we pass the - // IsCompleted check but before we call Take. - // We can simply catch the exception since the loop will break - // on the next iteration. - } - } - }); - - _path = Path.Combine(Environment.CurrentDirectory, "Ryujinx.log"); - - if (Logger.EnableFileLog) - { - _logWriter = new StreamWriter(File.Open(_path,FileMode.Create, FileAccess.Write)); - } - - _messageThread.IsBackground = true; - _messageThread.Start(); - } - - private static void PrintLog(LogEventArgs e) - { - StringBuilder sb = new StringBuilder(); - - sb.AppendFormat(@"{0:hh\:mm\:ss\.fff}", e.Time); - sb.Append(" | "); - sb.AppendFormat("{0:d4}", e.ThreadId); - sb.Append(' '); - sb.Append(e.Message); - - if (e.Data != null) - { - PropertyInfo[] props = e.Data.GetType().GetProperties(); - - sb.Append(' '); - - foreach (var prop in props) - { - sb.Append(prop.Name); - sb.Append(": "); - sb.Append(prop.GetValue(e.Data)); - sb.Append(" - "); - } - - // We remove the final '-' from the string - if (props.Length > 0) - { - sb.Remove(sb.Length - 3, 3); - } - } - - string message = sb.ToString(); - - if (_logColors.TryGetValue(e.Level, out ConsoleColor color)) - { - Console.ForegroundColor = color; - - Console.WriteLine(message); - - Console.ResetColor(); - } - else - { - Console.WriteLine(message); - } - - if (Logger.EnableFileLog) - { - _logWriter.WriteLine(message); - } - } - - public static void LogMessage(object sender, LogEventArgs e) - { - if (!_messageQueue.IsAddingCompleted) - { - _messageQueue.Add(e); - } - } - - public static void Close() - { - _messageQueue.CompleteAdding(); - - _messageThread.Join(); - - if (Logger.EnableFileLog) - { - _logWriter.Flush(); - _logWriter.Close(); - _logWriter.Dispose(); - } - } - } -} diff --git a/Ryujinx/Ui/NpadController.cs b/Ryujinx/Ui/NpadController.cs index 58d2d46a2..1b677c4f8 100644 --- a/Ryujinx/Ui/NpadController.cs +++ b/Ryujinx/Ui/NpadController.cs @@ -8,28 +8,24 @@ namespace Ryujinx.UI.Input public enum ControllerInputId { Invalid, - LStick, + RStick, + LShoulder, + RShoulder, + LTrigger, + RTrigger, + LJoystick, + RJoystick, DPadUp, DPadDown, DPadLeft, DPadRight, + Start, Back, - LShoulder, - - RStick, A, B, X, - Y, - Start, - RShoulder, - - LTrigger, - RTrigger, - - LJoystick, - RJoystick + Y } public struct NpadControllerLeft @@ -60,34 +56,55 @@ namespace Ryujinx.UI.Input public class NpadController { - public bool Enabled { private set; get; } - public int Index { private set; get; } - public float Deadzone { private set; get; } - public float TriggerThreshold { private set; get; } + /// + /// Enables or disables controller support + /// + public bool Enabled { get; private set; } - public NpadControllerLeft Left { private set; get; } - public NpadControllerRight Right { private set; get; } + /// + /// Controller Device Index + /// + public int Index { get; private set; } + + /// + /// Controller Analog Stick Deadzone + /// + public float Deadzone { get; private set; } + + /// + /// Controller Trigger Threshold + /// + public float TriggerThreshold { get; private set; } + + /// + /// Left JoyCon Controller Bindings + /// + public NpadControllerLeft LeftJoycon { get; private set; } + + /// + /// Right JoyCon Controller Bindings + /// + public NpadControllerRight RightJoycon { get; private set; } public NpadController( - bool enabled, - int index, - float deadzone, - float triggerThreshold, - NpadControllerLeft left, - NpadControllerRight right) + bool enabled, + int index, + float deadzone, + float triggerThreshold, + NpadControllerLeft leftJoycon, + NpadControllerRight rightJoycon) { Enabled = enabled; Index = index; Deadzone = deadzone; TriggerThreshold = triggerThreshold; - Left = left; - Right = right; + LeftJoycon = leftJoycon; + RightJoycon = rightJoycon; + } - //Unmapped controllers are problematic, skip them - if (GamePad.GetName(index) == "Unmapped Controller") - { - Enabled = false; - } + public void SetEnabled(bool enabled) + { + Enabled = enabled; } public HidControllerButtons GetButtons() @@ -101,23 +118,23 @@ namespace Ryujinx.UI.Input HidControllerButtons buttons = 0; - if (IsPressed(gpState, Left.DPadUp)) buttons |= HidControllerButtons.DpadUp; - if (IsPressed(gpState, Left.DPadDown)) buttons |= HidControllerButtons.DpadDown; - if (IsPressed(gpState, Left.DPadLeft)) buttons |= HidControllerButtons.DpadLeft; - if (IsPressed(gpState, Left.DPadRight)) buttons |= HidControllerButtons.DPadRight; - if (IsPressed(gpState, Left.StickButton)) buttons |= HidControllerButtons.StickLeft; - if (IsPressed(gpState, Left.ButtonMinus)) buttons |= HidControllerButtons.Minus; - if (IsPressed(gpState, Left.ButtonL)) buttons |= HidControllerButtons.L; - if (IsPressed(gpState, Left.ButtonZl)) buttons |= HidControllerButtons.Zl; + if (IsPressed(gpState, LeftJoycon.DPadUp)) buttons |= HidControllerButtons.DpadUp; + if (IsPressed(gpState, LeftJoycon.DPadDown)) buttons |= HidControllerButtons.DpadDown; + if (IsPressed(gpState, LeftJoycon.DPadLeft)) buttons |= HidControllerButtons.DpadLeft; + if (IsPressed(gpState, LeftJoycon.DPadRight)) buttons |= HidControllerButtons.DPadRight; + if (IsPressed(gpState, LeftJoycon.StickButton)) buttons |= HidControllerButtons.StickLeft; + if (IsPressed(gpState, LeftJoycon.ButtonMinus)) buttons |= HidControllerButtons.Minus; + if (IsPressed(gpState, LeftJoycon.ButtonL)) buttons |= HidControllerButtons.L; + if (IsPressed(gpState, LeftJoycon.ButtonZl)) buttons |= HidControllerButtons.Zl; - if (IsPressed(gpState, Right.ButtonA)) buttons |= HidControllerButtons.A; - if (IsPressed(gpState, Right.ButtonB)) buttons |= HidControllerButtons.B; - if (IsPressed(gpState, Right.ButtonX)) buttons |= HidControllerButtons.X; - if (IsPressed(gpState, Right.ButtonY)) buttons |= HidControllerButtons.Y; - if (IsPressed(gpState, Right.StickButton)) buttons |= HidControllerButtons.StickRight; - if (IsPressed(gpState, Right.ButtonPlus)) buttons |= HidControllerButtons.Plus; - if (IsPressed(gpState, Right.ButtonR)) buttons |= HidControllerButtons.R; - if (IsPressed(gpState, Right.ButtonZr)) buttons |= HidControllerButtons.Zr; + if (IsPressed(gpState, RightJoycon.ButtonA)) buttons |= HidControllerButtons.A; + if (IsPressed(gpState, RightJoycon.ButtonB)) buttons |= HidControllerButtons.B; + if (IsPressed(gpState, RightJoycon.ButtonX)) buttons |= HidControllerButtons.X; + if (IsPressed(gpState, RightJoycon.ButtonY)) buttons |= HidControllerButtons.Y; + if (IsPressed(gpState, RightJoycon.StickButton)) buttons |= HidControllerButtons.StickRight; + if (IsPressed(gpState, RightJoycon.ButtonPlus)) buttons |= HidControllerButtons.Plus; + if (IsPressed(gpState, RightJoycon.ButtonR)) buttons |= HidControllerButtons.R; + if (IsPressed(gpState, RightJoycon.ButtonZr)) buttons |= HidControllerButtons.Zr; return buttons; } @@ -129,7 +146,7 @@ namespace Ryujinx.UI.Input return (0, 0); } - return GetStick(Left.Stick); + return GetStick(LeftJoycon.Stick); } public (short, short) GetRightStick() @@ -139,7 +156,7 @@ namespace Ryujinx.UI.Input return (0, 0); } - return GetStick(Right.Stick); + return GetStick(RightJoycon.Stick); } private (short, short) GetStick(ControllerInputId joystick) diff --git a/Ryujinx/Ui/NpadKeyboard.cs b/Ryujinx/Ui/NpadKeyboard.cs index 704c61abc..1604da5bd 100644 --- a/Ryujinx/Ui/NpadKeyboard.cs +++ b/Ryujinx/Ui/NpadKeyboard.cs @@ -5,70 +5,69 @@ namespace Ryujinx.UI.Input { public struct NpadKeyboardLeft { - public int StickUp; - public int StickDown; - public int StickLeft; - public int StickRight; - public int StickButton; - public int DPadUp; - public int DPadDown; - public int DPadLeft; - public int DPadRight; - public int ButtonMinus; - public int ButtonL; - public int ButtonZl; + 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 int StickUp; - public int StickDown; - public int StickLeft; - public int StickRight; - public int StickButton; - public int ButtonA; - public int ButtonB; - public int ButtonX; - public int ButtonY; - public int ButtonPlus; - public int ButtonR; - public int ButtonZr; + 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 class NpadKeyboard { - public NpadKeyboardLeft Left; - public NpadKeyboardRight Right; + /// + /// Left JoyCon Keyboard Bindings + /// + public NpadKeyboardLeft LeftJoycon { get; private set; } - public NpadKeyboard( - NpadKeyboardLeft left, - NpadKeyboardRight right) - { - Left = left; - Right = right; - } + /// + /// Right JoyCon Keyboard Bindings + /// + public NpadKeyboardRight RightJoycon { get; private set; } public HidControllerButtons GetButtons(KeyboardState keyboard) { HidControllerButtons buttons = 0; - if (keyboard[(Key)Left.StickButton]) buttons |= HidControllerButtons.StickLeft; - if (keyboard[(Key)Left.DPadUp]) buttons |= HidControllerButtons.DpadUp; - if (keyboard[(Key)Left.DPadDown]) buttons |= HidControllerButtons.DpadDown; - if (keyboard[(Key)Left.DPadLeft]) buttons |= HidControllerButtons.DpadLeft; - if (keyboard[(Key)Left.DPadRight]) buttons |= HidControllerButtons.DPadRight; - if (keyboard[(Key)Left.ButtonMinus]) buttons |= HidControllerButtons.Minus; - if (keyboard[(Key)Left.ButtonL]) buttons |= HidControllerButtons.L; - if (keyboard[(Key)Left.ButtonZl]) buttons |= HidControllerButtons.Zl; + if (keyboard[(Key)LeftJoycon.StickButton]) buttons |= HidControllerButtons.StickLeft; + if (keyboard[(Key)LeftJoycon.DPadUp]) buttons |= HidControllerButtons.DpadUp; + if (keyboard[(Key)LeftJoycon.DPadDown]) buttons |= HidControllerButtons.DpadDown; + if (keyboard[(Key)LeftJoycon.DPadLeft]) buttons |= HidControllerButtons.DpadLeft; + if (keyboard[(Key)LeftJoycon.DPadRight]) buttons |= HidControllerButtons.DPadRight; + if (keyboard[(Key)LeftJoycon.ButtonMinus]) buttons |= HidControllerButtons.Minus; + if (keyboard[(Key)LeftJoycon.ButtonL]) buttons |= HidControllerButtons.L; + if (keyboard[(Key)LeftJoycon.ButtonZl]) buttons |= HidControllerButtons.Zl; - if (keyboard[(Key)Right.StickButton]) buttons |= HidControllerButtons.StickRight; - if (keyboard[(Key)Right.ButtonA]) buttons |= HidControllerButtons.A; - if (keyboard[(Key)Right.ButtonB]) buttons |= HidControllerButtons.B; - if (keyboard[(Key)Right.ButtonX]) buttons |= HidControllerButtons.X; - if (keyboard[(Key)Right.ButtonY]) buttons |= HidControllerButtons.Y; - if (keyboard[(Key)Right.ButtonPlus]) buttons |= HidControllerButtons.Plus; - if (keyboard[(Key)Right.ButtonR]) buttons |= HidControllerButtons.R; - if (keyboard[(Key)Right.ButtonZr]) buttons |= HidControllerButtons.Zr; + if (keyboard[(Key)RightJoycon.StickButton]) buttons |= HidControllerButtons.StickRight; + if (keyboard[(Key)RightJoycon.ButtonA]) buttons |= HidControllerButtons.A; + if (keyboard[(Key)RightJoycon.ButtonB]) buttons |= HidControllerButtons.B; + if (keyboard[(Key)RightJoycon.ButtonX]) buttons |= HidControllerButtons.X; + if (keyboard[(Key)RightJoycon.ButtonY]) buttons |= HidControllerButtons.Y; + if (keyboard[(Key)RightJoycon.ButtonPlus]) buttons |= HidControllerButtons.Plus; + if (keyboard[(Key)RightJoycon.ButtonR]) buttons |= HidControllerButtons.R; + if (keyboard[(Key)RightJoycon.ButtonZr]) buttons |= HidControllerButtons.Zr; return buttons; } @@ -78,10 +77,10 @@ namespace Ryujinx.UI.Input short dx = 0; short dy = 0; - if (keyboard[(Key)Left.StickUp]) dy = short.MaxValue; - if (keyboard[(Key)Left.StickDown]) dy = -short.MaxValue; - if (keyboard[(Key)Left.StickLeft]) dx = -short.MaxValue; - if (keyboard[(Key)Left.StickRight]) dx = short.MaxValue; + 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; return (dx, dy); } @@ -91,10 +90,10 @@ namespace Ryujinx.UI.Input short dx = 0; short dy = 0; - if (keyboard[(Key)Right.StickUp]) dy = short.MaxValue; - if (keyboard[(Key)Right.StickDown]) dy = -short.MaxValue; - if (keyboard[(Key)Right.StickLeft]) dx = -short.MaxValue; - if (keyboard[(Key)Right.StickRight]) dx = short.MaxValue; + 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; return (dx, dy); } diff --git a/Ryujinx/_schema.json b/Ryujinx/_schema.json new file mode 100644 index 000000000..28f351118 --- /dev/null +++ b/Ryujinx/_schema.json @@ -0,0 +1,823 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://ryujinx.org/_schema/config.json", + "type": "object", + "title": "Ryujinx Configuration Schema", + "required": [ + "graphics_shaders_dump_path", + "logging_enable_debug", + "logging_enable_stub", + "logging_enable_info", + "logging_enable_warn", + "logging_enable_error", + "logging_filtered_classes", + "enable_file_log", + "system_language", + "docked_mode", + "enable_vsync", + "enable_multicore_scheduling", + "enable_fs_integrity_checks", + "controller_type", + "keyboard_controls", + "gamepad_controls" + ], + "definitions": { + "key": { + "type": "string", + "enum": [ + "ShiftLeft", + "LShift", + "ShiftRight", + "RShift", + "ControlLeft", + "LControl", + "ControlRight", + "RControl", + "AltLeft", + "LAlt", + "AltRight", + "RAlt", + "WinLeft", + "LWin", + "WinRight", + "RWin", + "Menu", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12", + "F13", + "F14", + "F15", + "F16", + "F17", + "F18", + "F19", + "F20", + "F21", + "F22", + "F23", + "F24", + "F25", + "F26", + "F27", + "F28", + "F29", + "F30", + "F31", + "F32", + "F33", + "F34", + "F35", + "Up", + "Down", + "Left", + "Right", + "Enter", + "Escape", + "Space", + "Tab", + "BackSpace", + "Back", + "Insert", + "Delete", + "PageUp", + "PageDown", + "Home", + "End", + "CapsLock", + "ScrollLock", + "PrintScreen", + "Pause", + "NumLock", + "Clear", + "Sleep", + "Keypad0", + "Keypad1", + "Keypad2", + "Keypad3", + "Keypad4", + "Keypad5", + "Keypad6", + "Keypad7", + "Keypad8", + "Keypad9", + "KeypadDivide", + "KeypadMultiply", + "KeypadSubtract", + "KeypadMinus", + "KeypadAdd", + "KeypadPlus", + "KeypadDecimal", + "KeypadPeriod", + "KeypadEnter", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "Number0", + "Number1", + "Number2", + "Number3", + "Number4", + "Number5", + "Number6", + "Number7", + "Number8", + "Number9", + "Tilde", + "Grave", + "Minus", + "Plus", + "BracketLeft", + "LBracket", + "BracketRight", + "RBracket", + "Semicolon", + "Quote", + "Comma", + "Period", + "Slash", + "BackSlash", + "NonUSBackSlash", + "LastKey" + ] + }, + "input": { + "type": "string", + "enum": [ + "DPadUp", + "DPadDown", + "DPadLeft", + "DPadRight", + "LStick", + "RStick", + "LShoulder", + "RShoulder", + "LTrigger", + "RTrigger", + "LJoystick", + "RJoystick", + "A", + "B", + "X", + "Y", + "Start", + "Back" + ] + } + }, + "properties": { + "graphics_shaders_dump_path": { + "$id": "#/properties/graphics_shaders_dump_path", + "type": "string", + "title": "Graphics Shaders Dump Path", + "description": "Dumps shaders in this local directory", + "default": "", + "examples": [ + "C:\\ShaderDumps" + ] + }, + "logging_enable_debug": { + "$id": "#/properties/logging_enable_debug", + "type": "boolean", + "title": "Logging Enable Debug", + "description": "Enables printing debug log messages", + "default": false, + "examples": [ + true, + false + ] + }, + "logging_enable_stub": { + "$id": "#/properties/logging_enable_stub", + "type": "boolean", + "title": "Logging Enable Stub", + "description": "Enables printing stub log messages", + "default": true, + "examples": [ + true, + false + ] + }, + "logging_enable_info": { + "$id": "#/properties/logging_enable_info", + "type": "boolean", + "title": "Logging Enable Info", + "description": "Enables printing info log messages", + "default": true, + "examples": [ + true, + false + ] + }, + "logging_enable_warn": { + "$id": "#/properties/logging_enable_warn", + "type": "boolean", + "title": "Logging Enable Warn", + "description": "Enables printing warning log messages", + "default": true, + "examples": [ + true, + false + ] + }, + "logging_enable_error": { + "$id": "#/properties/logging_enable_error", + "type": "boolean", + "title": "Logging Enable Error", + "description": "Enables printing error log messages", + "default": true, + "examples": [ + true, + false + ] + }, + "logging_filtered_classes": { + "$id": "#/properties/logging_filtered_classes", + "type": "array", + "title": "Logging Filtered Classes", + "description": "Controls which log messages are written to the log targets", + "items": { + "type": "string", + "enum": [ + "Application", + "Audio", + "Cpu", + "Font", + "Emulation", + "Gpu", + "Hid", + "Kernel", + "KernelIpc", + "KernelScheduler", + "KernelSvc", + "Loader", + "Service", + "ServiceAcc", + "ServiceAm", + "ServiceApm", + "ServiceAudio", + "ServiceBsd", + "ServiceCaps", + "ServiceFriend", + "ServiceFs", + "ServiceHid", + "ServiceIrs", + "ServiceLdr", + "ServiceLm", + "ServiceMm", + "ServiceNfp", + "ServiceNifm", + "ServiceNs", + "ServiceNv", + "ServicePctl", + "ServicePl", + "ServicePrepo", + "ServicePsm", + "ServiceSet", + "ServiceSfdnsres", + "ServiceSm", + "ServiceSsl", + "ServiceSss", + "ServiceTime", + "ServiceVi" + ] + } + }, + "enable_file_log": { + "$id": "#/properties/enable_file_log", + "type": "boolean", + "title": "Enable File Log", + "description": "Enables logging to a file on disk", + "default": true, + "examples": [ + true, + false + ] + }, + "system_language": { + "$id": "#/properties/system_language", + "type": "string", + "title": "System Language", + "description": "Change System Language", + "default": "AmericanEnglish", + "enum": [ + "Japanese", + "AmericanEnglish", + "French", + "German", + "Italian", + "Spanish", + "Chinese", + "Korean", + "Dutch", + "Portuguese", + "Russian", + "Taiwanese", + "BritishEnglish", + "CanadianFrench", + "LatinAmericanSpanish", + "SimplifiedChinese", + "TraditionalChinese" + ], + "examples": [ + "AmericanEnglish" + ] + }, + "docked_mode": { + "$id": "#/properties/docked_mode", + "type": "boolean", + "title": "Enable Docked Mode", + "description": "Enables or disables Docked Mode", + "default": false, + "examples": [ + true, + false + ] + }, + "enable_vsync": { + "$id": "#/properties/enable_vsync", + "type": "boolean", + "title": "Enable Vertical Sync", + "description": "Enables or disables Vertical Sync", + "default": true, + "examples": [ + true, + false + ] + }, + "enable_multicore_scheduling": { + "$id": "#/properties/enable_multicore_scheduling", + "type": "boolean", + "title": "Enable Multicore Scheduling", + "description": "Enables or disables multi-core scheduling of threads", + "default": false, + "examples": [ + true, + false + ] + }, + "enable_fs_integrity_checks": { + "$id": "#/properties/enable_fs_integrity_checks", + "type": "boolean", + "title": "Enable Filesystem Integrity Checks", + "description": "Enables integrity checks on Game content files. Only applies to ROMs loaded as XCI files", + "default": true, + "examples": [ + true, + false + ] + }, + "controller_type": { + "$id": "#/properties/controller_type", + "type": "string", + "title": "Controller Type", + "default": "Handheld", + "enum": [ + "Handheld", + "ProController", + "NpadPair", + "NpadLeft", + "NpadRight" + ], + "examples": [ + "Handheld", + "ProController", + "NpadPair", + "NpadLeft", + "NpadRight" + ] + }, + "keyboard_controls": { + "$id": "#/properties/keyboard_controls", + "type": "object", + "title": "Keyboard Controls", + "required": [ + "left_joycon", + "right_joycon" + ], + "properties": { + "left_joycon": { + "$id": "#/properties/keyboard_controls/properties/left_joycon", + "type": "object", + "title": "Left JoyCon Controls", + "required": [ + "stick_up", + "stick_down", + "stick_left", + "stick_right", + "stick_button", + "dpad_up", + "dpad_down", + "dpad_left", + "dpad_right", + "button_minus", + "button_l", + "button_zl" + ], + "properties": { + "stick_up": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/stick_up", + "$ref": "#/definitions/key", + "title": "Stick Up", + "default": "w" + }, + "stick_down": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/stick_down", + "$ref": "#/definitions/key", + "title": "Stick Down", + "default": "S" + }, + "stick_left": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/stick_left", + "$ref": "#/definitions/key", + "title": "Stick Left", + "default": "A" + }, + "stick_right": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/stick_right", + "$ref": "#/definitions/key", + "title": "Stick Right", + "default": "D" + }, + "stick_button": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/stick_button", + "$ref": "#/definitions/key", + "title": "Stick Button", + "default": "F" + }, + "dpad_up": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/dpad_up", + "$ref": "#/definitions/key", + "title": "Dpad Up", + "default": "Up" + }, + "dpad_down": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/dpad_down", + "$ref": "#/definitions/key", + "title": "Dpad Down", + "default": "Down" + }, + "dpad_left": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/dpad_left", + "$ref": "#/definitions/key", + "title": "Dpad Left", + "default": "Left" + }, + "dpad_right": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/dpad_right", + "$ref": "#/definitions/key", + "title": "Dpad Right", + "default": "Right" + }, + "button_minus": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/button_minus", + "$ref": "#/definitions/key", + "title": "Button Minus", + "default": "Minus" + }, + "button_l": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/button_l", + "$ref": "#/definitions/key", + "title": "Button L", + "default": "E" + }, + "button_zl": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/button_zl", + "$ref": "#/definitions/key", + "title": "Button ZL", + "default": "Q" + } + } + }, + "right_joycon": { + "$id": "#/properties/keyboard_controls/properties/right_joycon", + "type": "object", + "title": "Right JoyCon Controls", + "required": [ + "stick_up", + "stick_down", + "stick_left", + "stick_right", + "stick_button", + "button_a", + "button_b", + "button_x", + "button_y", + "button_plus", + "button_r", + "button_zr" + ], + "properties": { + "stick_up": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/stick_up", + "$ref": "#/definitions/key", + "title": "Stick Up", + "default": "I" + }, + "stick_down": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/stick_down", + "$ref": "#/definitions/key", + "title": "Stick Down", + "default": "K" + }, + "stick_left": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/stick_left", + "$ref": "#/definitions/key", + "title": "Stick Left", + "default": "J" + }, + "stick_right": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/stick_right", + "$ref": "#/definitions/key", + "title": "Stick Right", + "default": "L" + }, + "stick_button": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/stick_button", + "$ref": "#/definitions/key", + "title": "Stick Button", + "default": "H" + }, + "button_a": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/button_a", + "$ref": "#/definitions/key", + "title": "Button A", + "default": "Z" + }, + "button_b": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/button_b", + "$ref": "#/definitions/key", + "title": "Button B", + "default": "X" + }, + "button_x": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/button_x", + "$ref": "#/definitions/key", + "title": "Button X", + "default": "C" + }, + "button_y": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/button_y", + "$ref": "#/definitions/key", + "title": "Button Y", + "default": "V" + }, + "button_plus": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/button_plus", + "$ref": "#/definitions/key", + "title": "Button Plus", + "default": "Plus" + }, + "button_r": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/button_r", + "$ref": "#/definitions/key", + "title": "Button R", + "default": "U" + }, + "button_zr": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/button_zr", + "$ref": "#/definitions/key", + "title": "Button Zr", + "default": "O" + } + } + } + } + }, + "gamepad_controls": { + "$id": "#/properties/gamepad_controls", + "type": "object", + "title": "GamePad Controls", + "required": [ + "left_joycon", + "right_joycon" + ], + "properties": { + "enable": { + "$id": "#/properties/gamepad_controls/properties/enable", + "type": "boolean", + "title": "Gamepad Enable", + "description": "Enables or disables controller support", + "default": true, + "examples": [ + true, + false + ] + }, + "index": { + "$id": "#/properties/gamepad_controls/properties/index", + "type": "integer", + "title": "Gamepad Index", + "description": "Controller Device Index", + "default": 0, + "minimum": 0, + "examples": [ + 0, + 1, + 2 + ] + }, + "deadzone": { + "$id": "#/properties/gamepad_controls/properties/deadzone", + "type": "number", + "title": "Gamepad Deadzone", + "description": "Controller Analog Stick Deadzone", + "default": 0.05, + "minimum": -32768.0, + "maximum": 32767.0, + "examples": [ + 0.05 + ] + }, + "trigger_threshold": { + "$id": "#/properties/gamepad_controls/properties/trigger_threshold", + "type": "number", + "title": "Controller Trigger Threshold", + "description": "The value of how pressed down each trigger has to be in order to register a button press", + "default": 0.5, + "minimum": 0.0, + "maximum": 1.0, + "examples": [ + 0.5 + ] + }, + "left_joycon": { + "$id": "#/properties/gamepad_controls/properties/left_joycon", + "type": "object", + "title": "Left JoyCon Controls", + "required": [ + "stick", + "stick_button", + "dpad_up", + "dpad_down", + "dpad_left", + "dpad_right", + "button_minus", + "button_l", + "button_zl" + ], + "properties": { + "stick": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/stick", + "$ref": "#/definitions/input", + "title": "Stick", + "default": "LJoystick" + }, + "stick_button": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/stick_button", + "$ref": "#/definitions/input", + "title": "Stick Button", + "default": "LStick" + }, + "dpad_up": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/dpad_up", + "$ref": "#/definitions/input", + "title": "Dpad Up", + "default": "DPadUp" + }, + "dpad_down": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/dpad_down", + "$ref": "#/definitions/input", + "title": "Dpad Down", + "default": "DPadDown" + }, + "dpad_left": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/dpad_left", + "$ref": "#/definitions/input", + "title": "Dpad Left", + "default": "DPadLeft" + }, + "dpad_right": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/dpad_right", + "$ref": "#/definitions/input", + "title": "Dpad Right", + "default": "DPadRight" + }, + "button_minus": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/button_minus", + "$ref": "#/definitions/input", + "title": "Button Minus", + "default": "Back" + }, + "button_l": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/button_l", + "$ref": "#/definitions/input", + "title": "Button L", + "default": "LShoulder" + }, + "button_zl": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/button_zl", + "$ref": "#/definitions/input", + "title": "Button ZL", + "default": "LTrigger" + } + } + }, + "right_joycon": { + "$id": "#/properties/gamepad_controls/properties/right_joycon", + "type": "object", + "title": "Right JoyCon Controls", + "required": [ + "stick", + "stick_button", + "button_a", + "button_b", + "button_x", + "button_y", + "button_plus", + "button_r", + "button_zr" + ], + "properties": { + "stick": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/stick", + "$ref": "#/definitions/input", + "title": "Stick", + "default": "RJoystick" + }, + "stick_button": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/stick_button", + "$ref": "#/definitions/input", + "title": "Stick Button", + "default": "RStick" + }, + "button_a": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/button_a", + "$ref": "#/definitions/input", + "title": "Button A", + "default": "B" + }, + "button_b": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/button_b", + "$ref": "#/definitions/input", + "title": "Button B", + "default": "A" + }, + "button_x": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/button_x", + "$ref": "#/definitions/input", + "title": "Button X", + "default": "Y" + }, + "button_y": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/button_y", + "$ref": "#/definitions/input", + "title": "Button Y", + "default": "X" + }, + "button_plus": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/button_plus", + "$ref": "#/definitions/input", + "title": "Button Plus", + "default": "Start" + }, + "button_r": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/button_r", + "$ref": "#/definitions/input", + "title": "Button R", + "default": "RShoulder" + }, + "button_zr": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/button_zr", + "$ref": "#/definitions/input", + "title": "Button ZR", + "default": "RTrigger" + } + } + } + } + } + } +} \ No newline at end of file