From 2d8b225665b807e7ab1be3b372a6a6a027724bba Mon Sep 17 00:00:00 2001 From: KxK Date: Thu, 5 Jun 2025 20:34:43 -0300 Subject: [PATCH 01/11] Implement CustomDeviceInfo class --- DS4Windows/DS4Library/DS4Devices.cs | 54 +++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/DS4Windows/DS4Library/DS4Devices.cs b/DS4Windows/DS4Library/DS4Devices.cs index 2f16fccd18..3704413065 100644 --- a/DS4Windows/DS4Library/DS4Devices.cs +++ b/DS4Windows/DS4Library/DS4Devices.cs @@ -68,6 +68,60 @@ internal VidPidInfo(int vid, int pid, string name = "Generic DS4", InputDeviceTy } } + public enum ConnectionTypeDeterminer + { + Undefined, + DS4, + DualSense, + SwitchPro, + JoyCon, + DS3, + } + + public class CustomDeviceInfo + { + public string Name { get; init; } + public int Vid { get; init; } + public int Pid { get; init; } + public InputDeviceType InputDevType { get; init; } + public VidPidFeatureSet FeatureSet { get; init; } + public bool EnableDetection { get; init; } + public ConnectionTypeDeterminer? ConnectionTypeDeterminer { get; init; } + + public CustomDeviceInfo() {} + + public VidPidInfo GetVidPidInfo() + { + CheckConnectionDelegate connectionTypeDeterminer = null; + switch (this.ConnectionTypeDeterminer) { + + case DS4Windows.ConnectionTypeDeterminer.DS4: + connectionTypeDeterminer = DS4Device.HidConnectionType; + break; + case DS4Windows.ConnectionTypeDeterminer.DualSense: + connectionTypeDeterminer = DualSenseDevice.HidConnectionType; + break; + case DS4Windows.ConnectionTypeDeterminer.SwitchPro: + connectionTypeDeterminer = SwitchProDevice.HidConnectionType; + break; + case DS4Windows.ConnectionTypeDeterminer.JoyCon: + connectionTypeDeterminer = JoyConDevice.HidConnectionType; + break; + case DS4Windows.ConnectionTypeDeterminer.DS3: + connectionTypeDeterminer = DS3Device.HidConnectionType; + break; + case DS4Windows.ConnectionTypeDeterminer.Undefined: + connectionTypeDeterminer = null; + break; + case null: + break; + default: + break; + } + return new VidPidInfo(Vid,Pid,Name,InputDevType,FeatureSet,connectionTypeDeterminer); + } + } + public class RequestElevationArgs : EventArgs { public const int STATUS_SUCCESS = 0; From cde49a62af3084ac82ea3b4f7da6c6d0964abc18 Mon Sep 17 00:00:00 2001 From: KxK Date: Thu, 5 Jun 2025 21:27:02 -0300 Subject: [PATCH 02/11] Introduce supportedDevices array initialized from knownDevices --- DS4Windows/DS4Library/DS4Devices.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/DS4Windows/DS4Library/DS4Devices.cs b/DS4Windows/DS4Library/DS4Devices.cs index 3704413065..6a578c09d4 100644 --- a/DS4Windows/DS4Library/DS4Devices.cs +++ b/DS4Windows/DS4Library/DS4Devices.cs @@ -240,6 +240,7 @@ public class DS4Devices new VidPidInfo(0x0C12, 0x0E15, "Playmax Wired Controller (PS4)", InputDeviceType.DS4, VidPidFeatureSet.NoBatteryReading | VidPidFeatureSet.NoGyroCalib), // Generic PS4 Controller by Playmax (brand primarily in New Zealand). Standard Wired PS4 controller, no Gyro, no Lightbar, no Battery. There is a newer model but I'm not sure if it uses a different Vid or Pid yet. }; + private static VidPidInfo[] supportedDevices = knownDevices; private static bool detectNewControllers = true; private static long timestamp; @@ -290,10 +291,10 @@ public static void findControllers() { lock (Devices) { - IEnumerable hDevices = HidDevices.EnumerateDS4(knownDevices); + IEnumerable hDevices = HidDevices.EnumerateDS4(supportedDevices); hDevices = hDevices.Where(d => { - VidPidInfo metainfo = knownDevices.Single(x => x.vid == d.Attributes.VendorId && + VidPidInfo metainfo = supportedDevices.Single(x => x.vid == d.Attributes.VendorId && x.pid == d.Attributes.ProductId); return PreparePendingDevice(d, metainfo); }); @@ -306,7 +307,7 @@ public static void findControllers() { // Need VidPidInfo instance to get CheckConnectionDelegate and // check the connection type - VidPidInfo metainfo = knownDevices.Single(x => x.vid == d.Attributes.VendorId && + VidPidInfo metainfo = supportedDevices.Single(x => x.vid == d.Attributes.VendorId && x.pid == d.Attributes.ProductId); //return DS4Device.HidConnectionType(d); @@ -324,7 +325,7 @@ public static void findControllers() //foreach (HidDevice hDevice in hDevices) { HidDevice hDevice = tempList[i]; - VidPidInfo metainfo = knownDevices.Single(x => x.vid == hDevice.Attributes.VendorId && + VidPidInfo metainfo = supportedDevices.Single(x => x.vid == hDevice.Attributes.VendorId && x.pid == hDevice.Attributes.ProductId); if (!metainfo.featureSet.HasFlag(VidPidFeatureSet.VendorDefinedDevice) && From 533627004c175b1cabd09a14febcc74384233570 Mon Sep 17 00:00:00 2001 From: KxK Date: Thu, 5 Jun 2025 21:33:24 -0300 Subject: [PATCH 03/11] introduce customDevices array and methods to manage it --- DS4Windows/DS4Library/DS4Devices.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/DS4Windows/DS4Library/DS4Devices.cs b/DS4Windows/DS4Library/DS4Devices.cs index 6a578c09d4..67c42b84b8 100644 --- a/DS4Windows/DS4Library/DS4Devices.cs +++ b/DS4Windows/DS4Library/DS4Devices.cs @@ -240,6 +240,7 @@ public class DS4Devices new VidPidInfo(0x0C12, 0x0E15, "Playmax Wired Controller (PS4)", InputDeviceType.DS4, VidPidFeatureSet.NoBatteryReading | VidPidFeatureSet.NoGyroCalib), // Generic PS4 Controller by Playmax (brand primarily in New Zealand). Standard Wired PS4 controller, no Gyro, no Lightbar, no Battery. There is a newer model but I'm not sure if it uses a different Vid or Pid yet. }; + private static CustomDeviceInfo[] customDevices = null; private static VidPidInfo[] supportedDevices = knownDevices; private static bool detectNewControllers = true; @@ -464,6 +465,21 @@ public static IEnumerable getDS4Controllers() Devices.Values.CopyTo(controllers, 0); return controllers; } + /// + /// Sets a new array of Custom Devices to be used in the detection logic, while also refreshing the SupportedDevices array + /// + /// + public static void SetCustomDevices(CustomDeviceInfo[] customDevices) + { + DS4Devices.customDevices = customDevices.ToArray(); + } + + /// + /// Returns a copy of the current array of Custom Devices + /// + /// + public static CustomDeviceInfo[] GetCustomDevices() { + return customDevices.ToArray(); } public static void stopControllers() From db3ae7b27b77707fa9629980a069677d4b798ac0 Mon Sep 17 00:00:00 2001 From: KxK Date: Thu, 5 Jun 2025 21:44:57 -0300 Subject: [PATCH 04/11] Update supportedDevices using custom merge/filter logic on know+supported device arrays --- DS4Windows/DS4Library/DS4Devices.cs | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/DS4Windows/DS4Library/DS4Devices.cs b/DS4Windows/DS4Library/DS4Devices.cs index 67c42b84b8..fee744c4ca 100644 --- a/DS4Windows/DS4Library/DS4Devices.cs +++ b/DS4Windows/DS4Library/DS4Devices.cs @@ -465,6 +465,38 @@ public static IEnumerable getDS4Controllers() Devices.Values.CopyTo(controllers, 0); return controllers; } + private static VidPidInfo[] GetSupportedDevices() + { + var supportedDevicesList = DS4Devices.knownDevices.ToList(); + + if (customDevices != null) { + foreach (var cDev in customDevices) { + AppLogger.LogToGui($"Custom device {cDev.Vid:X4}:{cDev.Pid:X4} ({cDev.Name}) set to{(cDev.EnableDetection ? "" : " NOT")} be detected. [ Type: {(InputDeviceType)cDev.InputDevType} ][ FeatureSet: {Convert.ToString((int)cDev.FeatureSet, 2):7} ][ ConnectionTypeDeterminer: {((cDev.ConnectionTypeDeterminer != null) ? (ConnectionTypeDeterminer)cDev.ConnectionTypeDeterminer : null )} ].", false); + int index = supportedDevicesList.FindIndex(device => (device.vid, device.pid) == (cDev.Vid, cDev.Pid)); + if(index >= 0) { + if (cDev.EnableDetection) { + supportedDevicesList[index] = cDev.GetVidPidInfo(); + } + else { + supportedDevicesList.RemoveAt(index); + } + } + else { + if(cDev.EnableDetection) { + supportedDevicesList.Add(cDev.GetVidPidInfo()); + } + else { + // + } + + } + + } + } + + return supportedDevicesList.ToArray(); + } + /// /// Sets a new array of Custom Devices to be used in the detection logic, while also refreshing the SupportedDevices array /// @@ -472,6 +504,7 @@ public static IEnumerable getDS4Controllers() public static void SetCustomDevices(CustomDeviceInfo[] customDevices) { DS4Devices.customDevices = customDevices.ToArray(); + supportedDevices = GetSupportedDevices(); } /// From 1de469e0e3b43a43ca7cfdcde6a113104f00b705 Mon Sep 17 00:00:00 2001 From: KxK Date: Thu, 5 Jun 2025 21:46:32 -0300 Subject: [PATCH 05/11] Implement methods for save/loading CustomDevices array to/from disk --- DS4Windows/DS4Library/DS4Devices.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/DS4Windows/DS4Library/DS4Devices.cs b/DS4Windows/DS4Library/DS4Devices.cs index fee744c4ca..3f732e04a7 100644 --- a/DS4Windows/DS4Library/DS4Devices.cs +++ b/DS4Windows/DS4Library/DS4Devices.cs @@ -19,9 +19,11 @@ You should have received a copy of the GNU General Public License using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Security.Principal; +using System.Text.Json; using DS4Windows.InputDevices; namespace DS4Windows @@ -191,6 +193,10 @@ public class DS4Devices internal const int JOYCON_R_PRODUCT_ID = 0x2007; internal const int JOYCON_CHARGING_GRIP_PRODUCT_ID = 0x200E; + + public const string customDevicesJsonFileName = "CustomDevices.json"; + public static string CustomDevicesJsonFilePath => Path.Combine(DS4Windows.Global.appdatapath, customDevicesJsonFileName); + // https://support.steampowered.com/kb_article.php?ref=5199-TOKV-4426&l=english web site has a list of other PS4 compatible device VID/PID values and brand names. // However, not all those are guaranteed to work with DS4Windows app so support is added case by case when users of DS4Windows app tests non-official DS4 gamepads. @@ -465,6 +471,22 @@ public static IEnumerable getDS4Controllers() Devices.Values.CopyTo(controllers, 0); return controllers; } + } + + public static CustomDeviceInfo[] LoadCustomDevicesListFromDisk() + { + var options = new JsonSerializerOptions { WriteIndented = true }; + string jsonString = File.ReadAllText(CustomDevicesJsonFilePath); + return JsonSerializer.Deserialize(jsonString); + } + + public static void SaveCustomDevicesListToDisk(CustomDeviceInfo[] cDevsList) + { + var options = new JsonSerializerOptions { WriteIndented = true }; + string jsonString = JsonSerializer.Serialize(cDevsList, options); + File.WriteAllText(CustomDevicesJsonFilePath, jsonString); + } + private static VidPidInfo[] GetSupportedDevices() { var supportedDevicesList = DS4Devices.knownDevices.ToList(); From 6279840d47d4a6f4b0b255384d7272446344dd32 Mon Sep 17 00:00:00 2001 From: KxK Date: Thu, 5 Jun 2025 21:50:26 -0300 Subject: [PATCH 06/11] Read custom devices from disk --- DS4Windows/App.xaml.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/DS4Windows/App.xaml.cs b/DS4Windows/App.xaml.cs index 93bdbdd06d..473547fec3 100644 --- a/DS4Windows/App.xaml.cs +++ b/DS4Windows/App.xaml.cs @@ -255,6 +255,21 @@ private void Application_Startup(object sender, StartupEventArgs e) rootHub.CheckHidHidePresence(); } + // Try loading custom devices from disk + CustomDeviceInfo[] customDevs = null; + if (File.Exists(DS4Devices.CustomDevicesJsonFilePath)) { + try { + customDevs = DS4Windows.DS4Devices.LoadCustomDevicesListFromDisk(); + } + catch { + rootHub.LogDebug(DS4WinWPF.Translations.Strings.CustomDevices_Log_LoadFail); + } + } + if(customDevs != null) { + DS4Devices.SetCustomDevices(customDevs); + } + + rootHub.LoadPermanentSlotsConfig(); window.LateChecks(parser); } From b2aa5ffa068bab2ec49bf8997085e44f87c3fc63 Mon Sep 17 00:00:00 2001 From: KxK Date: Thu, 5 Jun 2025 21:57:26 -0300 Subject: [PATCH 07/11] Look for custom devices on disk during app initialization --- DS4Windows/App.xaml.cs | 2 ++ DS4Windows/Translations/Strings.Designer.cs | 17 +++++++++++++++++ DS4Windows/Translations/Strings.resx | 6 ++++++ 3 files changed, 25 insertions(+) diff --git a/DS4Windows/App.xaml.cs b/DS4Windows/App.xaml.cs index 473547fec3..2bd8ce495b 100644 --- a/DS4Windows/App.xaml.cs +++ b/DS4Windows/App.xaml.cs @@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +using DS4Windows; using NLog; using System; using System.Collections.Generic; @@ -258,6 +259,7 @@ private void Application_Startup(object sender, StartupEventArgs e) // Try loading custom devices from disk CustomDeviceInfo[] customDevs = null; if (File.Exists(DS4Devices.CustomDevicesJsonFilePath)) { + rootHub.LogDebug(DS4WinWPF.Translations.Strings.CustomDevices_Log_LoadingFile); try { customDevs = DS4Windows.DS4Devices.LoadCustomDevicesListFromDisk(); } diff --git a/DS4Windows/Translations/Strings.Designer.cs b/DS4Windows/Translations/Strings.Designer.cs index c5bd10ca58..0e3fc05239 100644 --- a/DS4Windows/Translations/Strings.Designer.cs +++ b/DS4Windows/Translations/Strings.Designer.cs @@ -888,6 +888,23 @@ public static string Custom { } /// + /// Looks up a localized string similar to teste. + /// + public static string CustomDevices_Log_LoadFail { + get { + return ResourceManager.GetString("CustomDevices_Log_LoadFail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Loading list of custom devices type from disk.. + /// + public static string CustomDevices_Log_LoadingFile { + get { + return ResourceManager.GetString("CustomDevices_Log_LoadingFile", resourceCulture); + } + } + /// Looks up a localized string similar to Custom Exe Name. /// public static string CustomExeName { diff --git a/DS4Windows/Translations/Strings.resx b/DS4Windows/Translations/Strings.resx index 61010b4632..fbe0ef259d 100644 --- a/DS4Windows/Translations/Strings.resx +++ b/DS4Windows/Translations/Strings.resx @@ -1478,4 +1478,10 @@ Please click OK to confirm that you have read the message. Press Cancel to be re Inverse motors + + Loading list of custom devices type from disk. + + + teste + \ No newline at end of file From 2394b89590b1e489d3c5469775a1026dbb77aa00 Mon Sep 17 00:00:00 2001 From: KxK Date: Thu, 5 Jun 2025 22:05:50 -0300 Subject: [PATCH 08/11] Added custom devices manager in Devices Options Window --- .../ControllerRegisterOptionsWindow.xaml | 9 + .../DS4Forms/Converters/HexToIntConverter.cs | 31 ++ DS4Windows/DS4Forms/CustomDevicesEditor.xaml | 399 ++++++++++++++++++ .../DS4Forms/CustomDevicesEditor.xaml.cs | 189 +++++++++ .../CustomDevicesEditorViewModel.cs | 372 ++++++++++++++++ DS4Windows/Translations/Strings.Designer.cs | 270 ++++++++++++ DS4Windows/Translations/Strings.resx | 112 +++++ 7 files changed, 1382 insertions(+) create mode 100644 DS4Windows/DS4Forms/Converters/HexToIntConverter.cs create mode 100644 DS4Windows/DS4Forms/CustomDevicesEditor.xaml create mode 100644 DS4Windows/DS4Forms/CustomDevicesEditor.xaml.cs create mode 100644 DS4Windows/DS4Forms/ViewModels/CustomDevicesEditorViewModel.cs diff --git a/DS4Windows/DS4Forms/ControllerRegisterOptionsWindow.xaml b/DS4Windows/DS4Forms/ControllerRegisterOptionsWindow.xaml index 062d5c33cd..6d23eb322b 100644 --- a/DS4Windows/DS4Forms/ControllerRegisterOptionsWindow.xaml +++ b/DS4Windows/DS4Forms/ControllerRegisterOptionsWindow.xaml @@ -14,6 +14,8 @@ + + @@ -120,4 +122,11 @@ + + + + + + + diff --git a/DS4Windows/DS4Forms/Converters/HexToIntConverter.cs b/DS4Windows/DS4Forms/Converters/HexToIntConverter.cs new file mode 100644 index 0000000000..de3fdbc722 --- /dev/null +++ b/DS4Windows/DS4Forms/Converters/HexToIntConverter.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; + +namespace DS4WinWPF.DS4Forms.Converters +{ + internal class HexToIntConverter : IValueConverter + { + // Convert int -> hex string + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is int intValue) + return intValue.ToString("X"); // uppercase hex (no "0x") + return "0"; + } + + // Convert hex string -> int + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is string s && int.TryParse(s, NumberStyles.HexNumber, culture, out int result)) + return result; + + // Optional: fallback to 0 or throw an exception + return 0; + } + } +} diff --git a/DS4Windows/DS4Forms/CustomDevicesEditor.xaml b/DS4Windows/DS4Forms/CustomDevicesEditor.xaml new file mode 100644 index 0000000000..c3e12f0dec --- /dev/null +++ b/DS4Windows/DS4Forms/CustomDevicesEditor.xaml @@ -0,0 +1,399 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +