diff --git a/CONFIG.md b/CONFIG.md index eef7b68d99..b63f5e832b 100644 --- a/CONFIG.md +++ b/CONFIG.md @@ -1,123 +1,124 @@ ## Config File -`Ryujinx.conf` should be present in executable folder (It's an *.ini file) following this format: +`Config.jsonc` should be present in executable folder. The available settings follow: -- `Logging_Enable_Info` *(bool)* +- `graphics_shaders_dump_path` *(string)* + + Dump shaders in local directory (e.g. `C:\ShaderDumps`) + +- `logging_enable_debug` *(bool)* + + Enable the Debug Logging. + +- `logging_enable_stub` *(bool)* + + Enable the Trace Logging. + +- `logging_enable_info` *(bool)* Enable the Informations Logging. -- `Logging_Enable_Trace` *(bool)* +- `logging_enable_warn` *(bool)* - Enable the Trace Logging (Enabled in Debug recommended). - -- `Logging_Enable_Debug` *(bool)* + Enable the Warning Logging. - Enable the Debug Logging (Enabled in Debug recommended). +- `logging_enable_error` *(bool)* -- `Logging_Enable_Warn` *(bool)* + Enable the Error Logging. - Enable the Warning Logging (Enabled in Debug recommended). - -- `Logging_Enable_Error` *(bool)* - - Enable the Error Logging (Enabled in Debug recommended). - -- `Logging_Enable_Fatal` *(bool)* - - Enable the Fatal Logging (Enabled in Debug recommended). - -- `Logging_Enable_Ipc` *(bool)* - - Enable the Ipc Message Logging. - -- `Logging_Enable_LogFile` *(bool)* +- `enable_file_log` *(bool)* Enable writing the logging inside a Ryujinx.log file. - -- `GamePad_Index` *(int)* - The index of the Controller Device. - -- `GamePad_Deadzone` *(float)* +- `system_language` *(string)* - The deadzone of both analog sticks on the Controller. + Change System Language, [System Language list](https://gist.github.com/HorrorTroll/b6e4a88d774c3c9b3bdf54d79a7ca43b) -- `GamePad_Enable` *(bool)* - - Whether or not to enable Controller Support. - -- `Controls_Left_JoyConKeyboard_XX` *(int)* - ``` - Controls_Left_JoyConKeyboard_Stick_Up (int) - Controls_Left_JoyConKeyboard_Stick_Down (int) - Controls_Left_JoyConKeyboard_Stick_Left (int) - Controls_Left_JoyConKeyboard_Stick_Right (int) - Controls_Left_JoyConKeyboard_Stick_Button (int) - Controls_Left_JoyConKeyboard_DPad_Up (int) - Controls_Left_JoyConKeyboard_DPad_Down (int) - Controls_Left_JoyConKeyboard_DPad_Left (int) - Controls_Left_JoyConKeyboard_DPad_Right (int) - Controls_Left_JoyConKeyboard_Button_Minus (int) - Controls_Left_JoyConKeyboard_Button_L (int) - Controls_Left_JoyConKeyboard_Button_ZL (int) - ``` - - Keys of the Left Emulated Joycon, the values depend of the [OpenTK Enum Keys](https://github.com/opentk/opentk/blob/develop/src/OpenTK/Input/Key.cs). - - OpenTK use a QWERTY layout, so pay attention if you use another Keyboard Layout. - - Ex: `Controls_Left_JoyConKeyboard_Button_Minus = 52` > Tab key (All Layout). +- `docked_mode` *(bool)* -- `Controls_Right_JoyConKeyboard_XX` *(int)* - ``` - Controls_Right_JoyConKeyboard_Stick_Up (int) - Controls_Right_JoyConKeyboard_Stick_Down (int) - Controls_Right_JoyConKeyboard_Stick_Left (int) - Controls_Right_JoyConKeyboard_Stick_Right (int) - Controls_Right_JoyConKeyboard_Stick_Button (int) - Controls_Right_JoyConKeyboard_Button_A (int) - Controls_Right_JoyConKeyboard_Button_B (int) - Controls_Right_JoyConKeyboard_Button_X (int) - Controls_Right_JoyConKeyboard_Button_Y (int) - Controls_Right_JoyConKeyboard_Button_Plus (int) - Controls_Right_JoyConKeyboard_Button_R (int) - Controls_Right_JoyConKeyboard_Button_ZR (int) - ``` + Enable or Disable Docked Mode - Keys of the right Emulated Joycon, the values depend of the [OpenTK Enum Keys](https://github.com/opentk/opentk/blob/develop/src/OpenTK/Input/Key.cs). - - OpenTK use a QWERTY layout, so pay attention if you use another Keyboard Layout. - - Ex: `Controls_Right_JoyConKeyboard_Button_A = 83` > A key (QWERTY Layout) / Q key (AZERTY Layout). - -- `Controls_Left_JoyConController_XX` *(String)* - ``` - Controls_Left_JoyConController_Stick (String) - Controls_Left_JoyConController_Stick_Button (String) - Controls_Left_JoyConController_DPad_Up (String) - Controls_Left_JoyConController_DPad_Down (String) - Controls_Left_JoyConController_DPad_Left (String) - Controls_Left_JoyConController_DPad_Right (String) - Controls_Left_JoyConController_Button_Minus (String) - Controls_Left_JoyConController_Button_L (String) - Controls_Left_JoyConController_Button_ZL (String) - ``` - -- `Controls_Right_JoyConController_XX` *(String)* - ``` - Controls_Right_JoyConController_Stick (String) - Controls_Right_JoyConController_Stick_Button (String) - Controls_Right_JoyConController_Button_A (String) - Controls_Right_JoyConController_Button_B (String) - Controls_Right_JoyConController_Button_X (String) - Controls_Right_JoyConController_Button_Y (String) - Controls_Right_JoyConController_Button_Plus (String) - Controls_Right_JoyConController_Button_R (String) - Controls_Right_JoyConController_Button_ZR (String) - ``` +- `enable_vsync` *(bool)* -- Default Mapping - - Controller + Enable or Disable Game Vsync + +- `enable_multicore_scheduling` *(bool)* + + Enable or Disable Multi-core scheduling of threads + +- `enable_fs_integrity_checks` *(bool)* + + Enable integrity checks on Switch content files + +- `controller_type` *(string)* + + The primary controller's type. + Supported Values: `Handheld`, `ProController`, `NpadPair`, `NpadLeft`, `NpadRight` + +- `keyboard_controls` *(object)* : + - `left_joycon` *(object)* : + Left JoyCon Keyboard Bindings + - `stick_up` *(string)* + - `stick_down` *(string)* + - `stick_left` *(string)* + - `stick_right` *(string)* + - `stick_button` *(string)* + - `dpad_up` *(string)* + - `dpad_down` *(string)* + - `dpad_left` *(string)* + - `dpad_right` *(string)* + - `button_minus` *(string)* + - `button_l` *(string)* + - `button_zl` *(string)* + - `right_joycon` *(object)* : + Right JoyCon Keyboard Bindings + - `stick_up` *(string)* + - `stick_down` *(string)* + - `stick_left` *(string)* + - `stick_right` *(string)* + - `stick_button` *(string)* + - `button_a` *(string)* + - `button_b` *(string)* + - `button_x` *(string)* + - `button_y` *(string)* + - `button_plus` *(string)* + - `button_r` *(string)* + - `button_zr` *(string)* + +- `gamepad_controls` *(object)* : + - `enabled` *(bool)* + Whether or not to enable Controller Support. + - `index` *(int)* + The index of the Controller Device. + - `deadzone` *(number)* + The deadzone of both analog sticks on the Controller. + - `trigger_threshold` *(number)* + The value of how pressed down each trigger has to be in order to register a button press + - `left_joycon` *(object)* : + Left JoyCon Controller Bindings + - `stick` *(string)* + - `stick_button` *(string)* + - `dpad_up` *(string)* + - `dpad_down` *(string)* + - `dpad_left` *(string)* + - `dpad_right` *(string)* + - `button_minus` *(string)* + - `button_l` *(string)* + - `button_zl` *(string)* + - `right_joycon` *(object)* : + Right JoyCon Controller Bindings + - `stick` *(string)* + - `stick_button` *(string)* + - `button_a` *(string)* + - `button_b` *(string)* + - `button_x` *(string)* + - `button_y` *(string)* + - `button_plus` *(string)* + - `button_r` *(string)* + - `button_zr` *(string)* + +### Default Mapping + #### Controller - Left Joycon: - Analog Stick = Left Analog Stick - DPad Up = DPad Up @@ -137,7 +138,8 @@ - Plus = Start / Options - R = Right Shoulder Button - ZR = Right Trigger - - Keyboard + + #### Keyboard - Left Joycon: - Stick Up = W - Stick Down = S @@ -166,7 +168,7 @@ - R = U - ZR = O -- Valid Button Mappings +### Valid Button Mappings - A = The A / Cross Button - B = The B / Circle Button - X = The X / Square Button diff --git a/README.md b/README.md index bdf8588ae1..10279b5055 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ or just drag'n'drop the homebrew *.NRO / *.NSO or the game *.NSP / *.XCI on the - Controller Input is supported, see [CONFIG.md](CONFIG.md) - - Config File: `Ryujinx.conf` should be present in executable folder. + - Config File: `Config.jsonc` should be present in executable folder. For more information [you can go here](CONFIG.md). **Help** diff --git a/Ryujinx.Audio/Native/libsoundio/MarshalExtensions.cs b/Ryujinx.Audio/Native/libsoundio/MarshalExtensions.cs index ec3eef3757..dfa699c82a 100644 --- a/Ryujinx.Audio/Native/libsoundio/MarshalExtensions.cs +++ b/Ryujinx.Audio/Native/libsoundio/MarshalExtensions.cs @@ -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)); + } + } } diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOOutStream.cs b/Ryujinx.Audio/Native/libsoundio/SoundIOOutStream.cs index 0b77e1af84..a910142563 100644 --- a/Ryujinx.Audio/Native/libsoundio/SoundIOOutStream.cs +++ b/Ryujinx.Audio/Native/libsoundio/SoundIOOutStream.cs @@ -77,6 +77,12 @@ namespace SoundIOSharp } static readonly int software_latency_offset = (int)Marshal.OffsetOf ("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 ("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); + } } } diff --git a/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dll b/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dll index 53a83f0f45..48804312e0 100644 Binary files a/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dll and b/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dll differ diff --git a/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dylib b/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dylib index f6acabed3d..10171f4fb6 100644 Binary files a/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dylib and b/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dylib differ diff --git a/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.so b/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.so index 2b9cf1b30e..87c8b50657 100644 Binary files a/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.so and b/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.so differ diff --git a/Ryujinx.Audio/Native/libsoundio/libsoundio-interop.cs b/Ryujinx.Audio/Native/libsoundio/libsoundio-interop.cs index 6eb09370f1..5377582f31 100644 --- a/Ryujinx.Audio/Native/libsoundio/libsoundio-interop.cs +++ b/Ryujinx.Audio/Native/libsoundio/libsoundio-interop.cs @@ -178,7 +178,7 @@ namespace SoundIOSharp } [StructLayout(LayoutKind.Sequential)] - struct SoundIoChannelLayout // soundio.h (302, 8) + struct SoundIoChannelLayout // soundio.h (306, 8) { [CTypeDetails("Pointer")] 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")] public System.IntPtr @ptr; public int @step; } [StructLayout(LayoutKind.Sequential)] - struct SoundIo // soundio.h (324, 8) + struct SoundIo // soundio.h (328, 8) { [CTypeDetails("Pointer")] public System.IntPtr @userdata; [CTypeDetails("Pointer")] 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")] public System.IntPtr @soundio; [CTypeDetails("Pointer")] 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")] public System.IntPtr @device; public SoundIoFormat @format; public int @sample_rate; public SoundIoChannelLayout @layout; public double @software_latency; + public float @volume; [CTypeDetails("Pointer")] public System.IntPtr @userdata; [CTypeDetails("Pointer")] public delegate4 @write_callback; [CTypeDetails("Pointer")] 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")] 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")]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")]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")]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")]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")]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")]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")]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")]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")]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")]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")]System.IntPtr @a, [CTypeDetails("Pointer")]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")]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")]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")]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")]System.IntPtr @preferred_layouts, int @preferred_layout_count, [CTypeDetails("Pointer")]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")]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")]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")]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")]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")]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")]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")]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")]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")]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")]System.IntPtr @a, [CTypeDetails("Pointer")]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")]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")]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")]System.IntPtr @device, [CTypeDetails("Pointer")]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")]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")]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")]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")]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")]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")]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")]System.IntPtr @outstream, [CTypeDetails("Pointer")]System.IntPtr @areas, [CTypeDetails("Pointer")]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")]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")]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")]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")]System.IntPtr @outstream, [CTypeDetails("Pointer")]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")]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")]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")]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")]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")]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")]System.IntPtr @instream, [CTypeDetails("Pointer")]System.IntPtr @areas, [CTypeDetails("Pointer")]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")]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")]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")]System.IntPtr @instream, [CTypeDetails("Pointer")]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")]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")]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")]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")]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")]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")]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")]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")]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")]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")]System.IntPtr @ring_buffer); diff --git a/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioOut.cs b/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioOut.cs index 0d3e74ddb9..649aa8d72e 100644 --- a/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioOut.cs +++ b/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioOut.cs @@ -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(); } + + /// + /// Searches for a shared version of the default audio device + /// + /// The audio context + /// Whether to fallback to the raw default audio device if a non-raw device cannot be found + 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; + } + + /// + /// Determines if SoundIO can connect to a supported backend + /// + /// + 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(); + } + } + } } } \ No newline at end of file diff --git a/Ryujinx.Common/StructIOExtension.cs b/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs similarity index 83% rename from Ryujinx.Common/StructIOExtension.cs rename to Ryujinx.Common/Extensions/BinaryReaderExtensions.cs index 8671b1920b..49af946fd3 100644 --- a/Ryujinx.Common/StructIOExtension.cs +++ b/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs @@ -1,14 +1,13 @@ using System; -using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; -using System.Text; namespace Ryujinx.Common { - public static class StructIOExtension + public static class BinaryReaderExtensions { - public unsafe static T ReadStruct(this BinaryReader reader) where T : struct + public unsafe static T ReadStruct(this BinaryReader reader) + where T : struct { int size = Marshal.SizeOf(); @@ -20,7 +19,8 @@ namespace Ryujinx.Common } } - public unsafe static void WriteStruct(this BinaryWriter writer, T value) where T : struct + public unsafe static void WriteStruct(this BinaryWriter writer, T value) + where T : struct { long size = Marshal.SizeOf(); diff --git a/Ryujinx.Common/Extensions/EnumExtensions.cs b/Ryujinx.Common/Extensions/EnumExtensions.cs new file mode 100644 index 0000000000..560af88296 --- /dev/null +++ b/Ryujinx.Common/Extensions/EnumExtensions.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ryujinx.Common +{ + public static class EnumExtensions + { + public static T[] GetValues() + { + return (T[])Enum.GetValues(typeof(T)); + } + } +} diff --git a/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs b/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs new file mode 100644 index 0000000000..0c4396e7f2 --- /dev/null +++ b/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs @@ -0,0 +1,53 @@ +using System.Reflection; +using System.Text; + +namespace Ryujinx.Common.Logging +{ + internal class DefaultLogFormatter : ILogFormatter + { + private static readonly ObjectPool _stringBuilderPool = SharedPools.Default(); + + public string Format(LogEventArgs args) + { + StringBuilder sb = _stringBuilderPool.Allocate(); + + try + { + sb.Clear(); + + sb.AppendFormat(@"{0:hh\:mm\:ss\.fff}", args.Time); + sb.Append(" | "); + sb.AppendFormat("{0:d4}", args.ThreadId); + sb.Append(' '); + sb.Append(args.Message); + + if (args.Data != null) + { + PropertyInfo[] props = args.Data.GetType().GetProperties(); + + sb.Append(' '); + + foreach (var prop in props) + { + sb.Append(prop.Name); + sb.Append(": "); + sb.Append(prop.GetValue(args.Data)); + sb.Append(" - "); + } + + // We remove the final '-' from the string + if (props.Length > 0) + { + sb.Remove(sb.Length - 3, 3); + } + } + + return sb.ToString(); + } + finally + { + _stringBuilderPool.Release(sb); + } + } + } +} diff --git a/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs b/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs new file mode 100644 index 0000000000..9a55bc6b0d --- /dev/null +++ b/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Common.Logging +{ + interface ILogFormatter + { + string Format(LogEventArgs args); + } +} diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index f20347b6b5..66a83b376b 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -2,6 +2,7 @@ namespace Ryujinx.Common.Logging { public enum LogClass { + Application, Audio, Cpu, Font, diff --git a/Ryujinx.Common/Logging/Logger.cs b/Ryujinx.Common/Logging/Logger.cs index 35ca416bc9..88ebe4731a 100644 --- a/Ryujinx.Common/Logging/Logger.cs +++ b/Ryujinx.Common/Logging/Logger.cs @@ -1,8 +1,7 @@ using System; +using System.Collections.Generic; using System.Diagnostics; -using System.Reflection; using System.Runtime.CompilerServices; -using System.Text; using System.Threading; namespace Ryujinx.Common.Logging @@ -14,9 +13,9 @@ namespace Ryujinx.Common.Logging private static readonly bool[] m_EnabledLevels; private static readonly bool[] m_EnabledClasses; - public static event EventHandler Updated; + private static readonly List m_LogTargets; - public static bool EnableFileLog { get; set; } + public static event EventHandler Updated; static Logger() { @@ -33,9 +32,30 @@ namespace Ryujinx.Common.Logging m_EnabledClasses[index] = true; } + m_LogTargets = new List(); + m_Time = Stopwatch.StartNew(); } + public static void AddTarget(ILogTarget target) + { + m_LogTargets.Add(target); + + Updated += target.Log; + } + + public static void Shutdown() + { + Updated = null; + + foreach(var target in m_LogTargets) + { + target.Dispose(); + } + + m_LogTargets.Clear(); + } + public static void SetEnable(LogLevel logLevel, bool enabled) { m_EnabledLevels[(int)logLevel] = enabled; diff --git a/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs b/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs new file mode 100644 index 0000000000..a805a83b6e --- /dev/null +++ b/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; + +namespace Ryujinx.Common.Logging +{ + public enum AsyncLogTargetOverflowAction + { + /// + /// Block until there's more room in the queue + /// + Block = 0, + + /// + /// Discard the overflowing item + /// + Discard = 1 + } + + public class AsyncLogTargetWrapper : ILogTarget + { + private ILogTarget _target; + + private Thread _messageThread; + + private BlockingCollection _messageQueue; + + private readonly int _overflowTimeout; + + public AsyncLogTargetWrapper(ILogTarget target) + : this(target, -1, AsyncLogTargetOverflowAction.Block) + { } + + public AsyncLogTargetWrapper(ILogTarget target, int queueLimit, AsyncLogTargetOverflowAction overflowAction) + { + _target = target; + _messageQueue = new BlockingCollection(queueLimit); + _overflowTimeout = overflowAction == AsyncLogTargetOverflowAction.Block ? -1 : 0; + + _messageThread = new Thread(() => { + while (!_messageQueue.IsCompleted) + { + try + { + _target.Log(this, _messageQueue.Take()); + } + catch (InvalidOperationException) + { + // IOE means that Take() was called on a completed collection. + // Some other thread can call CompleteAdding after we pass the + // IsCompleted check but before we call Take. + // We can simply catch the exception since the loop will break + // on the next iteration. + } + } + }); + + _messageThread.IsBackground = true; + _messageThread.Start(); + } + + public void Log(object sender, LogEventArgs e) + { + if (!_messageQueue.IsAddingCompleted) + { + _messageQueue.TryAdd(e, _overflowTimeout); + } + } + + public void Dispose() + { + _messageQueue.CompleteAdding(); + _messageThread.Join(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs b/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs new file mode 100644 index 0000000000..871076a41a --- /dev/null +++ b/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Concurrent; + +namespace Ryujinx.Common.Logging +{ + public class ConsoleLogTarget : ILogTarget + { + private static readonly ConcurrentDictionary _logColors; + + private readonly ILogFormatter _formatter; + + static ConsoleLogTarget() + { + _logColors = new ConcurrentDictionary { + [ LogLevel.Stub ] = ConsoleColor.DarkGray, + [ LogLevel.Info ] = ConsoleColor.White, + [ LogLevel.Warning ] = ConsoleColor.Yellow, + [ LogLevel.Error ] = ConsoleColor.Red + }; + } + + public ConsoleLogTarget() + { + _formatter = new DefaultLogFormatter(); + } + + public void Log(object sender, LogEventArgs args) + { + if (_logColors.TryGetValue(args.Level, out ConsoleColor color)) + { + Console.ForegroundColor = color; + + Console.WriteLine(_formatter.Format(args)); + + Console.ResetColor(); + } + else + { + Console.WriteLine(_formatter.Format(args)); + } + } + + public void Dispose() + { + Console.ResetColor(); + } + } +} diff --git a/Ryujinx.Common/Logging/Targets/FileLogTarget.cs b/Ryujinx.Common/Logging/Targets/FileLogTarget.cs new file mode 100644 index 0000000000..85dc82497a --- /dev/null +++ b/Ryujinx.Common/Logging/Targets/FileLogTarget.cs @@ -0,0 +1,36 @@ +using System.IO; +using System.Text; + +namespace Ryujinx.Common.Logging +{ + public class FileLogTarget : ILogTarget + { + private static readonly ObjectPool _stringBuilderPool = SharedPools.Default(); + + private readonly StreamWriter _logWriter; + private readonly ILogFormatter _formatter; + + public FileLogTarget(string path) + : this(path, FileShare.Read, FileMode.Append) + { } + + public FileLogTarget(string path, FileShare fileShare, FileMode fileMode) + { + _logWriter = new StreamWriter(File.Open(path, fileMode, FileAccess.Write, fileShare)); + _formatter = new DefaultLogFormatter(); + } + + public void Log(object sender, LogEventArgs args) + { + _logWriter.WriteLine(_formatter.Format(args)); + _logWriter.Flush(); + } + + public void Dispose() + { + _logWriter.WriteLine("---- End of Log ----"); + _logWriter.Flush(); + _logWriter.Dispose(); + } + } +} diff --git a/Ryujinx.Common/Logging/Targets/ILogTarget.cs b/Ryujinx.Common/Logging/Targets/ILogTarget.cs new file mode 100644 index 0000000000..261c5e64b2 --- /dev/null +++ b/Ryujinx.Common/Logging/Targets/ILogTarget.cs @@ -0,0 +1,9 @@ +using System; + +namespace Ryujinx.Common.Logging +{ + public interface ILogTarget : IDisposable + { + void Log(object sender, LogEventArgs args); + } +} diff --git a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs new file mode 100644 index 0000000000..410394aa2d --- /dev/null +++ b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs @@ -0,0 +1,35 @@ +using System.IO; +using Utf8Json; + +namespace Ryujinx.Common.Logging +{ + public class JsonLogTarget : ILogTarget + { + private Stream _stream; + private bool _leaveOpen; + + public JsonLogTarget(Stream stream) + { + _stream = stream; + } + + public JsonLogTarget(Stream stream, bool leaveOpen) + { + _stream = stream; + _leaveOpen = leaveOpen; + } + + public void Log(object sender, LogEventArgs e) + { + JsonSerializer.Serialize(_stream, e); + } + + public void Dispose() + { + if (!_leaveOpen) + { + _stream.Dispose(); + } + } + } +} diff --git a/Ryujinx.Common/Pools/ObjectPool.cs b/Ryujinx.Common/Pools/ObjectPool.cs new file mode 100644 index 0000000000..dba671bb74 --- /dev/null +++ b/Ryujinx.Common/Pools/ObjectPool.cs @@ -0,0 +1,75 @@ +using System; +using System.Threading; + +namespace Ryujinx.Common +{ + public class ObjectPool + where T : class + { + private T _firstItem; + private readonly T[] _items; + + private readonly Func _factory; + + public ObjectPool(Func factory, int size) + { + _items = new T[size - 1]; + _factory = factory; + } + + public T Allocate() + { + var instance = _firstItem; + + if (instance == null || instance != Interlocked.CompareExchange(ref _firstItem, null, instance)) + { + instance = AllocateInternal(); + } + + return instance; + } + + private T AllocateInternal() + { + var items = _items; + + for (int i = 0; i < items.Length; i++) + { + var instance = items[i]; + + if (instance != null && instance == Interlocked.CompareExchange(ref items[i], null, instance)) + { + return instance; + } + } + + return _factory(); + } + + public void Release(T obj) + { + if (_firstItem == null) + { + _firstItem = obj; + } + else + { + ReleaseInternal(obj); + } + } + + private void ReleaseInternal(T obj) + { + var items = _items; + + for (int i = 0; i < items.Length; i++) + { + if (items[i] == null) + { + items[i] = obj; + break; + } + } + } + } +} diff --git a/Ryujinx.Common/Pools/SharedPools.cs b/Ryujinx.Common/Pools/SharedPools.cs new file mode 100644 index 0000000000..b4860b85b6 --- /dev/null +++ b/Ryujinx.Common/Pools/SharedPools.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Common +{ + public static class SharedPools + { + private static class DefaultPool + where T : class, new() + { + public static readonly ObjectPool Instance = new ObjectPool(() => new T(), 20); + } + + public static ObjectPool Default() + where T : class, new() + { + return DefaultPool.Instance; + } + } +} diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj index 91c1275be4..cf078db851 100644 --- a/Ryujinx.Common/Ryujinx.Common.csproj +++ b/Ryujinx.Common/Ryujinx.Common.csproj @@ -25,5 +25,9 @@ TRACE;USE_PROFILING true + + + + diff --git a/Ryujinx.Common/BitUtils.cs b/Ryujinx.Common/Utilities/BitUtils.cs similarity index 100% rename from Ryujinx.Common/BitUtils.cs rename to Ryujinx.Common/Utilities/BitUtils.cs diff --git a/Ryujinx.Common/HexUtils.cs b/Ryujinx.Common/Utilities/HexUtils.cs similarity index 100% rename from Ryujinx.Common/HexUtils.cs rename to Ryujinx.Common/Utilities/HexUtils.cs diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs index deccd890f7..6a928603ae 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs @@ -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) { diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index 77a451e959..854c827ee0 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -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)"; } } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index 24c85c8f0e..469092a23c 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -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); } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs index 6531138e7a..f0f92148e6 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs @@ -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); diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs index 9ff3b36a3b..1ca3ca1ce1 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs @@ -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); diff --git a/Ryujinx.HLE/FileSystem/FileSystemProvider.cs b/Ryujinx.HLE/FileSystem/FileSystemProvider.cs index a806c9eebb..f5459eece0 100644 --- a/Ryujinx.HLE/FileSystem/FileSystemProvider.cs +++ b/Ryujinx.HLE/FileSystem/FileSystemProvider.cs @@ -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 + }; + } } } diff --git a/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs b/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs index 8e2cae6430..82cdebd905 100644 --- a/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs +++ b/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs @@ -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); } } diff --git a/Ryujinx.HLE/FileSystem/PFsProvider.cs b/Ryujinx.HLE/FileSystem/PFsProvider.cs index fdddc9b089..69e7a9b83d 100644 --- a/Ryujinx.HLE/FileSystem/PFsProvider.cs +++ b/Ryujinx.HLE/FileSystem/PFsProvider.cs @@ -143,5 +143,10 @@ namespace Ryujinx.HLE.FileSystem { throw new NotSupportedException(); } + + public FileTimestamp GetFileTimeStampRaw(string name) + { + throw new NotImplementedException(); + } } } diff --git a/Ryujinx.HLE/FileSystem/RomFsProvider.cs b/Ryujinx.HLE/FileSystem/RomFsProvider.cs index 86bf234859..f64d99c704 100644 --- a/Ryujinx.HLE/FileSystem/RomFsProvider.cs +++ b/Ryujinx.HLE/FileSystem/RomFsProvider.cs @@ -160,5 +160,10 @@ namespace Ryujinx.HLE.FileSystem { throw new NotSupportedException(); } + + public FileTimestamp GetFileTimeStampRaw(string name) + { + throw new NotImplementedException(); + } } } diff --git a/Ryujinx.HLE/HOS/HomebrewRomFsStream.cs b/Ryujinx.HLE/HOS/HomebrewRomFsStream.cs new file mode 100644 index 0000000000..f39faf4d4c --- /dev/null +++ b/Ryujinx.HLE/HOS/HomebrewRomFsStream.cs @@ -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(); + } + } + } +} diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 8a419af343..b5ce555a16 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -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() diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs index 388dcc217c..6f8180c507 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs @@ -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(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(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(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; diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs index cbcb712f75..dd98e8a06e 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs @@ -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[0x80]; diff --git a/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs b/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs index 72839c2bff..8c98438501 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs @@ -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; } diff --git a/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/VoiceContext.cs b/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/VoiceContext.cs index a877081d55..5de856122e 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/VoiceContext.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/VoiceContext.cs @@ -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) { diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/FileTimestamp.cs b/Ryujinx.HLE/HOS/Services/FspSrv/FileTimestamp.cs new file mode 100644 index 0000000000..879fb78cde --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/FspSrv/FileTimestamp.cs @@ -0,0 +1,11 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.FspSrv +{ + struct FileTimestamp + { + public DateTime CreationDateTime; + public DateTime ModifiedDateTime; + public DateTime LastAccessDateTime; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs index 9e29446017..bcb9dbaf81 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs @@ -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(); @@ -368,6 +368,34 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } + // GetFileTimeStampRaw(buffer, 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) diff --git a/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs index 72d7787f88..88fdb79221 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs @@ -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 Commands => _commands; - private bool _isInitialized; - public IApplicationManagerInterface() { _commands = new Dictionary { - + { 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; + } } } diff --git a/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs b/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs index 12f7b69b12..89dde1f9bb 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs @@ -13,8 +13,15 @@ namespace Ryujinx.HLE.HOS.Services.Ns { _commands = new Dictionary { - //... + { 7996, GetApplicationManagerInterface } }; } + + public long GetApplicationManagerInterface(ServiceCtx context) + { + MakeObject(context, new IApplicationManagerInterface()); + + return 0; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Pm/IShellInterface.cs b/Ryujinx.HLE/HOS/Services/Pm/IShellInterface.cs new file mode 100644 index 0000000000..8880b3348e --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Pm/IShellInterface.cs @@ -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 _commands; + + public override IReadOnlyDictionary Commands => _commands; + + public IShellInterface() + { + _commands = new Dictionary + { + { 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; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs index 3853d82e6f..83a217a5d3 100644 --- a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs +++ b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs @@ -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(); diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index 6940bfc8df..914862f1c9 100644 --- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -23,9 +23,10 @@ namespace Ryujinx.HLE.HOS.Services.Sm { _commands = new Dictionary { - { 0, Initialize }, - { 1, GetService }, - { 2, RegisterService } + { 0, Initialize }, + { 1, GetService }, + { 2, RegisterService }, + { 3, UnregisterService } }; _registeredServices = new ConcurrentDictionary(); @@ -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; diff --git a/Ryujinx.HLE/HOS/Services/Sm/SmErr.cs b/Ryujinx.HLE/HOS/Services/Sm/SmErr.cs index 5b5a66dc2f..7dd3a20573 100644 --- a/Ryujinx.HLE/HOS/Services/Sm/SmErr.cs +++ b/Ryujinx.HLE/HOS/Services/Sm/SmErr.cs @@ -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; } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs index 039cc81f40..b272e0788d 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs @@ -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); diff --git a/Ryujinx.HLE/HOS/Services/Vi/ScalingMode.cs b/Ryujinx.HLE/HOS/Services/Vi/ScalingMode.cs new file mode 100644 index 0000000000..824a27b70a --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Vi/ScalingMode.cs @@ -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 + } +} diff --git a/Ryujinx.HLE/Loaders/Executables/NxRelocatableObject.cs b/Ryujinx.HLE/Loaders/Executables/NxRelocatableObject.cs index eb3ca94a98..e68fe2670e 100644 --- a/Ryujinx.HLE/Loaders/Executables/NxRelocatableObject.cs +++ b/Ryujinx.HLE/Loaders/Executables/NxRelocatableObject.cs @@ -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; } } } \ No newline at end of file diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index d661b273ad..4a15f616e7 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -112,7 +112,6 @@ namespace Ryujinx.HLE if (disposing) { System.Dispose(); - VsyncEvent.Dispose(); } } diff --git a/Ryujinx.sln b/Ryujinx.sln index d9dc723a73..e319321d22 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -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 diff --git a/Ryujinx/Config.cs b/Ryujinx/Config.cs deleted file mode 100644 index 629ead4d1b..0000000000 --- a/Ryujinx/Config.cs +++ /dev/null @@ -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(parser.Value("System_Language")); - - device.System.State.SetLanguage(SetLanguage); - - device.System.State.DockedMode = Convert.ToBoolean(parser.Value("Docked_Mode")); - - device.EnableDeviceVsync = Convert.ToBoolean(parser.Value("Enable_Vsync")); - - if (Convert.ToBoolean(parser.Value("Enable_MultiCore_Scheduling"))) - { - device.System.EnableMultiCoreScheduling(); - } - - device.System.FsIntegrityCheckLevel = Convert.ToBoolean(parser.Value("Enable_FS_Integrity_Checks")) - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - - HidControllerType ControllerType = Enum.Parse(parser.Value("Controller_Type")); - - device.Hid.InitilizePrimaryController(ControllerType); - - NpadKeyboard = new NpadKeyboard( - - new NpadKeyboardLeft - { - StickUp = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Stick_Up")), - StickDown = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Stick_Down")), - StickLeft = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Stick_Left")), - StickRight = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Stick_Right")), - StickButton = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Stick_Button")), - DPadUp = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_DPad_Up")), - DPadDown = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_DPad_Down")), - DPadLeft = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_DPad_Left")), - DPadRight = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_DPad_Right")), - ButtonMinus = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Button_Minus")), - ButtonL = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Button_L")), - ButtonZl = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Button_ZL")) - }, - - new NpadKeyboardRight - { - StickUp = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Stick_Up")), - StickDown = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Stick_Down")), - StickLeft = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Stick_Left")), - StickRight = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Stick_Right")), - StickButton = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Stick_Button")), - ButtonA = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_A")), - ButtonB = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_B")), - ButtonX = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_X")), - ButtonY = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_Y")), - ButtonPlus = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_Plus")), - ButtonR = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_R")), - ButtonZr = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_ZR")) - }); - - NpadController = new NpadController( - Convert.ToBoolean(parser.Value("GamePad_Enable")), - Convert.ToInt32 (parser.Value("GamePad_Index")), - (float)Convert.ToDouble (parser.Value("GamePad_Deadzone"), CultureInfo.InvariantCulture), - (float)Convert.ToDouble (parser.Value("GamePad_Trigger_Threshold"), CultureInfo.InvariantCulture), - - new NpadControllerLeft - { - Stick = ToId(parser.Value("Controls_Left_JoyConController_Stick")), - StickButton = ToId(parser.Value("Controls_Left_JoyConController_Stick_Button")), - DPadUp = ToId(parser.Value("Controls_Left_JoyConController_DPad_Up")), - DPadDown = ToId(parser.Value("Controls_Left_JoyConController_DPad_Down")), - DPadLeft = ToId(parser.Value("Controls_Left_JoyConController_DPad_Left")), - DPadRight = ToId(parser.Value("Controls_Left_JoyConController_DPad_Right")), - ButtonMinus = ToId(parser.Value("Controls_Left_JoyConController_Button_Minus")), - ButtonL = ToId(parser.Value("Controls_Left_JoyConController_Button_L")), - ButtonZl = ToId(parser.Value("Controls_Left_JoyConController_Button_ZL")) - }, - - new NpadControllerRight - { - Stick = ToId(parser.Value("Controls_Right_JoyConController_Stick")), - StickButton = ToId(parser.Value("Controls_Right_JoyConController_Stick_Button")), - ButtonA = ToId(parser.Value("Controls_Right_JoyConController_Button_A")), - ButtonB = ToId(parser.Value("Controls_Right_JoyConController_Button_B")), - ButtonX = ToId(parser.Value("Controls_Right_JoyConController_Button_X")), - ButtonY = ToId(parser.Value("Controls_Right_JoyConController_Button_Y")), - ButtonPlus = ToId(parser.Value("Controls_Right_JoyConController_Button_Plus")), - ButtonR = ToId(parser.Value("Controls_Right_JoyConController_Button_R")), - ButtonZr = ToId(parser.Value("Controls_Right_JoyConController_Button_ZR")) - }); - - 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 _values; - - public IniParser(string path) - { - _values = File.ReadLines(path) - .Where(line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith('#')) - .Select(line => line.Split('=', 2)) - .ToDictionary(parts => parts[0].Trim(), parts => parts.Length > 1 ? parts[1].Trim() : null); - } - - public string Value(string name) - { - return _values.TryGetValue(name, out string value) ? value : null; - } - } -} \ No newline at end of file diff --git a/Ryujinx/Config.jsonc b/Ryujinx/Config.jsonc new file mode 100644 index 0000000000..1ba601647a --- /dev/null +++ b/Ryujinx/Config.jsonc @@ -0,0 +1,124 @@ +{ + "$schema": "./_schema.json", + + // Dump shaders in local directory (e.g. `C:\ShaderDumps`) + "graphics_shaders_dump_path": "", + + // Enable print debug logs + "logging_enable_debug": false, + + // Enable print stubbed calls logs + "logging_enable_stub": true, + + // Enable print informations logs + "logging_enable_info": true, + + // Enable print warning logs + "logging_enable_warn": true, + + // Enable print error logs + "logging_enable_error": true, + + // Filtered log classes, in a JSON array, eg. `[ "Loader", "ServiceFs" ]` + "logging_filtered_classes": [ ], + + // Enable file logging + "enable_file_log": true, + + // Change System Language + // System Language list: https://gist.github.com/HorrorTroll/b6e4a88d774c3c9b3bdf54d79a7ca43b + "system_language": "AmericanEnglish", + + // Enable or Disable Docked Mode + "docked_mode": false, + + // Enable or Disable Game Vsync + "enable_vsync": true, + + // Enable or Disable Multi-core scheduling of threads + "enable_multicore_scheduling": false, + + // Enable integrity checks on Switch content files + "enable_fs_integrity_checks": true, + + // The primary controller's type + // Supported Values: Handheld, ProController, NpadPair, NpadLeft, NpadRight + "controller_type": "Handheld", + + // Keyboard Controls + // https://github.com/opentk/opentk/blob/master/src/OpenTK/Input/Key.cs + "keyboard_controls": { + // Left JoyCon Keyboard Bindings + "left_joycon": { + "stick_up": "W", + "stick_down": "S", + "stick_left": "A", + "stick_right": "D", + "stick_button": "F", + "dpad_up": "Up", + "dpad_down": "Down", + "dpad_left": "Left", + "dpad_right": "Right", + "button_minus": "Minus", + "button_l": "E", + "button_zl": "Q" + }, + + // Right JoyCon Keyboard Bindings + "right_joycon": { + "stick_up": "I", + "stick_down": "K", + "stick_left": "J", + "stick_right": "L", + "stick_button": "H", + "button_a": "Z", + "button_b": "X", + "button_x": "C", + "button_y": "V", + "button_plus": "Plus", + "button_r": "U", + "button_zr": "O" + } + }, + + // Controller Controls + "gamepad_controls": { + // Whether or not to enable Controller support + "enabled": true, + + // Controller Device Index + "index": 0, + + // Controller Analog Stick Deadzone + "deadzone": 0.05, + + // The value of how pressed down each trigger has to be in order to register a button press + "trigger_threshold": 0.5, + + // Left JoyCon Controller Bindings + "left_joycon": { + "stick": "LJoystick", + "stick_button": "LStick", + "dpad_up": "DPadUp", + "dpad_down": "DPadDown", + "dpad_left": "DPadLeft", + "dpad_right": "DPadRight", + "button_minus": "Back", + "button_l": "LShoulder", + "button_zl": "LTrigger" + }, + + // Right JoyCon Controller Bindings + "right_joycon": { + "stick": "RJoystick", + "stick_button": "RStick", + "button_a": "B", + "button_b": "A", + "button_x": "Y", + "button_y": "X", + "button_plus": "Start", + "button_r": "RShoulder", + "button_zr": "RTrigger" + } + } +} \ No newline at end of file diff --git a/Ryujinx/Configuration.cs b/Ryujinx/Configuration.cs new file mode 100644 index 0000000000..96f4d66f4b --- /dev/null +++ b/Ryujinx/Configuration.cs @@ -0,0 +1,239 @@ +using LibHac.IO; +using OpenTK.Input; +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using Ryujinx.HLE; +using Ryujinx.HLE.HOS.SystemState; +using Ryujinx.HLE.Input; +using Ryujinx.UI.Input; +using System; +using System.IO; +using System.Threading.Tasks; +using Utf8Json; +using Utf8Json.Resolvers; + +namespace Ryujinx +{ + public class Configuration + { + /// + /// The default configuration instance + /// + public static Configuration Instance { get; private set; } + + /// + /// Dumps shaders in this local directory + /// + public string GraphicsShadersDumpPath { get; private set; } + + /// + /// Enables printing debug log messages + /// + public bool LoggingEnableDebug { get; private set; } + + /// + /// Enables printing stub log messages + /// + public bool LoggingEnableStub { get; private set; } + + /// + /// Enables printing info log messages + /// + public bool LoggingEnableInfo { get; private set; } + + /// + /// Enables printing warning log messages + /// + public bool LoggingEnableWarn { get; private set; } + + /// + /// Enables printing error log messages + /// + public bool LoggingEnableError { get; private set; } + + /// + /// Controls which log messages are written to the log targets + /// + public LogClass[] LoggingFilteredClasses { get; private set; } + + /// + /// Enables or disables logging to a file on disk + /// + public bool EnableFileLog { get; private set; } + + /// + /// Change System Language + /// + public SystemLanguage SystemLanguage { get; private set; } + + /// + /// Enables or disables Docked Mode + /// + public bool DockedMode { get; private set; } + + /// + /// Enables or disables Vertical Sync + /// + public bool EnableVsync { get; private set; } + + /// + /// Enables or disables multi-core scheduling of threads + /// + public bool EnableMultiCoreScheduling { get; private set; } + + /// + /// Enables integrity checks on Game content files + /// + public bool EnableFsIntegrityChecks { get; private set; } + + /// + /// The primary controller's type + /// + public HidControllerType ControllerType { get; private set; } + + /// + /// Keyboard control bindings + /// + public NpadKeyboard KeyboardControls { get; private set; } + + /// + /// Controller control bindings + /// + public NpadController GamepadControls { get; private set; } + + /// + /// Loads a configuration file from disk + /// + /// The path to the JSON configuration file + public static void Load(string path) + { + var resolver = CompositeResolver.Create( + new[] { new ConfigurationEnumFormatter() }, + new[] { StandardResolver.AllowPrivateSnakeCase } + ); + + using (Stream stream = File.OpenRead(path)) + { + Instance = JsonSerializer.Deserialize(stream, resolver); + } + } + + /// + /// Loads a configuration file asynchronously from disk + /// + /// The path to the JSON configuration file + public static async Task LoadAsync(string path) + { + var resolver = CompositeResolver.Create( + new[] { new ConfigurationEnumFormatter() }, + new[] { StandardResolver.AllowPrivateSnakeCase } + ); + + using (Stream stream = File.OpenRead(path)) + { + Instance = await JsonSerializer.DeserializeAsync(stream, resolver); + } + } + + /// + /// Configures a instance + /// + /// The instance to configure + public static void Configure(Switch device) + { + if (Instance == null) + { + throw new InvalidOperationException("Configuration has not been loaded yet."); + } + + GraphicsConfig.ShadersDumpPath = Instance.GraphicsShadersDumpPath; + + Logger.AddTarget(new AsyncLogTargetWrapper( + new ConsoleLogTarget(), + 1000, + AsyncLogTargetOverflowAction.Block + )); + + if (Instance.EnableFileLog) + { + Logger.AddTarget(new AsyncLogTargetWrapper( + new FileLogTarget(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()) + { + Logger.SetEnable(logClass, false); + } + + foreach (var logClass in Instance.LoggingFilteredClasses) + { + Logger.SetEnable(logClass, true); + } + } + + device.EnableDeviceVsync = Instance.EnableVsync; + + device.System.State.DockedMode = Instance.DockedMode; + + device.System.State.SetLanguage(Instance.SystemLanguage); + + if (Instance.EnableMultiCoreScheduling) + { + device.System.EnableMultiCoreScheduling(); + } + + device.System.FsIntegrityCheckLevel = Instance.EnableFsIntegrityChecks + ? IntegrityCheckLevel.ErrorOnInvalid + : IntegrityCheckLevel.None; + + if(Instance.GamepadControls.Enabled) + { + if (GamePad.GetName(Instance.GamepadControls.Index) == "Unmapped Controller") + { + Instance.GamepadControls.SetEnabled(false); + } + } + + device.Hid.InitilizePrimaryController(Instance.ControllerType); + } + + private class ConfigurationEnumFormatter : IJsonFormatter + where T : struct + { + public void Serialize(ref JsonWriter writer, T value, IJsonFormatterResolver formatterResolver) + { + formatterResolver.GetFormatterWithVerify() + .Serialize(ref writer, value.ToString(), formatterResolver); + } + + public T Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) + { + if (reader.ReadIsNull()) + { + return default(T); + } + + var enumName = formatterResolver.GetFormatterWithVerify() + .Deserialize(ref reader, formatterResolver); + + if(Enum.TryParse(enumName, out T result)) + { + return result; + } + + return default(T); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index 16b2d9d346..c14db0e765 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -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(); } } diff --git a/Ryujinx/Ryujinx.conf b/Ryujinx/Ryujinx.conf deleted file mode 100644 index bf936cddff..0000000000 --- a/Ryujinx/Ryujinx.conf +++ /dev/null @@ -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 diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index 5d4c71c74f..ab0ee599e1 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -32,7 +32,7 @@ - + PreserveNewest diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index 0f89fa9fa5..9738d6db09 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -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 diff --git a/Ryujinx/Ui/Log.cs b/Ryujinx/Ui/Log.cs deleted file mode 100644 index 5daae14026..0000000000 --- a/Ryujinx/Ui/Log.cs +++ /dev/null @@ -1,140 +0,0 @@ -using Ryujinx.Common.Logging; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Text; -using System.Threading; - -namespace Ryujinx -{ - static class Log - { - private static readonly string _path; - - private static StreamWriter _logWriter; - - private static Thread _messageThread; - - private static BlockingCollection _messageQueue; - - private static Dictionary _logColors; - - static Log() - { - _logColors = new Dictionary() - { - { LogLevel.Stub, ConsoleColor.DarkGray }, - { LogLevel.Info, ConsoleColor.White }, - { LogLevel.Warning, ConsoleColor.Yellow }, - { LogLevel.Error, ConsoleColor.Red } - }; - - _messageQueue = new BlockingCollection(10); - - _messageThread = new Thread(() => - { - while (!_messageQueue.IsCompleted) - { - try - { - PrintLog(_messageQueue.Take()); - } - catch (InvalidOperationException) - { - // IOE means that Take() was called on a completed collection. - // Some other thread can call CompleteAdding after we pass the - // IsCompleted check but before we call Take. - // We can simply catch the exception since the loop will break - // on the next iteration. - } - } - }); - - _path = Path.Combine(Environment.CurrentDirectory, "Ryujinx.log"); - - if (Logger.EnableFileLog) - { - _logWriter = new StreamWriter(File.Open(_path,FileMode.Create, FileAccess.Write)); - } - - _messageThread.IsBackground = true; - _messageThread.Start(); - } - - private static void PrintLog(LogEventArgs e) - { - StringBuilder sb = new StringBuilder(); - - sb.AppendFormat(@"{0:hh\:mm\:ss\.fff}", e.Time); - sb.Append(" | "); - sb.AppendFormat("{0:d4}", e.ThreadId); - sb.Append(' '); - sb.Append(e.Message); - - if (e.Data != null) - { - PropertyInfo[] props = e.Data.GetType().GetProperties(); - - sb.Append(' '); - - foreach (var prop in props) - { - sb.Append(prop.Name); - sb.Append(": "); - sb.Append(prop.GetValue(e.Data)); - sb.Append(" - "); - } - - // We remove the final '-' from the string - if (props.Length > 0) - { - sb.Remove(sb.Length - 3, 3); - } - } - - string message = sb.ToString(); - - if (_logColors.TryGetValue(e.Level, out ConsoleColor color)) - { - Console.ForegroundColor = color; - - Console.WriteLine(message); - - Console.ResetColor(); - } - else - { - Console.WriteLine(message); - } - - if (Logger.EnableFileLog) - { - _logWriter.WriteLine(message); - } - } - - public static void LogMessage(object sender, LogEventArgs e) - { - if (!_messageQueue.IsAddingCompleted) - { - _messageQueue.Add(e); - } - } - - public static void Close() - { - _messageQueue.CompleteAdding(); - - _messageThread.Join(); - - if (Logger.EnableFileLog) - { - _logWriter.Flush(); - _logWriter.Close(); - _logWriter.Dispose(); - } - } - } -} diff --git a/Ryujinx/Ui/NpadController.cs b/Ryujinx/Ui/NpadController.cs index 58d2d46a2f..1b677c4f80 100644 --- a/Ryujinx/Ui/NpadController.cs +++ b/Ryujinx/Ui/NpadController.cs @@ -8,28 +8,24 @@ namespace Ryujinx.UI.Input public enum ControllerInputId { Invalid, - LStick, + RStick, + LShoulder, + RShoulder, + LTrigger, + RTrigger, + LJoystick, + RJoystick, DPadUp, DPadDown, DPadLeft, DPadRight, + Start, Back, - LShoulder, - - RStick, A, B, X, - Y, - Start, - RShoulder, - - LTrigger, - RTrigger, - - LJoystick, - RJoystick + Y } public struct NpadControllerLeft @@ -60,34 +56,55 @@ namespace Ryujinx.UI.Input public class NpadController { - public bool Enabled { private set; get; } - public int Index { private set; get; } - public float Deadzone { private set; get; } - public float TriggerThreshold { private set; get; } + /// + /// Enables or disables controller support + /// + public bool Enabled { get; private set; } - public NpadControllerLeft Left { private set; get; } - public NpadControllerRight Right { private set; get; } + /// + /// Controller Device Index + /// + public int Index { get; private set; } + + /// + /// Controller Analog Stick Deadzone + /// + public float Deadzone { get; private set; } + + /// + /// Controller Trigger Threshold + /// + public float TriggerThreshold { get; private set; } + + /// + /// Left JoyCon Controller Bindings + /// + public NpadControllerLeft LeftJoycon { get; private set; } + + /// + /// Right JoyCon Controller Bindings + /// + public NpadControllerRight RightJoycon { get; private set; } public NpadController( - bool enabled, - int index, - float deadzone, - float triggerThreshold, - NpadControllerLeft left, - NpadControllerRight right) + bool enabled, + int index, + float deadzone, + float triggerThreshold, + NpadControllerLeft leftJoycon, + NpadControllerRight rightJoycon) { Enabled = enabled; Index = index; Deadzone = deadzone; TriggerThreshold = triggerThreshold; - Left = left; - Right = right; + LeftJoycon = leftJoycon; + RightJoycon = rightJoycon; + } - //Unmapped controllers are problematic, skip them - if (GamePad.GetName(index) == "Unmapped Controller") - { - Enabled = false; - } + public void SetEnabled(bool enabled) + { + Enabled = enabled; } public HidControllerButtons GetButtons() @@ -101,23 +118,23 @@ namespace Ryujinx.UI.Input HidControllerButtons buttons = 0; - if (IsPressed(gpState, Left.DPadUp)) buttons |= HidControllerButtons.DpadUp; - if (IsPressed(gpState, Left.DPadDown)) buttons |= HidControllerButtons.DpadDown; - if (IsPressed(gpState, Left.DPadLeft)) buttons |= HidControllerButtons.DpadLeft; - if (IsPressed(gpState, Left.DPadRight)) buttons |= HidControllerButtons.DPadRight; - if (IsPressed(gpState, Left.StickButton)) buttons |= HidControllerButtons.StickLeft; - if (IsPressed(gpState, Left.ButtonMinus)) buttons |= HidControllerButtons.Minus; - if (IsPressed(gpState, Left.ButtonL)) buttons |= HidControllerButtons.L; - if (IsPressed(gpState, Left.ButtonZl)) buttons |= HidControllerButtons.Zl; + if (IsPressed(gpState, LeftJoycon.DPadUp)) buttons |= HidControllerButtons.DpadUp; + if (IsPressed(gpState, LeftJoycon.DPadDown)) buttons |= HidControllerButtons.DpadDown; + if (IsPressed(gpState, LeftJoycon.DPadLeft)) buttons |= HidControllerButtons.DpadLeft; + if (IsPressed(gpState, LeftJoycon.DPadRight)) buttons |= HidControllerButtons.DPadRight; + if (IsPressed(gpState, LeftJoycon.StickButton)) buttons |= HidControllerButtons.StickLeft; + if (IsPressed(gpState, LeftJoycon.ButtonMinus)) buttons |= HidControllerButtons.Minus; + if (IsPressed(gpState, LeftJoycon.ButtonL)) buttons |= HidControllerButtons.L; + if (IsPressed(gpState, LeftJoycon.ButtonZl)) buttons |= HidControllerButtons.Zl; - if (IsPressed(gpState, Right.ButtonA)) buttons |= HidControllerButtons.A; - if (IsPressed(gpState, Right.ButtonB)) buttons |= HidControllerButtons.B; - if (IsPressed(gpState, Right.ButtonX)) buttons |= HidControllerButtons.X; - if (IsPressed(gpState, Right.ButtonY)) buttons |= HidControllerButtons.Y; - if (IsPressed(gpState, Right.StickButton)) buttons |= HidControllerButtons.StickRight; - if (IsPressed(gpState, Right.ButtonPlus)) buttons |= HidControllerButtons.Plus; - if (IsPressed(gpState, Right.ButtonR)) buttons |= HidControllerButtons.R; - if (IsPressed(gpState, Right.ButtonZr)) buttons |= HidControllerButtons.Zr; + if (IsPressed(gpState, RightJoycon.ButtonA)) buttons |= HidControllerButtons.A; + if (IsPressed(gpState, RightJoycon.ButtonB)) buttons |= HidControllerButtons.B; + if (IsPressed(gpState, RightJoycon.ButtonX)) buttons |= HidControllerButtons.X; + if (IsPressed(gpState, RightJoycon.ButtonY)) buttons |= HidControllerButtons.Y; + if (IsPressed(gpState, RightJoycon.StickButton)) buttons |= HidControllerButtons.StickRight; + if (IsPressed(gpState, RightJoycon.ButtonPlus)) buttons |= HidControllerButtons.Plus; + if (IsPressed(gpState, RightJoycon.ButtonR)) buttons |= HidControllerButtons.R; + if (IsPressed(gpState, RightJoycon.ButtonZr)) buttons |= HidControllerButtons.Zr; return buttons; } @@ -129,7 +146,7 @@ namespace Ryujinx.UI.Input return (0, 0); } - return GetStick(Left.Stick); + return GetStick(LeftJoycon.Stick); } public (short, short) GetRightStick() @@ -139,7 +156,7 @@ namespace Ryujinx.UI.Input return (0, 0); } - return GetStick(Right.Stick); + return GetStick(RightJoycon.Stick); } private (short, short) GetStick(ControllerInputId joystick) diff --git a/Ryujinx/Ui/NpadKeyboard.cs b/Ryujinx/Ui/NpadKeyboard.cs index 704c61abc2..1604da5bda 100644 --- a/Ryujinx/Ui/NpadKeyboard.cs +++ b/Ryujinx/Ui/NpadKeyboard.cs @@ -5,70 +5,69 @@ namespace Ryujinx.UI.Input { public struct NpadKeyboardLeft { - public int StickUp; - public int StickDown; - public int StickLeft; - public int StickRight; - public int StickButton; - public int DPadUp; - public int DPadDown; - public int DPadLeft; - public int DPadRight; - public int ButtonMinus; - public int ButtonL; - public int ButtonZl; + public Key StickUp; + public Key StickDown; + public Key StickLeft; + public Key StickRight; + public Key StickButton; + public Key DPadUp; + public Key DPadDown; + public Key DPadLeft; + public Key DPadRight; + public Key ButtonMinus; + public Key ButtonL; + public Key ButtonZl; } public struct NpadKeyboardRight { - public int StickUp; - public int StickDown; - public int StickLeft; - public int StickRight; - public int StickButton; - public int ButtonA; - public int ButtonB; - public int ButtonX; - public int ButtonY; - public int ButtonPlus; - public int ButtonR; - public int ButtonZr; + public Key StickUp; + public Key StickDown; + public Key StickLeft; + public Key StickRight; + public Key StickButton; + public Key ButtonA; + public Key ButtonB; + public Key ButtonX; + public Key ButtonY; + public Key ButtonPlus; + public Key ButtonR; + public Key ButtonZr; } public class NpadKeyboard { - public NpadKeyboardLeft Left; - public NpadKeyboardRight Right; + /// + /// Left JoyCon Keyboard Bindings + /// + public NpadKeyboardLeft LeftJoycon { get; private set; } - public NpadKeyboard( - NpadKeyboardLeft left, - NpadKeyboardRight right) - { - Left = left; - Right = right; - } + /// + /// Right JoyCon Keyboard Bindings + /// + public NpadKeyboardRight RightJoycon { get; private set; } public HidControllerButtons GetButtons(KeyboardState keyboard) { HidControllerButtons buttons = 0; - if (keyboard[(Key)Left.StickButton]) buttons |= HidControllerButtons.StickLeft; - if (keyboard[(Key)Left.DPadUp]) buttons |= HidControllerButtons.DpadUp; - if (keyboard[(Key)Left.DPadDown]) buttons |= HidControllerButtons.DpadDown; - if (keyboard[(Key)Left.DPadLeft]) buttons |= HidControllerButtons.DpadLeft; - if (keyboard[(Key)Left.DPadRight]) buttons |= HidControllerButtons.DPadRight; - if (keyboard[(Key)Left.ButtonMinus]) buttons |= HidControllerButtons.Minus; - if (keyboard[(Key)Left.ButtonL]) buttons |= HidControllerButtons.L; - if (keyboard[(Key)Left.ButtonZl]) buttons |= HidControllerButtons.Zl; + if (keyboard[(Key)LeftJoycon.StickButton]) buttons |= HidControllerButtons.StickLeft; + if (keyboard[(Key)LeftJoycon.DPadUp]) buttons |= HidControllerButtons.DpadUp; + if (keyboard[(Key)LeftJoycon.DPadDown]) buttons |= HidControllerButtons.DpadDown; + if (keyboard[(Key)LeftJoycon.DPadLeft]) buttons |= HidControllerButtons.DpadLeft; + if (keyboard[(Key)LeftJoycon.DPadRight]) buttons |= HidControllerButtons.DPadRight; + if (keyboard[(Key)LeftJoycon.ButtonMinus]) buttons |= HidControllerButtons.Minus; + if (keyboard[(Key)LeftJoycon.ButtonL]) buttons |= HidControllerButtons.L; + if (keyboard[(Key)LeftJoycon.ButtonZl]) buttons |= HidControllerButtons.Zl; - if (keyboard[(Key)Right.StickButton]) buttons |= HidControllerButtons.StickRight; - if (keyboard[(Key)Right.ButtonA]) buttons |= HidControllerButtons.A; - if (keyboard[(Key)Right.ButtonB]) buttons |= HidControllerButtons.B; - if (keyboard[(Key)Right.ButtonX]) buttons |= HidControllerButtons.X; - if (keyboard[(Key)Right.ButtonY]) buttons |= HidControllerButtons.Y; - if (keyboard[(Key)Right.ButtonPlus]) buttons |= HidControllerButtons.Plus; - if (keyboard[(Key)Right.ButtonR]) buttons |= HidControllerButtons.R; - if (keyboard[(Key)Right.ButtonZr]) buttons |= HidControllerButtons.Zr; + if (keyboard[(Key)RightJoycon.StickButton]) buttons |= HidControllerButtons.StickRight; + if (keyboard[(Key)RightJoycon.ButtonA]) buttons |= HidControllerButtons.A; + if (keyboard[(Key)RightJoycon.ButtonB]) buttons |= HidControllerButtons.B; + if (keyboard[(Key)RightJoycon.ButtonX]) buttons |= HidControllerButtons.X; + if (keyboard[(Key)RightJoycon.ButtonY]) buttons |= HidControllerButtons.Y; + if (keyboard[(Key)RightJoycon.ButtonPlus]) buttons |= HidControllerButtons.Plus; + if (keyboard[(Key)RightJoycon.ButtonR]) buttons |= HidControllerButtons.R; + if (keyboard[(Key)RightJoycon.ButtonZr]) buttons |= HidControllerButtons.Zr; return buttons; } @@ -78,10 +77,10 @@ namespace Ryujinx.UI.Input short dx = 0; short dy = 0; - if (keyboard[(Key)Left.StickUp]) dy = short.MaxValue; - if (keyboard[(Key)Left.StickDown]) dy = -short.MaxValue; - if (keyboard[(Key)Left.StickLeft]) dx = -short.MaxValue; - if (keyboard[(Key)Left.StickRight]) dx = short.MaxValue; + if (keyboard[(Key)LeftJoycon.StickUp]) dy = short.MaxValue; + if (keyboard[(Key)LeftJoycon.StickDown]) dy = -short.MaxValue; + if (keyboard[(Key)LeftJoycon.StickLeft]) dx = -short.MaxValue; + if (keyboard[(Key)LeftJoycon.StickRight]) dx = short.MaxValue; return (dx, dy); } @@ -91,10 +90,10 @@ namespace Ryujinx.UI.Input short dx = 0; short dy = 0; - if (keyboard[(Key)Right.StickUp]) dy = short.MaxValue; - if (keyboard[(Key)Right.StickDown]) dy = -short.MaxValue; - if (keyboard[(Key)Right.StickLeft]) dx = -short.MaxValue; - if (keyboard[(Key)Right.StickRight]) dx = short.MaxValue; + if (keyboard[(Key)RightJoycon.StickUp]) dy = short.MaxValue; + if (keyboard[(Key)RightJoycon.StickDown]) dy = -short.MaxValue; + if (keyboard[(Key)RightJoycon.StickLeft]) dx = -short.MaxValue; + if (keyboard[(Key)RightJoycon.StickRight]) dx = short.MaxValue; return (dx, dy); } diff --git a/Ryujinx/_schema.json b/Ryujinx/_schema.json new file mode 100644 index 0000000000..28f3511181 --- /dev/null +++ b/Ryujinx/_schema.json @@ -0,0 +1,823 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://ryujinx.org/_schema/config.json", + "type": "object", + "title": "Ryujinx Configuration Schema", + "required": [ + "graphics_shaders_dump_path", + "logging_enable_debug", + "logging_enable_stub", + "logging_enable_info", + "logging_enable_warn", + "logging_enable_error", + "logging_filtered_classes", + "enable_file_log", + "system_language", + "docked_mode", + "enable_vsync", + "enable_multicore_scheduling", + "enable_fs_integrity_checks", + "controller_type", + "keyboard_controls", + "gamepad_controls" + ], + "definitions": { + "key": { + "type": "string", + "enum": [ + "ShiftLeft", + "LShift", + "ShiftRight", + "RShift", + "ControlLeft", + "LControl", + "ControlRight", + "RControl", + "AltLeft", + "LAlt", + "AltRight", + "RAlt", + "WinLeft", + "LWin", + "WinRight", + "RWin", + "Menu", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12", + "F13", + "F14", + "F15", + "F16", + "F17", + "F18", + "F19", + "F20", + "F21", + "F22", + "F23", + "F24", + "F25", + "F26", + "F27", + "F28", + "F29", + "F30", + "F31", + "F32", + "F33", + "F34", + "F35", + "Up", + "Down", + "Left", + "Right", + "Enter", + "Escape", + "Space", + "Tab", + "BackSpace", + "Back", + "Insert", + "Delete", + "PageUp", + "PageDown", + "Home", + "End", + "CapsLock", + "ScrollLock", + "PrintScreen", + "Pause", + "NumLock", + "Clear", + "Sleep", + "Keypad0", + "Keypad1", + "Keypad2", + "Keypad3", + "Keypad4", + "Keypad5", + "Keypad6", + "Keypad7", + "Keypad8", + "Keypad9", + "KeypadDivide", + "KeypadMultiply", + "KeypadSubtract", + "KeypadMinus", + "KeypadAdd", + "KeypadPlus", + "KeypadDecimal", + "KeypadPeriod", + "KeypadEnter", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "Number0", + "Number1", + "Number2", + "Number3", + "Number4", + "Number5", + "Number6", + "Number7", + "Number8", + "Number9", + "Tilde", + "Grave", + "Minus", + "Plus", + "BracketLeft", + "LBracket", + "BracketRight", + "RBracket", + "Semicolon", + "Quote", + "Comma", + "Period", + "Slash", + "BackSlash", + "NonUSBackSlash", + "LastKey" + ] + }, + "input": { + "type": "string", + "enum": [ + "DPadUp", + "DPadDown", + "DPadLeft", + "DPadRight", + "LStick", + "RStick", + "LShoulder", + "RShoulder", + "LTrigger", + "RTrigger", + "LJoystick", + "RJoystick", + "A", + "B", + "X", + "Y", + "Start", + "Back" + ] + } + }, + "properties": { + "graphics_shaders_dump_path": { + "$id": "#/properties/graphics_shaders_dump_path", + "type": "string", + "title": "Graphics Shaders Dump Path", + "description": "Dumps shaders in this local directory", + "default": "", + "examples": [ + "C:\\ShaderDumps" + ] + }, + "logging_enable_debug": { + "$id": "#/properties/logging_enable_debug", + "type": "boolean", + "title": "Logging Enable Debug", + "description": "Enables printing debug log messages", + "default": false, + "examples": [ + true, + false + ] + }, + "logging_enable_stub": { + "$id": "#/properties/logging_enable_stub", + "type": "boolean", + "title": "Logging Enable Stub", + "description": "Enables printing stub log messages", + "default": true, + "examples": [ + true, + false + ] + }, + "logging_enable_info": { + "$id": "#/properties/logging_enable_info", + "type": "boolean", + "title": "Logging Enable Info", + "description": "Enables printing info log messages", + "default": true, + "examples": [ + true, + false + ] + }, + "logging_enable_warn": { + "$id": "#/properties/logging_enable_warn", + "type": "boolean", + "title": "Logging Enable Warn", + "description": "Enables printing warning log messages", + "default": true, + "examples": [ + true, + false + ] + }, + "logging_enable_error": { + "$id": "#/properties/logging_enable_error", + "type": "boolean", + "title": "Logging Enable Error", + "description": "Enables printing error log messages", + "default": true, + "examples": [ + true, + false + ] + }, + "logging_filtered_classes": { + "$id": "#/properties/logging_filtered_classes", + "type": "array", + "title": "Logging Filtered Classes", + "description": "Controls which log messages are written to the log targets", + "items": { + "type": "string", + "enum": [ + "Application", + "Audio", + "Cpu", + "Font", + "Emulation", + "Gpu", + "Hid", + "Kernel", + "KernelIpc", + "KernelScheduler", + "KernelSvc", + "Loader", + "Service", + "ServiceAcc", + "ServiceAm", + "ServiceApm", + "ServiceAudio", + "ServiceBsd", + "ServiceCaps", + "ServiceFriend", + "ServiceFs", + "ServiceHid", + "ServiceIrs", + "ServiceLdr", + "ServiceLm", + "ServiceMm", + "ServiceNfp", + "ServiceNifm", + "ServiceNs", + "ServiceNv", + "ServicePctl", + "ServicePl", + "ServicePrepo", + "ServicePsm", + "ServiceSet", + "ServiceSfdnsres", + "ServiceSm", + "ServiceSsl", + "ServiceSss", + "ServiceTime", + "ServiceVi" + ] + } + }, + "enable_file_log": { + "$id": "#/properties/enable_file_log", + "type": "boolean", + "title": "Enable File Log", + "description": "Enables logging to a file on disk", + "default": true, + "examples": [ + true, + false + ] + }, + "system_language": { + "$id": "#/properties/system_language", + "type": "string", + "title": "System Language", + "description": "Change System Language", + "default": "AmericanEnglish", + "enum": [ + "Japanese", + "AmericanEnglish", + "French", + "German", + "Italian", + "Spanish", + "Chinese", + "Korean", + "Dutch", + "Portuguese", + "Russian", + "Taiwanese", + "BritishEnglish", + "CanadianFrench", + "LatinAmericanSpanish", + "SimplifiedChinese", + "TraditionalChinese" + ], + "examples": [ + "AmericanEnglish" + ] + }, + "docked_mode": { + "$id": "#/properties/docked_mode", + "type": "boolean", + "title": "Enable Docked Mode", + "description": "Enables or disables Docked Mode", + "default": false, + "examples": [ + true, + false + ] + }, + "enable_vsync": { + "$id": "#/properties/enable_vsync", + "type": "boolean", + "title": "Enable Vertical Sync", + "description": "Enables or disables Vertical Sync", + "default": true, + "examples": [ + true, + false + ] + }, + "enable_multicore_scheduling": { + "$id": "#/properties/enable_multicore_scheduling", + "type": "boolean", + "title": "Enable Multicore Scheduling", + "description": "Enables or disables multi-core scheduling of threads", + "default": false, + "examples": [ + true, + false + ] + }, + "enable_fs_integrity_checks": { + "$id": "#/properties/enable_fs_integrity_checks", + "type": "boolean", + "title": "Enable Filesystem Integrity Checks", + "description": "Enables integrity checks on Game content files. Only applies to ROMs loaded as XCI files", + "default": true, + "examples": [ + true, + false + ] + }, + "controller_type": { + "$id": "#/properties/controller_type", + "type": "string", + "title": "Controller Type", + "default": "Handheld", + "enum": [ + "Handheld", + "ProController", + "NpadPair", + "NpadLeft", + "NpadRight" + ], + "examples": [ + "Handheld", + "ProController", + "NpadPair", + "NpadLeft", + "NpadRight" + ] + }, + "keyboard_controls": { + "$id": "#/properties/keyboard_controls", + "type": "object", + "title": "Keyboard Controls", + "required": [ + "left_joycon", + "right_joycon" + ], + "properties": { + "left_joycon": { + "$id": "#/properties/keyboard_controls/properties/left_joycon", + "type": "object", + "title": "Left JoyCon Controls", + "required": [ + "stick_up", + "stick_down", + "stick_left", + "stick_right", + "stick_button", + "dpad_up", + "dpad_down", + "dpad_left", + "dpad_right", + "button_minus", + "button_l", + "button_zl" + ], + "properties": { + "stick_up": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/stick_up", + "$ref": "#/definitions/key", + "title": "Stick Up", + "default": "w" + }, + "stick_down": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/stick_down", + "$ref": "#/definitions/key", + "title": "Stick Down", + "default": "S" + }, + "stick_left": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/stick_left", + "$ref": "#/definitions/key", + "title": "Stick Left", + "default": "A" + }, + "stick_right": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/stick_right", + "$ref": "#/definitions/key", + "title": "Stick Right", + "default": "D" + }, + "stick_button": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/stick_button", + "$ref": "#/definitions/key", + "title": "Stick Button", + "default": "F" + }, + "dpad_up": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/dpad_up", + "$ref": "#/definitions/key", + "title": "Dpad Up", + "default": "Up" + }, + "dpad_down": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/dpad_down", + "$ref": "#/definitions/key", + "title": "Dpad Down", + "default": "Down" + }, + "dpad_left": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/dpad_left", + "$ref": "#/definitions/key", + "title": "Dpad Left", + "default": "Left" + }, + "dpad_right": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/dpad_right", + "$ref": "#/definitions/key", + "title": "Dpad Right", + "default": "Right" + }, + "button_minus": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/button_minus", + "$ref": "#/definitions/key", + "title": "Button Minus", + "default": "Minus" + }, + "button_l": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/button_l", + "$ref": "#/definitions/key", + "title": "Button L", + "default": "E" + }, + "button_zl": { + "$id": "#/properties/keyboard_controls/properties/left_joycon/properties/button_zl", + "$ref": "#/definitions/key", + "title": "Button ZL", + "default": "Q" + } + } + }, + "right_joycon": { + "$id": "#/properties/keyboard_controls/properties/right_joycon", + "type": "object", + "title": "Right JoyCon Controls", + "required": [ + "stick_up", + "stick_down", + "stick_left", + "stick_right", + "stick_button", + "button_a", + "button_b", + "button_x", + "button_y", + "button_plus", + "button_r", + "button_zr" + ], + "properties": { + "stick_up": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/stick_up", + "$ref": "#/definitions/key", + "title": "Stick Up", + "default": "I" + }, + "stick_down": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/stick_down", + "$ref": "#/definitions/key", + "title": "Stick Down", + "default": "K" + }, + "stick_left": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/stick_left", + "$ref": "#/definitions/key", + "title": "Stick Left", + "default": "J" + }, + "stick_right": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/stick_right", + "$ref": "#/definitions/key", + "title": "Stick Right", + "default": "L" + }, + "stick_button": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/stick_button", + "$ref": "#/definitions/key", + "title": "Stick Button", + "default": "H" + }, + "button_a": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/button_a", + "$ref": "#/definitions/key", + "title": "Button A", + "default": "Z" + }, + "button_b": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/button_b", + "$ref": "#/definitions/key", + "title": "Button B", + "default": "X" + }, + "button_x": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/button_x", + "$ref": "#/definitions/key", + "title": "Button X", + "default": "C" + }, + "button_y": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/button_y", + "$ref": "#/definitions/key", + "title": "Button Y", + "default": "V" + }, + "button_plus": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/button_plus", + "$ref": "#/definitions/key", + "title": "Button Plus", + "default": "Plus" + }, + "button_r": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/button_r", + "$ref": "#/definitions/key", + "title": "Button R", + "default": "U" + }, + "button_zr": { + "$id": "#/properties/keyboard_controls/properties/right_joycon/properties/button_zr", + "$ref": "#/definitions/key", + "title": "Button Zr", + "default": "O" + } + } + } + } + }, + "gamepad_controls": { + "$id": "#/properties/gamepad_controls", + "type": "object", + "title": "GamePad Controls", + "required": [ + "left_joycon", + "right_joycon" + ], + "properties": { + "enable": { + "$id": "#/properties/gamepad_controls/properties/enable", + "type": "boolean", + "title": "Gamepad Enable", + "description": "Enables or disables controller support", + "default": true, + "examples": [ + true, + false + ] + }, + "index": { + "$id": "#/properties/gamepad_controls/properties/index", + "type": "integer", + "title": "Gamepad Index", + "description": "Controller Device Index", + "default": 0, + "minimum": 0, + "examples": [ + 0, + 1, + 2 + ] + }, + "deadzone": { + "$id": "#/properties/gamepad_controls/properties/deadzone", + "type": "number", + "title": "Gamepad Deadzone", + "description": "Controller Analog Stick Deadzone", + "default": 0.05, + "minimum": -32768.0, + "maximum": 32767.0, + "examples": [ + 0.05 + ] + }, + "trigger_threshold": { + "$id": "#/properties/gamepad_controls/properties/trigger_threshold", + "type": "number", + "title": "Controller Trigger Threshold", + "description": "The value of how pressed down each trigger has to be in order to register a button press", + "default": 0.5, + "minimum": 0.0, + "maximum": 1.0, + "examples": [ + 0.5 + ] + }, + "left_joycon": { + "$id": "#/properties/gamepad_controls/properties/left_joycon", + "type": "object", + "title": "Left JoyCon Controls", + "required": [ + "stick", + "stick_button", + "dpad_up", + "dpad_down", + "dpad_left", + "dpad_right", + "button_minus", + "button_l", + "button_zl" + ], + "properties": { + "stick": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/stick", + "$ref": "#/definitions/input", + "title": "Stick", + "default": "LJoystick" + }, + "stick_button": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/stick_button", + "$ref": "#/definitions/input", + "title": "Stick Button", + "default": "LStick" + }, + "dpad_up": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/dpad_up", + "$ref": "#/definitions/input", + "title": "Dpad Up", + "default": "DPadUp" + }, + "dpad_down": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/dpad_down", + "$ref": "#/definitions/input", + "title": "Dpad Down", + "default": "DPadDown" + }, + "dpad_left": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/dpad_left", + "$ref": "#/definitions/input", + "title": "Dpad Left", + "default": "DPadLeft" + }, + "dpad_right": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/dpad_right", + "$ref": "#/definitions/input", + "title": "Dpad Right", + "default": "DPadRight" + }, + "button_minus": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/button_minus", + "$ref": "#/definitions/input", + "title": "Button Minus", + "default": "Back" + }, + "button_l": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/button_l", + "$ref": "#/definitions/input", + "title": "Button L", + "default": "LShoulder" + }, + "button_zl": { + "$id": "#/properties/gamepad_controls/properties/left_joycon/properties/button_zl", + "$ref": "#/definitions/input", + "title": "Button ZL", + "default": "LTrigger" + } + } + }, + "right_joycon": { + "$id": "#/properties/gamepad_controls/properties/right_joycon", + "type": "object", + "title": "Right JoyCon Controls", + "required": [ + "stick", + "stick_button", + "button_a", + "button_b", + "button_x", + "button_y", + "button_plus", + "button_r", + "button_zr" + ], + "properties": { + "stick": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/stick", + "$ref": "#/definitions/input", + "title": "Stick", + "default": "RJoystick" + }, + "stick_button": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/stick_button", + "$ref": "#/definitions/input", + "title": "Stick Button", + "default": "RStick" + }, + "button_a": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/button_a", + "$ref": "#/definitions/input", + "title": "Button A", + "default": "B" + }, + "button_b": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/button_b", + "$ref": "#/definitions/input", + "title": "Button B", + "default": "A" + }, + "button_x": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/button_x", + "$ref": "#/definitions/input", + "title": "Button X", + "default": "Y" + }, + "button_y": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/button_y", + "$ref": "#/definitions/input", + "title": "Button Y", + "default": "X" + }, + "button_plus": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/button_plus", + "$ref": "#/definitions/input", + "title": "Button Plus", + "default": "Start" + }, + "button_r": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/button_r", + "$ref": "#/definitions/input", + "title": "Button R", + "default": "RShoulder" + }, + "button_zr": { + "$id": "#/properties/gamepad_controls/properties/right_joycon/properties/button_zr", + "$ref": "#/definitions/input", + "title": "Button ZR", + "default": "RTrigger" + } + } + } + } + } + } +} \ No newline at end of file