Logger and Configuration Refactoring (#573)

* Logging: Refactor log targets into Ryujinx.Common

* Logger: Implement JSON Log Target

* Logger: Optimize Console/File logging targets

Implement a simple ObjectPool to pool up StringBuilders to avoid causing excessive GCing of gen1/2 items when large amounts of log entries are being generated.

We can also pre-determine the async overflow action at initialization time, allowing for an easy optimization in the message enqueue function, avoiding a number of comparisons.

* Logger: Implement LogFormatters

* Config: Refactor configuration file and loading

* Config: Rename to .jsonc to avoid highlighting issues in VSC and GitHub

* Resolve style nits

* Config: Resolve incorrect default key binding

* Config: Also update key binding default in schema

* Tidy up namespace imports

* Config: Update CONFIG.md to reflect new Config file
This commit is contained in:
jduncanator 2019-02-11 23:00:32 +11:00 committed by gdkchan
parent a694420d11
commit d306115750
31 changed files with 1844 additions and 691 deletions

206
CONFIG.md
View file

@ -1,123 +1,124 @@
## Config File ## 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. Enable the Informations Logging.
- `Logging_Enable_Trace` *(bool)* - `logging_enable_warn` *(bool)*
Enable the Trace Logging (Enabled in Debug recommended). Enable the Warning Logging.
- `Logging_Enable_Debug` *(bool)* - `logging_enable_error` *(bool)*
Enable the Debug Logging (Enabled in Debug recommended). Enable the Error Logging.
- `Logging_Enable_Warn` *(bool)* - `enable_file_log` *(bool)*
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 writing the logging inside a Ryujinx.log file. Enable writing the logging inside a Ryujinx.log file.
- `GamePad_Index` *(int)* - `system_language` *(string)*
The index of the Controller Device. Change System Language, [System Language list](https://gist.github.com/HorrorTroll/b6e4a88d774c3c9b3bdf54d79a7ca43b)
- `GamePad_Deadzone` *(float)* - `docked_mode` *(bool)*
The deadzone of both analog sticks on the Controller. Enable or Disable Docked Mode
- `GamePad_Enable` *(bool)* - `enable_vsync` *(bool)*
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. 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)*
- `Controls_Left_JoyConKeyboard_XX` *(int)* ### Default Mapping
``` #### Controller
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).
- `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)
```
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)
```
- Default Mapping
- Controller
- Left Joycon: - Left Joycon:
- Analog Stick = Left Analog Stick - Analog Stick = Left Analog Stick
- DPad Up = DPad Up - DPad Up = DPad Up
@ -137,7 +138,8 @@
- Plus = Start / Options - Plus = Start / Options
- R = Right Shoulder Button - R = Right Shoulder Button
- ZR = Right Trigger - ZR = Right Trigger
- Keyboard
#### Keyboard
- Left Joycon: - Left Joycon:
- Stick Up = W - Stick Up = W
- Stick Down = S - Stick Down = S
@ -166,7 +168,7 @@
- R = U - R = U
- ZR = O - ZR = O
- Valid Button Mappings ### Valid Button Mappings
- A = The A / Cross Button - A = The A / Cross Button
- B = The B / Circle Button - B = The B / Circle Button
- X = The X / Square Button - X = The X / Square Button

View file

@ -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) - 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). For more information [you can go here](CONFIG.md).
**Help** **Help**

View file

@ -1,14 +1,13 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
namespace Ryujinx.Common namespace Ryujinx.Common
{ {
public static class StructIOExtension public static class BinaryReaderExtensions
{ {
public unsafe static T ReadStruct<T>(this BinaryReader reader) where T : struct public unsafe static T ReadStruct<T>(this BinaryReader reader)
where T : struct
{ {
int size = Marshal.SizeOf<T>(); int size = Marshal.SizeOf<T>();
@ -20,7 +19,8 @@ namespace Ryujinx.Common
} }
} }
public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value) where T : struct public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value)
where T : struct
{ {
long size = Marshal.SizeOf<T>(); long size = Marshal.SizeOf<T>();

View file

@ -0,0 +1,12 @@
using System;
namespace Ryujinx.Common
{
public static class EnumExtensions
{
public static T[] GetValues<T>()
{
return (T[])Enum.GetValues(typeof(T));
}
}
}

View file

@ -0,0 +1,53 @@
using System.Reflection;
using System.Text;
namespace Ryujinx.Common.Logging
{
internal class DefaultLogFormatter : ILogFormatter
{
private static readonly ObjectPool<StringBuilder> _stringBuilderPool = SharedPools.Default<StringBuilder>();
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);
}
}
}
}

View file

@ -0,0 +1,7 @@
namespace Ryujinx.Common.Logging
{
interface ILogFormatter
{
string Format(LogEventArgs args);
}
}

View file

@ -2,6 +2,7 @@ namespace Ryujinx.Common.Logging
{ {
public enum LogClass public enum LogClass
{ {
Application,
Audio, Audio,
Cpu, Cpu,
Font, Font,

View file

@ -1,8 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text;
using System.Threading; using System.Threading;
namespace Ryujinx.Common.Logging namespace Ryujinx.Common.Logging
@ -14,9 +13,9 @@ namespace Ryujinx.Common.Logging
private static readonly bool[] m_EnabledLevels; private static readonly bool[] m_EnabledLevels;
private static readonly bool[] m_EnabledClasses; private static readonly bool[] m_EnabledClasses;
public static event EventHandler<LogEventArgs> Updated; private static readonly List<ILogTarget> m_LogTargets;
public static bool EnableFileLog { get; set; } public static event EventHandler<LogEventArgs> Updated;
static Logger() static Logger()
{ {
@ -33,9 +32,30 @@ namespace Ryujinx.Common.Logging
m_EnabledClasses[index] = true; m_EnabledClasses[index] = true;
} }
m_LogTargets = new List<ILogTarget>();
m_Time = Stopwatch.StartNew(); 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) public static void SetEnable(LogLevel logLevel, bool enabled)
{ {
m_EnabledLevels[(int)logLevel] = enabled; m_EnabledLevels[(int)logLevel] = enabled;

View file

@ -0,0 +1,76 @@
using System;
using System.Collections.Concurrent;
using System.Threading;
namespace Ryujinx.Common.Logging
{
public enum AsyncLogTargetOverflowAction
{
/// <summary>
/// Block until there's more room in the queue
/// </summary>
Block = 0,
/// <summary>
/// Discard the overflowing item
/// </summary>
Discard = 1
}
public class AsyncLogTargetWrapper : ILogTarget
{
private ILogTarget _target;
private Thread _messageThread;
private BlockingCollection<LogEventArgs> _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<LogEventArgs>(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();
}
}
}

View file

@ -0,0 +1,48 @@
using System;
using System.Collections.Concurrent;
namespace Ryujinx.Common.Logging
{
public class ConsoleLogTarget : ILogTarget
{
private static readonly ConcurrentDictionary<LogLevel, ConsoleColor> _logColors;
private readonly ILogFormatter _formatter;
static ConsoleLogTarget()
{
_logColors = new ConcurrentDictionary<LogLevel, ConsoleColor> {
[ 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();
}
}
}

View file

@ -0,0 +1,36 @@
using System.IO;
using System.Text;
namespace Ryujinx.Common.Logging
{
public class FileLogTarget : ILogTarget
{
private static readonly ObjectPool<StringBuilder> _stringBuilderPool = SharedPools.Default<StringBuilder>();
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();
}
}
}

View file

@ -0,0 +1,9 @@
using System;
namespace Ryujinx.Common.Logging
{
public interface ILogTarget : IDisposable
{
void Log(object sender, LogEventArgs args);
}
}

View file

@ -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();
}
}
}
}

View file

@ -0,0 +1,75 @@
using System;
using System.Threading;
namespace Ryujinx.Common
{
public class ObjectPool<T>
where T : class
{
private T _firstItem;
private readonly T[] _items;
private readonly Func<T> _factory;
public ObjectPool(Func<T> 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;
}
}
}
}
}

View file

@ -0,0 +1,17 @@
namespace Ryujinx.Common
{
public static class SharedPools
{
private static class DefaultPool<T>
where T : class, new()
{
public static readonly ObjectPool<T> Instance = new ObjectPool<T>(() => new T(), 20);
}
public static ObjectPool<T> Default<T>()
where T : class, new()
{
return DefaultPool<T>.Instance;
}
}
}

View file

@ -13,4 +13,8 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Utf8Json" Version="1.3.7" />
</ItemGroup>
</Project> </Project>

View file

@ -112,7 +112,6 @@ namespace Ryujinx.HLE
if (disposing) if (disposing)
{ {
System.Dispose(); System.Dispose();
VsyncEvent.Dispose(); VsyncEvent.Dispose();
} }
} }

View file

@ -23,6 +23,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luea", "Ryujinx.LLE\Luea.cs
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Common", "Ryujinx.Common\Ryujinx.Common.csproj", "{5FD4E4F6-8928-4B3C-BE07-28A675C17226}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Common", "Ryujinx.Common\Ryujinx.Common.csproj", "{5FD4E4F6-8928-4B3C-BE07-28A675C17226}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{464D8AB7-B056-4A99-B207-B8DCFB47AAA9}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -73,6 +75,10 @@ Global
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection 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 GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {110169B3-3328-4730-8AB0-BA05BEF75C1A} SolutionGuid = {110169B3-3328-4730-8AB0-BA05BEF75C1A}
EndGlobalSection EndGlobalSection

View file

@ -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<SystemLanguage>(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<HidControllerType>(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<string, string> _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;
}
}
}

124
Ryujinx/Config.jsonc Normal file
View file

@ -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"
}
}
}

239
Ryujinx/Configuration.cs Normal file
View file

@ -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
{
/// <summary>
/// The default configuration instance
/// </summary>
public static Configuration Instance { get; private set; }
/// <summary>
/// Dumps shaders in this local directory
/// </summary>
public string GraphicsShadersDumpPath { get; private set; }
/// <summary>
/// Enables printing debug log messages
/// </summary>
public bool LoggingEnableDebug { get; private set; }
/// <summary>
/// Enables printing stub log messages
/// </summary>
public bool LoggingEnableStub { get; private set; }
/// <summary>
/// Enables printing info log messages
/// </summary>
public bool LoggingEnableInfo { get; private set; }
/// <summary>
/// Enables printing warning log messages
/// </summary>
public bool LoggingEnableWarn { get; private set; }
/// <summary>
/// Enables printing error log messages
/// </summary>
public bool LoggingEnableError { get; private set; }
/// <summary>
/// Controls which log messages are written to the log targets
/// </summary>
public LogClass[] LoggingFilteredClasses { get; private set; }
/// <summary>
/// Enables or disables logging to a file on disk
/// </summary>
public bool EnableFileLog { get; private set; }
/// <summary>
/// Change System Language
/// </summary>
public SystemLanguage SystemLanguage { get; private set; }
/// <summary>
/// Enables or disables Docked Mode
/// </summary>
public bool DockedMode { get; private set; }
/// <summary>
/// Enables or disables Vertical Sync
/// </summary>
public bool EnableVsync { get; private set; }
/// <summary>
/// Enables or disables multi-core scheduling of threads
/// </summary>
public bool EnableMultiCoreScheduling { get; private set; }
/// <summary>
/// Enables integrity checks on Game content files
/// </summary>
public bool EnableFsIntegrityChecks { get; private set; }
/// <summary>
/// The primary controller's type
/// </summary>
public HidControllerType ControllerType { get; private set; }
/// <summary>
/// Keyboard control bindings
/// </summary>
public NpadKeyboard KeyboardControls { get; private set; }
/// <summary>
/// Controller control bindings
/// </summary>
public NpadController GamepadControls { get; private set; }
/// <summary>
/// Loads a configuration file from disk
/// </summary>
/// <param name="path">The path to the JSON configuration file</param>
public static void Load(string path)
{
var resolver = CompositeResolver.Create(
new[] { new ConfigurationEnumFormatter<Key>() },
new[] { StandardResolver.AllowPrivateSnakeCase }
);
using (Stream stream = File.OpenRead(path))
{
Instance = JsonSerializer.Deserialize<Configuration>(stream, resolver);
}
}
/// <summary>
/// Loads a configuration file asynchronously from disk
/// </summary>
/// <param name="path">The path to the JSON configuration file</param>
public static async Task LoadAsync(string path)
{
var resolver = CompositeResolver.Create(
new[] { new ConfigurationEnumFormatter<Key>() },
new[] { StandardResolver.AllowPrivateSnakeCase }
);
using (Stream stream = File.OpenRead(path))
{
Instance = await JsonSerializer.DeserializeAsync<Configuration>(stream, resolver);
}
}
/// <summary>
/// Configures a <see cref="Switch"/> instance
/// </summary>
/// <param name="device">The instance to configure</param>
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<LogClass>())
{
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<T> : IJsonFormatter<T>
where T : struct
{
public void Serialize(ref JsonWriter writer, T value, IJsonFormatterResolver formatterResolver)
{
formatterResolver.GetFormatterWithVerify<string>()
.Serialize(ref writer, value.ToString(), formatterResolver);
}
public T Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
{
if (reader.ReadIsNull())
{
return default(T);
}
var enumName = formatterResolver.GetFormatterWithVerify<string>()
.Deserialize(ref reader, formatterResolver);
if(Enum.TryParse<T>(enumName, out T result))
{
return result;
}
return default(T);
}
}
}
}

