Profiler config broken in thes commit. Next will fix

Merge branch 'original_master' into profiling

# Conflicts:
#	Ryujinx.Common/Ryujinx.Common.csproj
#	Ryujinx.sln
#	Ryujinx/Config.cs
#	Ryujinx/Ryujinx.conf
#	Ryujinx/Ui/GLScreen.cs
This commit is contained in:
Andy Adshead 2019-02-15 10:04:47 +00:00
commit 7c011cfca7
64 changed files with 2904 additions and 980 deletions

210
CONFIG.md
View file

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

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)
- 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**

View file

@ -3,21 +3,36 @@ using System.Runtime.InteropServices;
namespace SoundIOSharp
{
public static class MarshalEx
{
public static double ReadDouble (IntPtr handle, int offset = 0)
{
return BitConverter.Int64BitsToDouble (Marshal.ReadInt64 (handle, offset));
}
public static class MarshalEx
{
public static double ReadDouble(IntPtr handle, int offset = 0)
{
return BitConverter.Int64BitsToDouble(Marshal.ReadInt64(handle, offset));
}
public static void WriteDouble (IntPtr handle, double value)
{
WriteDouble (handle, 0, value);
}
public static void WriteDouble(IntPtr handle, double value)
{
WriteDouble(handle, 0, value);
}
public static void WriteDouble (IntPtr handle, int offset, double value)
{
Marshal.WriteInt64 (handle, offset, BitConverter.DoubleToInt64Bits (value));
}
}
public static void WriteDouble(IntPtr handle, int offset, double value)
{
Marshal.WriteInt64(handle, offset, BitConverter.DoubleToInt64Bits(value));
}
public static float ReadFloat(IntPtr handle, int offset = 0)
{
return BitConverter.Int32BitsToSingle(Marshal.ReadInt32(handle, offset));
}
public static void WriteFloat(IntPtr handle, float value)
{
WriteFloat(handle, 0, value);
}
public static void WriteFloat(IntPtr handle, int offset, float value)
{
Marshal.WriteInt32(handle, offset, BitConverter.SingleToInt32Bits(value));
}
}
}

View file

@ -77,6 +77,12 @@ namespace SoundIOSharp
}
static readonly int software_latency_offset = (int)Marshal.OffsetOf<SoundIoOutStream> ("software_latency");
public float Volume {
get { return MarshalEx.ReadFloat (handle, volume_offset); }
set { MarshalEx.WriteFloat (handle, volume_offset, value); }
}
static readonly int volume_offset = (int)Marshal.OffsetOf<SoundIoOutStream> ("volume");
// error_callback
public Action ErrorCallback {
get { return error_callback; }
@ -237,5 +243,12 @@ namespace SoundIOSharp
return *dptr;
}
}
public void SetVolume (double volume)
{
var ret = (SoundIoError) Natives.soundio_outstream_set_volume (handle, volume);
if (ret != SoundIoError.SoundIoErrorNone)
throw new SoundIOException (ret);
}
}
}

View file

