diff options
| author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
|---|---|---|
| committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
| commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
| tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.Common/Configuration | |
| parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) | |
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.Common/Configuration')
39 files changed, 1133 insertions, 0 deletions
diff --git a/src/Ryujinx.Common/Configuration/AntiAliasing.cs b/src/Ryujinx.Common/Configuration/AntiAliasing.cs new file mode 100644 index 00000000..159108ae --- /dev/null +++ b/src/Ryujinx.Common/Configuration/AntiAliasing.cs @@ -0,0 +1,16 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration +{ + [JsonConverter(typeof(TypedStringEnumConverter<AntiAliasing>))] + public enum AntiAliasing + { + None, + Fxaa, + SmaaLow, + SmaaMedium, + SmaaHigh, + SmaaUltra + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/AppDataManager.cs b/src/Ryujinx.Common/Configuration/AppDataManager.cs new file mode 100644 index 00000000..d6e77843 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/AppDataManager.cs @@ -0,0 +1,149 @@ +using Ryujinx.Common.Logging; +using System; +using System.IO; + +namespace Ryujinx.Common.Configuration +{ + public static class AppDataManager + { + public const string DefaultBaseDir = "Ryujinx"; + public const string DefaultPortableDir = "portable"; + + // The following 3 are always part of Base Directory + private const string GamesDir = "games"; + private const string ProfilesDir = "profiles"; + private const string KeysDir = "system"; + + public enum LaunchMode + { + UserProfile, + Portable, + Custom + } + + public static LaunchMode Mode { get; private set; } + + public static string BaseDirPath { get; private set; } + public static string GamesDirPath { get; private set; } + public static string ProfilesDirPath { get; private set; } + public static string KeysDirPath { get; private set; } + public static string KeysDirPathUser { get; } + + public const string DefaultNandDir = "bis"; + public const string DefaultSdcardDir = "sdcard"; + private const string DefaultModsDir = "mods"; + + public static string CustomModsPath { get; set; } + public static string CustomSdModsPath {get; set; } + public static string CustomNandPath { get; set; } // TODO: Actually implement this into VFS + public static string CustomSdCardPath { get; set; } // TODO: Actually implement this into VFS + + static AppDataManager() + { + KeysDirPathUser = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".switch"); + } + + public static void Initialize(string baseDirPath) + { + string appDataPath; + if (OperatingSystem.IsMacOS()) + { + appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Library", "Application Support"); + } + else + { + appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + } + + if (appDataPath.Length == 0) + { + appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + } + + string userProfilePath = Path.Combine(appDataPath, DefaultBaseDir); + string portablePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DefaultPortableDir); + + if (Directory.Exists(portablePath)) + { + BaseDirPath = portablePath; + Mode = LaunchMode.Portable; + } + else + { + BaseDirPath = userProfilePath; + Mode = LaunchMode.UserProfile; + } + + if (baseDirPath != null && baseDirPath != userProfilePath) + { + if (!Directory.Exists(baseDirPath)) + { + Logger.Error?.Print(LogClass.Application, $"Custom Data Directory '{baseDirPath}' does not exist. Falling back to {Mode}..."); + } + else + { + BaseDirPath = baseDirPath; + Mode = LaunchMode.Custom; + } + } + + BaseDirPath = Path.GetFullPath(BaseDirPath); // convert relative paths + + // NOTE: Moves the Ryujinx folder in `~/.config` to `~/Library/Application Support` if one is found + // and a Ryujinx folder does not already exist in Application Support. + // Also creates a symlink from `~/.config/Ryujinx` to `~/Library/Application Support/Ryujinx` to preserve backwards compatibility. + // This should be removed in the future. + if (OperatingSystem.IsMacOS() && Mode == LaunchMode.UserProfile) + { + string oldConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir); + if (Path.Exists(oldConfigPath) && !Path.Exists(BaseDirPath)) + { + CopyDirectory(oldConfigPath, BaseDirPath); + Directory.Delete(oldConfigPath, true); + Directory.CreateSymbolicLink(oldConfigPath, BaseDirPath); + } + } + + SetupBasePaths(); + } + + private static void SetupBasePaths() + { + Directory.CreateDirectory(BaseDirPath); + Directory.CreateDirectory(GamesDirPath = Path.Combine(BaseDirPath, GamesDir)); + Directory.CreateDirectory(ProfilesDirPath = Path.Combine(BaseDirPath, ProfilesDir)); + Directory.CreateDirectory(KeysDirPath = Path.Combine(BaseDirPath, KeysDir)); + } + + private static void CopyDirectory(string sourceDir, string destinationDir) + { + var dir = new DirectoryInfo(sourceDir); + + if (!dir.Exists) + { + throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}"); + } + + DirectoryInfo[] subDirs = dir.GetDirectories(); + Directory.CreateDirectory(destinationDir); + + foreach (FileInfo file in dir.GetFiles()) + { + if (file.Name == ".DS_Store") + { + continue; + } + + file.CopyTo(Path.Combine(destinationDir, file.Name)); + } + + foreach (DirectoryInfo subDir in subDirs) + { + CopyDirectory(subDir.FullName, Path.Combine(destinationDir, subDir.Name)); + } + } + + public static string GetModsPath() => CustomModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultModsDir)).FullName; + public static string GetSdModsPath() => CustomSdModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultSdcardDir, "atmosphere")).FullName; + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/AspectRatioExtensions.cs b/src/Ryujinx.Common/Configuration/AspectRatioExtensions.cs new file mode 100644 index 00000000..5e97ed19 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/AspectRatioExtensions.cs @@ -0,0 +1,63 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration +{ + [JsonConverter(typeof(TypedStringEnumConverter<AspectRatio>))] + public enum AspectRatio + { + Fixed4x3, + Fixed16x9, + Fixed16x10, + Fixed21x9, + Fixed32x9, + Stretched + } + + public static class AspectRatioExtensions + { + public static float ToFloat(this AspectRatio aspectRatio) + { + return aspectRatio.ToFloatX() / aspectRatio.ToFloatY(); + } + + public static float ToFloatX(this AspectRatio aspectRatio) + { + return aspectRatio switch + { + AspectRatio.Fixed4x3 => 4.0f, + AspectRatio.Fixed16x9 => 16.0f, + AspectRatio.Fixed16x10 => 16.0f, + AspectRatio.Fixed21x9 => 21.0f, + AspectRatio.Fixed32x9 => 32.0f, + _ => 16.0f + }; + } + + public static float ToFloatY(this AspectRatio aspectRatio) + { + return aspectRatio switch + { + AspectRatio.Fixed4x3 => 3.0f, + AspectRatio.Fixed16x9 => 9.0f, + AspectRatio.Fixed16x10 => 10.0f, + AspectRatio.Fixed21x9 => 9.0f, + AspectRatio.Fixed32x9 => 9.0f, + _ => 9.0f + }; + } + + public static string ToText(this AspectRatio aspectRatio) + { + return aspectRatio switch + { + AspectRatio.Fixed4x3 => "4:3", + AspectRatio.Fixed16x9 => "16:9", + AspectRatio.Fixed16x10 => "16:10", + AspectRatio.Fixed21x9 => "21:9", + AspectRatio.Fixed32x9 => "32:9", + _ => "Stretched" + }; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/BackendThreading.cs b/src/Ryujinx.Common/Configuration/BackendThreading.cs new file mode 100644 index 00000000..8833b3f0 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/BackendThreading.cs @@ -0,0 +1,13 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration +{ + [JsonConverter(typeof(TypedStringEnumConverter<BackendThreading>))] + public enum BackendThreading + { + Auto, + Off, + On + } +} diff --git a/src/Ryujinx.Common/Configuration/DownloadableContentContainer.cs b/src/Ryujinx.Common/Configuration/DownloadableContentContainer.cs new file mode 100644 index 00000000..b6ae2f3f --- /dev/null +++ b/src/Ryujinx.Common/Configuration/DownloadableContentContainer.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration +{ + public struct DownloadableContentContainer + { + [JsonPropertyName("path")] + public string ContainerPath { get; set; } + [JsonPropertyName("dlc_nca_list")] + public List<DownloadableContentNca> DownloadableContentNcaList { get; set; } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs b/src/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs new file mode 100644 index 00000000..132c45a4 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/DownloadableContentJsonSerializerContext.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(List<DownloadableContentContainer>))] + public partial class DownloadableContentJsonSerializerContext : JsonSerializerContext + { + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/DownloadableContentNca.cs b/src/Ryujinx.Common/Configuration/DownloadableContentNca.cs new file mode 100644 index 00000000..80b67300 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/DownloadableContentNca.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration +{ + public struct DownloadableContentNca + { + [JsonPropertyName("path")] + public string FullPath { get; set; } + [JsonPropertyName("title_id")] + public ulong TitleId { get; set; } + [JsonPropertyName("is_enabled")] + public bool Enabled { get; set; } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/GraphicsBackend.cs b/src/Ryujinx.Common/Configuration/GraphicsBackend.cs new file mode 100644 index 00000000..d74dd6e1 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/GraphicsBackend.cs @@ -0,0 +1,12 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration +{ + [JsonConverter(typeof(TypedStringEnumConverter<GraphicsBackend>))] + public enum GraphicsBackend + { + Vulkan, + OpenGl + } +} diff --git a/src/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs b/src/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs new file mode 100644 index 00000000..ad12302a --- /dev/null +++ b/src/Ryujinx.Common/Configuration/GraphicsDebugLevel.cs @@ -0,0 +1,14 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration +{ + [JsonConverter(typeof(TypedStringEnumConverter<GraphicsDebugLevel>))] + public enum GraphicsDebugLevel + { + None, + Error, + Slowdowns, + All + } +} diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs new file mode 100644 index 00000000..ad1fa667 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs @@ -0,0 +1,58 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid.Controller +{ + [JsonConverter(typeof(TypedStringEnumConverter<GamepadInputId>))] + public enum GamepadInputId : byte + { + Unbound, + A, + B, + X, + Y, + LeftStick, + RightStick, + LeftShoulder, + RightShoulder, + + // Likely axis + LeftTrigger, + // Likely axis + RightTrigger, + + DpadUp, + DpadDown, + DpadLeft, + DpadRight, + + // Special buttons + + Minus, + Plus, + + Back = Minus, + Start = Plus, + + Guide, + Misc1, + + // Xbox Elite paddle + Paddle1, + Paddle2, + Paddle3, + Paddle4, + + // PS5 touchpad button + Touchpad, + + // Virtual buttons for single joycon + SingleLeftTrigger0, + SingleRightTrigger0, + + SingleLeftTrigger1, + SingleRightTrigger1, + + Count + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/GenericControllerInputConfig.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/GenericControllerInputConfig.cs new file mode 100644 index 00000000..d7f0e788 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/GenericControllerInputConfig.cs @@ -0,0 +1,82 @@ +using Ryujinx.Common.Configuration.Hid.Controller.Motion; +using System; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid.Controller +{ + public class GenericControllerInputConfig<Button, Stick> : GenericInputConfigurationCommon<Button> where Button : unmanaged where Stick : unmanaged + { + [JsonIgnore] + private float _deadzoneLeft; + [JsonIgnore] + private float _deadzoneRight; + [JsonIgnore] + private float _triggerThreshold; + + /// <summary> + /// Left JoyCon Controller Stick Bindings + /// </summary> + public JoyconConfigControllerStick<Button, Stick> LeftJoyconStick { get; set; } + + /// <summary> + /// Right JoyCon Controller Stick Bindings + /// </summary> + public JoyconConfigControllerStick<Button, Stick> RightJoyconStick { get; set; } + + /// <summary> + /// Controller Left Analog Stick Deadzone + /// </summary> + public float DeadzoneLeft + { + get => _deadzoneLeft; set + { + _deadzoneLeft = MathF.Round(value, 3); + OnPropertyChanged(); + } + } + + /// <summary> + /// Controller Right Analog Stick Deadzone + /// </summary> + public float DeadzoneRight + { + get => _deadzoneRight; set + { + _deadzoneRight = MathF.Round(value, 3); + OnPropertyChanged(); + } + } + + /// <summary> + /// Controller Left Analog Stick Range + /// </summary> + public float RangeLeft { get; set; } + + /// <summary> + /// Controller Right Analog Stick Range + /// </summary> + public float RangeRight { get; set; } + + /// <summary> + /// Controller Trigger Threshold + /// </summary> + public float TriggerThreshold + { + get => _triggerThreshold; set + { + _triggerThreshold = MathF.Round(value, 3); + OnPropertyChanged(); + } + } + + /// <summary> + /// Controller Motion Settings + /// </summary> + public MotionConfigController Motion { get; set; } + + /// <summary> + /// Controller Rumble Settings + /// </summary> + public RumbleConfigController Rumble { get; set; } + } +} diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs new file mode 100644 index 00000000..869cff4f --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Common.Configuration.Hid.Controller +{ + public class JoyconConfigControllerStick<Button, Stick> where Button: unmanaged where Stick: unmanaged + { + public Stick Joystick { get; set; } + public bool InvertStickX { get; set; } + public bool InvertStickY { get; set; } + public bool Rotate90CW { get; set; } + public Button StickButton { get; set; } + } +} diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/CemuHookMotionConfigController.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/CemuHookMotionConfigController.cs new file mode 100644 index 00000000..2a5a73ff --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/CemuHookMotionConfigController.cs @@ -0,0 +1,30 @@ +namespace Ryujinx.Common.Configuration.Hid.Controller.Motion +{ + public class CemuHookMotionConfigController : MotionConfigController + { + /// <summary> + /// Motion Controller Slot + /// </summary> + public int Slot { get; set; } + + /// <summary> + /// Motion Controller Alternative Slot, for RightJoyCon in Pair mode + /// </summary> + public int AltSlot { get; set; } + + /// <summary> + /// Mirror motion input in Pair mode + /// </summary> + public bool MirrorInput { get; set; } + + /// <summary> + /// Host address of the DSU Server + /// </summary> + public string DsuServerHost { get; set; } + + /// <summary> + /// Port of the DSU Server + /// </summary> + public int DsuServerPort { get; set; } + } +} diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs new file mode 100644 index 00000000..2b9e0af4 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/JsonMotionConfigControllerConverter.cs @@ -0,0 +1,79 @@ +using Ryujinx.Common.Utilities; +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid.Controller.Motion +{ + class JsonMotionConfigControllerConverter : JsonConverter<MotionConfigController> + { + private static readonly MotionConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + + private static MotionInputBackendType GetMotionInputBackendType(ref Utf8JsonReader reader) + { + // Temporary reader to get the backend type + Utf8JsonReader tempReader = reader; + + MotionInputBackendType result = MotionInputBackendType.Invalid; + + while (tempReader.Read()) + { + // NOTE: We scan all properties ignoring the depth entirely on purpose. + // The reason behind this is that we cannot track in a reliable way the depth of the object because Utf8JsonReader never emit the first TokenType == StartObject if the json start with an object. + // As such, this code will try to parse very field named "motion_backend" to the correct enum. + if (tempReader.TokenType == JsonTokenType.PropertyName) + { + string propertyName = tempReader.GetString(); + + if (propertyName.Equals("motion_backend")) + { + tempReader.Read(); + + if (tempReader.TokenType == JsonTokenType.String) + { + string backendTypeRaw = tempReader.GetString(); + + if (!Enum.TryParse(backendTypeRaw, out result)) + { + result = MotionInputBackendType.Invalid; + } + else + { + break; + } + } + } + } + } + + return result; + } + + public override MotionConfigController Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + MotionInputBackendType motionBackendType = GetMotionInputBackendType(ref reader); + + return motionBackendType switch + { + MotionInputBackendType.GamepadDriver => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardMotionConfigController), + MotionInputBackendType.CemuHook => JsonSerializer.Deserialize(ref reader, SerializerContext.CemuHookMotionConfigController), + _ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"), + }; + } + + public override void Write(Utf8JsonWriter writer, MotionConfigController value, JsonSerializerOptions options) + { + switch (value.MotionBackend) + { + case MotionInputBackendType.GamepadDriver: + JsonSerializer.Serialize(writer, value as StandardMotionConfigController, SerializerContext.StandardMotionConfigController); + break; + case MotionInputBackendType.CemuHook: + JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, SerializerContext.CemuHookMotionConfigController); + break; + default: + throw new ArgumentException($"Unknown motion backend type {value.MotionBackend}"); + } + } + } +} diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs new file mode 100644 index 00000000..7636aa41 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigController.cs @@ -0,0 +1,25 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid.Controller.Motion +{ + [JsonConverter(typeof(JsonMotionConfigControllerConverter))] + public class MotionConfigController + { + public MotionInputBackendType MotionBackend { get; set; } + + /// <summary> + /// Gyro Sensitivity + /// </summary> + public int Sensitivity { get; set; } + + /// <summary> + /// Gyro Deadzone + /// </summary> + public double GyroDeadzone { get; set; } + + /// <summary> + /// Enable Motion Controls + /// </summary> + public bool EnableMotion { get; set; } + } +} diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs new file mode 100644 index 00000000..5cd9e452 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionConfigJsonSerializerContext.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid.Controller.Motion +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(MotionConfigController))] + [JsonSerializable(typeof(CemuHookMotionConfigController))] + [JsonSerializable(typeof(StandardMotionConfigController))] + public partial class MotionConfigJsonSerializerContext : JsonSerializerContext + { + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs new file mode 100644 index 00000000..c6551047 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/MotionInputBackendType.cs @@ -0,0 +1,13 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid.Controller.Motion +{ + [JsonConverter(typeof(TypedStringEnumConverter<MotionInputBackendType>))] + public enum MotionInputBackendType : byte + { + Invalid, + GamepadDriver, + CemuHook + } +} diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/StandardMotionConfigController.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/StandardMotionConfigController.cs new file mode 100644 index 00000000..df925444 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/Motion/StandardMotionConfigController.cs @@ -0,0 +1,4 @@ +namespace Ryujinx.Common.Configuration.Hid.Controller.Motion +{ + public class StandardMotionConfigController : MotionConfigController { } +} diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/RumbleConfigController.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/RumbleConfigController.cs new file mode 100644 index 00000000..48be4f13 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/RumbleConfigController.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Common.Configuration.Hid.Controller +{ + public class RumbleConfigController + { + /// <summary> + /// Controller Strong Rumble Multiplier + /// </summary> + public float StrongRumble { get; set; } + + /// <summary> + /// Controller Weak Rumble Multiplier + /// </summary> + public float WeakRumble { get; set; } + + /// <summary> + /// Enable Rumble + /// </summary> + public bool EnableRumble { get; set; } + } +} diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/StandardControllerInputConfig.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/StandardControllerInputConfig.cs new file mode 100644 index 00000000..4154a42b --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/StandardControllerInputConfig.cs @@ -0,0 +1,4 @@ +namespace Ryujinx.Common.Configuration.Hid.Controller +{ + public class StandardControllerInputConfig : GenericControllerInputConfig<GamepadInputId, StickInputId> { } +} diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/StickInputId.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/StickInputId.cs new file mode 100644 index 00000000..5fc4d1c8 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/StickInputId.cs @@ -0,0 +1,15 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid.Controller +{ + [JsonConverter(typeof(TypedStringEnumConverter<StickInputId>))] + public enum StickInputId : byte + { + Unbound, + Left, + Right, + + Count + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/Hid/ControllerType.cs b/src/Ryujinx.Common/Configuration/Hid/ControllerType.cs new file mode 100644 index 00000000..70f811c8 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/ControllerType.cs @@ -0,0 +1,23 @@ +using Ryujinx.Common.Utilities; +using System; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid +{ + // This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical + [Flags] + [JsonConverter(typeof(TypedStringEnumConverter<ControllerType>))] + public enum ControllerType : int + { + None, + ProController = 1 << 0, + Handheld = 1 << 1, + JoyconPair = 1 << 2, + JoyconLeft = 1 << 3, + JoyconRight = 1 << 4, + Invalid = 1 << 5, + Pokeball = 1 << 6, + SystemExternal = 1 << 29, + System = 1 << 30 + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/Hid/GenericInputConfigurationCommon.cs b/src/Ryujinx.Common/Configuration/Hid/GenericInputConfigurationCommon.cs new file mode 100644 index 00000000..3d43817e --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/GenericInputConfigurationCommon.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Common.Configuration.Hid +{ + public class GenericInputConfigurationCommon<Button> : InputConfig where Button : unmanaged + { + /// <summary> + /// Left JoyCon Controller Bindings + /// </summary> + public LeftJoyconCommonConfig<Button> LeftJoycon { get; set; } + + /// <summary> + /// Right JoyCon Controller Bindings + /// </summary> + public RightJoyconCommonConfig<Button> RightJoycon { get; set; } + } +} diff --git a/src/Ryujinx.Common/Configuration/Hid/InputBackendType.cs b/src/Ryujinx.Common/Configuration/Hid/InputBackendType.cs new file mode 100644 index 00000000..1db3f570 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/InputBackendType.cs @@ -0,0 +1,13 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid +{ + [JsonConverter(typeof(TypedStringEnumConverter<InputBackendType>))] + public enum InputBackendType + { + Invalid, + WindowKeyboard, + GamepadSDL2, + } +} diff --git a/src/Ryujinx.Common/Configuration/Hid/InputConfig.cs b/src/Ryujinx.Common/Configuration/Hid/InputConfig.cs new file mode 100644 index 00000000..16c8f8e3 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/InputConfig.cs @@ -0,0 +1,41 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid +{ + [JsonConverter(typeof(JsonInputConfigConverter))] + public class InputConfig : INotifyPropertyChanged + { + /// <summary> + /// The current version of the input file format + /// </summary> + public const int CurrentVersion = 1; + + public int Version { get; set; } + + public InputBackendType Backend { get; set; } + + /// <summary> + /// Controller id + /// </summary> + public string Id { get; set; } + + /// <summary> + /// Controller's Type + /// </summary> + public ControllerType ControllerType { get; set; } + + /// <summary> + /// Player's Index for the controller + /// </summary> + public PlayerIndex PlayerIndex { get; set; } + + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs b/src/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs new file mode 100644 index 00000000..254c4feb --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/InputConfigJsonSerializerContext.cs @@ -0,0 +1,14 @@ +using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Common.Configuration.Hid.Keyboard; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(InputConfig))] + [JsonSerializable(typeof(StandardKeyboardInputConfig))] + [JsonSerializable(typeof(StandardControllerInputConfig))] + public partial class InputConfigJsonSerializerContext : JsonSerializerContext + { + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs b/src/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs new file mode 100644 index 00000000..08bbcbf1 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/JsonInputConfigConverter.cs @@ -0,0 +1,81 @@ +using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Common.Configuration.Hid.Keyboard; +using Ryujinx.Common.Utilities; +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid +{ + public class JsonInputConfigConverter : JsonConverter<InputConfig> + { + private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + + private static InputBackendType GetInputBackendType(ref Utf8JsonReader reader) + { + // Temporary reader to get the backend type + Utf8JsonReader tempReader = reader; + + InputBackendType result = InputBackendType.Invalid; + + while (tempReader.Read()) + { + // NOTE: We scan all properties ignoring the depth entirely on purpose. + // The reason behind this is that we cannot track in a reliable way the depth of the object because Utf8JsonReader never emit the first TokenType == StartObject if the json start with an object. + // As such, this code will try to parse very field named "backend" to the correct enum. + if (tempReader.TokenType == JsonTokenType.PropertyName) + { + string propertyName = tempReader.GetString(); + + if (propertyName.Equals("backend")) + { + tempReader.Read(); + + if (tempReader.TokenType == JsonTokenType.String) + { + string backendTypeRaw = tempReader.GetString(); + + if (!Enum.TryParse(backendTypeRaw, out result)) + { + result = InputBackendType.Invalid; + } + else + { + break; + } + } + } + } + } + + return result; + } + + public override InputConfig Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + InputBackendType backendType = GetInputBackendType(ref reader); + + return backendType switch + { + InputBackendType.WindowKeyboard => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardKeyboardInputConfig), + InputBackendType.GamepadSDL2 => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardControllerInputConfig), + _ => throw new InvalidOperationException($"Unknown backend type {backendType}"), + }; + } + + public override void Write(Utf8JsonWriter writer, InputConfig value, JsonSerializerOptions options) + { + switch (value.Backend) + { + case InputBackendType.WindowKeyboard: + JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, SerializerContext.StandardKeyboardInputConfig); + break; + case InputBackendType.GamepadSDL2: + JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, SerializerContext.StandardControllerInputConfig); + break; + default: + throw new ArgumentException($"Unknown backend type {value.Backend}"); + } + } + } +} diff --git a/src/Ryujinx.Common/Configuration/Hid/Key.cs b/src/Ryujinx.Common/Configuration/Hid/Key.cs new file mode 100644 index 00000000..3501b8ae --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/Key.cs @@ -0,0 +1,143 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid +{ + [JsonConverter(typeof(TypedStringEnumConverter<Key>))] + public enum Key + { + Unknown, + ShiftLeft, + ShiftRight, + ControlLeft, + ControlRight, + AltLeft, + AltRight, + WinLeft, + WinRight, + 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, + Insert, + Delete, + PageUp, + PageDown, + Home, + End, + CapsLock, + ScrollLock, + PrintScreen, + Pause, + NumLock, + Clear, + Keypad0, + Keypad1, + Keypad2, + Keypad3, + Keypad4, + Keypad5, + Keypad6, + Keypad7, + Keypad8, + Keypad9, + KeypadDivide, + KeypadMultiply, + KeypadSubtract, + KeypadAdd, + KeypadDecimal, + 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, + BracketRight, + Semicolon, + Quote, + Comma, + Period, + Slash, + BackSlash, + Unbound, + + Count + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/Hid/Keyboard/GenericKeyboardInputConfig.cs b/src/Ryujinx.Common/Configuration/Hid/Keyboard/GenericKeyboardInputConfig.cs new file mode 100644 index 00000000..b6c82c93 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/Keyboard/GenericKeyboardInputConfig.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Common.Configuration.Hid.Keyboard +{ + public class GenericKeyboardInputConfig<Key> : GenericInputConfigurationCommon<Key> where Key : unmanaged + { + /// <summary> + /// Left JoyCon Controller Stick Bindings + /// </summary> + public JoyconConfigKeyboardStick<Key> LeftJoyconStick { get; set; } + + /// <summary> + /// Right JoyCon Controller Stick Bindings + /// </summary> + public JoyconConfigKeyboardStick<Key> RightJoyconStick { get; set; } + } +} diff --git a/src/Ryujinx.Common/Configuration/Hid/Keyboard/JoyconConfigKeyboardStick.cs b/src/Ryujinx.Common/Configuration/Hid/Keyboard/JoyconConfigKeyboardStick.cs new file mode 100644 index 00000000..cadc17e8 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/Keyboard/JoyconConfigKeyboardStick.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Common.Configuration.Hid.Keyboard +{ + public class JoyconConfigKeyboardStick<Key> where Key: unmanaged + { + public Key StickUp { get; set; } + public Key StickDown { get; set; } + public Key StickLeft { get; set; } + public Key StickRight { get; set; } + public Key StickButton { get; set; } + } +} diff --git a/src/Ryujinx.Common/Configuration/Hid/Keyboard/StandardKeyboardInputConfig.cs b/src/Ryujinx.Common/Configuration/Hid/Keyboard/StandardKeyboardInputConfig.cs new file mode 100644 index 00000000..054d777d --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/Keyboard/StandardKeyboardInputConfig.cs @@ -0,0 +1,4 @@ +namespace Ryujinx.Common.Configuration.Hid.Keyboard +{ + public class StandardKeyboardInputConfig : GenericKeyboardInputConfig<Key> { } +} diff --git a/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs new file mode 100644 index 00000000..1a10c2a5 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Common.Configuration.Hid +{ + // NOTE: Please don't change this to struct. + // This breaks Avalonia's TwoWay binding, which makes us unable to save new KeyboardHotkeys. + public class KeyboardHotkeys + { + public Key ToggleVsync { get; set; } + public Key Screenshot { get; set; } + public Key ShowUi { get; set; } + public Key Pause { get; set; } + public Key ToggleMute { get; set; } + public Key ResScaleUp { get; set; } + public Key ResScaleDown { get; set; } + public Key VolumeUp { get; set; } + public Key VolumeDown { get; set; } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/Hid/LeftJoyconCommonConfig.cs b/src/Ryujinx.Common/Configuration/Hid/LeftJoyconCommonConfig.cs new file mode 100644 index 00000000..a57240c4 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/LeftJoyconCommonConfig.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Common.Configuration.Hid +{ + public class LeftJoyconCommonConfig<Button> + { + public Button ButtonMinus { get; set; } + public Button ButtonL { get; set; } + public Button ButtonZl { get; set; } + public Button ButtonSl { get; set; } + public Button ButtonSr { get; set; } + public Button DpadUp { get; set; } + public Button DpadDown { get; set; } + public Button DpadLeft { get; set; } + public Button DpadRight { get; set; } + } +} diff --git a/src/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs b/src/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs new file mode 100644 index 00000000..dd6495d4 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs @@ -0,0 +1,22 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration.Hid +{ + // This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical + [JsonConverter(typeof(TypedStringEnumConverter<PlayerIndex>))] + public enum PlayerIndex : int + { + Player1 = 0, + Player2 = 1, + Player3 = 2, + Player4 = 3, + Player5 = 4, + Player6 = 5, + Player7 = 6, + Player8 = 7, + Handheld = 8, + Unknown = 9, + Auto = 10 // Shouldn't be used directly + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/Hid/RightJoyconCommonConfig.cs b/src/Ryujinx.Common/Configuration/Hid/RightJoyconCommonConfig.cs new file mode 100644 index 00000000..ca2d0176 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/Hid/RightJoyconCommonConfig.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Common.Configuration.Hid +{ + public class RightJoyconCommonConfig<Button> + { + public Button ButtonPlus { get; set; } + public Button ButtonR { get; set; } + public Button ButtonZr { get; set; } + public Button ButtonSl { get; set; } + public Button ButtonSr { get; set; } + public Button ButtonX { get; set; } + public Button ButtonB { get; set; } + public Button ButtonY { get; set; } + public Button ButtonA { get; set; } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/MemoryManagerMode.cs b/src/Ryujinx.Common/Configuration/MemoryManagerMode.cs new file mode 100644 index 00000000..f10fd6f1 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/MemoryManagerMode.cs @@ -0,0 +1,13 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration +{ + [JsonConverter(typeof(TypedStringEnumConverter<MemoryManagerMode>))] + public enum MemoryManagerMode : byte + { + SoftwarePageTable, + HostMapped, + HostMappedUnsafe + } +} diff --git a/src/Ryujinx.Common/Configuration/ScalingFilter.cs b/src/Ryujinx.Common/Configuration/ScalingFilter.cs new file mode 100644 index 00000000..e38c7d73 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/ScalingFilter.cs @@ -0,0 +1,13 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration +{ + [JsonConverter(typeof(TypedStringEnumConverter<ScalingFilter>))] + public enum ScalingFilter + { + Bilinear, + Nearest, + Fsr + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/TitleUpdateMetadata.cs b/src/Ryujinx.Common/Configuration/TitleUpdateMetadata.cs new file mode 100644 index 00000000..ea208e9c --- /dev/null +++ b/src/Ryujinx.Common/Configuration/TitleUpdateMetadata.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Ryujinx.Common.Configuration +{ + public struct TitleUpdateMetadata + { + public string Selected { get; set; } + public List<string> Paths { get; set; } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs b/src/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs new file mode 100644 index 00000000..5b661b87 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/TitleUpdateMetadataJsonSerializerContext.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(TitleUpdateMetadata))] + public partial class TitleUpdateMetadataJsonSerializerContext : JsonSerializerContext + { + } +}
\ No newline at end of file |