View file

@ -20,9 +20,8 @@ namespace Ryujinx
Switch device = new Switch(renderer, audioOut); Switch device = new Switch(renderer, audioOut);
Config.Read(device); Configuration.Load("Config.jsonc");
Configuration.Configure(device);
Logger.Updated += Log.LogMessage;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
@ -40,13 +39,13 @@ namespace Ryujinx
if (romFsFiles.Length > 0) 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]); device.LoadCart(args[0], romFsFiles[0]);
} }
else else
{ {
Console.WriteLine("Loading as cart WITHOUT RomFS."); Logger.PrintInfo(LogClass.Application, "Loading as cart WITHOUT RomFS.");
device.LoadCart(args[0]); device.LoadCart(args[0]);
} }
@ -56,20 +55,20 @@ namespace Ryujinx
switch (Path.GetExtension(args[0]).ToLowerInvariant()) switch (Path.GetExtension(args[0]).ToLowerInvariant())
{ {
case ".xci": case ".xci":
Console.WriteLine("Loading as XCI."); Logger.PrintInfo(LogClass.Application, "Loading as XCI.");
device.LoadXci(args[0]); device.LoadXci(args[0]);
break; break;
case ".nca": case ".nca":
Console.WriteLine("Loading as NCA."); Logger.PrintInfo(LogClass.Application, "Loading as NCA.");
device.LoadNca(args[0]); device.LoadNca(args[0]);
break; break;
case ".nsp": case ".nsp":
case ".pfs0": case ".pfs0":
Console.WriteLine("Loading as NSP."); Logger.PrintInfo(LogClass.Application, "Loading as NSP.");
device.LoadNsp(args[0]); device.LoadNsp(args[0]);
break; break;
default: default:
Console.WriteLine("Loading as homebrew."); Logger.PrintInfo(LogClass.Application, "Loading as homebrew.");
device.LoadProgram(args[0]); device.LoadProgram(args[0]);
break; break;
} }
@ -77,7 +76,7 @@ namespace Ryujinx
} }
else 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)) using (GlScreen screen = new GlScreen(device, renderer))
@ -88,11 +87,13 @@ namespace Ryujinx
} }
audioOut.Dispose(); audioOut.Dispose();
Logger.Shutdown();
} }
private static void CurrentDomain_ProcessExit(object sender, EventArgs e) private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{ {
Log.Close(); Logger.Shutdown();
} }
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
@ -103,7 +104,7 @@ namespace Ryujinx
if (e.IsTerminating) if (e.IsTerminating)
{ {
Log.Close(); Logger.Shutdown();
} }
} }