@ -178,7 +178,7 @@ namespace SoundIOSharp
}
[StructLayout(LayoutKind.Sequential)]
struct SoundIoChannelLayout // soundio.h (302, 8)
struct SoundIoChannelLayout // soundio.h (306, 8)
{
[CTypeDetails("Pointer<byte>")] public System.IntPtr @name;
public int @channel_count;
@ -187,21 +187,21 @@ namespace SoundIOSharp
}
[StructLayout(LayoutKind.Sequential)]
struct SoundIoSampleRateRange // soundio.h (309, 8)
struct SoundIoSampleRateRange // soundio.h (313, 8)
{
public int @min;
public int @max;
}
[StructLayout(LayoutKind.Sequential)]
struct SoundIoChannelArea // soundio.h (315, 8)
struct SoundIoChannelArea // soundio.h (319, 8)
{
[CTypeDetails("Pointer<byte>")] public System.IntPtr @ptr;
public int @step;
}
[StructLayout(LayoutKind.Sequential)]
struct SoundIo // soundio.h (324, 8)
struct SoundIo // soundio.h (328, 8)
{
[CTypeDetails("Pointer<void>")] public System.IntPtr @userdata;
[CTypeDetails("Pointer<void (SoundIo *)>")] public delegate0 @on_devices_change;
@ -215,7 +215,7 @@ namespace SoundIOSharp
}
[StructLayout(LayoutKind.Sequential)]
struct SoundIoDevice // soundio.h (383, 8)
struct SoundIoDevice // soundio.h (387, 8)
{
[CTypeDetails("Pointer<SoundIo>")] public System.IntPtr @soundio;
[CTypeDetails("Pointer<byte>")] public System.IntPtr @id;
@ -239,13 +239,14 @@ namespace SoundIOSharp
}
[StructLayout(LayoutKind.Sequential)]
struct SoundIoOutStream // soundio.h (493, 8)
struct SoundIoOutStream // soundio.h (497, 8)
{
[CTypeDetails("Pointer<SoundIoDevice>")] public System.IntPtr @device;
public SoundIoFormat @format;
public int @sample_rate;
public SoundIoChannelLayout @layout;
public double @software_latency;
public float @volume;
[CTypeDetails("Pointer<void>")] public System.IntPtr @userdata;
[CTypeDetails("Pointer<void (SoundIoOutStream *, int, int)>")] public delegate4 @write_callback;
[CTypeDetails("Pointer<void (SoundIoOutStream *)>")] public delegate5 @underflow_callback;
@ -258,7 +259,7 @@ namespace SoundIOSharp
}
[StructLayout(LayoutKind.Sequential)]
struct SoundIoInStream // soundio.h (595, 8)
struct SoundIoInStream // soundio.h (600, 8)
{
[CTypeDetails("Pointer<SoundIoDevice>")] public System.IntPtr @device;
public SoundIoFormat @format;
@ -277,302 +278,306 @@ namespace SoundIOSharp
}
[StructLayout(LayoutKind.Sequential)]
struct SoundIoRingBuffer // soundio.h (1167, 8)
struct SoundIoRingBuffer // soundio.h (1170, 8)
{
}
partial class Natives
{
const string LibraryName = "libsoundio";
// function soundio_version_string - soundio.h (677, 28)
// function soundio_version_string - soundio.h (682, 28)
[DllImport(LibraryName)]
internal static extern System.IntPtr soundio_version_string();
// function soundio_version_major - soundio.h (679, 20)
// function soundio_version_major - soundio.h (684, 20)
[DllImport(LibraryName)]
internal static extern int soundio_version_major();
// function soundio_version_minor - soundio.h (681, 20)
// function soundio_version_minor - soundio.h (686, 20)
[DllImport(LibraryName)]
internal static extern int soundio_version_minor();
// function soundio_version_patch - soundio.h (683, 20)
// function soundio_version_patch - soundio.h (688, 20)
[DllImport(LibraryName)]
internal static extern int soundio_version_patch();
// function soundio_create - soundio.h (689, 32)
// function soundio_create - soundio.h (694, 32)
[DllImport(LibraryName)]
internal static extern System.IntPtr soundio_create();
// function soundio_destroy - soundio.h (690, 21)
// function soundio_destroy - soundio.h (695, 21)
[DllImport(LibraryName)]
internal static extern void soundio_destroy([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
// function soundio_connect - soundio.h (700, 20)
// function soundio_connect - soundio.h (705, 20)
[DllImport(LibraryName)]
internal static extern int soundio_connect([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
// function soundio_connect_backend - soundio.h (712, 20)
// function soundio_connect_backend - soundio.h (717, 20)
[DllImport(LibraryName)]
internal static extern int soundio_connect_backend([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio, SoundIoBackend @backend);
// function soundio_disconnect - soundio.h (713, 21)
// function soundio_disconnect - soundio.h (718, 21)
[DllImport(LibraryName)]
internal static extern void soundio_disconnect([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
// function soundio_strerror - soundio.h (716, 28)
// function soundio_strerror - soundio.h (721, 28)
[DllImport(LibraryName)]
internal static extern System.IntPtr soundio_strerror(int @error);
// function soundio_backend_name - soundio.h (718, 28)
// function soundio_backend_name - soundio.h (723, 28)
[DllImport(LibraryName)]
internal static extern System.IntPtr soundio_backend_name(SoundIoBackend @backend);
// function soundio_backend_count - soundio.h (721, 20)
// function soundio_backend_count - soundio.h (726, 20)
[DllImport(LibraryName)]
internal static extern int soundio_backend_count([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
// function soundio_get_backend - soundio.h (724, 36)
// function soundio_get_backend - soundio.h (729, 36)
[DllImport(LibraryName)]
internal static extern SoundIoBackend soundio_get_backend([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio, int @index);
// function soundio_have_backend - soundio.h (727, 21)
// function soundio_have_backend - soundio.h (732, 21)
[DllImport(LibraryName)]
internal static extern bool soundio_have_backend(SoundIoBackend @backend);
// function soundio_flush_events - soundio.h (751, 21)
// function soundio_flush_events - soundio.h (756, 21)
[DllImport(LibraryName)]
internal static extern void soundio_flush_events([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
// function soundio_wait_events - soundio.h (755, 21)
// function soundio_wait_events - soundio.h (760, 21)
[DllImport(LibraryName)]
internal static extern void soundio_wait_events([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
// function soundio_wakeup - soundio.h (758, 21)
// function soundio_wakeup - soundio.h (763, 21)
[DllImport(LibraryName)]
internal static extern void soundio_wakeup([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
// function soundio_force_device_scan - soundio.h (775, 21)
// function soundio_force_device_scan - soundio.h (780, 21)
[DllImport(LibraryName)]
internal static extern void soundio_force_device_scan([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
// function soundio_channel_layout_equal - soundio.h (782, 21)
// function soundio_channel_layout_equal - soundio.h (787, 21)
[DllImport(LibraryName)]
internal static extern bool soundio_channel_layout_equal([CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @a, [CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @b);
// function soundio_get_channel_name - soundio.h (786, 28)
// function soundio_get_channel_name - soundio.h (791, 28)
[DllImport(LibraryName)]
internal static extern System.IntPtr soundio_get_channel_name(SoundIoChannelId @id);
// function soundio_parse_channel_id - soundio.h (790, 38)
// function soundio_parse_channel_id - soundio.h (795, 38)
[DllImport(LibraryName)]
internal static extern SoundIoChannelId soundio_parse_channel_id([CTypeDetails("Pointer<byte>")]System.IntPtr @str, int @str_len);
// function soundio_channel_layout_builtin_count - soundio.h (793, 20)
// function soundio_channel_layout_builtin_count - soundio.h (798, 20)
[DllImport(LibraryName)]
internal static extern int soundio_channel_layout_builtin_count();
// function soundio_channel_layout_get_builtin - soundio.h (798, 51)
// function soundio_channel_layout_get_builtin - soundio.h (803, 51)
[DllImport(LibraryName)]
internal static extern System.IntPtr soundio_channel_layout_get_builtin(int @index);
// function soundio_channel_layout_get_default - soundio.h (801, 51)
// function soundio_channel_layout_get_default - soundio.h (806, 51)
[DllImport(LibraryName)]
internal static extern System.IntPtr soundio_channel_layout_get_default(int @channel_count);
// function soundio_channel_layout_find_channel - soundio.h (804, 20)
// function soundio_channel_layout_find_channel - soundio.h (809, 20)
[DllImport(LibraryName)]
internal static extern int soundio_channel_layout_find_channel([CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @layout, SoundIoChannelId @channel);
// function soundio_channel_layout_detect_builtin - soundio.h (809, 21)
// function soundio_channel_layout_detect_builtin - soundio.h (814, 21)
[DllImport(LibraryName)]
internal static extern bool soundio_channel_layout_detect_builtin([CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @layout);
// function soundio_best_matching_channel_layout - soundio.h (814, 51)
// function soundio_best_matching_channel_layout - soundio.h (819, 51)
[DllImport(LibraryName)]
internal static extern System.IntPtr soundio_best_matching_channel_layout([CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @preferred_layouts, int @preferred_layout_count, [CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @available_layouts, int @available_layout_count);
// function soundio_sort_channel_layouts - soundio.h (819, 21)
// function soundio_sort_channel_layouts - soundio.h (824, 21)
[DllImport(LibraryName)]
internal static extern void soundio_sort_channel_layouts([CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @layouts, int @layout_count);
// function soundio_get_bytes_per_sample - soundio.h (825, 20)
// function soundio_get_bytes_per_sample - soundio.h (830, 20)
[DllImport(LibraryName)]
internal static extern int soundio_get_bytes_per_sample(SoundIoFormat @format);
// function soundio_get_bytes_per_frame - soundio.h (828, 19)
// function soundio_get_bytes_per_frame - soundio.h (833, 19)
[DllImport(LibraryName)]
internal static extern int soundio_get_bytes_per_frame(SoundIoFormat @format, int @channel_count);
// function soundio_get_bytes_per_second - soundio.h (833, 19)
// function soundio_get_bytes_per_second - soundio.h (838, 19)
[DllImport(LibraryName)]
internal static extern int soundio_get_bytes_per_second(SoundIoFormat @format, int @channel_count, int @sample_rate);
// function soundio_format_string - soundio.h (840, 29)
// function soundio_format_string - soundio.h (845, 29)
[DllImport(LibraryName)]
internal static extern System.IntPtr soundio_format_string(SoundIoFormat @format);
// function soundio_input_device_count - soundio.h (856, 20)
// function soundio_input_device_count - soundio.h (861, 20)
[DllImport(LibraryName)]
internal static extern int soundio_input_device_count([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
// function soundio_output_device_count - soundio.h (859, 20)
// function soundio_output_device_count - soundio.h (864, 20)
[DllImport(LibraryName)]
internal static extern int soundio_output_device_count([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
// function soundio_get_input_device - soundio.h (865, 38)
// function soundio_get_input_device - soundio.h (870, 38)
[DllImport(LibraryName)]
internal static extern System.IntPtr soundio_get_input_device([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio, int @index);
// function soundio_get_output_device - soundio.h (870, 38)
// function soundio_get_output_device - soundio.h (875, 38)
[DllImport(LibraryName)]
internal static extern System.IntPtr soundio_get_output_device([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio, int @index);
// function soundio_default_input_device_index - soundio.h (875, 20)
// function soundio_default_input_device_index - soundio.h (880, 20)
[DllImport(LibraryName)]
internal static extern int soundio_default_input_device_index([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
// function soundio_default_output_device_index - soundio.h (880, 20)
// function soundio_default_output_device_index - soundio.h (885, 20)
[DllImport(LibraryName)]
internal static extern int soundio_default_output_device_index([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
// function soundio_device_ref - soundio.h (883, 21)
// function soundio_device_ref - soundio.h (888, 21)
[DllImport(LibraryName)]
internal static extern void soundio_device_ref([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device);
// function soundio_device_unref - soundio.h (886, 21)
// function soundio_device_unref - soundio.h (891, 21)
[DllImport(LibraryName)]
internal static extern void soundio_device_unref([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device);
// function soundio_device_equal - soundio.h (890, 21)
// function soundio_device_equal - soundio.h (895, 21)
[DllImport(LibraryName)]
internal static extern bool soundio_device_equal([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @a, [CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @b);
// function soundio_device_sort_channel_layouts - soundio.h (895, 21)
// function soundio_device_sort_channel_layouts - soundio.h (900, 21)
[DllImport(LibraryName)]
internal static extern void soundio_device_sort_channel_layouts([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device);
// function soundio_device_supports_format - soundio.h (899, 21)
// function soundio_device_supports_format - soundio.h (904, 21)
[DllImport(LibraryName)]
internal static extern bool soundio_device_supports_format([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device, SoundIoFormat @format);
// function soundio_device_supports_layout - soundio.h (904, 21)
// function soundio_device_supports_layout - soundio.h (909, 21)
[DllImport(LibraryName)]
internal static extern bool soundio_device_supports_layout([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device, [CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @layout);
// function soundio_device_supports_sample_rate - soundio.h (909, 21)
// function soundio_device_supports_sample_rate - soundio.h (914, 21)
[DllImport(LibraryName)]
internal static extern bool soundio_device_supports_sample_rate([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device, int @sample_rate);
// function soundio_device_nearest_sample_rate - soundio.h (914, 20)
// function soundio_device_nearest_sample_rate - soundio.h (919, 20)
[DllImport(LibraryName)]
internal static extern int soundio_device_nearest_sample_rate([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device, int @sample_rate);
// function soundio_outstream_create - soundio.h (924, 41)
// function soundio_outstream_create - soundio.h (929, 41)
[DllImport(LibraryName)]
internal static extern System.IntPtr soundio_outstream_create([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device);
// function soundio_outstream_destroy - soundio.h (926, 21)
// function soundio_outstream_destroy - soundio.h (931, 21)
[DllImport(LibraryName)]
internal static extern void soundio_outstream_destroy([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream);
// function soundio_outstream_open - soundio.h (950, 20)
// function soundio_outstream_open - soundio.h (954, 20)
[DllImport(LibraryName)]
internal static extern int soundio_outstream_open([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream);
// function soundio_outstream_start - soundio.h (961, 20)
// function soundio_outstream_start - soundio.h (965, 20)
[DllImport(LibraryName)]
internal static extern int soundio_outstream_start([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream);
// function soundio_outstream_begin_write - soundio.h (993, 20)
// function soundio_outstream_begin_write - soundio.h (997, 20)
[DllImport(LibraryName)]
internal static extern int soundio_outstream_begin_write([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream, [CTypeDetails("Pointer<System.IntPtr>")]System.IntPtr @areas, [CTypeDetails("Pointer<int>")]System.IntPtr @frame_count);
// function soundio_outstream_end_write - soundio.h (1005, 20)
// function soundio_outstream_end_write - soundio.h (1009, 20)
[DllImport(LibraryName)]
internal static extern int soundio_outstream_end_write([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream);
// function soundio_outstream_clear_buffer - soundio.h (1020, 20)
// function soundio_outstream_clear_buffer - soundio.h (1024, 20)
[DllImport(LibraryName)]
internal static extern int soundio_outstream_clear_buffer([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream);
// function soundio_outstream_pause - soundio.h (1041, 20)
// function soundio_outstream_pause - soundio.h (1045, 20)
[DllImport(LibraryName)]
internal static extern int soundio_outstream_pause([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream, bool @pause);
// function soundio_outstream_get_latency - soundio.h (1054, 20)
// function soundio_outstream_get_latency - soundio.h (1058, 20)
[DllImport(LibraryName)]
internal static extern int soundio_outstream_get_latency([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream, [CTypeDetails("Pointer<double>")]System.IntPtr @out_latency);
// function soundio_instream_create - soundio.h (1064, 40)
// function soundio_outstream_set_volume - soundio.h (1061, 20)
[DllImport(LibraryName)]
internal static extern int soundio_outstream_set_volume([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream, double @volume);
// function soundio_instream_create - soundio.h (1071, 40)
[DllImport(LibraryName)]
internal static extern System.IntPtr soundio_instream_create([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device);
// function soundio_instream_destroy - soundio.h (1066, 21)
// function soundio_instream_destroy - soundio.h (1073, 21)
[DllImport(LibraryName)]
internal static extern void soundio_instream_destroy([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream);
// function soundio_instream_open - soundio.h (1086, 20)
// function soundio_instream_open - soundio.h (1093, 20)
[DllImport(LibraryName)]
internal static extern int soundio_instream_open([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream);
// function soundio_instream_start - soundio.h (1095, 20)
// function soundio_instream_start - soundio.h (1102, 20)
[DllImport(LibraryName)]
internal static extern int soundio_instream_start([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream);
// function soundio_instream_begin_read - soundio.h (1126, 20)
// function soundio_instream_begin_read - soundio.h (1133, 20)
[DllImport(LibraryName)]
internal static extern int soundio_instream_begin_read([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream, [CTypeDetails("Pointer<System.IntPtr>")]System.IntPtr @areas, [CTypeDetails("Pointer<int>")]System.IntPtr @frame_count);
// function soundio_instream_end_read - soundio.h (1136, 20)
// function soundio_instream_end_read - soundio.h (1143, 20)
[DllImport(LibraryName)]
internal static extern int soundio_instream_end_read([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream);
// function soundio_instream_pause - soundio.h (1149, 20)
// function soundio_instream_pause - soundio.h (1156, 20)
[DllImport(LibraryName)]
internal static extern int soundio_instream_pause([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream, bool @pause);
// function soundio_instream_get_latency - soundio.h (1159, 20)
// function soundio_instream_get_latency - soundio.h (1166, 20)
[DllImport(LibraryName)]
internal static extern int soundio_instream_get_latency([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream, [CTypeDetails("Pointer<double>")]System.IntPtr @out_latency);
// function soundio_ring_buffer_create - soundio.h (1173, 42)
// function soundio_ring_buffer_create - soundio.h (1181, 42)
[DllImport(LibraryName)]
internal static extern System.IntPtr soundio_ring_buffer_create([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio, int @requested_capacity);
// function soundio_ring_buffer_destroy - soundio.h (1174, 21)
// function soundio_ring_buffer_destroy - soundio.h (1182, 21)
[DllImport(LibraryName)]
internal static extern void soundio_ring_buffer_destroy([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
// function soundio_ring_buffer_capacity - soundio.h (1178, 20)
// function soundio_ring_buffer_capacity - soundio.h (1186, 20)
[DllImport(LibraryName)]
internal static extern int soundio_ring_buffer_capacity([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
// function soundio_ring_buffer_write_ptr - soundio.h (1181, 22)
// function soundio_ring_buffer_write_ptr - soundio.h (1189, 22)
[DllImport(LibraryName)]
internal static extern System.IntPtr soundio_ring_buffer_write_ptr([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
// function soundio_ring_buffer_advance_write_ptr - soundio.h (1183, 21)
// function soundio_ring_buffer_advance_write_ptr - soundio.h (1191, 21)
[DllImport(LibraryName)]
internal static extern void soundio_ring_buffer_advance_write_ptr([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer, int @count);
// function soundio_ring_buffer_read_ptr - soundio.h (1186, 22)
// function soundio_ring_buffer_read_ptr - soundio.h (1194, 22)
[DllImport(LibraryName)]
internal static extern System.IntPtr soundio_ring_buffer_read_ptr([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
// function soundio_ring_buffer_advance_read_ptr - soundio.h (1188, 21)
// function soundio_ring_buffer_advance_read_ptr - soundio.h (1196, 21)
[DllImport(LibraryName)]
internal static extern void soundio_ring_buffer_advance_read_ptr([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer, int @count);
// function soundio_ring_buffer_fill_count - soundio.h (1191, 20)
// function soundio_ring_buffer_fill_count - soundio.h (1199, 20)
[DllImport(LibraryName)]
internal static extern int soundio_ring_buffer_fill_count([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
// function soundio_ring_buffer_free_count - soundio.h (1194, 20)
// function soundio_ring_buffer_free_count - soundio.h (1202, 20)
[DllImport(LibraryName)]
internal static extern int soundio_ring_buffer_free_count([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
// function soundio_ring_buffer_clear - soundio.h (1197, 21)
// function soundio_ring_buffer_clear - soundio.h (1205, 21)
[DllImport(LibraryName)]
internal static extern void soundio_ring_buffer_clear([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);

View file

@ -36,60 +36,7 @@ namespace Ryujinx.Audio
{
get
{
SoundIO context = null;
SoundIODevice device = null;
SoundIOOutStream stream = null;
bool backendDisconnected = false;
try
{
context = new SoundIO();
context.OnBackendDisconnect = (i) => {
backendDisconnected = true;
};
context.Connect();
context.FlushEvents();
if(backendDisconnected)
{
return false;
}
device = context.GetOutputDevice(context.DefaultOutputDeviceIndex);
if(device == null || backendDisconnected)
{
return false;
}
stream = device.CreateOutStream();
if(stream == null || backendDisconnected)
{
return false;
}
return true;
}
catch
{
return false;
}
finally
{
if(stream != null)
{
stream.Dispose();
}
if(context != null)
{
context.Dispose();
}
}
return IsSupportedInternal();
}
}
@ -103,7 +50,7 @@ namespace Ryujinx.Audio
m_AudioContext.Connect();
m_AudioContext.FlushEvents();
m_AudioDevice = m_AudioContext.GetOutputDevice(m_AudioContext.DefaultOutputDeviceIndex);
m_AudioDevice = FindNonRawDefaultAudioDevice(m_AudioContext, true);
m_TrackPool = new SoundIoAudioTrackPool(m_AudioContext, m_AudioDevice, MaximumTracks);
}
@ -244,5 +191,99 @@ namespace Ryujinx.Audio
m_AudioContext.Disconnect();
m_AudioContext.Dispose();
}
/// <summary>
/// Searches for a shared version of the default audio device
/// </summary>
/// <param name="audioContext">The <see cref="SoundIO"/> audio context</param>
/// <param name="fallback">Whether to fallback to the raw default audio device if a non-raw device cannot be found</param>
private static SoundIODevice FindNonRawDefaultAudioDevice(SoundIO audioContext, bool fallback = false)
{
SoundIODevice defaultAudioDevice = audioContext.GetOutputDevice(audioContext.DefaultOutputDeviceIndex);
if(!defaultAudioDevice.IsRaw)
{
return defaultAudioDevice;
}
for(var i = 0; i < audioContext.BackendCount; i++)
{
SoundIODevice audioDevice = audioContext.GetOutputDevice(i);
if (audioDevice.Id == defaultAudioDevice.Id && !audioDevice.IsRaw)
{
return audioDevice;
}
}
return fallback ? defaultAudioDevice : null;
}
/// <summary>
/// Determines if SoundIO can connect to a supported backend
/// </summary>
/// <returns></returns>
private static bool IsSupportedInternal()
{
SoundIO context = null;
SoundIODevice device = null;
SoundIOOutStream stream = null;
bool backendDisconnected = false;
try
{
context = new SoundIO();
context.OnBackendDisconnect = (i) => {
backendDisconnected = true;
};
context.Connect();
context.FlushEvents();
if(backendDisconnected)
{
return false;
}
if(context.OutputDeviceCount == 0)
{
return false;
}
device = FindNonRawDefaultAudioDevice(context);
if(device == null || backendDisconnected)
{
return false;
}
stream = device.CreateOutStream();
if(stream == null || backendDisconnected)
{
return false;
}
return true;
}
catch
{
return false;
}
finally
{
if(stream != null)
{
stream.Dispose();
}
if(context != null)
{
context.Dispose();
}
}
}
}
}

View file

@ -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<T>(this BinaryReader reader) where T : struct
public unsafe static T ReadStruct<T>(this BinaryReader reader)
where T : struct
{
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>();

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
{
Application,
Audio,
Cpu,
Font,

View file

@ -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<LogEventArgs> Updated;
private static readonly List<ILogTarget> m_LogTargets;
public static bool EnableFileLog { get; set; }
public static event EventHandler<LogEventArgs> Updated;
static Logger()
{
@ -33,9 +32,30 @@ namespace Ryujinx.Common.Logging
m_EnabledClasses[index] = true;
}
m_LogTargets = new List<ILogTarget>();
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;

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

@ -25,5 +25,9 @@
<DefineConstants>TRACE;USE_PROFILING</DefineConstants>
<Optimize>true</Optimize>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Utf8Json" Version="1.3.7" />
</ItemGroup>
</Project>

View file

@ -159,25 +159,23 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Shader.SetExtraData(New.FlipX, New.FlipY, New.Instance);
}
//Note: Uncomment SetFrontFace and SetCullFace when flipping issues are solved
if (New.FrontFace != Old.FrontFace)
{
GL.FrontFace(OGLEnumConverter.GetFrontFace(New.FrontFace));
}
//if (New.FrontFace != Old.FrontFace)
//{
// GL.FrontFace(OGLEnumConverter.GetFrontFace(New.FrontFace));
//}
if (New.CullFaceEnabled != Old.CullFaceEnabled)
{
Enable(EnableCap.CullFace, New.CullFaceEnabled);
}
//if (New.CullFaceEnabled != Old.CullFaceEnabled)
//{
// Enable(EnableCap.CullFace, New.CullFaceEnabled);
//}
//if (New.CullFaceEnabled)
//{
// if (New.CullFace != Old.CullFace)
// {
// GL.CullFace(OGLEnumConverter.GetCullFace(New.CullFace));
// }
//}
if (New.CullFaceEnabled)
{
if (New.CullFace != Old.CullFace)
{
GL.CullFace(OGLEnumConverter.GetCullFace(New.CullFace));
}
}
if (New.DepthTestEnabled != Old.DepthTestEnabled)
{

View file

@ -845,9 +845,7 @@ namespace Ryujinx.Graphics.Gal.Shader
{
case GlslDecl.PointCoordAttrX: return "gl_PointCoord.x";
case GlslDecl.PointCoordAttrY: return "gl_PointCoord.y";
//Note: It's a guess that Maxwell's face is 1 when gl_FrontFacing == true
case GlslDecl.FaceAttr: return "(gl_FrontFacing ? 1 : 0)";
case GlslDecl.FaceAttr: return "(gl_FrontFacing ? -1 : 0)";
}
}

View file

@ -1148,41 +1148,87 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
}
private enum XmadMode
{
Cfull = 0,
Clo = 1,
Chi = 2,
Csfu = 3,
Cbcc = 4
}
private static void EmitXmad(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
{
//TODO: Confirm SignAB/C, it is just a guess.
//TODO: Implement Mode 3 (CSFU), what it does?
bool SignAB = OpCode.Read(48);
bool SignC = OpCode.Read(49);
bool HighB = OpCode.Read(52);
bool HighA = OpCode.Read(53);
bool SignedA = OpCode.Read(48);
bool SignedB = OpCode.Read(49);
bool HighB = OpCode.Read(52);
bool HighA = OpCode.Read(53);
int Mode = OpCode.Read(50, 7);
ShaderIrNode OperA = OpCode.Gpr8(), OperB, OperC;
ShaderIrOperImm Imm16 = new ShaderIrOperImm(16);
ShaderIrOperImm ImmMsk = new ShaderIrOperImm(0xffff);
ShaderIrInst ShiftAB = SignAB ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
ShaderIrInst ShiftC = SignC ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
if (HighA)
{
OperA = new ShaderIrOp(ShiftAB, OperA, Imm16);
}
switch (Oper)
{
case ShaderOper.CR: OperB = OpCode.Cbuf34(); break;
case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break;
case ShaderOper.RC: OperB = OpCode.Gpr39(); break;
case ShaderOper.RR: OperB = OpCode.Gpr20(); break;
case ShaderOper.CR: OperB = OpCode.Cbuf34(); break;
case ShaderOper.Imm: OperB = OpCode.ImmU16_20(); break;
case ShaderOper.RC: OperB = OpCode.Gpr39(); break;
case ShaderOper.RR: OperB = OpCode.Gpr20(); break;
default: throw new ArgumentException(nameof(Oper));
}
bool ProductShiftLeft = false, Merge = false;
ShaderIrNode OperB2 = OperB;
if (Oper == ShaderOper.Imm)
{
int Imm = ((ShaderIrOperImm)OperB2).Value;
if (!HighB)
{
Imm <<= 16;
}
if (SignedB)
{
Imm >>= 16;
}
else
{
Imm = (int)((uint)Imm >> 16);
}
OperB2 = new ShaderIrOperImm(Imm);
}
ShaderIrOperImm Imm16 = new ShaderIrOperImm(16);
//If we are working with the lower 16-bits of the A/B operands,
//we need to shift the lower 16-bits to the top 16-bits. Later,
//they will be right shifted. For U16 types, this will be a logical
//right shift, and for S16 types, a arithmetic right shift.
if (!HighA)
{
OperA = new ShaderIrOp(ShaderIrInst.Lsl, OperA, Imm16);
}
if (!HighB && Oper != ShaderOper.Imm)
{
OperB2 = new ShaderIrOp(ShaderIrInst.Lsl, OperB2, Imm16);
}
ShaderIrInst ShiftA = SignedA ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
ShaderIrInst ShiftB = SignedB ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
OperA = new ShaderIrOp(ShiftA, OperA, Imm16);
if (Oper != ShaderOper.Imm)
{
OperB2 = new ShaderIrOp(ShiftB, OperB2, Imm16);
}
bool ProductShiftLeft = false;
bool Merge = false;
if (Oper == ShaderOper.RC)
{
@ -1196,40 +1242,53 @@ namespace Ryujinx.Graphics.Gal.Shader
Merge = OpCode.Read(37);
}
switch (Mode)
{
//CLO.
case 1: OperC = ExtendTo32(OperC, SignC, 16); break;
//CHI.
case 2: OperC = new ShaderIrOp(ShiftC, OperC, Imm16); break;
}
ShaderIrNode OperBH = OperB;
if (HighB)
{
OperBH = new ShaderIrOp(ShiftAB, OperBH, Imm16);
}
ShaderIrOp MulOp = new ShaderIrOp(ShaderIrInst.Mul, OperA, OperBH);
ShaderIrOp MulOp = new ShaderIrOp(ShaderIrInst.Mul, OperA, OperB2);
if (ProductShiftLeft)
{
MulOp = new ShaderIrOp(ShaderIrInst.Lsl, MulOp, Imm16);
}
switch ((XmadMode)Mode)
{
case XmadMode.Clo: OperC = ExtendTo32(OperC, Signed: false, Size: 16); break;
case XmadMode.Chi: OperC = new ShaderIrOp(ShaderIrInst.Lsr, OperC, Imm16); break;
case XmadMode.Cbcc:
{
ShaderIrOp OperBLsh16 = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16);
OperC = new ShaderIrOp(ShaderIrInst.Add, OperC, OperBLsh16);
break;
}
case XmadMode.Csfu:
{
ShaderIrOperImm Imm31 = new ShaderIrOperImm(31);
ShaderIrOp SignAdjustA = new ShaderIrOp(ShaderIrInst.Lsr, OperA, Imm31);
ShaderIrOp SignAdjustB = new ShaderIrOp(ShaderIrInst.Lsr, OperB2, Imm31);
SignAdjustA = new ShaderIrOp(ShaderIrInst.Lsl, SignAdjustA, Imm16);
SignAdjustB = new ShaderIrOp(ShaderIrInst.Lsl, SignAdjustB, Imm16);
ShaderIrOp SignAdjust = new ShaderIrOp(ShaderIrInst.Add, SignAdjustA, SignAdjustB);
OperC = new ShaderIrOp(ShaderIrInst.Sub, OperC, SignAdjust);
break;
}
}
ShaderIrOp AddOp = new ShaderIrOp(ShaderIrInst.Add, MulOp, OperC);
if (Merge)
{
AddOp = new ShaderIrOp(ShaderIrInst.And, AddOp, ImmMsk);
OperB = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16);
AddOp = new ShaderIrOp(ShaderIrInst.Or, AddOp, OperB);
}
ShaderIrOperImm Imm16Mask = new ShaderIrOperImm(0xffff);
if (Mode == 4)
{
AddOp = new ShaderIrOp(ShaderIrInst.And, AddOp, Imm16Mask);
OperB = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16);
AddOp = new ShaderIrOp(ShaderIrInst.Or, AddOp, OperB);
}

View file

@ -138,6 +138,11 @@ namespace Ryujinx.Graphics.Gal.Shader
return new ShaderIrOperImmf(BitConverter.Int32BitsToSingle((int)(OpCode >> 20)));
}
private static ShaderIrOperImm ImmU16_20(this long OpCode)
{
return new ShaderIrOperImm(OpCode.Read(20, 0xffff));
}
private static ShaderIrOperImm Imm19_20(this long OpCode)
{
int Value = OpCode.Read(20, 0x7ffff);

View file

@ -1023,8 +1023,6 @@ namespace Ryujinx.Graphics.Graphics3d
long Timestamp = PerformanceCounter.ElapsedMilliseconds;
Timestamp = (long)(Timestamp * 615384.615385);
Vmm.WriteInt64(Position + 0, Counter);
Vmm.WriteInt64(Position + 8, Timestamp);

View file

@ -1,5 +1,6 @@
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.FspSrv;
using Ryujinx.HLE.Utilities;
using System;
using System.Collections.Generic;
using System.IO;
@ -279,5 +280,34 @@ namespace Ryujinx.HLE.FileSystem
throw new InvalidOperationException($"Path {path} is not a child directory of {_rootPath}");
}
public FileTimestamp GetFileTimeStampRaw(string name)
{
CheckIfDescendentOfRootPath(name);
DateTime creationDateTime = DateTime.UnixEpoch;
DateTime modifiedDateTime = DateTime.UnixEpoch;
DateTime lastAccessDateTime = DateTime.UnixEpoch;
if (File.Exists(name))
{
creationDateTime = File.GetCreationTime(name);
modifiedDateTime = File.GetLastWriteTime(name);
lastAccessDateTime = File.GetLastAccessTime(name);
}
else if (Directory.Exists(name))
{
creationDateTime = Directory.GetCreationTime(name);
modifiedDateTime = Directory.GetLastWriteTime(name);
lastAccessDateTime = Directory.GetLastAccessTime(name);
}
return new FileTimestamp
{
CreationDateTime = creationDateTime,
ModifiedDateTime = modifiedDateTime,
LastAccessDateTime = lastAccessDateTime
};
}
}
}

View file

@ -1,5 +1,6 @@
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.FspSrv;
using System;
namespace Ryujinx.HLE.FileSystem
{
@ -36,5 +37,7 @@ namespace Ryujinx.HLE.FileSystem
long GetFreeSpace(ServiceCtx context);
long GetTotalSpace(ServiceCtx context);
FileTimestamp GetFileTimeStampRaw(string name);
}
}

View file

@ -143,5 +143,10 @@ namespace Ryujinx.HLE.FileSystem
{
throw new NotSupportedException();
}
public FileTimestamp GetFileTimeStampRaw(string name)
{
throw new NotImplementedException();
}
}
}

View file

@ -160,5 +160,10 @@ namespace Ryujinx.HLE.FileSystem
{
throw new NotSupportedException();
}
public FileTimestamp GetFileTimeStampRaw(string name)
{
throw new NotImplementedException();
}
}
}

View file

@ -0,0 +1,77 @@
using System;
using System.IO;
namespace Ryujinx.HLE.HOS
{
class HomebrewRomFsStream : Stream
{
private Stream _baseStream;
private long _positionOffset;
public HomebrewRomFsStream(Stream baseStream, long positionOffset)
{
_baseStream = baseStream;
_positionOffset = positionOffset;
_baseStream.Position = _positionOffset;
}
public override bool CanRead => _baseStream.CanRead;
public override bool CanSeek => _baseStream.CanSeek;
public override bool CanWrite => false;
public override long Length => _baseStream.Length - _positionOffset;
public override long Position
{
get
{
return _baseStream.Position - _positionOffset;
}
set
{
_baseStream.Position = value + _positionOffset;
}
}
public override void Flush()
{
_baseStream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
return _baseStream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
if (origin == SeekOrigin.Begin)
{
offset += _positionOffset;
}
return _baseStream.Seek(offset, origin);
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_baseStream.Dispose();
}
}
}
}

View file

@ -102,6 +102,8 @@ namespace Ryujinx.HLE.HOS
public Horizon(Switch device)
{
ControlData = new Nacp();
Device = device;
State = new SystemStateMgr();
@ -549,14 +551,58 @@ namespace Ryujinx.HLE.HOS
bool isNro = Path.GetExtension(filePath).ToLower() == ".nro";
using (FileStream input = new FileStream(filePath, FileMode.Open))
{
IExecutable staticObject = isNro
? (IExecutable)new NxRelocatableObject(input)
: new NxStaticObject(input);
FileStream input = new FileStream(filePath, FileMode.Open);
ProgramLoader.LoadStaticObjects(this, metaData, new IExecutable[] { staticObject });
IExecutable staticObject;
if (isNro)
{
NxRelocatableObject obj = new NxRelocatableObject(input);
staticObject = obj;
// homebrew NRO can actually have some data after the actual NRO
if (input.Length > obj.FileSize)
{
input.Position = obj.FileSize;
BinaryReader reader = new BinaryReader(input);
uint asetMagic = reader.ReadUInt32();
if (asetMagic == 0x54455341)
{
uint asetVersion = reader.ReadUInt32();
if (asetVersion == 0)
{
ulong iconOffset = reader.ReadUInt64();
ulong iconSize = reader.ReadUInt64();
ulong nacpOffset = reader.ReadUInt64();
ulong nacpSize = reader.ReadUInt64();
ulong romfsOffset = reader.ReadUInt64();
ulong romfsSize = reader.ReadUInt64();
if (romfsSize != 0)
{
Device.FileSystem.SetRomFs(new HomebrewRomFsStream(input, obj.FileSize + (long)romfsOffset));
}
}
else
{
Logger.PrintWarning(LogClass.Loader, $"Unsupported ASET header version found \"{asetVersion}\"");
}
}
}
}
else
{
staticObject = new NxStaticObject(input);
}
ContentManager.LoadEntries();
ProgramLoader.LoadStaticObjects(this, metaData, new IExecutable[] { staticObject });
}
private Npdm GetDefaultNpdm()

View file

@ -386,6 +386,132 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return _process.MemoryManager.UnmapPhysicalMemory(address, size);
}
public KernelResult MapProcessCodeMemory64(int handle, ulong dst, ulong src, ulong size)
{
return MapProcessCodeMemory(handle, dst, src, size);
}
public KernelResult MapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
{
if (!PageAligned(dst) || !PageAligned(src))
{
return KernelResult.InvalidAddress;
}
if (!PageAligned(size) || size == 0)
{
return KernelResult.InvalidSize;
}
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle);
if (targetProcess == null)
{
return KernelResult.InvalidHandle;
}
if (targetProcess.MemoryManager.OutsideAddrSpace(dst, size) ||
targetProcess.MemoryManager.OutsideAddrSpace(src, size) ||
targetProcess.MemoryManager.InsideAliasRegion(dst, size) ||
targetProcess.MemoryManager.InsideHeapRegion(dst, size))
{
return KernelResult.InvalidMemRange;
}
if (size + dst <= dst || size + src <= src)
{
return KernelResult.InvalidMemState;
}
return targetProcess.MemoryManager.MapProcessCodeMemory(dst, src, size);
}
public KernelResult UnmapProcessCodeMemory64(int handle, ulong dst, ulong src, ulong size)
{
return UnmapProcessCodeMemory(handle, dst, src, size);
}
public KernelResult UnmapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
{
if (!PageAligned(dst) || !PageAligned(src))
{
return KernelResult.InvalidAddress;
}
if (!PageAligned(size) || size == 0)
{
return KernelResult.InvalidSize;
}
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle);
if (targetProcess == null)
{
return KernelResult.InvalidHandle;
}
if (targetProcess.MemoryManager.OutsideAddrSpace(dst, size) ||
targetProcess.MemoryManager.OutsideAddrSpace(src, size) ||
targetProcess.MemoryManager.InsideAliasRegion(dst, size) ||
targetProcess.MemoryManager.InsideHeapRegion(dst, size))
{
return KernelResult.InvalidMemRange;
}
if (size + dst <= dst || size + src <= src)
{
return KernelResult.InvalidMemState;
}
return targetProcess.MemoryManager.UnmapProcessCodeMemory(dst, src, size);
}
public KernelResult SetProcessMemoryPermission64(int handle, ulong src, ulong size, MemoryPermission permission)
{
return SetProcessMemoryPermission(handle, src, size, permission);
}
public KernelResult SetProcessMemoryPermission(int handle, ulong src, ulong size, MemoryPermission permission)
{
if (!PageAligned(src))
{
return KernelResult.InvalidAddress;
}
if (!PageAligned(size) || size == 0)
{
return KernelResult.InvalidSize;
}
if (permission != MemoryPermission.None &&
permission != MemoryPermission.Read &&
permission != MemoryPermission.ReadAndWrite &&
permission != MemoryPermission.ReadAndExecute)
{
return KernelResult.InvalidPermission;
}
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
KProcess targetProcess = currentProcess.HandleTable.GetObject<KProcess>(handle);
if (targetProcess == null)
{
return KernelResult.InvalidHandle;
}
if (targetProcess.MemoryManager.OutsideAddrSpace(src, size))
{
return KernelResult.InvalidMemState;
}
return targetProcess.MemoryManager.SetProcessMemoryPermission(src, size, permission);
}
private static bool PageAligned(ulong position)
{
return (position & (KMemoryManager.PageSize - 1)) == 0;

View file

@ -71,7 +71,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{ 0x6f, nameof(SvcHandler.GetSystemInfo64) },
{ 0x70, nameof(SvcHandler.CreatePort64) },
{ 0x71, nameof(SvcHandler.ManageNamedPort64) },
{ 0x72, nameof(SvcHandler.ConnectToPort64) }
{ 0x72, nameof(SvcHandler.ConnectToPort64) },
{ 0x73, nameof(SvcHandler.SetProcessMemoryPermission64) },
{ 0x77, nameof(SvcHandler.MapProcessCodeMemory64) },
{ 0x78, nameof(SvcHandler.UnmapProcessCodeMemory64) }
};
_svcTable64 = new Action<SvcHandler, CpuThreadState>[0x80];

View file

@ -312,7 +312,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer
foreach (VoiceContext voice in _voices)
{
if (!voice.Playing)
if (!voice.Playing || voice.CurrentWaveBuffer.Size == 0)
{
continue;
}

View file

@ -14,20 +14,24 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer
private int _bufferIndex;
private int _offset;
public int SampleRate;
public int ChannelsCount;
public int SampleRate { get; set; }
public int ChannelsCount { get; set; }
public float Volume;
public float Volume { get; set; }
public PlayState PlayState;
public PlayState PlayState { get; set; }
public SampleFormat SampleFormat;
public SampleFormat SampleFormat { get; set; }
public AdpcmDecoderContext AdpcmCtx;
public AdpcmDecoderContext AdpcmCtx { get; set; }
public WaveBuffer[] WaveBuffers;
public WaveBuffer[] WaveBuffers { get; }
public VoiceOut OutStatus;
public WaveBuffer CurrentWaveBuffer => WaveBuffers[_bufferIndex];
private VoiceOut _outStatus;
public VoiceOut OutStatus => _outStatus;
private int[] _samples;
@ -56,9 +60,9 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer
_bufferIndex = 0;
_offset = 0;
OutStatus.PlayedSamplesCount = 0;
OutStatus.PlayedWaveBuffersCount = 0;
OutStatus.VoiceDropsCount = 0;
_outStatus.PlayedSamplesCount = 0;
_outStatus.PlayedWaveBuffersCount = 0;
_outStatus.VoiceDropsCount = 0;
}
public int[] GetBufferData(MemoryManager memory, int maxSamples, out int samplesCount)
@ -94,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer
samplesCount = size / AudioConsts.HostChannelsCount;
OutStatus.PlayedSamplesCount += samplesCount;
_outStatus.PlayedSamplesCount += samplesCount;
_offset += size;
@ -107,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer
SetBufferIndex((_bufferIndex + 1) & 3);
}
OutStatus.PlayedWaveBuffersCount++;
_outStatus.PlayedWaveBuffersCount++;
if (wb.LastBuffer != 0)
{

View file

@ -0,0 +1,11 @@
using System;
namespace Ryujinx.HLE.HOS.Services.FspSrv
{
struct FileTimestamp
{
public DateTime CreationDateTime;
public DateTime ModifiedDateTime;
public DateTime LastAccessDateTime;
}
}

View file

@ -38,8 +38,8 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
{ 10, Commit },
{ 11, GetFreeSpaceSize },
{ 12, GetTotalSpaceSize },
{ 13, CleanDirectoryRecursively }
//{ 14, GetFileTimeStampRaw }
{ 13, CleanDirectoryRecursively },
{ 14, GetFileTimeStampRaw }
};
_openPaths = new HashSet<string>();
@ -368,6 +368,34 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
return 0;
}
// GetFileTimeStampRaw(buffer<bytes<0x301>, 0x19, 0x301> path) -> bytes<0x20> timestamp
public long GetFileTimeStampRaw(ServiceCtx context)
{
string name = ReadUtf8String(context);
string path = _provider.GetFullPath(name);
if (_provider.FileExists(path) || _provider.DirectoryExists(path))
{
FileTimestamp timestamp = _provider.GetFileTimeStampRaw(path);
context.ResponseData.Write(new DateTimeOffset(timestamp.CreationDateTime).ToUnixTimeSeconds());
context.ResponseData.Write(new DateTimeOffset(timestamp.ModifiedDateTime).ToUnixTimeSeconds());
context.ResponseData.Write(new DateTimeOffset(timestamp.LastAccessDateTime).ToUnixTimeSeconds());
byte[] data = new byte[8];
// is valid?
data[0] = 1;
context.ResponseData.Write(data);
return 0;
}
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
}
private bool IsPathAlreadyInUse(string path)
{
lock (_openPaths)

View file

@ -1,5 +1,9 @@
using Ryujinx.HLE.HOS.Ipc;
using LibHac;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using System;
using System.Collections.Generic;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Ns
{
@ -9,14 +13,211 @@ namespace Ryujinx.HLE.HOS.Services.Ns
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
private bool _isInitialized;
public IApplicationManagerInterface()
{
_commands = new Dictionary<int, ServiceProcessRequest>
{
{ 400, GetApplicationControlData }
};
}
public long GetApplicationControlData(ServiceCtx context)
{
long position = context.Request.ReceiveBuff[0].Position;
Nacp nacp = context.Device.System.ControlData;
for (int i = 0; i < 0x10; i++)
{
NacpDescription description = nacp.Descriptions[i];
byte[] titleData = new byte[0x200];
byte[] developerData = new byte[0x100];
if (description !=null && description.Title != null)
{
byte[] titleDescriptionData = Encoding.ASCII.GetBytes(description.Title);
Buffer.BlockCopy(titleDescriptionData, 0, titleData, 0, titleDescriptionData.Length);
}
if (description != null && description.Developer != null)
{
byte[] developerDescriptionData = Encoding.ASCII.GetBytes(description.Developer);
Buffer.BlockCopy(developerDescriptionData, 0, developerData, 0, developerDescriptionData.Length);
}
context.Memory.WriteBytes(position, titleData);
context.Memory.WriteBytes(position + 0x200, developerData);
position += i * 0x300;
}
byte[] isbn = new byte[0x25];
if (nacp.Isbn != null)
{
byte[] isbnData = Encoding.ASCII.GetBytes(nacp.Isbn);
Buffer.BlockCopy(isbnData, 0, isbn, 0, isbnData.Length);
}
context.Memory.WriteBytes(position, isbn);
position += isbn.Length;
context.Memory.WriteByte(position++, nacp.StartupUserAccount);
context.Memory.WriteByte(position++, nacp.TouchScreenUsageMode);
context.Memory.WriteByte(position++, nacp.AocRegistrationType);
context.Memory.WriteInt32(position, nacp.AttributeFlag);
position += 4;
context.Memory.WriteUInt32(position, nacp.SupportedLanguageFlag);
position += 4;
context.Memory.WriteUInt32(position, nacp.ParentalControlFlag);
position += 4;
context.Memory.WriteByte(position++, nacp.Screenshot);
context.Memory.WriteByte(position++, nacp.VideoCapture);
context.Memory.WriteByte(position++, nacp.DataLossConfirmation);
context.Memory.WriteByte(position++, nacp.PlayLogPolicy);
context.Memory.WriteUInt64(position, nacp.PresenceGroupId);
position += 8;
for (int i = 0; i < nacp.RatingAge.Length; i++)
{
context.Memory.WriteSByte(position++, nacp.RatingAge[i]);
}
byte[] displayVersion = new byte[0x10];
if (nacp.DisplayVersion != null)
{
byte[] displayVersionData = Encoding.ASCII.GetBytes(nacp.DisplayVersion);
Buffer.BlockCopy(displayVersionData, 0, displayVersion, 0, displayVersionData.Length);
}
context.Memory.WriteBytes(position, displayVersion);
position += displayVersion.Length;
context.Memory.WriteUInt64(position, nacp.AddOnContentBaseId);
position += 8;
context.Memory.WriteUInt64(position, nacp.SaveDataOwnerId);
position += 8;
context.Memory.WriteInt64(position, nacp.UserAccountSaveDataSize);
position += 8;
context.Memory.WriteInt64(position, nacp.UserAccountSaveDataJournalSize);
position += 8;
context.Memory.WriteInt64(position, nacp.DeviceSaveDataSize);
position += 8;
context.Memory.WriteInt64(position, nacp.DeviceSaveDataJournalSize);
position += 8;
context.Memory.WriteInt64(position, nacp.BcatDeliveryCacheStorageSize);
position += 8;
byte[] applicationErrorCodeCategory = new byte[0x8];
if (nacp.ApplicationErrorCodeCategory != null)
{
byte[] applicationErrorCodeCategoryData = Encoding.ASCII.GetBytes(nacp.ApplicationErrorCodeCategory);
Buffer.BlockCopy(applicationErrorCodeCategoryData, 0, applicationErrorCodeCategoryData, 0, applicationErrorCodeCategoryData.Length);
}
context.Memory.WriteBytes(position, applicationErrorCodeCategory);
position += applicationErrorCodeCategory.Length;
for (int i = 0; i < nacp.LocalCommunicationId.Length; i++)
{
context.Memory.WriteUInt64(position, nacp.LocalCommunicationId[i]);
position += 8;
}
context.Memory.WriteByte(position++, nacp.LogoType);
context.Memory.WriteByte(position++, nacp.LogoHandling);
context.Memory.WriteByte(position++, nacp.RuntimeAddOnContentInstall);
byte[] reserved000 = new byte[0x3];
context.Memory.WriteBytes(position, reserved000);
position += reserved000.Length;
context.Memory.WriteByte(position++, nacp.CrashReport);
context.Memory.WriteByte(position++, nacp.Hdcp);
context.Memory.WriteUInt64(position, nacp.SeedForPseudoDeviceId);
position += 8;
byte[] bcatPassphrase = new byte[65];
if (nacp.BcatPassphrase != null)
{
byte[] bcatPassphraseData = Encoding.ASCII.GetBytes(nacp.BcatPassphrase);
Buffer.BlockCopy(bcatPassphraseData, 0, bcatPassphrase, 0, bcatPassphraseData.Length);
}
context.Memory.WriteBytes(position, bcatPassphrase);
position += bcatPassphrase.Length;
context.Memory.WriteByte(position++, nacp.Reserved01);
byte[] reserved02 = new byte[0x6];
context.Memory.WriteBytes(position, reserved02);
position += reserved02.Length;
context.Memory.WriteInt64(position, nacp.UserAccountSaveDataSizeMax);
position += 8;
context.Memory.WriteInt64(position, nacp.UserAccountSaveDataJournalSizeMax);
position += 8;
context.Memory.WriteInt64(position, nacp.DeviceSaveDataSizeMax);
position += 8;
context.Memory.WriteInt64(position, nacp.DeviceSaveDataJournalSizeMax);
position += 8;
context.Memory.WriteInt64(position, nacp.TemporaryStorageSize);
position += 8;
context.Memory.WriteInt64(position, nacp.CacheStorageSize);
position += 8;
context.Memory.WriteInt64(position, nacp.CacheStorageJournalSize);
position += 8;
context.Memory.WriteInt64(position, nacp.CacheStorageDataAndJournalSizeMax);
position += 8;
context.Memory.WriteInt16(position, nacp.CacheStorageIndex);
position += 2;
byte[] reserved03 = new byte[0x6];
context.Memory.WriteBytes(position, reserved03);
position += reserved03.Length;
for (int i = 0; i < 16; i++)
{
ulong value = 0;
if (nacp.PlayLogQueryableApplicationId.Count > i)
{
value = nacp.PlayLogQueryableApplicationId[i];
}
context.Memory.WriteUInt64(position, value);
position += 8;
}
context.Memory.WriteByte(position++, nacp.PlayLogQueryCapability);
context.Memory.WriteByte(position++, nacp.RepairFlag);
context.Memory.WriteByte(position++, nacp.ProgramIndex);
return 0;
}
}
}

View file

@ -13,8 +13,15 @@ namespace Ryujinx.HLE.HOS.Services.Ns
{
_commands = new Dictionary<int, ServiceProcessRequest>
{
//...
{ 7996, GetApplicationManagerInterface }
};
}
public long GetApplicationManagerInterface(ServiceCtx context)
{
MakeObject(context, new IApplicationManagerInterface());
return 0;
}
}
}

View file

@ -0,0 +1,34 @@
using Ryujinx.HLE.HOS.Ipc;
using System;
using System.Collections.Generic;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Pm
{
class IShellInterface : IpcService
{
private Dictionary<int, ServiceProcessRequest> _commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
public IShellInterface()
{
_commands = new Dictionary<int, ServiceProcessRequest>
{
{ 6, GetApplicationPid }
};
}
// GetApplicationPid() -> u64
public long GetApplicationPid(ServiceCtx context)
{
// FIXME: This is wrong but needed to make hb loader works
// TODO: Change this when we will have a way to process via a PM like interface.
long pid = context.Process.Pid;
context.ResponseData.Write(pid);
return 0;
}
}
}

View file

@ -17,6 +17,7 @@ using Ryujinx.HLE.HOS.Services.Ns;
using Ryujinx.HLE.HOS.Services.Nv;
using Ryujinx.HLE.HOS.Services.Pctl;
using Ryujinx.HLE.HOS.Services.Pl;
using Ryujinx.HLE.HOS.Services.Pm;
using Ryujinx.HLE.HOS.Services.Prepo;
using Ryujinx.HLE.HOS.Services.Psm;
using Ryujinx.HLE.HOS.Services.Set;
@ -131,6 +132,7 @@ namespace Ryujinx.HLE.HOS.Services
case "ns:am":
return new IApplicationManagerInterface();
case "ns:am2":
case "ns:ec":
return new IServiceGetterInterface();
@ -161,6 +163,9 @@ namespace Ryujinx.HLE.HOS.Services
case "pl:u":
return new ISharedFontManager();
case "pm:shell":
return new IShellInterface();
case "prepo:a":
return new IPrepoService();

View file

@ -23,9 +23,10 @@ namespace Ryujinx.HLE.HOS.Services.Sm
{
_commands = new Dictionary<int, ServiceProcessRequest>
{
{ 0, Initialize },
{ 1, GetService },
{ 2, RegisterService }
{ 0, Initialize },
{ 1, GetService },
{ 2, RegisterService },
{ 3, UnregisterService }
};
_registeredServices = new ConcurrentDictionary<string, KPort>();
@ -128,6 +129,36 @@ namespace Ryujinx.HLE.HOS.Services.Sm
return 0;
}
public long UnregisterService(ServiceCtx context)
{
if (!_isInitialized)
{
return ErrorCode.MakeError(ErrorModule.Sm, SmErr.NotInitialized);
}
long namePosition = context.RequestData.BaseStream.Position;
string name = ReadName(context);
context.RequestData.BaseStream.Seek(namePosition + 8, SeekOrigin.Begin);
bool isLight = (context.RequestData.ReadInt32() & 1) != 0;
int maxSessions = context.RequestData.ReadInt32();
if (name == string.Empty)
{
return ErrorCode.MakeError(ErrorModule.Sm, SmErr.InvalidName);
}
if (!_registeredServices.TryRemove(name, out _))
{
return ErrorCode.MakeError(ErrorModule.Sm, SmErr.NotRegistered);
}
return 0;
}
private static string ReadName(ServiceCtx context)
{
string name = string.Empty;

View file

@ -5,5 +5,6 @@ namespace Ryujinx.HLE.HOS.Services.Sm
public const int NotInitialized = 2;
public const int AlreadyRegistered = 4;
public const int InvalidName = 6;
public const int NotRegistered = 7;
}
}

View file

@ -6,6 +6,7 @@ using System;
using System.IO;
using System.Text;
using static Ryujinx.HLE.HOS.ErrorCode;
using static Ryujinx.HLE.HOS.Services.Android.Parcel;
namespace Ryujinx.HLE.HOS.Services.Vi
@ -35,6 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi
{ 2030, CreateStrayLayer },
{ 2031, DestroyStrayLayer },
{ 2101, SetLayerScalingMode },
{ 2102, ConvertScalingMode },
{ 5202, GetDisplayVSyncEvent }
};
@ -176,6 +178,35 @@ namespace Ryujinx.HLE.HOS.Services.Vi
return 0;
}
public long ConvertScalingMode(ServiceCtx context)
{
SrcScalingMode scalingMode = (SrcScalingMode)context.RequestData.ReadInt32();
DstScalingMode? destScalingMode = ConvetScalingModeImpl(scalingMode);
if (!destScalingMode.HasValue)
{
return MakeError(ErrorModule.Vi, 1);
}
context.ResponseData.Write((ulong)destScalingMode);
return 0;
}
private DstScalingMode? ConvetScalingModeImpl(SrcScalingMode srcScalingMode)
{
switch (srcScalingMode)
{
case SrcScalingMode.None: return DstScalingMode.None;
case SrcScalingMode.Freeze: return DstScalingMode.Freeze;
case SrcScalingMode.ScaleAndCrop: return DstScalingMode.ScaleAndCrop;
case SrcScalingMode.ScaleToWindow: return DstScalingMode.ScaleToWindow;
case SrcScalingMode.PreserveAspectRatio: return DstScalingMode.PreserveAspectRatio;
}
return null;
}
public long GetDisplayVSyncEvent(ServiceCtx context)
{
string name = GetDisplayName(context);

View file

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Vi
{
enum SrcScalingMode
{
Freeze = 0,
ScaleToWindow = 1,
ScaleAndCrop = 2,
None = 3,
PreserveAspectRatio = 4
}
enum DstScalingMode
{
None = 0,
Freeze = 1,
ScaleToWindow = 2,
ScaleAndCrop = 3,
PreserveAspectRatio = 4
}
}

View file

@ -13,6 +13,7 @@ namespace Ryujinx.HLE.Loaders.Executables
public int RoOffset { get; private set; }
public int DataOffset { get; private set; }
public int BssSize { get; private set; }
public int FileSize { get; private set; }
public int BssOffset => DataOffset + Data.Length;
@ -59,6 +60,8 @@ namespace Ryujinx.HLE.Loaders.Executables
Text = Read(textOffset, textSize);
Ro = Read(roOffset, roSize);
Data = Read(dataOffset, dataSize);
FileSize = fileSize;
}
}
}

View file

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

View file

@ -26,7 +26,11 @@ 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
<<<<<<< HEAD
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Profiler", "Ryujinx.Profiler\Ryujinx.Profiler.csproj", "{4E69B67F-8CA7-42CF-A9E1-CCB0915DFB34}"
=======
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{464D8AB7-B056-4A99-B207-B8DCFB47AAA9}"
>>>>>>> original_master
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -128,6 +132,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

View file

@ -1,225 +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;
using Ryujinx.Profiler;
using Ryujinx.Ui;
using Ryujinx.Common;
namespace Ryujinx
{
public static class Config
{
public static NpadKeyboard NpadKeyboard { get; private set; }
public static NpadController NpadController { get; private set; }
public static NPadDebug NPadDebug { 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);
}
}
}
}
string profilePath = parser.Value("Profile_Dump_Path");
double updateRateHz = Convert.ToSingle(parser.Value("Profiling_Update_Rate"));
Profile.Configure(new ProfilerSettings()
{
Enabled = Convert.ToBoolean(parser.Value("Profiling_Enabled")),
FileDumpEnabled = profilePath != "",
DumpLocation = profilePath,
UpdateRate = (float)((updateRateHz <= 0) ? -1 : 1.0f / updateRateHz),
History = (long)(Convert.ToDouble(parser.Value("Profiling_History")) * PerformanceCounter.TicksPerSecond),
MaxLevel = Convert.ToInt32(parser.Value("Profiling_Max_Level")),
});
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"))
});
NPadDebug = new NPadDebug(
new NPadDebugButtons()
{
ToggleProfiler = Convert.ToInt16(parser.Value(("Controls_Debug_Toggle_Profiler"))),
});
}
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(Path.Combine(Program.ApplicationDirectory, "Ryujinx.log")),
1000,
AsyncLogTargetOverflowAction.Block
));
}
Logger.SetEnable(LogLevel.Debug, Instance.LoggingEnableDebug);
Logger.SetEnable(LogLevel.Stub, Instance.LoggingEnableStub);
Logger.SetEnable(LogLevel.Info, Instance.LoggingEnableInfo);
Logger.SetEnable(LogLevel.Warning, Instance.LoggingEnableWarn);
Logger.SetEnable(LogLevel.Error, Instance.LoggingEnableError);
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

@ -11,6 +11,8 @@ namespace Ryujinx
{
class Program
{
public static string ApplicationDirectory => AppDomain.CurrentDomain.BaseDirectory;
static void Main(string[] args)
{
Console.Title = "Ryujinx Console";
@ -21,9 +23,8 @@ namespace Ryujinx
Switch device = new Switch(renderer, audioOut);
Config.Read(device);
Logger.Updated += Log.LogMessage;
Configuration.Load(Path.Combine(ApplicationDirectory, "Config.jsonc"));
Configuration.Configure(device);
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
@ -41,13 +42,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]);
}
@ -57,20 +58,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;
}
@ -78,7 +79,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))
@ -91,11 +92,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)
@ -106,7 +109,7 @@ namespace Ryujinx
if (e.IsTerminating)
{
Log.Close();
Logger.Shutdown();
}
}

View file

@ -1,125 +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 profiling (Only available on a profiling enabled build)
Profiling_Enabled = true
#Set profile file dump location, if blank file dumping disabled. (e.g. `ProfileDump.csv`)
Profile_Dump_Path =
#Update rate for profiler UI, in hertz. -1 updates every time a frame is issued
Profiling_Update_Rate = 4
#Set how long to keep profiling data in seconds, reduce if profiling is taking too much RAM
Profiling_History = 5
#Set the maximum profiling level. Higher values may cause a heavy load on your system but will allow you to profile in more detail.
Profiling_Max_Level = 0
#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
#Debug Controls
Controls_Debug_Toggle_Profiler = 10

View file

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

View file

@ -163,24 +163,24 @@ namespace Ryujinx
#endif
// Normal Input
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

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
{
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; }
/// <summary>
/// Enables or disables controller support
/// </summary>
public bool Enabled { get; private set; }
public NpadControllerLeft Left { private set; get; }
public NpadControllerRight Right { private set; get; }
/// <summary>
/// Controller Device Index
/// </summary>
public int Index { get; private set; }
/// <summary>
/// Controller Analog Stick Deadzone
/// </summary>
public float Deadzone { get; private set; }
/// <summary>
/// Controller Trigger Threshold
/// </summary>
public float TriggerThreshold { get; private set; }
/// <summary>
/// Left JoyCon Controller Bindings
/// </summary>
public NpadControllerLeft LeftJoycon { get; private set; }
/// <summary>
/// Right JoyCon Controller Bindings
/// </summary>
public NpadControllerRight RightJoycon { get; private set; }
public NpadController(
bool enabled,
int index,
float deadzone,
float triggerThreshold,
NpadControllerLeft 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)

View file

@ -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;
/// <summary>
/// Left JoyCon Keyboard Bindings
/// </summary>
public NpadKeyboardLeft LeftJoycon { get; private set; }
public NpadKeyboard(
NpadKeyboardLeft left,
NpadKeyboardRight right)
{
Left = left;
Right = right;
}
/// <summary>
/// Right JoyCon Keyboard Bindings
/// </summary>
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);
}

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