From 19344c40b0f9689d987ab3446aed96f3ccb3324c Mon Sep 17 00:00:00 2001 From: Travis Nickles Date: Wed, 15 Apr 2020 16:25:54 -0500 Subject: [PATCH] Use ScpVBus for X360 emulation rather than ViGEmBus Test branch --- DS4Windows/DS4Control/ControlService.cs | 179 ++--- DS4Windows/DS4Control/OutputSlotManager.cs | 30 +- DS4Windows/DS4Control/ScpDevice.cs | 693 +++++++++++++++++++ DS4Windows/DS4Control/X360BusDevice.cs | 294 ++++++++ DS4Windows/DS4Control/Xbox360ScpOutDevice.cs | 59 ++ DS4Windows/DS4WinWPF.csproj | 11 +- 6 files changed, 1168 insertions(+), 98 deletions(-) create mode 100644 DS4Windows/DS4Control/ScpDevice.cs create mode 100644 DS4Windows/DS4Control/X360BusDevice.cs create mode 100644 DS4Windows/DS4Control/Xbox360ScpOutDevice.cs diff --git a/DS4Windows/DS4Control/ControlService.cs b/DS4Windows/DS4Control/ControlService.cs index 2895df5..a9ae63b 100644 --- a/DS4Windows/DS4Control/ControlService.cs +++ b/DS4Windows/DS4Control/ControlService.cs @@ -5,13 +5,14 @@ using System.Threading.Tasks; using System.Threading; using System.Diagnostics; using static DS4Windows.Global; -using Nefarius.ViGEm.Client; +//using Nefarius.ViGEm.Client; namespace DS4Windows { public class ControlService { - public ViGEmClient vigemTestClient = null; + public X360BusDevice x360Bus = null; + //public ViGEmClient vigemTestClient = null; public const int DS4_CONTROLLER_COUNT = 4; public DS4Device[] DS4Controllers = new DS4Device[DS4_CONTROLLER_COUNT]; public Mouse[] touchPad = new Mouse[DS4_CONTROLLER_COUNT]; @@ -357,7 +358,8 @@ namespace DS4Windows private void startViGEm() { - tempThread = new Thread(() => { try { vigemTestClient = new ViGEmClient(); } catch { } }); + tempThread = new Thread(() => { try { x360Bus = new X360BusDevice(); } catch { } }); + //tempThread = new Thread(() => { try { vigemTestClient = new ViGEmClient(); } catch { } }); tempThread.Priority = ThreadPriority.AboveNormal; tempThread.IsBackground = true; tempThread.Start(); @@ -371,11 +373,18 @@ namespace DS4Windows private void stopViGEm() { - if (vigemTestClient != null) + if (x360Bus != null) { - vigemTestClient.Dispose(); - vigemTestClient = null; + x360Bus.UnplugAll(); + x360Bus.Stop(); + x360Bus = null; } + + //if (vigemTestClient != null) + //{ + // vigemTestClient.Dispose(); + // vigemTestClient = null; + //} } public void PluginOutDev(int index, DS4Device device) @@ -389,94 +398,98 @@ namespace DS4Windows activeOutDevType[index] = OutContType.X360; //Xbox360OutDevice tempXbox = new Xbox360OutDevice(vigemTestClient); - Xbox360OutDevice tempXbox = outputslotMan.AllocateController(OutContType.X360, vigemTestClient) - as Xbox360OutDevice; + Xbox360ScpOutDevice tempXbox = outputslotMan.AllocateController(OutContType.X360, x360Bus, index) + as Xbox360ScpOutDevice; //outputDevices[index] = tempXbox; int devIndex = index; - Nefarius.ViGEm.Client.Targets.Xbox360FeedbackReceivedEventHandler p = (sender, args) => - { - SetDevRumble(device, args.LargeMotor, args.SmallMotor, devIndex); - }; - tempXbox.cont.FeedbackReceived += p; - tempXbox.forceFeedbackCall = p; + tempXbox.FeedbackReceived += (sender, small, large, slotIdx) => + { + SetDevRumble(device, large, small, devIndex); + }; + //Nefarius.ViGEm.Client.Targets.Xbox360FeedbackReceivedEventHandler p = (sender, args) => + // { + // SetDevRumble(device, args.LargeMotor, args.SmallMotor, devIndex); + // }; + //tempXbox.cont.FeedbackReceived += p; + //tempXbox.forceFeedbackCall = p; outputslotMan.DeferredPlugin(tempXbox, index, outputDevices); //tempXbox.Connect(); //LogDebug("X360 Controller #" + (index + 1) + " connected"); } - else if (contType == OutContType.DS4) - { - LogDebug("Plugging in DS4 Controller for input #" + (index + 1)); - activeOutDevType[index] = OutContType.DS4; - //DS4OutDevice tempDS4 = new DS4OutDevice(vigemTestClient); - DS4OutDevice tempDS4 = outputslotMan.AllocateController(OutContType.DS4, vigemTestClient) - as DS4OutDevice; - //outputDevices[index] = tempDS4; - int devIndex = index; - Nefarius.ViGEm.Client.Targets.DualShock4FeedbackReceivedEventHandler p = (sender, args) => - { - //bool useRumble = false; bool useLight = false; - byte largeMotor = args.LargeMotor; - byte smallMotor = args.SmallMotor; - SetDevRumble(device, largeMotor, smallMotor, devIndex); - //DS4Color color = new DS4Color(args.LightbarColor.Red, - // args.LightbarColor.Green, - // args.LightbarColor.Blue); - ///*Console.WriteLine("IN EVENT"); - //Console.WriteLine("Rumble ({0}, {1}) | Light ({2}, {3}, {4}) {5}", - // largeMotor, smallMotor, color.red, color.green, color.blue, DateTime.Now.ToLongTimeString()); - // */ - //if (largeMotor != 0 || smallMotor != 0) - //{ - // useRumble = true; - //} + //else if (contType == OutContType.DS4) + //{ + // LogDebug("Plugging in DS4 Controller for input #" + (index + 1)); + // activeOutDevType[index] = OutContType.DS4; + // //DS4OutDevice tempDS4 = new DS4OutDevice(vigemTestClient); + // DS4OutDevice tempDS4 = outputslotMan.AllocateController(OutContType.DS4, vigemTestClient) + // as DS4OutDevice; + // //outputDevices[index] = tempDS4; + // int devIndex = index; + // //Nefarius.ViGEm.Client.Targets.DualShock4FeedbackReceivedEventHandler p = (sender, args) => + // // { + // // //bool useRumble = false; bool useLight = false; + // // byte largeMotor = args.LargeMotor; + // // byte smallMotor = args.SmallMotor; + // // SetDevRumble(device, largeMotor, smallMotor, devIndex); + // // //DS4Color color = new DS4Color(args.LightbarColor.Red, + // // // args.LightbarColor.Green, + // // // args.LightbarColor.Blue); + // // ///*Console.WriteLine("IN EVENT"); + // // //Console.WriteLine("Rumble ({0}, {1}) | Light ({2}, {3}, {4}) {5}", + // // // largeMotor, smallMotor, color.red, color.green, color.blue, DateTime.Now.ToLongTimeString()); + // // // */ + // // //if (largeMotor != 0 || smallMotor != 0) + // // //{ + // // // useRumble = true; + // // //} - //if (color.red != 0 || color.green != 0 || color.blue != 0) - //{ - // useLight = true; - //} + // // //if (color.red != 0 || color.green != 0 || color.blue != 0) + // // //{ + // // // useLight = true; + // // //} - //if (!useRumble && !useLight) - //{ - // //Console.WriteLine("Fallback"); - // if (device.LeftHeavySlowRumble != 0 || device.RightLightFastRumble != 0) - // { - // useRumble = true; - // } - // /*else if (device.LightBarColor.red != 0 || - // device.LightBarColor.green != 0 || - // device.LightBarColor.blue != 0) - // { - // useLight = true; - // } - // */ - //} + // // //if (!useRumble && !useLight) + // // //{ + // // // //Console.WriteLine("Fallback"); + // // // if (device.LeftHeavySlowRumble != 0 || device.RightLightFastRumble != 0) + // // // { + // // // useRumble = true; + // // // } + // // // /*else if (device.LightBarColor.red != 0 || + // // // device.LightBarColor.green != 0 || + // // // device.LightBarColor.blue != 0) + // // // { + // // // useLight = true; + // // // } + // // // */ + // // //} - //if (useRumble) - //{ - // //Console.WriteLine("Perform rumble"); - // SetDevRumble(device, largeMotor, smallMotor, devIndex); - //} + // // //if (useRumble) + // // //{ + // // // //Console.WriteLine("Perform rumble"); + // // // SetDevRumble(device, largeMotor, smallMotor, devIndex); + // // //} - //if (useLight) - //{ - // //Console.WriteLine("Change lightbar color"); - // DS4HapticState haptics = new DS4HapticState - // { - // LightBarColor = color, - // }; - // device.SetHapticState(ref haptics); - //} + // // //if (useLight) + // // //{ + // // // //Console.WriteLine("Change lightbar color"); + // // // DS4HapticState haptics = new DS4HapticState + // // // { + // // // LightBarColor = color, + // // // }; + // // // device.SetHapticState(ref haptics); + // // //} - //Console.WriteLine(); - }; - tempDS4.cont.FeedbackReceived += p; - tempDS4.forceFeedbackCall = p; + // // //Console.WriteLine(); + // //}; + // //tempDS4.cont.FeedbackReceived += p; + // //tempDS4.forceFeedbackCall = p; - outputslotMan.DeferredPlugin(tempDS4, index, outputDevices); - //tempDS4.Connect(); - //LogDebug("DS4 Controller #" + (index + 1) + " connected"); - } + // outputslotMan.DeferredPlugin(tempDS4, index, outputDevices); + // //tempDS4.Connect(); + // //LogDebug("DS4 Controller #" + (index + 1) + " connected"); + //} } useDInputOnly[index] = false; @@ -506,8 +519,8 @@ namespace DS4Windows public bool Start(bool showlog = true) { startViGEm(); - if (vigemTestClient != null) - //if (x360Bus.Open() && x360Bus.Start()) + //if (vigemTestClient != null) + if (x360Bus.Open() && x360Bus.Start()) { if (showlog) LogDebug(DS4WinWPF.Properties.Resources.Starting); diff --git a/DS4Windows/DS4Control/OutputSlotManager.cs b/DS4Windows/DS4Control/OutputSlotManager.cs index 429a143..d630c45 100644 --- a/DS4Windows/DS4Control/OutputSlotManager.cs +++ b/DS4Windows/DS4Control/OutputSlotManager.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using Nefarius.ViGEm.Client; +//using Nefarius.ViGEm.Client; namespace DS4Windows { @@ -20,16 +20,32 @@ namespace DS4Windows public bool RunningQueue { get => runningQueue; } - public OutputDevice AllocateController(OutContType contType, ViGEmClient client) + //public OutputDevice AllocateController(OutContType contType, ViGEmClient client) + //{ + // OutputDevice outputDevice = null; + // switch(contType) + // { + // case OutContType.X360: + // outputDevice = new Xbox360OutDevice(client); + // break; + // case OutContType.DS4: + // outputDevice = new DS4OutDevice(client); + // break; + // case OutContType.None: + // default: + // break; + // } + + // return outputDevice; + //} + + public OutputDevice AllocateController(OutContType contType, X360BusDevice client, int idx) { OutputDevice outputDevice = null; - switch(contType) + switch (contType) { case OutContType.X360: - outputDevice = new Xbox360OutDevice(client); - break; - case OutContType.DS4: - outputDevice = new DS4OutDevice(client); + outputDevice = new Xbox360ScpOutDevice(client, idx); break; case OutContType.None: default: diff --git a/DS4Windows/DS4Control/ScpDevice.cs b/DS4Windows/DS4Control/ScpDevice.cs new file mode 100644 index 0000000..19eabfa --- /dev/null +++ b/DS4Windows/DS4Control/ScpDevice.cs @@ -0,0 +1,693 @@ +using System; +using System.Collections.Generic; + +using System.Runtime.InteropServices; +using System.Security; +using Microsoft.Win32.SafeHandles; + +namespace DS4Windows +{ + [SuppressUnmanagedCodeSecurity] + public class ScpDevice + { + public virtual Boolean IsActive + { + get { return m_IsActive; } + } + + public virtual String Path + { + get { return m_Path; } + } + + public ScpDevice(String Class) + { + this.m_Class = new Guid(Class); + } + + + public virtual Boolean Open(Int32 Instance = 0) + { + String DevicePath = String.Empty; + m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE; + + if (Find(m_Class, ref DevicePath, Instance)) + { + Open(DevicePath); + } + + return m_IsActive; + } + + public virtual Boolean Open(String DevicePath) + { + m_Path = DevicePath.ToUpper(); + m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE; + + if (GetDeviceHandle(m_Path)) + { + if (WinUsb_Initialize(m_FileHandle, ref m_WinUsbHandle)) + { + if (InitializeDevice()) + { + m_IsActive = true; + } + else + { + WinUsb_Free(m_WinUsbHandle); + m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE; + } + } + else + { + m_FileHandle.Close(); + } + } + + return m_IsActive; + } + + public virtual Boolean Start() + { + return m_IsActive; + } + + public virtual Boolean Stop() + { + m_IsActive = false; + + if (!(m_WinUsbHandle == (IntPtr) INVALID_HANDLE_VALUE)) + { + WinUsb_AbortPipe(m_WinUsbHandle, m_IntIn); + WinUsb_AbortPipe(m_WinUsbHandle, m_BulkIn); + WinUsb_AbortPipe(m_WinUsbHandle, m_BulkOut); + + WinUsb_Free(m_WinUsbHandle); + m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE; + } + + if (m_FileHandle != null && !m_FileHandle.IsInvalid && !m_FileHandle.IsClosed) + { + m_FileHandle.Close(); + m_FileHandle = null; + } + + return true; + } + + public virtual Boolean Close() + { + return Stop(); + } + + + public virtual Boolean ReadIntPipe (Byte[] Buffer, Int32 Length, ref Int32 Transfered) + { + if (!m_IsActive) return false; + + return WinUsb_ReadPipe(m_WinUsbHandle, m_IntIn, Buffer, Length, ref Transfered, IntPtr.Zero); + } + + public virtual Boolean ReadBulkPipe (Byte[] Buffer, Int32 Length, ref Int32 Transfered) + { + if (!m_IsActive) return false; + + return WinUsb_ReadPipe(m_WinUsbHandle, m_BulkIn, Buffer, Length, ref Transfered, IntPtr.Zero); + } + + public virtual Boolean WriteIntPipe (Byte[] Buffer, Int32 Length, ref Int32 Transfered) + { + if (!m_IsActive) return false; + + return WinUsb_WritePipe(m_WinUsbHandle, m_IntOut, Buffer, Length, ref Transfered, IntPtr.Zero); + } + + public virtual Boolean WriteBulkPipe(Byte[] Buffer, Int32 Length, ref Int32 Transfered) + { + if (!m_IsActive) return false; + + return WinUsb_WritePipe(m_WinUsbHandle, m_BulkOut, Buffer, Length, ref Transfered, IntPtr.Zero); + } + + + public virtual Boolean SendTransfer(Byte RequestType, Byte Request, UInt16 Value, Byte[] Buffer, ref Int32 Transfered) + { + if (!m_IsActive) return false; + + WINUSB_SETUP_PACKET Setup = new WINUSB_SETUP_PACKET(); + + Setup.RequestType = RequestType; + Setup.Request = Request; + Setup.Value = Value; + Setup.Index = 0; + Setup.Length = (UInt16) Buffer.Length; + + return WinUsb_ControlTransfer(m_WinUsbHandle, Setup, Buffer, Buffer.Length, ref Transfered, IntPtr.Zero); + } + + + #region Constant and Structure Definitions + public const Int32 SERVICE_CONTROL_STOP = 0x00000001; + public const Int32 SERVICE_CONTROL_SHUTDOWN = 0x00000005; + public const Int32 SERVICE_CONTROL_DEVICEEVENT = 0x0000000B; + public const Int32 SERVICE_CONTROL_POWEREVENT = 0x0000000D; + + public const Int32 DBT_DEVICEARRIVAL = 0x8000; + public const Int32 DBT_DEVICEQUERYREMOVE = 0x8001; + public const Int32 DBT_DEVICEREMOVECOMPLETE = 0x8004; + public const Int32 DBT_DEVTYP_DEVICEINTERFACE = 0x0005; + public const Int32 DBT_DEVTYP_HANDLE = 0x0006; + + public const Int32 PBT_APMRESUMEAUTOMATIC = 0x0012; + public const Int32 PBT_APMSUSPEND = 0x0004; + + public const Int32 DEVICE_NOTIFY_WINDOW_HANDLE = 0x0000; + public const Int32 DEVICE_NOTIFY_SERVICE_HANDLE = 0x0001; + public const Int32 DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 0x0004; + + public const Int32 WM_CREATE = 0x0001; + public const Int32 WM_DEVICECHANGE = 0x0219; + + public const Int32 DIGCF_PRESENT = 0x0002; + public const Int32 DIGCF_DEVICEINTERFACE = 0x0010; + + public delegate Int32 ServiceControlHandlerEx(Int32 Control, Int32 Type, IntPtr Data, IntPtr Context); + + [StructLayout(LayoutKind.Sequential)] + public class DEV_BROADCAST_DEVICEINTERFACE + { + internal Int32 dbcc_size; + internal Int32 dbcc_devicetype; + internal Int32 dbcc_reserved; + internal Guid dbcc_classguid; + internal Int16 dbcc_name; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public class DEV_BROADCAST_DEVICEINTERFACE_M + { + public Int32 dbcc_size; + public Int32 dbcc_devicetype; + public Int32 dbcc_reserved; + + [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 16)] + public Byte[] dbcc_classguid; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)] + public Char[] dbcc_name; + } + + [StructLayout(LayoutKind.Sequential)] + public class DEV_BROADCAST_HDR + { + public Int32 dbch_size; + public Int32 dbch_devicetype; + public Int32 dbch_reserved; + } + + [StructLayout(LayoutKind.Sequential)] + protected struct SP_DEVICE_INTERFACE_DATA + { + internal Int32 cbSize; + internal Guid InterfaceClassGuid; + internal Int32 Flags; + internal IntPtr Reserved; + } + + protected const UInt32 FILE_ATTRIBUTE_NORMAL = 0x80; + protected const UInt32 FILE_FLAG_OVERLAPPED = 0x40000000; + protected const UInt32 FILE_SHARE_READ = 1; + protected const UInt32 FILE_SHARE_WRITE = 2; + protected const UInt32 GENERIC_READ = 0x80000000; + protected const UInt32 GENERIC_WRITE = 0x40000000; + protected const Int32 INVALID_HANDLE_VALUE = -1; + protected const UInt32 OPEN_EXISTING = 3; + protected const UInt32 DEVICE_SPEED = 1; + protected const Byte USB_ENDPOINT_DIRECTION_MASK = 0x80; + + protected enum POLICY_TYPE + { + SHORT_PACKET_TERMINATE = 1, + AUTO_CLEAR_STALL = 2, + PIPE_TRANSFER_TIMEOUT = 3, + IGNORE_SHORT_PACKETS = 4, + ALLOW_PARTIAL_READS = 5, + AUTO_FLUSH = 6, + RAW_IO = 7, + } + + protected enum USBD_PIPE_TYPE + { + UsbdPipeTypeControl = 0, + UsbdPipeTypeIsochronous = 1, + UsbdPipeTypeBulk = 2, + UsbdPipeTypeInterrupt = 3, + } + + protected enum USB_DEVICE_SPEED + { + UsbLowSpeed = 1, + UsbFullSpeed = 2, + UsbHighSpeed = 3, + } + + [StructLayout(LayoutKind.Sequential)] + protected struct USB_CONFIGURATION_DESCRIPTOR + { + internal Byte bLength; + internal Byte bDescriptorType; + internal UInt16 wTotalLength; + internal Byte bNumInterfaces; + internal Byte bConfigurationValue; + internal Byte iConfiguration; + internal Byte bmAttributes; + internal Byte MaxPower; + } + + [StructLayout(LayoutKind.Sequential)] + protected struct USB_INTERFACE_DESCRIPTOR + { + internal Byte bLength; + internal Byte bDescriptorType; + internal Byte bInterfaceNumber; + internal Byte bAlternateSetting; + internal Byte bNumEndpoints; + internal Byte bInterfaceClass; + internal Byte bInterfaceSubClass; + internal Byte bInterfaceProtocol; + internal Byte iInterface; + } + + [StructLayout(LayoutKind.Sequential)] + protected struct WINUSB_PIPE_INFORMATION + { + internal USBD_PIPE_TYPE PipeType; + internal Byte PipeId; + internal UInt16 MaximumPacketSize; + internal Byte Interval; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + protected struct WINUSB_SETUP_PACKET + { + internal Byte RequestType; + internal Byte Request; + internal UInt16 Value; + internal UInt16 Index; + internal UInt16 Length; + } + + protected const Int32 DIF_PROPERTYCHANGE = 0x12; + protected const Int32 DICS_ENABLE = 1; + protected const Int32 DICS_DISABLE = 2; + protected const Int32 DICS_PROPCHANGE = 3; + protected const Int32 DICS_FLAG_GLOBAL = 1; + + [StructLayout(LayoutKind.Sequential)] + protected struct SP_CLASSINSTALL_HEADER + { + internal Int32 cbSize; + internal Int32 InstallFunction; + } + + [StructLayout(LayoutKind.Sequential)] + protected struct SP_PROPCHANGE_PARAMS + { + internal SP_CLASSINSTALL_HEADER ClassInstallHeader; + internal Int32 StateChange; + internal Int32 Scope; + internal Int32 HwProfile; + } + #endregion + + #region Protected Data Members + protected Guid m_Class = Guid.Empty; + protected String m_Path = String.Empty; + + protected SafeFileHandle m_FileHandle = null; + protected IntPtr m_WinUsbHandle = IntPtr.Zero; + + protected Byte m_IntIn = 0xFF; + protected Byte m_IntOut = 0xFF; + protected Byte m_BulkIn = 0xFF; + protected Byte m_BulkOut = 0xFF; + + protected Boolean m_IsActive = false; + #endregion + + #region Static Helper Methods + public enum Notified { Ignore = 0x0000, Arrival = 0x8000, QueryRemove = 0x8001, Removal = 0x8004 }; + + public static Boolean RegisterNotify(IntPtr Form, Guid Class, ref IntPtr Handle, Boolean Window = true) + { + IntPtr devBroadcastDeviceInterfaceBuffer = IntPtr.Zero; + + try + { + DEV_BROADCAST_DEVICEINTERFACE devBroadcastDeviceInterface = new DEV_BROADCAST_DEVICEINTERFACE(); + Int32 Size = Marshal.SizeOf(devBroadcastDeviceInterface); + + devBroadcastDeviceInterface.dbcc_size = Size; + devBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + devBroadcastDeviceInterface.dbcc_reserved = 0; + devBroadcastDeviceInterface.dbcc_classguid = Class; + + devBroadcastDeviceInterfaceBuffer = Marshal.AllocHGlobal(Size); + Marshal.StructureToPtr(devBroadcastDeviceInterface, devBroadcastDeviceInterfaceBuffer, true); + + Handle = RegisterDeviceNotification(Form, devBroadcastDeviceInterfaceBuffer, Window ? DEVICE_NOTIFY_WINDOW_HANDLE : DEVICE_NOTIFY_SERVICE_HANDLE); + + Marshal.PtrToStructure(devBroadcastDeviceInterfaceBuffer, devBroadcastDeviceInterface); + + return Handle != IntPtr.Zero; + } + catch (Exception ex) + { + Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); + throw; + } + finally + { + if (devBroadcastDeviceInterfaceBuffer != IntPtr.Zero) + { + Marshal.FreeHGlobal(devBroadcastDeviceInterfaceBuffer); + } + } + } + + public static Boolean UnregisterNotify(IntPtr Handle) + { + try + { + return UnregisterDeviceNotification(Handle); + } + catch (Exception ex) + { + Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); + throw; + } + } + #endregion + + #region Protected Methods + protected virtual Boolean Find(Guid Target, ref String Path, Int32 Instance = 0) + { + IntPtr detailDataBuffer = IntPtr.Zero; + IntPtr deviceInfoSet = IntPtr.Zero; + + try + { + SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(), da = new SP_DEVICE_INTERFACE_DATA(); + Int32 bufferSize = 0, memberIndex = 0; + + deviceInfoSet = SetupDiGetClassDevs(ref Target, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + DeviceInterfaceData.cbSize = da.cbSize = Marshal.SizeOf(DeviceInterfaceData); + + while (SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref Target, memberIndex, ref DeviceInterfaceData)) + { + SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, ref da); + { + detailDataBuffer = Marshal.AllocHGlobal(bufferSize); + + Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8); + + if (SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, ref da)) + { + IntPtr pDevicePathName = new IntPtr(IntPtr.Size == 4 ? detailDataBuffer.ToInt32() + 4: detailDataBuffer.ToInt64() + 4); + + Path = Marshal.PtrToStringAuto(pDevicePathName).ToUpper(); + Marshal.FreeHGlobal(detailDataBuffer); + + if (memberIndex == Instance) return true; + } + else Marshal.FreeHGlobal(detailDataBuffer); + } + + memberIndex++; + } + } + catch (Exception ex) + { + Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); + throw; + } + finally + { + if (deviceInfoSet != IntPtr.Zero) + { + SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + } + + return false; + } + + protected virtual Boolean GetDeviceInstance(ref String Instance) + { + IntPtr detailDataBuffer = IntPtr.Zero; + IntPtr deviceInfoSet = IntPtr.Zero; + + try + { + SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(), da = new SP_DEVICE_INTERFACE_DATA(); + Int32 bufferSize = 0, memberIndex = 0; + + deviceInfoSet = SetupDiGetClassDevs(ref m_Class, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + DeviceInterfaceData.cbSize = da.cbSize = Marshal.SizeOf(DeviceInterfaceData); + + while (SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref m_Class, memberIndex, ref DeviceInterfaceData)) + { + SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, ref da); + { + detailDataBuffer = Marshal.AllocHGlobal(bufferSize); + + Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8); + + if (SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, ref da)) + { + IntPtr pDevicePathName = new IntPtr(IntPtr.Size == 4 ? detailDataBuffer.ToInt32() + 4 : detailDataBuffer.ToInt64() + 4); + + String Current = Marshal.PtrToStringAuto(pDevicePathName).ToUpper(); + Marshal.FreeHGlobal(detailDataBuffer); + + if (Current == Path) + { + Int32 nBytes = 256; + IntPtr ptrInstanceBuf = Marshal.AllocHGlobal(nBytes); + + CM_Get_Device_ID(da.Flags, ptrInstanceBuf, nBytes, 0); + Instance = Marshal.PtrToStringAuto(ptrInstanceBuf).ToUpper(); + + Marshal.FreeHGlobal(ptrInstanceBuf); + return true; + } + } + else Marshal.FreeHGlobal(detailDataBuffer); + } + + memberIndex++; + } + } + catch (Exception ex) + { + Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); + throw; + } + finally + { + if (deviceInfoSet != IntPtr.Zero) + { + SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + } + + return false; + } + + protected virtual Boolean GetDeviceHandle(String Path) + { + m_FileHandle = CreateFile(Path, (GENERIC_WRITE | GENERIC_READ), FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | 0x20000000 | 0x80000000, 0); + + return !m_FileHandle.IsInvalid; + } + + protected virtual Boolean UsbEndpointDirectionIn(Int32 addr) + { + return (addr & 0x80) == 0x80; + } + + protected virtual Boolean UsbEndpointDirectionOut(Int32 addr) + { + return (addr & 0x80) == 0x00; + } + + protected virtual Boolean InitializeDevice() + { + try + { + USB_INTERFACE_DESCRIPTOR ifaceDescriptor = new USB_INTERFACE_DESCRIPTOR(); + WINUSB_PIPE_INFORMATION pipeInfo = new WINUSB_PIPE_INFORMATION(); + + if (WinUsb_QueryInterfaceSettings(m_WinUsbHandle, 0, ref ifaceDescriptor)) + { + for (Int32 i = 0; i < ifaceDescriptor.bNumEndpoints; i++) + { + WinUsb_QueryPipe(m_WinUsbHandle, 0, System.Convert.ToByte(i), ref pipeInfo); + + if (((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeBulk) & UsbEndpointDirectionIn(pipeInfo.PipeId))) + { + m_BulkIn = pipeInfo.PipeId; + WinUsb_FlushPipe(m_WinUsbHandle, m_BulkIn); + } + else if (((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeBulk) & UsbEndpointDirectionOut(pipeInfo.PipeId))) + { + m_BulkOut = pipeInfo.PipeId; + WinUsb_FlushPipe(m_WinUsbHandle, m_BulkOut); + } + else if ((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeInterrupt) & UsbEndpointDirectionIn(pipeInfo.PipeId)) + { + m_IntIn = pipeInfo.PipeId; + WinUsb_FlushPipe(m_WinUsbHandle, m_IntIn); + } + else if ((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeInterrupt) & UsbEndpointDirectionOut(pipeInfo.PipeId)) + { + m_IntOut = pipeInfo.PipeId; + WinUsb_FlushPipe(m_WinUsbHandle, m_IntOut); + } + } + + return true; + } + + return false; + } + catch (Exception ex) + { + Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); + throw; + } + } + + protected virtual Boolean RestartDevice(String InstanceId) + { + IntPtr deviceInfoSet = IntPtr.Zero; + + try + { + SP_DEVICE_INTERFACE_DATA deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(); + + deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData); + deviceInfoSet = SetupDiGetClassDevs(ref m_Class, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + if (SetupDiOpenDeviceInfo(deviceInfoSet, InstanceId, IntPtr.Zero, 0, ref deviceInterfaceData)) + { + SP_PROPCHANGE_PARAMS props = new SP_PROPCHANGE_PARAMS(); + + props.ClassInstallHeader = new SP_CLASSINSTALL_HEADER(); + props.ClassInstallHeader.cbSize = Marshal.SizeOf(props.ClassInstallHeader); + props.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE; + + props.Scope = DICS_FLAG_GLOBAL; + props.StateChange = DICS_PROPCHANGE; + props.HwProfile = 0x00; + + if (SetupDiSetClassInstallParams(deviceInfoSet, ref deviceInterfaceData, ref props, Marshal.SizeOf(props))) + { + return SetupDiChangeState(deviceInfoSet, ref deviceInterfaceData); + } + } + } + catch (Exception ex) + { + Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message); + throw; + } + finally + { + if (deviceInfoSet != IntPtr.Zero) + { + SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + } + + return false; + } + #endregion + + #region Interop Definitions + [DllImport("setupapi.dll", SetLastError = true)] + protected static extern Int32 SetupDiCreateDeviceInfoList(ref System.Guid ClassGuid, Int32 hwndParent); + + [DllImport("setupapi.dll", SetLastError = true)] + protected static extern Int32 SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet); + + [DllImport("setupapi.dll", SetLastError = true)] + protected static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, IntPtr DeviceInfoData, ref System.Guid InterfaceClassGuid, Int32 MemberIndex, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData); + + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + protected static extern IntPtr SetupDiGetClassDevs(ref System.Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, Int32 Flags); + + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + protected static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, IntPtr DeviceInfoData); + + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + protected static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, ref SP_DEVICE_INTERFACE_DATA DeviceInfoData); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + protected static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr NotificationFilter, Int32 Flags); + + [DllImport("user32.dll", SetLastError = true)] + protected static extern Boolean UnregisterDeviceNotification(IntPtr Handle); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + protected static extern SafeFileHandle CreateFile(String lpFileName, UInt32 dwDesiredAccess, UInt32 dwShareMode, IntPtr lpSecurityAttributes, UInt32 dwCreationDisposition, UInt32 dwFlagsAndAttributes, UInt32 hTemplateFile); + + [DllImport("winusb.dll", SetLastError = true)] + protected static extern Boolean WinUsb_Initialize(SafeFileHandle DeviceHandle, ref IntPtr InterfaceHandle); + + [DllImport("winusb.dll", SetLastError = true)] + protected static extern Boolean WinUsb_QueryInterfaceSettings(IntPtr InterfaceHandle, Byte AlternateInterfaceNumber, ref USB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor); + + [DllImport("winusb.dll", SetLastError = true)] + protected static extern Boolean WinUsb_QueryPipe(IntPtr InterfaceHandle, Byte AlternateInterfaceNumber, Byte PipeIndex, ref WINUSB_PIPE_INFORMATION PipeInformation); + + [DllImport("winusb.dll", SetLastError = true)] + protected static extern Boolean WinUsb_AbortPipe(IntPtr InterfaceHandle, Byte PipeID); + + [DllImport("winusb.dll", SetLastError = true)] + protected static extern Boolean WinUsb_FlushPipe(IntPtr InterfaceHandle, Byte PipeID); + + [DllImport("winusb.dll", SetLastError = true)] + protected static extern Boolean WinUsb_ControlTransfer(IntPtr InterfaceHandle, WINUSB_SETUP_PACKET SetupPacket, Byte[] Buffer, Int32 BufferLength, ref Int32 LengthTransferred, IntPtr Overlapped); + + [DllImport("winusb.dll", SetLastError = true)] + protected static extern Boolean WinUsb_ReadPipe(IntPtr InterfaceHandle, Byte PipeID, Byte[] Buffer, Int32 BufferLength, ref Int32 LengthTransferred, IntPtr Overlapped); + + [DllImport("winusb.dll", SetLastError = true)] + protected static extern Boolean WinUsb_WritePipe(IntPtr InterfaceHandle, Byte PipeID, Byte[] Buffer, Int32 BufferLength, ref Int32 LengthTransferred, IntPtr Overlapped); + + [DllImport("winusb.dll", SetLastError = true)] + protected static extern Boolean WinUsb_Free(IntPtr InterfaceHandle); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern IntPtr RegisterServiceCtrlHandlerEx(String ServiceName, ServiceControlHandlerEx Callback, IntPtr Context); + + [DllImport("kernel32.dll", SetLastError = true)] + protected static extern Boolean DeviceIoControl(SafeFileHandle DeviceHandle, Int32 IoControlCode, Byte[] InBuffer, Int32 InBufferSize, Byte[] OutBuffer, Int32 OutBufferSize, ref Int32 BytesReturned, IntPtr Overlapped); + + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + protected static extern Int32 CM_Get_Device_ID(Int32 dnDevInst, IntPtr Buffer, Int32 BufferLen, Int32 ulFlags); + + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + protected static extern Boolean SetupDiOpenDeviceInfo(IntPtr DeviceInfoSet, String DeviceInstanceId, IntPtr hwndParent, Int32 Flags, ref SP_DEVICE_INTERFACE_DATA DeviceInfoData); + + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + protected static extern Boolean SetupDiChangeState(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData); + + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + protected static extern Boolean SetupDiSetClassInstallParams(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, ref SP_PROPCHANGE_PARAMS ClassInstallParams, Int32 ClassInstallParamsSize); + #endregion + } +} diff --git a/DS4Windows/DS4Control/X360BusDevice.cs b/DS4Windows/DS4Control/X360BusDevice.cs new file mode 100644 index 0000000..ebc3f49 --- /dev/null +++ b/DS4Windows/DS4Control/X360BusDevice.cs @@ -0,0 +1,294 @@ +using System; +using System.Collections.Generic; + +using System.Runtime.InteropServices; +using System.Security; +using Microsoft.Win32.SafeHandles; + +namespace DS4Windows +{ + [SuppressUnmanagedCodeSecurity] + public class X360BusDevice : ScpDevice + { + private const String DS3_BUS_CLASS_GUID = "{F679F562-3164-42CE-A4DB-E7DDBE723909}"; + private const int CONTROLLER_OFFSET = 1; // Device 0 is the virtual USB hub itself, and we leave devices 1-10 available for other software (like the Scarlet.Crush DualShock driver itself) + private const int inputResolution = 127 - (-128); + private const float reciprocalInputResolution = 1 / (float)inputResolution; + private const int outputResolution = 32767 - (-32768); + + private int firstController = 1; + // Device 0 is the virtual USB hub itself, and we can leave more available for other software (like the Scarlet.Crush DualShock driver) + public int FirstController + { + get { return firstController; } + set { firstController = value > 0 ? value : 1; } + } + + protected Int32 Scale(Int32 Value, Boolean Flip) + { + unchecked + { + Value -= 0x80; + + //float temp = (Value - (-128)) / (float)inputResolution; + float temp = (Value - (-128)) * reciprocalInputResolution; + if (Flip) temp = (temp - 0.5f) * -1.0f + 0.5f; + + return (Int32)(temp * outputResolution + (-32768)); + } + } + + + public X360BusDevice() + : base(DS3_BUS_CLASS_GUID) + { + } + + /* public override Boolean Open(int Instance = 0) + { + if (base.Open(Instance)) + { + } + + return true; + } */ + + public override Boolean Open(String DevicePath) + { + m_Path = DevicePath; + m_WinUsbHandle = (IntPtr)INVALID_HANDLE_VALUE; + + if (GetDeviceHandle(m_Path)) + { + m_IsActive = true; + } + + return true; + } + + public override Boolean Start() + { + if (IsActive) + { + } + + return true; + } + + public override Boolean Stop() + { + if (IsActive) + { + //Unplug(0); + } + + return base.Stop(); + } + + public override Boolean Close() + { + if (IsActive) + { + Unplug(0); + } + + return base.Close(); + } + + + public void Parse(DS4State state, Byte[] Output, int device) + { + Output[0] = 0x1C; + Output[4] = (Byte)(device + firstController); + Output[9] = 0x14; + + for (int i = 10; i < 28; i++) + { + Output[i] = 0; + } + + unchecked + { + if (state.Share) Output[10] |= (Byte)(1 << 5); // Back + if (state.L3) Output[10] |= (Byte)(1 << 6); // Left Thumb + if (state.R3) Output[10] |= (Byte)(1 << 7); // Right Thumb + if (state.Options) Output[10] |= (Byte)(1 << 4); // Start + + if (state.DpadUp) Output[10] |= (Byte)(1 << 0); // Up + if (state.DpadRight) Output[10] |= (Byte)(1 << 3); // Down + if (state.DpadDown) Output[10] |= (Byte)(1 << 1); // Right + if (state.DpadLeft) Output[10] |= (Byte)(1 << 2); // Left + + if (state.L1) Output[11] |= (Byte)(1 << 0); // Left Shoulder + if (state.R1) Output[11] |= (Byte)(1 << 1); // Right Shoulder + + if (state.Triangle) Output[11] |= (Byte)(1 << 7); // Y + if (state.Circle) Output[11] |= (Byte)(1 << 5); // B + if (state.Cross) Output[11] |= (Byte)(1 << 4); // A + if (state.Square) Output[11] |= (Byte)(1 << 6); // X + + if (state.PS) Output[11] |= (Byte)(1 << 2); // Guide + + SASteeringWheelEmulationAxisType steeringWheelMappedAxis = Global.GetSASteeringWheelEmulationAxis(device); + Int32 ThumbLX; + Int32 ThumbLY; + Int32 ThumbRX; + Int32 ThumbRY; + + Output[12] = state.L2; // Left Trigger + Output[13] = state.R2; // Right Trigger + + switch(steeringWheelMappedAxis) + { + case SASteeringWheelEmulationAxisType.None: + ThumbLX = Scale(state.LX, false); + ThumbLY = Scale(state.LY, true); + ThumbRX = Scale(state.RX, false); + ThumbRY = Scale(state.RY, true); + break; + + case SASteeringWheelEmulationAxisType.LX: + ThumbLX = state.SASteeringWheelEmulationUnit; + ThumbLY = Scale(state.LY, true); + ThumbRX = Scale(state.RX, false); + ThumbRY = Scale(state.RY, true); + break; + + case SASteeringWheelEmulationAxisType.LY: + ThumbLX = Scale(state.LX, false); + ThumbLY = state.SASteeringWheelEmulationUnit; + ThumbRX = Scale(state.RX, false); + ThumbRY = Scale(state.RY, true); + break; + + case SASteeringWheelEmulationAxisType.RX: + ThumbLX = Scale(state.LX, false); + ThumbLY = Scale(state.LY, true); + ThumbRX = state.SASteeringWheelEmulationUnit; + ThumbRY = Scale(state.RY, true); + break; + + case SASteeringWheelEmulationAxisType.RY: + ThumbLX = Scale(state.LX, false); + ThumbLY = Scale(state.LY, true); + ThumbRX = Scale(state.RX, false); + ThumbRY = state.SASteeringWheelEmulationUnit; + break; + + case SASteeringWheelEmulationAxisType.L2R2: + Output[12] = Output[13] = 0; + if (state.SASteeringWheelEmulationUnit >= 0) Output[12] = (Byte)state.SASteeringWheelEmulationUnit; + else Output[13] = (Byte)state.SASteeringWheelEmulationUnit; + goto case SASteeringWheelEmulationAxisType.None; + + case SASteeringWheelEmulationAxisType.VJoy1X: + case SASteeringWheelEmulationAxisType.VJoy2X: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_X); + goto case SASteeringWheelEmulationAxisType.None; + + case SASteeringWheelEmulationAxisType.VJoy1Y: + case SASteeringWheelEmulationAxisType.VJoy2Y: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Y); + goto case SASteeringWheelEmulationAxisType.None; + + case SASteeringWheelEmulationAxisType.VJoy1Z: + case SASteeringWheelEmulationAxisType.VJoy2Z: + DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Z); + goto case SASteeringWheelEmulationAxisType.None; + + default: + // Should never come here but just in case use the NONE case as default handler.... + goto case SASteeringWheelEmulationAxisType.None; + } + + Output[14] = (Byte)((ThumbLX >> 0) & 0xFF); // LX + Output[15] = (Byte)((ThumbLX >> 8) & 0xFF); + Output[16] = (Byte)((ThumbLY >> 0) & 0xFF); // LY + Output[17] = (Byte)((ThumbLY >> 8) & 0xFF); + Output[18] = (Byte)((ThumbRX >> 0) & 0xFF); // RX + Output[19] = (Byte)((ThumbRX >> 8) & 0xFF); + Output[20] = (Byte)((ThumbRY >> 0) & 0xFF); // RY + Output[21] = (Byte)((ThumbRY >> 8) & 0xFF); + } + } + + public Boolean Plugin(Int32 Serial) + { + if (IsActive) + { + Int32 Transfered = 0; + Byte[] Buffer = new Byte[16]; + + Buffer[0] = 0x10; + Buffer[1] = 0x00; + Buffer[2] = 0x00; + Buffer[3] = 0x00; + + Serial += firstController; + Buffer[4] = (Byte)((Serial >> 0) & 0xFF); + Buffer[5] = (Byte)((Serial >> 8) & 0xFF); + Buffer[6] = (Byte)((Serial >> 16) & 0xFF); + Buffer[7] = (Byte)((Serial >> 24) & 0xFF); + + return DeviceIoControl(m_FileHandle, 0x2A4000, Buffer, Buffer.Length, null, 0, ref Transfered, IntPtr.Zero); + } + + return false; + } + + public Boolean Unplug(Int32 Serial) + { + if (IsActive) + { + Int32 Transfered = 0; + Byte[] Buffer = new Byte[16]; + + Buffer[0] = 0x10; + Buffer[1] = 0x00; + Buffer[2] = 0x00; + Buffer[3] = 0x00; + + Serial += firstController; + Buffer[4] = (Byte)((Serial >> 0) & 0xFF); + Buffer[5] = (Byte)((Serial >> 8) & 0xFF); + Buffer[6] = (Byte)((Serial >> 16) & 0xFF); + Buffer[7] = (Byte)((Serial >> 24) & 0xFF); + + return DeviceIoControl(m_FileHandle, 0x2A4004, Buffer, Buffer.Length, null, 0, ref Transfered, IntPtr.Zero); + } + + return false; + } + + public Boolean UnplugAll() //not yet implemented, not sure if will + { + if (IsActive) + { + Int32 Transfered = 0; + Byte[] Buffer = new Byte[16]; + + Buffer[0] = 0x10; + Buffer[1] = 0x00; + Buffer[2] = 0x00; + Buffer[3] = 0x00; + + return DeviceIoControl(m_FileHandle, 0x2A4004, Buffer, Buffer.Length, null, 0, ref Transfered, IntPtr.Zero); + } + + return false; + } + + + public Boolean Report(Byte[] Input, Byte[] Output) + { + if (IsActive) + { + Int32 Transfered = 0; + + return DeviceIoControl(m_FileHandle, 0x2A400C, Input, Input.Length, Output, Output.Length, ref Transfered, IntPtr.Zero) && Transfered > 0; + } + + return false; + } + } +} diff --git a/DS4Windows/DS4Control/Xbox360ScpOutDevice.cs b/DS4Windows/DS4Control/Xbox360ScpOutDevice.cs new file mode 100644 index 0000000..dfd59dd --- /dev/null +++ b/DS4Windows/DS4Control/Xbox360ScpOutDevice.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DS4Windows +{ + class Xbox360ScpOutDevice : OutputDevice + { + private const int inputResolution = 127 - (-128); + private const float reciprocalInputResolution = 1 / (float)inputResolution; + private const int outputResolution = 32767 - (-32768); + public const string devType = "X360"; + + private byte[] report = new byte[28]; + private byte[] rumble = new byte[8]; + + private X360BusDevice x360Bus; + private int slotIdx = 0; + + public delegate void Xbox360FeedbackReceivedEventHandler(Xbox360ScpOutDevice sender, byte large, byte small, int idx); + public event Xbox360FeedbackReceivedEventHandler FeedbackReceived; + + public Xbox360ScpOutDevice(X360BusDevice client, int idx) + { + this.x360Bus = client; + slotIdx = idx; + } + + public override void Connect() + { + x360Bus.Plugin(slotIdx); + } + + public override void ConvertandSendReport(DS4State state, int device) + { + x360Bus.Parse(state, report, slotIdx); + if (x360Bus.Report(report, rumble)) + { + byte Big = rumble[3]; + byte Small = rumble[4]; + + if (rumble[1] == 0x08) + { + FeedbackReceived?.Invoke(this, Big, Small, slotIdx); + } + } + } + + public override void Disconnect() + { + FeedbackReceived = null; + x360Bus.Unplug(slotIdx); + } + + public override string GetDeviceType() => devType; + } +} diff --git a/DS4Windows/DS4WinWPF.csproj b/DS4Windows/DS4WinWPF.csproj index ea1b1a0..ff21a65 100644 --- a/DS4Windows/DS4WinWPF.csproj +++ b/DS4Windows/DS4WinWPF.csproj @@ -106,12 +106,6 @@ ..\packages\TaskScheduler.2.8.17\lib\net452\Microsoft.Win32.TaskScheduler.dll False - - .\libs\x64\Nefarius.ViGEm.Client\Nefarius.ViGEm.Client.dll - - - .\libs\x86\Nefarius.ViGEm.Client\Nefarius.ViGEm.Client.dll - ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll False @@ -172,7 +166,6 @@ - @@ -185,10 +178,12 @@ + - + + About.xaml