View file

@ -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

View file

@ -20,7 +20,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="Ryujinx.conf"> <None Update="Config.jsonc">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>

View file

@ -142,24 +142,24 @@ namespace Ryujinx
{ {
KeyboardState keyboard = _keyboard.Value; 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 //Keyboard has priority stick-wise
if (leftJoystickDx == 0 && leftJoystickDy == 0) if (leftJoystickDx == 0 && leftJoystickDy == 0)
{ {
(leftJoystickDx, leftJoystickDy) = Config.NpadController.GetLeftStick(); (leftJoystickDx, leftJoystickDy) = Configuration.Instance.GamepadControls.GetLeftStick();
} }
if (rightJoystickDx == 0 && rightJoystickDy == 0) if (rightJoystickDx == 0 && rightJoystickDy == 0)
{ {
(rightJoystickDx, rightJoystickDy) = Config.NpadController.GetRightStick(); (rightJoystickDx, rightJoystickDy) = Configuration.Instance.GamepadControls.GetRightStick();
} }
leftJoystick = new HidJoystickPosition leftJoystick = new HidJoystickPosition

View file

@ -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<LogEventArgs> _messageQueue;
private static Dictionary<LogLevel, ConsoleColor> _logColors;
static Log()
{
_logColors = new Dictionary<LogLevel, ConsoleColor>()
{
{ LogLevel.Stub, ConsoleColor.DarkGray },
{ LogLevel.Info, ConsoleColor.White },
{ LogLevel.Warning, ConsoleColor.Yellow },
{ LogLevel.Error, ConsoleColor.Red }
};
_messageQueue = new BlockingCollection<LogEventArgs>(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();
}
}
}
}

View file

@ -8,28 +8,24 @@ namespace Ryujinx.UI.Input
public enum ControllerInputId public enum ControllerInputId
{ {
Invalid, Invalid,
LStick, LStick,
RStick,
LShoulder,
RShoulder,
LTrigger,
RTrigger,
LJoystick,
RJoystick,
DPadUp, DPadUp,
DPadDown, DPadDown,
DPadLeft, DPadLeft,
DPadRight, DPadRight,
Start,
Back, Back,
LShoulder,
RStick,
A, A,
B, B,
X, X,
Y, Y
Start,
RShoulder,
LTrigger,
RTrigger,
LJoystick,
RJoystick
} }
public struct NpadControllerLeft public struct NpadControllerLeft
@ -60,34 +56,55 @@ namespace Ryujinx.UI.Input
public class NpadController public class NpadController
{ {
public bool Enabled { private set; get; } /// <summary>
public int Index { private set; get; } /// Enables or disables controller support
public float Deadzone { private set; get; } /// </summary>
public float TriggerThreshold { private set; get; } public bool Enabled { get; private set; }
public NpadControllerLeft Left { private set; get; } /// <summary>
public NpadControllerRight Right { private set; get; } /// Controller Device Index
/// </summary>
public int Index { get; private set; }
/// <summary>
/// Controller Analog Stick Deadzone
/// </summary>
public float Deadzone { get; private set; }
/// <summary>
/// Controller Trigger Threshold
/// </summary>
public float TriggerThreshold { get; private set; }
/// <summary>
/// Left JoyCon Controller Bindings
/// </summary>
public NpadControllerLeft LeftJoycon { get; private set; }
/// <summary>
/// Right JoyCon Controller Bindings
/// </summary>
public NpadControllerRight RightJoycon { get; private set; }
public NpadController( public NpadController(
bool enabled, bool enabled,
int index, int index,
float deadzone, float deadzone,
float triggerThreshold, float triggerThreshold,
NpadControllerLeft left, NpadControllerLeft leftJoycon,
NpadControllerRight right) NpadControllerRight rightJoycon)
{ {
Enabled = enabled; Enabled = enabled;
Index = index; Index = index;
Deadzone = deadzone; Deadzone = deadzone;
TriggerThreshold = triggerThreshold; TriggerThreshold = triggerThreshold;
Left = left; LeftJoycon = leftJoycon;
Right = right; 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() public HidControllerButtons GetButtons()
@ -101,23 +118,23 @@ namespace Ryujinx.UI.Input
HidControllerButtons buttons = 0; HidControllerButtons buttons = 0;
if (IsPressed(gpState, Left.DPadUp)) buttons |= HidControllerButtons.DpadUp; if (IsPressed(gpState, LeftJoycon.DPadUp)) buttons |= HidControllerButtons.DpadUp;
if (IsPressed(gpState, Left.DPadDown)) buttons |= HidControllerButtons.DpadDown; if (IsPressed(gpState, LeftJoycon.DPadDown)) buttons |= HidControllerButtons.DpadDown;
if (IsPressed(gpState, Left.DPadLeft)) buttons |= HidControllerButtons.DpadLeft; if (IsPressed(gpState, LeftJoycon.DPadLeft)) buttons |= HidControllerButtons.DpadLeft;
if (IsPressed(gpState, Left.DPadRight)) buttons |= HidControllerButtons.DPadRight; if (IsPressed(gpState, LeftJoycon.DPadRight)) buttons |= HidControllerButtons.DPadRight;
if (IsPressed(gpState, Left.StickButton)) buttons |= HidControllerButtons.StickLeft; if (IsPressed(gpState, LeftJoycon.StickButton)) buttons |= HidControllerButtons.StickLeft;
if (IsPressed(gpState, Left.ButtonMinus)) buttons |= HidControllerButtons.Minus; if (IsPressed(gpState, LeftJoycon.ButtonMinus)) buttons |= HidControllerButtons.Minus;
if (IsPressed(gpState, Left.ButtonL)) buttons |= HidControllerButtons.L; if (IsPressed(gpState, LeftJoycon.ButtonL)) buttons |= HidControllerButtons.L;
if (IsPressed(gpState, Left.ButtonZl)) buttons |= HidControllerButtons.Zl; if (IsPressed(gpState, LeftJoycon.ButtonZl)) buttons |= HidControllerButtons.Zl;
if (IsPressed(gpState, Right.ButtonA)) buttons |= HidControllerButtons.A; if (IsPressed(gpState, RightJoycon.ButtonA)) buttons |= HidControllerButtons.A;
if (IsPressed(gpState, Right.ButtonB)) buttons |= HidControllerButtons.B; if (IsPressed(gpState, RightJoycon.ButtonB)) buttons |= HidControllerButtons.B;
if (IsPressed(gpState, Right.ButtonX)) buttons |= HidControllerButtons.X; if (IsPressed(gpState, RightJoycon.ButtonX)) buttons |= HidControllerButtons.X;
if (IsPressed(gpState, Right.ButtonY)) buttons |= HidControllerButtons.Y; if (IsPressed(gpState, RightJoycon.ButtonY)) buttons |= HidControllerButtons.Y;
if (IsPressed(gpState, Right.StickButton)) buttons |= HidControllerButtons.StickRight; if (IsPressed(gpState, RightJoycon.StickButton)) buttons |= HidControllerButtons.StickRight;
if (IsPressed(gpState, Right.ButtonPlus)) buttons |= HidControllerButtons.Plus; if (IsPressed(gpState, RightJoycon.ButtonPlus)) buttons |= HidControllerButtons.Plus;
if (IsPressed(gpState, Right.ButtonR)) buttons |= HidControllerButtons.R; if (IsPressed(gpState, RightJoycon.ButtonR)) buttons |= HidControllerButtons.R;
if (IsPressed(gpState, Right.ButtonZr)) buttons |= HidControllerButtons.Zr; if (IsPressed(gpState, RightJoycon.ButtonZr)) buttons |= HidControllerButtons.Zr;
return buttons; return buttons;
} }
@ -129,7 +146,7 @@ namespace Ryujinx.UI.Input
return (0, 0); return (0, 0);
} }
return GetStick(Left.Stick); return GetStick(LeftJoycon.Stick);
} }
public (short, short) GetRightStick() public (short, short) GetRightStick()
@ -139,7 +156,7 @@ namespace Ryujinx.UI.Input
return (0, 0); return (0, 0);
} }
return GetStick(Right.Stick); return GetStick(RightJoycon.Stick);
} }
private (short, short) GetStick(ControllerInputId joystick) private (short, short) GetStick(ControllerInputId joystick)

View file

@ -5,70 +5,69 @@ namespace Ryujinx.UI.Input
{ {
public struct NpadKeyboardLeft public struct NpadKeyboardLeft
{ {
public int StickUp; public Key StickUp;
public int StickDown; public Key StickDown;
public int StickLeft; public Key StickLeft;
public int StickRight; public Key StickRight;
public int StickButton; public Key StickButton;
public int DPadUp; public Key DPadUp;
public int DPadDown; public Key DPadDown;
public int DPadLeft; public Key DPadLeft;
public int DPadRight; public Key DPadRight;
public int ButtonMinus; public Key ButtonMinus;
public int ButtonL; public Key ButtonL;
public int ButtonZl; public Key ButtonZl;
} }
public struct NpadKeyboardRight public struct NpadKeyboardRight
{ {
public int StickUp; public Key StickUp;
public int StickDown; public Key StickDown;
public int StickLeft; public Key StickLeft;
public int StickRight; public Key StickRight;
public int StickButton; public Key StickButton;
public int ButtonA; public Key ButtonA;
public int ButtonB; public Key ButtonB;
public int ButtonX; public Key ButtonX;
public int ButtonY; public Key ButtonY;
public int ButtonPlus; public Key ButtonPlus;
public int ButtonR; public Key ButtonR;
public int ButtonZr; public Key ButtonZr;
} }
public class NpadKeyboard public class NpadKeyboard
{ {
public NpadKeyboardLeft Left; /// <summary>
public NpadKeyboardRight Right; /// Left JoyCon Keyboard Bindings
/// </summary>
public NpadKeyboardLeft LeftJoycon { get; private set; }
public NpadKeyboard( /// <summary>
NpadKeyboardLeft left, /// Right JoyCon Keyboard Bindings
NpadKeyboardRight right) /// </summary>
{ public NpadKeyboardRight RightJoycon { get; private set; }
Left = left;
Right = right;
}
public HidControllerButtons GetButtons(KeyboardState keyboard) public HidControllerButtons GetButtons(KeyboardState keyboard)
{ {
HidControllerButtons buttons = 0; HidControllerButtons buttons = 0;
if (keyboard[(Key)Left.StickButton]) buttons |= HidControllerButtons.StickLeft; if (keyboard[(Key)LeftJoycon.StickButton]) buttons |= HidControllerButtons.StickLeft;
if (keyboard[(Key)Left.DPadUp]) buttons |= HidControllerButtons.DpadUp; if (keyboard[(Key)LeftJoycon.DPadUp]) buttons |= HidControllerButtons.DpadUp;
if (keyboard[(Key)Left.DPadDown]) buttons |= HidControllerButtons.DpadDown; if (keyboard[(Key)LeftJoycon.DPadDown]) buttons |= HidControllerButtons.DpadDown;
if (keyboard[(Key)Left.DPadLeft]) buttons |= HidControllerButtons.DpadLeft; if (keyboard[(Key)LeftJoycon.DPadLeft]) buttons |= HidControllerButtons.DpadLeft;
if (keyboard[(Key)Left.DPadRight]) buttons |= HidControllerButtons.DPadRight; if (keyboard[(Key)LeftJoycon.DPadRight]) buttons |= HidControllerButtons.DPadRight;
if (keyboard[(Key)Left.ButtonMinus]) buttons |= HidControllerButtons.Minus; if (keyboard[(Key)LeftJoycon.ButtonMinus]) buttons |= HidControllerButtons.Minus;
if (keyboard[(Key)Left.ButtonL]) buttons |= HidControllerButtons.L; if (keyboard[(Key)LeftJoycon.ButtonL]) buttons |= HidControllerButtons.L;
if (keyboard[(Key)Left.ButtonZl]) buttons |= HidControllerButtons.Zl; if (keyboard[(Key)LeftJoycon.ButtonZl]) buttons |= HidControllerButtons.Zl;
if (keyboard[(Key)Right.StickButton]) buttons |= HidControllerButtons.StickRight; if (keyboard[(Key)RightJoycon.StickButton]) buttons |= HidControllerButtons.StickRight;
if (keyboard[(Key)Right.ButtonA]) buttons |= HidControllerButtons.A; if (keyboard[(Key)RightJoycon.ButtonA]) buttons |= HidControllerButtons.A;
if (keyboard[(Key)Right.ButtonB]) buttons |= HidControllerButtons.B; if (keyboard[(Key)RightJoycon.ButtonB]) buttons |= HidControllerButtons.B;
if (keyboard[(Key)Right.ButtonX]) buttons |= HidControllerButtons.X; if (keyboard[(Key)RightJoycon.ButtonX]) buttons |= HidControllerButtons.X;
if (keyboard[(Key)Right.ButtonY]) buttons |= HidControllerButtons.Y; if (keyboard[(Key)RightJoycon.ButtonY]) buttons |= HidControllerButtons.Y;
if (keyboard[(Key)Right.ButtonPlus]) buttons |= HidControllerButtons.Plus; if (keyboard[(Key)RightJoycon.ButtonPlus]) buttons |= HidControllerButtons.Plus;
if (keyboard[(Key)Right.ButtonR]) buttons |= HidControllerButtons.R; if (keyboard[(Key)RightJoycon.ButtonR]) buttons |= HidControllerButtons.R;
if (keyboard[(Key)Right.ButtonZr]) buttons |= HidControllerButtons.Zr; if (keyboard[(Key)RightJoycon.ButtonZr]) buttons |= HidControllerButtons.Zr;
return buttons; return buttons;
} }
@ -78,10 +77,10 @@ namespace Ryujinx.UI.Input
short dx = 0; short dx = 0;
short dy = 0; short dy = 0;
if (keyboard[(Key)Left.StickUp]) dy = short.MaxValue; if (keyboard[(Key)LeftJoycon.StickUp]) dy = short.MaxValue;
if (keyboard[(Key)Left.StickDown]) dy = -short.MaxValue; if (keyboard[(Key)LeftJoycon.StickDown]) dy = -short.MaxValue;
if (keyboard[(Key)Left.StickLeft]) dx = -short.MaxValue; if (keyboard[(Key)LeftJoycon.StickLeft]) dx = -short.MaxValue;
if (keyboard[(Key)Left.StickRight]) dx = short.MaxValue; if (keyboard[(Key)LeftJoycon.StickRight]) dx = short.MaxValue;
return (dx, dy); return (dx, dy);
} }
@ -91,10 +90,10 @@ namespace Ryujinx.UI.Input
short dx = 0; short dx = 0;
short dy = 0; short dy = 0;
if (keyboard[(Key)Right.StickUp]) dy = short.MaxValue; if (keyboard[(Key)RightJoycon.StickUp]) dy = short.MaxValue;
if (keyboard[(Key)Right.StickDown]) dy = -short.MaxValue; if (keyboard[(Key)RightJoycon.StickDown]) dy = -short.MaxValue;
if (keyboard[(Key)Right.StickLeft]) dx = -short.MaxValue; if (keyboard[(Key)RightJoycon.StickLeft]) dx = -short.MaxValue;
if (keyboard[(Key)Right.StickRight]) dx = short.MaxValue; if (keyboard[(Key)RightJoycon.StickRight]) dx = short.MaxValue;
return (dx, dy); return (dx, dy);
} }

823
Ryujinx/_schema.json Normal file
View file

@ -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"
}
}
}
}
}
}
}