From 164d56b6ebd6bbe60ab66ac5cb36abc9bb1ba06e Mon Sep 17 00:00:00 2001 From: Will Xyen Date: Sun, 13 Sep 2020 19:45:52 -0700 Subject: [PATCH] vigem-sdvxio: Add implementation --- Module.mk | 66 +-- src/imports/ViGEm/Client.h | 519 ++++++++++++++++++++ src/imports/ViGEm/Common.h | 191 +++++++ src/imports/ViGEm/Util.h | 68 +++ src/imports/ViGEm/km/BusShared.h | 399 +++++++++++++++ src/imports/import_32_indep_ViGEmClient.def | 27 + src/imports/import_64_indep_ViGEmClient.def | 27 + src/main/vigem-sdvxio/Module.mk | 20 + src/main/vigem-sdvxio/config-vigem-sdvxio.c | 128 +++++ src/main/vigem-sdvxio/config-vigem-sdvxio.h | 17 + src/main/vigem-sdvxio/main.c | 183 +++++++ src/main/vigemstub/Module.mk | 12 + src/main/vigemstub/helper.c | 45 ++ src/main/vigemstub/helper.h | 9 + 14 files changed, 1683 insertions(+), 28 deletions(-) create mode 100644 src/imports/ViGEm/Client.h create mode 100644 src/imports/ViGEm/Common.h create mode 100644 src/imports/ViGEm/Util.h create mode 100644 src/imports/ViGEm/km/BusShared.h create mode 100644 src/imports/import_32_indep_ViGEmClient.def create mode 100644 src/imports/import_64_indep_ViGEmClient.def create mode 100644 src/main/vigem-sdvxio/Module.mk create mode 100644 src/main/vigem-sdvxio/config-vigem-sdvxio.c create mode 100644 src/main/vigem-sdvxio/config-vigem-sdvxio.h create mode 100644 src/main/vigem-sdvxio/main.c create mode 100644 src/main/vigemstub/Module.mk create mode 100644 src/main/vigemstub/helper.c create mode 100644 src/main/vigemstub/helper.h diff --git a/Module.mk b/Module.mk index 2096b06..956ee6e 100644 --- a/Module.mk +++ b/Module.mk @@ -149,6 +149,8 @@ include src/main/security/Module.mk include src/main/unicorntail/Module.mk include src/main/util/Module.mk include src/main/vefxio/Module.mk +include src/main/vigem-sdvxio/Module.mk +include src/main/vigemstub/Module.mk include src/test/cconfig/Module.mk include src/test/d3d9hook/Module.mk @@ -202,11 +204,8 @@ $(zipdir)/iidx-09-to-12.zip: \ build/bin/indep-32/iidxhook1.dll \ build/bin/indep-32/config.exe \ build/bin/indep-32/eamio.dll \ - build/bin/indep-32/eamio-icca.dll \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/iidxio.dll \ - build/bin/indep-32/iidxio-ezusb.dll \ - build/bin/indep-32/iidxio-ezusb2.dll \ build/bin/indep-32/vefxio.dll \ build/bin/indep-32/inject.exe \ dist/iidx/config.bat \ @@ -230,11 +229,8 @@ $(zipdir)/iidx-13.zip: \ build/bin/avs2_0-32/iidxhook2.dll \ build/bin/indep-32/config.exe \ build/bin/indep-32/eamio.dll \ - build/bin/indep-32/eamio-icca.dll \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/iidxio.dll \ - build/bin/indep-32/iidxio-ezusb.dll \ - build/bin/indep-32/iidxio-ezusb2.dll \ build/bin/indep-32/vefxio.dll \ build/bin/indep-32/inject.exe \ dist/iidx/config.bat \ @@ -249,11 +245,8 @@ $(zipdir)/iidx-14-to-17.zip: \ build/bin/avs2_0-32/iidxhook3.dll \ build/bin/indep-32/config.exe \ build/bin/indep-32/eamio.dll \ - build/bin/indep-32/eamio-icca.dll \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/iidxio.dll \ - build/bin/indep-32/iidxio-ezusb.dll \ - build/bin/indep-32/iidxio-ezusb2.dll \ build/bin/indep-32/vefxio.dll \ build/bin/indep-32/inject.exe \ dist/iidx/config.bat \ @@ -275,11 +268,8 @@ $(zipdir)/iidx-18.zip: \ build/bin/avs2_1101-32/launcher.exe \ build/bin/indep-32/config.exe \ build/bin/indep-32/eamio.dll \ - build/bin/indep-32/eamio-icca.dll \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/iidxio.dll \ - build/bin/indep-32/iidxio-ezusb.dll \ - build/bin/indep-32/iidxio-ezusb2.dll \ build/bin/indep-32/vefxio.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-18.bat \ @@ -294,11 +284,8 @@ $(zipdir)/iidx-19.zip: \ build/bin/avs2_1304-32/launcher.exe \ build/bin/indep-32/config.exe \ build/bin/indep-32/eamio.dll \ - build/bin/indep-32/eamio-icca.dll \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/iidxio.dll \ - build/bin/indep-32/iidxio-ezusb.dll \ - build/bin/indep-32/iidxio-ezusb2.dll \ build/bin/indep-32/vefxio.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-19.bat \ @@ -313,11 +300,8 @@ $(zipdir)/iidx-20.zip: \ build/bin/avs2_1508-32/launcher.exe \ build/bin/indep-32/config.exe \ build/bin/indep-32/eamio.dll \ - build/bin/indep-32/eamio-icca.dll \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/iidxio.dll \ - build/bin/indep-32/iidxio-ezusb.dll \ - build/bin/indep-32/iidxio-ezusb2.dll \ build/bin/indep-32/vefxio.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-20.bat \ @@ -332,11 +316,8 @@ $(zipdir)/iidx-21-to-24.zip: \ build/bin/avs2_1601-32/launcher.exe \ build/bin/indep-32/config.exe \ build/bin/indep-32/eamio.dll \ - build/bin/indep-32/eamio-icca.dll \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/iidxio.dll \ - build/bin/indep-32/iidxio-ezusb.dll \ - build/bin/indep-32/iidxio-ezusb2.dll \ build/bin/indep-32/vefxio.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-21.bat \ @@ -357,11 +338,8 @@ $(zipdir)/iidx-25-to-26.zip: \ build/bin/avs2_1700-64/launcher.exe \ build/bin/indep-64/config.exe \ build/bin/indep-64/eamio.dll \ - build/bin/indep-64/eamio-icca.dll \ build/bin/indep-64/geninput.dll \ build/bin/indep-64/iidxio.dll \ - build/bin/indep-64/iidxio-ezusb.dll \ - build/bin/indep-64/iidxio-ezusb2.dll \ build/bin/indep-64/vefxio.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-25.bat \ @@ -373,6 +351,22 @@ $(zipdir)/iidx-25-to-26.zip: \ $(V)echo ... $@ $(V)zip -j $@ $^ +$(zipdir)/iidx-hwio-x86.zip: \ + build/bin/indep-32/eamio-icca.dll \ + build/bin/indep-32/iidxio-ezusb.dll \ + build/bin/indep-32/iidxio-ezusb2.dll \ + | $(zipdir)/ + $(V)echo ... $@ + $(V)zip -j $@ $^ + +$(zipdir)/iidx-hwio-x64.zip: \ + build/bin/indep-64/eamio-icca.dll \ + build/bin/indep-64/iidxio-ezusb.dll \ + build/bin/indep-64/iidxio-ezusb2.dll \ + | $(zipdir)/ + $(V)echo ... $@ + $(V)zip -j $@ $^ + $(zipdir)/jb-01.zip: \ build/bin/avs2_803-32/jbhook1.dll \ build/bin/indep-32/inject.exe \ @@ -420,8 +414,6 @@ $(zipdir)/sdvx-01-to-04.zip: \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/sdvxio.dll \ - build/bin/indep-32/sdvxio-kfca.dll \ - build/bin/indep-32/sdvxio-bio2.dll \ dist/sdvx/config.bat \ dist/sdvx/gamestart.bat \ | $(zipdir)/ @@ -435,8 +427,6 @@ $(zipdir)/sdvx-05.zip: \ build/bin/indep-64/eamio.dll \ build/bin/indep-64/geninput.dll \ build/bin/indep-64/sdvxio.dll \ - build/bin/indep-64/sdvxio-kfca.dll \ - build/bin/indep-64/sdvxio-bio2.dll \ dist/sdvx5/config.bat \ dist/sdvx5/gamestart.bat \ dist/sdvx5/sdvxhook2.conf \ @@ -458,6 +448,22 @@ $(zipdir)/sdvx-05-cn.zip: \ $(V)echo ... $@ $(V)zip -j $@ $^ +$(zipdir)/sdvx-hwio-x86.zip: \ + build/bin/indep-32/sdvxio-kfca.dll \ + build/bin/indep-32/sdvxio-bio2.dll \ + build/bin/indep-32/vigem-sdvxio.exe \ + | $(zipdir)/ + $(V)echo ... $@ + $(V)zip -j $@ $^ + +$(zipdir)/sdvx-hwio-x64.zip: \ + build/bin/indep-64/sdvxio-kfca.dll \ + build/bin/indep-64/sdvxio-bio2.dll \ + build/bin/indep-64/vigem-sdvxio.exe \ + | $(zipdir)/ + $(V)echo ... $@ + $(V)zip -j $@ $^ + $(zipdir)/ddr-12-to-16.zip: \ build/bin/avs2_1508-32/launcher.exe \ build/bin/avs2_1508-32/ddrhook.dll \ @@ -556,12 +562,16 @@ $(BUILDDIR)/bemanitools.zip: \ $(zipdir)/iidx-20.zip \ $(zipdir)/iidx-21-to-24.zip \ $(zipdir)/iidx-25-to-26.zip \ + $(zipdir)/iidx-hwio-x86.zip \ + $(zipdir)/iidx-hwio-x64.zip \ $(zipdir)/jb-01.zip \ $(zipdir)/jb-05-to-07.zip \ $(zipdir)/jb-08.zip \ $(zipdir)/sdvx-01-to-04.zip \ $(zipdir)/sdvx-05.zip \ $(zipdir)/sdvx-05-cn.zip \ + $(zipdir)/sdvx-hwio-x86.zip \ + $(zipdir)/sdvx-hwio-x64.zip \ $(zipdir)/tools.zip \ $(zipdir)/tools-x64.zip \ CHANGELOG.md \ diff --git a/src/imports/ViGEm/Client.h b/src/imports/ViGEm/Client.h new file mode 100644 index 0000000..24df1a1 --- /dev/null +++ b/src/imports/ViGEm/Client.h @@ -0,0 +1,519 @@ +/* +MIT License + +Copyright (c) 2017-2019 Nefarius Software Solutions e.U. and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef ViGEmClient_h__ +#define ViGEmClient_h__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ViGEm/Common.h" + +#ifdef VIGEM_DYNAMIC +#ifdef VIGEM_EXPORTS +#define VIGEM_API __declspec(dllexport) +#else +#define VIGEM_API __declspec(dllimport) +#endif +#else +#define VIGEM_API +#endif + +/** + * \typedef enum _VIGEM_ERRORS + * + * \brief Defines an alias representing the ViGEm errors. + */ +typedef enum _VIGEM_ERRORS { + VIGEM_ERROR_NONE = 0x20000000, + VIGEM_ERROR_BUS_NOT_FOUND = 0xE0000001, + VIGEM_ERROR_NO_FREE_SLOT = 0xE0000002, + VIGEM_ERROR_INVALID_TARGET = 0xE0000003, + VIGEM_ERROR_REMOVAL_FAILED = 0xE0000004, + VIGEM_ERROR_ALREADY_CONNECTED = 0xE0000005, + VIGEM_ERROR_TARGET_UNINITIALIZED = 0xE0000006, + VIGEM_ERROR_TARGET_NOT_PLUGGED_IN = 0xE0000007, + VIGEM_ERROR_BUS_VERSION_MISMATCH = 0xE0000008, + VIGEM_ERROR_BUS_ACCESS_FAILED = 0xE0000009, + VIGEM_ERROR_CALLBACK_ALREADY_REGISTERED = 0xE0000010, + VIGEM_ERROR_CALLBACK_NOT_FOUND = 0xE0000011, + VIGEM_ERROR_BUS_ALREADY_CONNECTED = 0xE0000012, + VIGEM_ERROR_BUS_INVALID_HANDLE = 0xE0000013, + VIGEM_ERROR_XUSB_USERINDEX_OUT_OF_RANGE = 0xE0000014, + VIGEM_ERROR_INVALID_PARAMETER = 0xE0000015 + +} VIGEM_ERROR; + +/** + * \def VIGEM_SUCCESS(_val_) (_val_ == VIGEM_ERROR_NONE); + * + * \brief A macro that defines success. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param _val_ The VIGEM_ERROR value. + */ +#define VIGEM_SUCCESS(_val_) (_val_ == VIGEM_ERROR_NONE) + +/** + * \typedef struct _VIGEM_CLIENT_T *PVIGEM_CLIENT + * + * \brief Defines an alias representing a driver connection object. + */ +typedef struct _VIGEM_CLIENT_T *PVIGEM_CLIENT; + +/** + * \typedef struct _VIGEM_TARGET_T *PVIGEM_TARGET + * + * \brief Defines an alias representing a target device object. + */ +typedef struct _VIGEM_TARGET_T *PVIGEM_TARGET; + +typedef _Function_class_(EVT_VIGEM_TARGET_ADD_RESULT) VOID CALLBACK + EVT_VIGEM_TARGET_ADD_RESULT( + PVIGEM_CLIENT Client, PVIGEM_TARGET Target, VIGEM_ERROR Result); + +typedef EVT_VIGEM_TARGET_ADD_RESULT *PFN_VIGEM_TARGET_ADD_RESULT; + +typedef _Function_class_(EVT_VIGEM_X360_NOTIFICATION) VOID CALLBACK + EVT_VIGEM_X360_NOTIFICATION( + PVIGEM_CLIENT Client, + PVIGEM_TARGET Target, + UCHAR LargeMotor, + UCHAR SmallMotor, + UCHAR LedNumber, + LPVOID UserData); + +typedef EVT_VIGEM_X360_NOTIFICATION *PFN_VIGEM_X360_NOTIFICATION; + +typedef _Function_class_(EVT_VIGEM_DS4_NOTIFICATION) VOID CALLBACK + EVT_VIGEM_DS4_NOTIFICATION( + PVIGEM_CLIENT Client, + PVIGEM_TARGET Target, + UCHAR LargeMotor, + UCHAR SmallMotor, + DS4_LIGHTBAR_COLOR LightbarColor, + LPVOID UserData); + +typedef EVT_VIGEM_DS4_NOTIFICATION *PFN_VIGEM_DS4_NOTIFICATION; + +/** + * \fn PVIGEM_CLIENT vigem_alloc(void); + * + * \brief Allocates an object representing a driver connection. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \return A new driver connection object. + */ +VIGEM_API PVIGEM_CLIENT vigem_alloc(void); + +/** + * \fn void vigem_free(PVIGEM_CLIENT vigem); + * + * \brief Frees up memory used by the driver connection object. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param vigem The driver connection object. + */ +VIGEM_API void vigem_free(PVIGEM_CLIENT vigem); + +/** + * \fn VIGEM_ERROR vigem_connect(PVIGEM_CLIENT vigem); + * + * \brief Initializes the driver object and establishes a connection to the + * emulation bus driver. Returns an error if no compatible bus device has been + * found. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param vigem The driver connection object. + * + * \return A VIGEM_ERROR. + */ +VIGEM_API VIGEM_ERROR vigem_connect(PVIGEM_CLIENT vigem); + +/** + * \fn void vigem_disconnect(PVIGEM_CLIENT vigem); + * + * \brief Disconnects from the bus device and resets the driver object state. + * The driver object may be reused again after calling this function. When + * called, all targets which may still be connected will be destroyed + * automatically. Be aware, that allocated target objects won't be automatically + * freed, this has to be taken care of by the caller. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param vigem The driver connection object. + */ +VIGEM_API void vigem_disconnect(PVIGEM_CLIENT vigem); + +/** + * \fn PVIGEM_TARGET vigem_target_x360_alloc(void); + * + * \brief Allocates an object representing an Xbox 360 Controller device. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \return A PVIGEM_TARGET representing an Xbox 360 Controller device. + */ +VIGEM_API PVIGEM_TARGET vigem_target_x360_alloc(void); + +/** + * \fn PVIGEM_TARGET vigem_target_ds4_alloc(void); + * + * \brief Allocates an object representing a DualShock 4 Controller device. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \return A PVIGEM_TARGET representing a DualShock 4 Controller device. + */ +VIGEM_API PVIGEM_TARGET vigem_target_ds4_alloc(void); + +/** + * \fn void vigem_target_free(PVIGEM_TARGET target); + * + * \brief Frees up memory used by the target device object. This does not + * automatically remove the associated device from the bus, if present. If the + * target device doesn't get removed before this call, the device becomes + * orphaned until the owning process is terminated. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param target The target device object. + */ +VIGEM_API void vigem_target_free(PVIGEM_TARGET target); + +/** + * \fn VIGEM_ERROR vigem_target_add(PVIGEM_CLIENT vigem, PVIGEM_TARGET target); + * + * \brief Adds a provided target device to the bus driver, which is equal to a + * device plug-in event of a physical hardware device. This function blocks + * until the target device is in full operational mode. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param vigem The driver connection object. + * \param target The target device object. + * + * \return A VIGEM_ERROR. + */ +VIGEM_API VIGEM_ERROR +vigem_target_add(PVIGEM_CLIENT vigem, PVIGEM_TARGET target); + +/** + * \fn VIGEM_ERROR vigem_target_add_async(PVIGEM_CLIENT vigem, PVIGEM_TARGET + * target, PVIGEM_TARGET_ADD_RESULT result); + * + * \brief Adds a provided target device to the bus driver, which is equal to a + * device plug-in event of a physical hardware device. This function immediately + * returns. An optional callback may be registered which gets called on error or + * if the target device has become fully operational. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param vigem The driver connection object. + * \param target The target device object. + * \param result An optional function getting called when the target device + * becomes available. + * + * \return A VIGEM_ERROR. + */ +VIGEM_API VIGEM_ERROR vigem_target_add_async( + PVIGEM_CLIENT vigem, + PVIGEM_TARGET target, + PFN_VIGEM_TARGET_ADD_RESULT result); + +/** + * \fn VIGEM_ERROR vigem_target_remove(PVIGEM_CLIENT vigem, PVIGEM_TARGET + * target); + * + * \brief Removes a provided target device from the bus driver, which is equal + * to a device unplug event of a physical hardware device. The target device + * object may be reused after this function is called. If this function is never + * called on target device objects, they will be removed from the bus when the + * owning process terminates. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param vigem The driver connection object. + * \param target The target device object. + * + * \return A VIGEM_ERROR. + */ +VIGEM_API VIGEM_ERROR +vigem_target_remove(PVIGEM_CLIENT vigem, PVIGEM_TARGET target); + +/** + * \fn VIGEM_ERROR vigem_target_x360_register_notification(PVIGEM_CLIENT vigem, + * PVIGEM_TARGET target, PVIGEM_X360_NOTIFICATION notification); + * + * \brief Registers a function which gets called, when LED index or vibration + * state changes occur on the provided target device. This function fails if the + * provided target device isn't fully operational or in an erroneous state. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param vigem The driver connection object. + * \param target The target device object. + * \param notification The notification callback. + * \param userData The user data passed to the notification callback. + * + * \return A VIGEM_ERROR. + */ +VIGEM_API VIGEM_ERROR vigem_target_x360_register_notification( + PVIGEM_CLIENT vigem, + PVIGEM_TARGET target, + PFN_VIGEM_X360_NOTIFICATION notification, + LPVOID userData); + +/** + * \fn VIGEM_ERROR vigem_target_ds4_register_notification(PVIGEM_CLIENT vigem, + * PVIGEM_TARGET target, PVIGEM_DS4_NOTIFICATION notification); + * + * \brief Registers a function which gets called, when LightBar or vibration + * state changes occur on the provided target device. This function fails if the + * provided target device isn't fully operational or in an erroneous state. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param vigem The driver connection object. + * \param target The target device object. + * \param notification The notification callback. + * \param userData The user data passed to the notification callback. + * + * \return A VIGEM_ERROR. + */ +VIGEM_API VIGEM_ERROR vigem_target_ds4_register_notification( + PVIGEM_CLIENT vigem, + PVIGEM_TARGET target, + PFN_VIGEM_DS4_NOTIFICATION notification, + LPVOID userData); + +/** + * \fn void vigem_target_x360_unregister_notification(PVIGEM_TARGET target); + * + * \brief Removes a previously registered callback function from the provided + * target object. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param target The target device object. + */ +VIGEM_API void vigem_target_x360_unregister_notification(PVIGEM_TARGET target); + +/** + * \fn void vigem_target_ds4_unregister_notification(PVIGEM_TARGET target); + * + * \brief Removes a previously registered callback function from the provided + * target object. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param target The target device object. + */ +VIGEM_API void vigem_target_ds4_unregister_notification(PVIGEM_TARGET target); + +/** + * \fn void vigem_target_set_vid(PVIGEM_TARGET target, USHORT vid); + * + * \brief Overrides the default Vendor ID value with the provided one. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param target The target device object. + * \param vid The Vendor ID to set. + */ +VIGEM_API void vigem_target_set_vid(PVIGEM_TARGET target, USHORT vid); + +/** + * \fn void vigem_target_set_pid(PVIGEM_TARGET target, USHORT pid); + * + * \brief Overrides the default Product ID value with the provided one. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param target The target device object. + * \param pid The Product ID to set. + */ +VIGEM_API void vigem_target_set_pid(PVIGEM_TARGET target, USHORT pid); + +/** + * \fn USHORT vigem_target_get_vid(PVIGEM_TARGET target); + * + * \brief Returns the Vendor ID of the provided target device object. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param target The target device object. + * + * \return The Vendor ID. + */ +VIGEM_API USHORT vigem_target_get_vid(PVIGEM_TARGET target); + +/** + * \fn USHORT vigem_target_get_pid(PVIGEM_TARGET target); + * + * \brief Returns the Product ID of the provided target device object. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param target The target device object. + * + * \return The Product ID. + */ +VIGEM_API USHORT vigem_target_get_pid(PVIGEM_TARGET target); + +/** + * \fn VIGEM_ERROR vigem_target_x360_update(PVIGEM_CLIENT vigem, PVIGEM_TARGET + * target, XUSB_REPORT report); + * + * \brief Sends a state report to the provided target device. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param vigem The driver connection object. + * \param target The target device object. + * \param report The report to send to the target device. + * + * \return A VIGEM_ERROR. + */ +VIGEM_API VIGEM_ERROR vigem_target_x360_update( + PVIGEM_CLIENT vigem, PVIGEM_TARGET target, XUSB_REPORT report); + +/** + * \fn VIGEM_ERROR vigem_target_ds4_update(PVIGEM_CLIENT vigem, PVIGEM_TARGET + * target, DS4_REPORT report); + * + * \brief Sends a state report to the provided target device. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param vigem The driver connection object. + * \param target The target device object. + * \param report The report to send to the target device. + * + * \return A VIGEM_ERROR. + */ +VIGEM_API VIGEM_ERROR vigem_target_ds4_update( + PVIGEM_CLIENT vigem, PVIGEM_TARGET target, DS4_REPORT report); + +/** + * \fn ULONG vigem_target_get_index(PVIGEM_TARGET target); + * + * \brief Returns the internal index (serial number) the bus driver assigned + * to the provided target device object. Note that this value is specific to the + * inner workings of the bus driver, it does not reflect related values like + * player index or device arrival order experienced by other APIs. It may be + * used to identify the target device object for its lifetime. This value + * becomes invalid once the target device is removed from the bus and may change + * on the next addition of the device. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param target The target device object. + * + * \return The internally used index of the target device. + */ +VIGEM_API ULONG vigem_target_get_index(PVIGEM_TARGET target); + +/** + * \fn VIGEM_TARGET_TYPE vigem_target_get_type(PVIGEM_TARGET target); + * + * \brief Returns the type of the provided target device object. + * + * \author Benjamin "Nefarius" Höglinger + * \date 28.08.2017 + * + * \param target The target device object. + * + * \return A VIGEM_TARGET_TYPE. + */ +VIGEM_API VIGEM_TARGET_TYPE vigem_target_get_type(PVIGEM_TARGET target); + +/** + * \fn BOOL vigem_target_is_attached(PVIGEM_TARGET target); + * + * \brief Returns TRUE if the provided target device object is currently + * attached to the bus, FALSE otherwise. + * + * \author Benjamin "Nefarius" Höglinger + * \date 30.08.2017 + * + * \param target The target device object. + * + * \return TRUE if device is attached to the bus, FALSE otherwise. + */ +VIGEM_API BOOL vigem_target_is_attached(PVIGEM_TARGET target); + +/** + * \fn VIGEM_API VIGEM_ERROR vigem_target_x360_get_user_index(PVIGEM_CLIENT + * vigem, PVIGEM_TARGET target, PULONG index); + * + * \brief Returns the user index of the emulated Xenon device. This value + * correspondents to the (zero-based) index number representing the player + * number via LED present on a physical controller and is compatible to the + * dwUserIndex propery of the XInput* APIs. + * + * \author Benjamin "Nefarius" Höglinger + * \date 10.05.2018 + * + * \param vigem The driver connection object. + * \param target The target device object. + * \param index The (zero-based) user index of the Xenon device. + * + * \return A VIGEM_ERROR. + */ +VIGEM_API VIGEM_ERROR vigem_target_x360_get_user_index( + PVIGEM_CLIENT vigem, PVIGEM_TARGET target, PULONG index); + +#ifdef __cplusplus +} +#endif + +#endif // ViGEmClient_h__ diff --git a/src/imports/ViGEm/Common.h b/src/imports/ViGEm/Common.h new file mode 100644 index 0000000..2557764 --- /dev/null +++ b/src/imports/ViGEm/Common.h @@ -0,0 +1,191 @@ +/* +MIT License + +Copyright (c) 2017-2019 Nefarius Software Solutions e.U. and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#pragma once + +// +// Represents the desired target type for the emulated device. +// +typedef enum _VIGEM_TARGET_TYPE { + // + // Microsoft Xbox 360 Controller (wired) + // + Xbox360Wired = 0, + // + // Sony DualShock 4 (wired) + // + DualShock4Wired = 2 // NOTE: 1 skipped on purpose to maintain compatibility + +} VIGEM_TARGET_TYPE, + *PVIGEM_TARGET_TYPE; + +// +// Possible XUSB report buttons. +// +typedef enum _XUSB_BUTTON { + XUSB_GAMEPAD_DPAD_UP = 0x0001, + XUSB_GAMEPAD_DPAD_DOWN = 0x0002, + XUSB_GAMEPAD_DPAD_LEFT = 0x0004, + XUSB_GAMEPAD_DPAD_RIGHT = 0x0008, + XUSB_GAMEPAD_START = 0x0010, + XUSB_GAMEPAD_BACK = 0x0020, + XUSB_GAMEPAD_LEFT_THUMB = 0x0040, + XUSB_GAMEPAD_RIGHT_THUMB = 0x0080, + XUSB_GAMEPAD_LEFT_SHOULDER = 0x0100, + XUSB_GAMEPAD_RIGHT_SHOULDER = 0x0200, + XUSB_GAMEPAD_GUIDE = 0x0400, + XUSB_GAMEPAD_A = 0x1000, + XUSB_GAMEPAD_B = 0x2000, + XUSB_GAMEPAD_X = 0x4000, + XUSB_GAMEPAD_Y = 0x8000 + +} XUSB_BUTTON, + *PXUSB_BUTTON; + +// +// Represents an XINPUT_GAMEPAD-compatible report structure. +// +typedef struct _XUSB_REPORT { + USHORT wButtons; + BYTE bLeftTrigger; + BYTE bRightTrigger; + SHORT sThumbLX; + SHORT sThumbLY; + SHORT sThumbRX; + SHORT sThumbRY; + +} XUSB_REPORT, *PXUSB_REPORT; + +// +// Initializes a _XUSB_REPORT structure. +// +VOID FORCEINLINE XUSB_REPORT_INIT(_Out_ PXUSB_REPORT Report) +{ + RtlZeroMemory(Report, sizeof(XUSB_REPORT)); +} + +// +// The color value (RGB) of a DualShock 4 Lightbar +// +typedef struct _DS4_LIGHTBAR_COLOR { + // + // Red part of the Lightbar (0-255). + // + UCHAR Red; + + // + // Green part of the Lightbar (0-255). + // + UCHAR Green; + + // + // Blue part of the Lightbar (0-255). + // + UCHAR Blue; + +} DS4_LIGHTBAR_COLOR, *PDS4_LIGHTBAR_COLOR; + +// +// DualShock 4 digital buttons +// +typedef enum _DS4_BUTTONS { + DS4_BUTTON_THUMB_RIGHT = 1 << 15, + DS4_BUTTON_THUMB_LEFT = 1 << 14, + DS4_BUTTON_OPTIONS = 1 << 13, + DS4_BUTTON_SHARE = 1 << 12, + DS4_BUTTON_TRIGGER_RIGHT = 1 << 11, + DS4_BUTTON_TRIGGER_LEFT = 1 << 10, + DS4_BUTTON_SHOULDER_RIGHT = 1 << 9, + DS4_BUTTON_SHOULDER_LEFT = 1 << 8, + DS4_BUTTON_TRIANGLE = 1 << 7, + DS4_BUTTON_CIRCLE = 1 << 6, + DS4_BUTTON_CROSS = 1 << 5, + DS4_BUTTON_SQUARE = 1 << 4 + +} DS4_BUTTONS, + *PDS4_BUTTONS; + +// +// DualShock 4 special buttons +// +typedef enum _DS4_SPECIAL_BUTTONS { + DS4_SPECIAL_BUTTON_PS = 1 << 0, + DS4_SPECIAL_BUTTON_TOUCHPAD = 1 << 1 + +} DS4_SPECIAL_BUTTONS, + *PDS4_SPECIAL_BUTTONS; + +// +// DualShock 4 directional pad (HAT) values +// +typedef enum _DS4_DPAD_DIRECTIONS { + DS4_BUTTON_DPAD_NONE = 0x8, + DS4_BUTTON_DPAD_NORTHWEST = 0x7, + DS4_BUTTON_DPAD_WEST = 0x6, + DS4_BUTTON_DPAD_SOUTHWEST = 0x5, + DS4_BUTTON_DPAD_SOUTH = 0x4, + DS4_BUTTON_DPAD_SOUTHEAST = 0x3, + DS4_BUTTON_DPAD_EAST = 0x2, + DS4_BUTTON_DPAD_NORTHEAST = 0x1, + DS4_BUTTON_DPAD_NORTH = 0x0 + +} DS4_DPAD_DIRECTIONS, + *PDS4_DPAD_DIRECTIONS; + +// +// DualShock 4 HID Input report +// +typedef struct _DS4_REPORT { + BYTE bThumbLX; + BYTE bThumbLY; + BYTE bThumbRX; + BYTE bThumbRY; + USHORT wButtons; + BYTE bSpecial; + BYTE bTriggerL; + BYTE bTriggerR; + +} DS4_REPORT, *PDS4_REPORT; + +// +// Sets the current state of the D-PAD on a DualShock 4 report. +// +VOID FORCEINLINE +DS4_SET_DPAD(_Out_ PDS4_REPORT Report, _In_ DS4_DPAD_DIRECTIONS Dpad) +{ + Report->wButtons &= ~0xF; + Report->wButtons |= (USHORT) Dpad; +} + +VOID FORCEINLINE DS4_REPORT_INIT(_Out_ PDS4_REPORT Report) +{ + RtlZeroMemory(Report, sizeof(DS4_REPORT)); + + Report->bThumbLX = 0x80; + Report->bThumbLY = 0x80; + Report->bThumbRX = 0x80; + Report->bThumbRY = 0x80; + + DS4_SET_DPAD(Report, DS4_BUTTON_DPAD_NONE); +} diff --git a/src/imports/ViGEm/Util.h b/src/imports/ViGEm/Util.h new file mode 100644 index 0000000..0dafdc7 --- /dev/null +++ b/src/imports/ViGEm/Util.h @@ -0,0 +1,68 @@ +#pragma once + +#include "ViGEm/Common.h" +#include + +VOID FORCEINLINE +XUSB_TO_DS4_REPORT(_Out_ PXUSB_REPORT Input, _Out_ PDS4_REPORT Output) +{ + if (Input->wButtons & XUSB_GAMEPAD_BACK) + Output->wButtons |= DS4_BUTTON_SHARE; + if (Input->wButtons & XUSB_GAMEPAD_START) + Output->wButtons |= DS4_BUTTON_OPTIONS; + if (Input->wButtons & XUSB_GAMEPAD_LEFT_THUMB) + Output->wButtons |= DS4_BUTTON_THUMB_LEFT; + if (Input->wButtons & XUSB_GAMEPAD_RIGHT_THUMB) + Output->wButtons |= DS4_BUTTON_THUMB_RIGHT; + if (Input->wButtons & XUSB_GAMEPAD_LEFT_SHOULDER) + Output->wButtons |= DS4_BUTTON_SHOULDER_LEFT; + if (Input->wButtons & XUSB_GAMEPAD_RIGHT_SHOULDER) + Output->wButtons |= DS4_BUTTON_SHOULDER_RIGHT; + if (Input->wButtons & XUSB_GAMEPAD_GUIDE) + Output->bSpecial |= DS4_SPECIAL_BUTTON_PS; + if (Input->wButtons & XUSB_GAMEPAD_A) + Output->wButtons |= DS4_BUTTON_CROSS; + if (Input->wButtons & XUSB_GAMEPAD_B) + Output->wButtons |= DS4_BUTTON_CIRCLE; + if (Input->wButtons & XUSB_GAMEPAD_X) + Output->wButtons |= DS4_BUTTON_SQUARE; + if (Input->wButtons & XUSB_GAMEPAD_Y) + Output->wButtons |= DS4_BUTTON_TRIANGLE; + + Output->bTriggerL = Input->bLeftTrigger; + Output->bTriggerR = Input->bRightTrigger; + + if (Input->bLeftTrigger > 0) + Output->wButtons |= DS4_BUTTON_TRIGGER_LEFT; + if (Input->bRightTrigger > 0) + Output->wButtons |= DS4_BUTTON_TRIGGER_RIGHT; + + if (Input->wButtons & XUSB_GAMEPAD_DPAD_UP) + DS4_SET_DPAD(Output, DS4_BUTTON_DPAD_NORTH); + if (Input->wButtons & XUSB_GAMEPAD_DPAD_RIGHT) + DS4_SET_DPAD(Output, DS4_BUTTON_DPAD_EAST); + if (Input->wButtons & XUSB_GAMEPAD_DPAD_DOWN) + DS4_SET_DPAD(Output, DS4_BUTTON_DPAD_SOUTH); + if (Input->wButtons & XUSB_GAMEPAD_DPAD_LEFT) + DS4_SET_DPAD(Output, DS4_BUTTON_DPAD_WEST); + + if (Input->wButtons & XUSB_GAMEPAD_DPAD_UP && + Input->wButtons & XUSB_GAMEPAD_DPAD_RIGHT) + DS4_SET_DPAD(Output, DS4_BUTTON_DPAD_NORTHEAST); + if (Input->wButtons & XUSB_GAMEPAD_DPAD_RIGHT && + Input->wButtons & XUSB_GAMEPAD_DPAD_DOWN) + DS4_SET_DPAD(Output, DS4_BUTTON_DPAD_SOUTHEAST); + if (Input->wButtons & XUSB_GAMEPAD_DPAD_DOWN && + Input->wButtons & XUSB_GAMEPAD_DPAD_LEFT) + DS4_SET_DPAD(Output, DS4_BUTTON_DPAD_SOUTHWEST); + if (Input->wButtons & XUSB_GAMEPAD_DPAD_LEFT && + Input->wButtons & XUSB_GAMEPAD_DPAD_UP) + DS4_SET_DPAD(Output, DS4_BUTTON_DPAD_NORTHWEST); + + Output->bThumbLX = ((Input->sThumbLX + ((USHRT_MAX / 2) + 1)) / 257); + Output->bThumbLY = (-(Input->sThumbLY + ((USHRT_MAX / 2) - 1)) / 257); + Output->bThumbLY = (Output->bThumbLY == 0) ? 0xFF : Output->bThumbLY; + Output->bThumbRX = ((Input->sThumbRX + ((USHRT_MAX / 2) + 1)) / 257); + Output->bThumbRY = (-(Input->sThumbRY + ((USHRT_MAX / 2) + 1)) / 257); + Output->bThumbRY = (Output->bThumbRY == 0) ? 0xFF : Output->bThumbRY; +} diff --git a/src/imports/ViGEm/km/BusShared.h b/src/imports/ViGEm/km/BusShared.h new file mode 100644 index 0000000..618eca6 --- /dev/null +++ b/src/imports/ViGEm/km/BusShared.h @@ -0,0 +1,399 @@ +/* +MIT License + +Copyright (c) 2016-2019 Nefarius Software Solutions e.U. and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// +// GUID identifying the bus device. Used by client library to detect and +// communicate. +// +// IMPORTANT: make sure to change this value if you fork it or introduce +// breaking changes! +// +// {96E42B22-F5E9-42F8-B043-ED0F932F014F} +DEFINE_GUID( + GUID_DEVINTERFACE_BUSENUM_VIGEM, + 0x96E42B22, + 0xF5E9, + 0x42F8, + 0xB0, + 0x43, + 0xED, + 0x0F, + 0x93, + 0x2F, + 0x01, + 0x4F); + +#pragma once + +#include "ViGEm/Common.h" + +// +// Common version for user-mode library and driver compatibility +// +// On initialization, the user-mode library has this number embedded +// and sends it to the bus on its enumeration. The bus compares this +// number to the one it was compiled with. If they match, the bus +// access is permitted and success reported. If they mismatch, an +// error is reported and the user-mode library skips this instance. +// +#define VIGEM_COMMON_VERSION 0x0001 + +#define FILE_DEVICE_BUSENUM FILE_DEVICE_BUS_EXTENDER +#define BUSENUM_IOCTL(_index_) \ + CTL_CODE(FILE_DEVICE_BUSENUM, _index_, METHOD_BUFFERED, FILE_READ_DATA) +#define BUSENUM_W_IOCTL(_index_) \ + CTL_CODE(FILE_DEVICE_BUSENUM, _index_, METHOD_BUFFERED, FILE_WRITE_DATA) +#define BUSENUM_R_IOCTL(_index_) \ + CTL_CODE(FILE_DEVICE_BUSENUM, _index_, METHOD_BUFFERED, FILE_READ_DATA) +#define BUSENUM_RW_IOCTL(_index_) \ + CTL_CODE( \ + FILE_DEVICE_BUSENUM, \ + _index_, \ + METHOD_BUFFERED, \ + FILE_WRITE_DATA | FILE_READ_DATA) + +#define IOCTL_VIGEM_BASE 0x801 + +// +// IO control codes +// +#define IOCTL_VIGEM_PLUGIN_TARGET BUSENUM_W_IOCTL(IOCTL_VIGEM_BASE + 0x000) +#define IOCTL_VIGEM_UNPLUG_TARGET BUSENUM_W_IOCTL(IOCTL_VIGEM_BASE + 0x001) +#define IOCTL_VIGEM_CHECK_VERSION BUSENUM_W_IOCTL(IOCTL_VIGEM_BASE + 0x002) + +#define IOCTL_XUSB_REQUEST_NOTIFICATION \ + BUSENUM_RW_IOCTL(IOCTL_VIGEM_BASE + 0x200) +#define IOCTL_XUSB_SUBMIT_REPORT BUSENUM_W_IOCTL(IOCTL_VIGEM_BASE + 0x201) +#define IOCTL_DS4_SUBMIT_REPORT BUSENUM_W_IOCTL(IOCTL_VIGEM_BASE + 0x202) +#define IOCTL_DS4_REQUEST_NOTIFICATION BUSENUM_W_IOCTL(IOCTL_VIGEM_BASE + 0x203) +//#define IOCTL_XGIP_SUBMIT_REPORT BUSENUM_W_IOCTL (IOCTL_VIGEM_BASE + +//0x204) #define IOCTL_XGIP_SUBMIT_INTERRUPT BUSENUM_W_IOCTL +//(IOCTL_VIGEM_BASE + 0x205) +#define IOCTL_XUSB_GET_USER_INDEX BUSENUM_RW_IOCTL(IOCTL_VIGEM_BASE + 0x206) + +// +// Data structure used in PlugIn and UnPlug ioctls +// + +#pragma region Plugin + +// +// Data structure used in IOCTL_VIGEM_PLUGIN_TARGET requests. +// +typedef struct _VIGEM_PLUGIN_TARGET { + // + // sizeof (struct _BUSENUM_HARDWARE) + // + IN ULONG Size; + + // + // Serial number of target device. + // + IN ULONG SerialNo; + + // + // Type of the target device to emulate. + // + VIGEM_TARGET_TYPE TargetType; + + // + // If set, the vendor ID the emulated device is reporting + // + USHORT VendorId; + + // + // If set, the product ID the emulated device is reporting + // + USHORT ProductId; + +} VIGEM_PLUGIN_TARGET, *PVIGEM_PLUGIN_TARGET; + +// +// Initializes a VIGEM_PLUGIN_TARGET structure. +// +VOID FORCEINLINE VIGEM_PLUGIN_TARGET_INIT( + _Out_ PVIGEM_PLUGIN_TARGET PlugIn, + _In_ ULONG SerialNo, + _In_ VIGEM_TARGET_TYPE TargetType) +{ + RtlZeroMemory(PlugIn, sizeof(VIGEM_PLUGIN_TARGET)); + + PlugIn->Size = sizeof(VIGEM_PLUGIN_TARGET); + PlugIn->SerialNo = SerialNo; + PlugIn->TargetType = TargetType; +} + +#pragma endregion + +#pragma region Unplug + +// +// Data structure used in IOCTL_VIGEM_UNPLUG_TARGET requests. +// +typedef struct _VIGEM_UNPLUG_TARGET { + // + // sizeof (struct _REMOVE_HARDWARE) + // + IN ULONG Size; + + // + // Serial number of target device. + // + ULONG SerialNo; + +} VIGEM_UNPLUG_TARGET, *PVIGEM_UNPLUG_TARGET; + +// +// Initializes a VIGEM_UNPLUG_TARGET structure. +// +VOID FORCEINLINE +VIGEM_UNPLUG_TARGET_INIT(_Out_ PVIGEM_UNPLUG_TARGET UnPlug, _In_ ULONG SerialNo) +{ + RtlZeroMemory(UnPlug, sizeof(VIGEM_UNPLUG_TARGET)); + + UnPlug->Size = sizeof(VIGEM_UNPLUG_TARGET); + UnPlug->SerialNo = SerialNo; +} + +#pragma endregion + +#pragma region Check version + +typedef struct _VIGEM_CHECK_VERSION { + IN ULONG Size; + + IN ULONG Version; + +} VIGEM_CHECK_VERSION, *PVIGEM_CHECK_VERSION; + +VOID FORCEINLINE VIGEM_CHECK_VERSION_INIT( + _Out_ PVIGEM_CHECK_VERSION CheckVersion, _In_ ULONG Version) +{ + RtlZeroMemory(CheckVersion, sizeof(VIGEM_CHECK_VERSION)); + + CheckVersion->Size = sizeof(VIGEM_CHECK_VERSION); + CheckVersion->Version = Version; +} + +#pragma endregion + +#pragma region XUSB(aka Xbox 360 device) section + +// +// Data structure used in IOCTL_XUSB_REQUEST_NOTIFICATION requests. +// +typedef struct _XUSB_REQUEST_NOTIFICATION { + // + // sizeof(struct _XUSB_REQUEST_NOTIFICATION) + // + ULONG Size; + + // + // Serial number of target device. + // + ULONG SerialNo; + + // + // Vibration intensity value of the large motor (0-255). + // + UCHAR LargeMotor; + + // + // Vibration intensity value of the small motor (0-255). + // + UCHAR SmallMotor; + + // + // Index number of the slot/LED that XUSB.sys has assigned. + // + UCHAR LedNumber; + +} XUSB_REQUEST_NOTIFICATION, *PXUSB_REQUEST_NOTIFICATION; + +// +// Initializes a XUSB_REQUEST_NOTIFICATION structure. +// +VOID FORCEINLINE XUSB_REQUEST_NOTIFICATION_INIT( + _Out_ PXUSB_REQUEST_NOTIFICATION Request, _In_ ULONG SerialNo) +{ + RtlZeroMemory(Request, sizeof(XUSB_REQUEST_NOTIFICATION)); + + Request->Size = sizeof(XUSB_REQUEST_NOTIFICATION); + Request->SerialNo = SerialNo; +} + +// +// Data structure used in IOCTL_XUSB_SUBMIT_REPORT requests. +// +typedef struct _XUSB_SUBMIT_REPORT { + // + // sizeof(struct _XUSB_SUBMIT_REPORT) + // + ULONG Size; + + // + // Serial number of target device. + // + ULONG SerialNo; + + // + // Report to submit to the target device. + // + XUSB_REPORT Report; + +} XUSB_SUBMIT_REPORT, *PXUSB_SUBMIT_REPORT; + +// +// Initializes an XUSB report. +// +VOID FORCEINLINE +XUSB_SUBMIT_REPORT_INIT(_Out_ PXUSB_SUBMIT_REPORT Report, _In_ ULONG SerialNo) +{ + RtlZeroMemory(Report, sizeof(XUSB_SUBMIT_REPORT)); + + Report->Size = sizeof(XUSB_SUBMIT_REPORT); + Report->SerialNo = SerialNo; +} + +typedef struct _XUSB_GET_USER_INDEX { + // + // sizeof(struct _XUSB_GET_USER_INDEX) + // + ULONG Size; + + // + // Serial number of target device. + // + ULONG SerialNo; + + // + // User index of target device. + // + OUT ULONG UserIndex; + +} XUSB_GET_USER_INDEX, *PXUSB_GET_USER_INDEX; + +// +// Initializes XUSB_GET_USER_INDEX structure. +// +VOID FORCEINLINE XUSB_GET_USER_INDEX_INIT( + _Out_ PXUSB_GET_USER_INDEX GetRequest, _In_ ULONG SerialNo) +{ + RtlZeroMemory(GetRequest, sizeof(XUSB_GET_USER_INDEX)); + + GetRequest->Size = sizeof(XUSB_GET_USER_INDEX); + GetRequest->SerialNo = SerialNo; +} + +#pragma endregion + +#pragma region DualShock 4 section + +typedef struct _DS4_OUTPUT_REPORT { + // + // Vibration intensity value of the small motor (0-255). + // + UCHAR SmallMotor; + + // + // Vibration intensity value of the large motor (0-255). + // + UCHAR LargeMotor; + + // + // Color values of the Lightbar. + // + DS4_LIGHTBAR_COLOR LightbarColor; + +} DS4_OUTPUT_REPORT, *PDS4_OUTPUT_REPORT; + +// +// Data structure used in IOCTL_DS4_REQUEST_NOTIFICATION requests. +// +typedef struct _DS4_REQUEST_NOTIFICATION { + // + // sizeof(struct _XUSB_REQUEST_NOTIFICATION) + // + ULONG Size; + + // + // Serial number of target device. + // + ULONG SerialNo; + + // + // The HID output report + // + DS4_OUTPUT_REPORT Report; + +} DS4_REQUEST_NOTIFICATION, *PDS4_REQUEST_NOTIFICATION; + +// +// Initializes a DS4_REQUEST_NOTIFICATION structure. +// +VOID FORCEINLINE DS4_REQUEST_NOTIFICATION_INIT( + _Out_ PDS4_REQUEST_NOTIFICATION Request, _In_ ULONG SerialNo) +{ + RtlZeroMemory(Request, sizeof(DS4_REQUEST_NOTIFICATION)); + + Request->Size = sizeof(DS4_REQUEST_NOTIFICATION); + Request->SerialNo = SerialNo; +} + +// +// DualShock 4 request data +// +typedef struct _DS4_SUBMIT_REPORT { + // + // sizeof(struct _DS4_SUBMIT_REPORT) + // + ULONG Size; + + // + // Serial number of target device. + // + ULONG SerialNo; + + // + // HID Input report + // + DS4_REPORT Report; + +} DS4_SUBMIT_REPORT, *PDS4_SUBMIT_REPORT; + +// +// Initializes a DualShock 4 report. +// +VOID FORCEINLINE +DS4_SUBMIT_REPORT_INIT(_Out_ PDS4_SUBMIT_REPORT Report, _In_ ULONG SerialNo) +{ + RtlZeroMemory(Report, sizeof(DS4_SUBMIT_REPORT)); + + Report->Size = sizeof(DS4_SUBMIT_REPORT); + Report->SerialNo = SerialNo; + + DS4_REPORT_INIT(&Report->Report); +} + +#pragma endregion diff --git a/src/imports/import_32_indep_ViGEmClient.def b/src/imports/import_32_indep_ViGEmClient.def new file mode 100644 index 0000000..ceaf00c --- /dev/null +++ b/src/imports/import_32_indep_ViGEmClient.def @@ -0,0 +1,27 @@ +LIBRARY ViGEmClient + +EXPORTS + vigem_alloc + vigem_connect + vigem_disconnect + vigem_free + vigem_target_add + vigem_target_add_async + vigem_target_ds4_alloc + vigem_target_ds4_register_notification + vigem_target_ds4_unregister_notification + vigem_target_ds4_update + vigem_target_free + vigem_target_get_index + vigem_target_get_pid + vigem_target_get_type + vigem_target_get_vid + vigem_target_is_attached + vigem_target_remove + vigem_target_set_pid + vigem_target_set_vid + vigem_target_x360_alloc + vigem_target_x360_get_user_index + vigem_target_x360_register_notification + vigem_target_x360_unregister_notification + vigem_target_x360_update diff --git a/src/imports/import_64_indep_ViGEmClient.def b/src/imports/import_64_indep_ViGEmClient.def new file mode 100644 index 0000000..ceaf00c --- /dev/null +++ b/src/imports/import_64_indep_ViGEmClient.def @@ -0,0 +1,27 @@ +LIBRARY ViGEmClient + +EXPORTS + vigem_alloc + vigem_connect + vigem_disconnect + vigem_free + vigem_target_add + vigem_target_add_async + vigem_target_ds4_alloc + vigem_target_ds4_register_notification + vigem_target_ds4_unregister_notification + vigem_target_ds4_update + vigem_target_free + vigem_target_get_index + vigem_target_get_pid + vigem_target_get_type + vigem_target_get_vid + vigem_target_is_attached + vigem_target_remove + vigem_target_set_pid + vigem_target_set_vid + vigem_target_x360_alloc + vigem_target_x360_get_user_index + vigem_target_x360_register_notification + vigem_target_x360_unregister_notification + vigem_target_x360_update diff --git a/src/main/vigem-sdvxio/Module.mk b/src/main/vigem-sdvxio/Module.mk new file mode 100644 index 0000000..6907fbf --- /dev/null +++ b/src/main/vigem-sdvxio/Module.mk @@ -0,0 +1,20 @@ +exes += vigem-sdvxio + +deplibs_vigem-sdvxio := \ + ViGEmClient \ + +cppflags_vigem-sdvxio := \ + -I src/imports \ + +ldflags_vigem-sdvxio := \ + -lsetupapi \ + +libs_vigem-sdvxio := \ + cconfig \ + sdvxio \ + util \ + vigemstub \ + +src_vigem-sdvxio := \ + main.c \ + config-vigem-sdvxio.c \ diff --git a/src/main/vigem-sdvxio/config-vigem-sdvxio.c b/src/main/vigem-sdvxio/config-vigem-sdvxio.c new file mode 100644 index 0000000..1060351 --- /dev/null +++ b/src/main/vigem-sdvxio/config-vigem-sdvxio.c @@ -0,0 +1,128 @@ +#include "cconfig/cconfig-main.h" +#include "cconfig/cconfig-util.h" + +#include "vigem-sdvxio/config-vigem-sdvxio.h" + +#include "util/log.h" + +#define VIGEM_SDVXIO_CONFIG_ENABLE_KEYLIGHT_KEY "sdvxio.enable_keylight" +#define VIGEM_SDVXIO_CONFIG_PWM_WINGS_KEY "sdvxio.pwm_wings" +#define VIGEM_SDVXIO_CONFIG_PWM_CONTROLLER_KEY "sdvxio.pwm_controller" +#define VIGEM_SDVXIO_CONFIG_AMP_VOLUME_KEY "sdvxio.amp_volume" + +#define VIGEM_SDVXIO_CONFIG_DEFAULT_ENABLE_KEYLIGHT_VALUE true +#define VIGEM_SDVXIO_CONFIG_DEFAULT_PWM_WINGS_VALUE 128 +#define VIGEM_SDVXIO_CONFIG_DEFAULT_PWM_CONTROLLER_VALUE 64 +#define VIGEM_SDVXIO_CONFIG_DEFAULT_AMP_VOLUME_VALUE 48 + +void vigem_sdvxio_config_init(struct cconfig *config) +{ + cconfig_util_set_bool( + config, + VIGEM_SDVXIO_CONFIG_ENABLE_KEYLIGHT_KEY, + VIGEM_SDVXIO_CONFIG_DEFAULT_ENABLE_KEYLIGHT_VALUE, + "Enable input based key lighting"); + + cconfig_util_set_int( + config, + VIGEM_SDVXIO_CONFIG_PWM_WINGS_KEY, + VIGEM_SDVXIO_CONFIG_DEFAULT_PWM_WINGS_VALUE, + "Brightness to set wings to (0-255)"); + + cconfig_util_set_int( + config, + VIGEM_SDVXIO_CONFIG_PWM_CONTROLLER_KEY, + VIGEM_SDVXIO_CONFIG_DEFAULT_PWM_CONTROLLER_VALUE, + "Brightness to set control deck to (0-255)"); + + cconfig_util_set_int( + config, + VIGEM_SDVXIO_CONFIG_AMP_VOLUME_KEY, + VIGEM_SDVXIO_CONFIG_DEFAULT_AMP_VOLUME_VALUE, + "SDVXIO digital amp volume (0-96) 0 is high, 96 is low."); +} + +void vigem_sdvxio_config_get( + struct vigem_sdvxio_config *vigem_config, struct cconfig *config) +{ + if (!cconfig_util_get_bool( + config, + VIGEM_SDVXIO_CONFIG_ENABLE_KEYLIGHT_KEY, + &vigem_config->enable_keylight, + VIGEM_SDVXIO_CONFIG_DEFAULT_ENABLE_KEYLIGHT_VALUE)) { + log_warning( + "Invalid value for key '%s' specified, fallback " + "to default '%d'", + VIGEM_SDVXIO_CONFIG_ENABLE_KEYLIGHT_KEY, + VIGEM_SDVXIO_CONFIG_DEFAULT_ENABLE_KEYLIGHT_VALUE); + } + + if (!cconfig_util_get_int( + config, + VIGEM_SDVXIO_CONFIG_PWM_WINGS_KEY, + &vigem_config->pwm_wings, + VIGEM_SDVXIO_CONFIG_DEFAULT_PWM_WINGS_VALUE)) { + log_warning( + "Invalid value for key '%s' specified, fallback " + "to default '%d'", + VIGEM_SDVXIO_CONFIG_PWM_WINGS_KEY, + VIGEM_SDVXIO_CONFIG_DEFAULT_PWM_WINGS_VALUE); + } + + if (!cconfig_util_get_int( + config, + VIGEM_SDVXIO_CONFIG_PWM_CONTROLLER_KEY, + &vigem_config->pwm_controller, + VIGEM_SDVXIO_CONFIG_DEFAULT_PWM_CONTROLLER_VALUE)) { + log_warning( + "Invalid value for key '%s' specified, fallback " + "to default '%d'", + VIGEM_SDVXIO_CONFIG_PWM_CONTROLLER_KEY, + VIGEM_SDVXIO_CONFIG_DEFAULT_PWM_CONTROLLER_VALUE); + } + + if (!cconfig_util_get_int( + config, + VIGEM_SDVXIO_CONFIG_AMP_VOLUME_KEY, + &vigem_config->amp_volume, + VIGEM_SDVXIO_CONFIG_DEFAULT_AMP_VOLUME_VALUE)) { + log_warning( + "Invalid value for key '%s' specified, fallback " + "to default '%d'", + VIGEM_SDVXIO_CONFIG_AMP_VOLUME_KEY, + VIGEM_SDVXIO_CONFIG_DEFAULT_AMP_VOLUME_VALUE); + } +} + +void get_vigem_sdvxio_config(struct vigem_sdvxio_config *config_out) +{ + struct cconfig *config; + + config = cconfig_init(); + + vigem_sdvxio_config_init(config); + + if (!cconfig_main_config_init( + config, + "--config", + "vigem-sdvxio.conf", + "--help", + "-h", + "vigem-sdvxio", + CCONFIG_CMD_USAGE_OUT_STDOUT)) { + cconfig_finit(config); + exit(EXIT_FAILURE); + } + + vigem_sdvxio_config_get(config_out, config); + + cconfig_finit(config); + + if (config_out->pwm_controller > 255) { + config_out->pwm_controller = 255; + } + + if (config_out->pwm_wings > 255) { + config_out->pwm_wings = 255; + } +} diff --git a/src/main/vigem-sdvxio/config-vigem-sdvxio.h b/src/main/vigem-sdvxio/config-vigem-sdvxio.h new file mode 100644 index 0000000..359fe29 --- /dev/null +++ b/src/main/vigem-sdvxio/config-vigem-sdvxio.h @@ -0,0 +1,17 @@ +#ifndef VIGEM_SDVXIO_CONFIG_H +#define VIGEM_SDVXIO_CONFIG_H + +#include + +#include "cconfig/cconfig.h" + +struct vigem_sdvxio_config { + bool enable_keylight; + int32_t pwm_wings; + int32_t pwm_controller; + int32_t amp_volume; +}; + +void get_vigem_sdvxio_config(struct vigem_sdvxio_config *config_out); + +#endif \ No newline at end of file diff --git a/src/main/vigem-sdvxio/main.c b/src/main/vigem-sdvxio/main.c new file mode 100644 index 0000000..04b2b42 --- /dev/null +++ b/src/main/vigem-sdvxio/main.c @@ -0,0 +1,183 @@ +#include +#include +#include +#include + +#include +#include + +#include "ViGEm/Client.h" + +#include "bemanitools/sdvxio.h" +#include "util/log.h" +#include "util/thread.h" +#include "vigemstub/helper.h" + +#include "vigem-sdvxio/config-vigem-sdvxio.h" + +int64_t convert_analog_to_s16(uint16_t val) +{ + // val is 10 bit + return (int64_t)(val * 64); +} + +bool check_key(uint16_t input, size_t idx_in) +{ + if ((input >> idx_in) & 1) { + return true; + } + + return false; +} + +uint16_t check_assign_key(uint16_t input, size_t idx_in, size_t bit_out) +{ + if (check_key(input, idx_in)) { + return bit_out; + } + + return 0; +} + +uint16_t check_assign_gpio(uint16_t input, size_t idx_in, size_t gpio_out) +{ + if (check_key(input, idx_in)) { + return 1 << gpio_out; + } + + return 0; +} + +void gpio_keylight(uint16_t gpio0, uint16_t gpio1) +{ + uint16_t gpio_lights = 0; + + gpio_lights |= check_assign_gpio( + gpio0, SDVX_IO_IN_GPIO_0_START, SDVX_IO_OUT_GPIO_START); + gpio_lights |= + check_assign_gpio(gpio0, SDVX_IO_IN_GPIO_0_A, SDVX_IO_OUT_GPIO_A); + gpio_lights |= + check_assign_gpio(gpio0, SDVX_IO_IN_GPIO_0_B, SDVX_IO_OUT_GPIO_B); + gpio_lights |= + check_assign_gpio(gpio0, SDVX_IO_IN_GPIO_0_C, SDVX_IO_OUT_GPIO_C); + gpio_lights |= + check_assign_gpio(gpio1, SDVX_IO_IN_GPIO_1_D, SDVX_IO_OUT_GPIO_D); + gpio_lights |= + check_assign_gpio(gpio1, SDVX_IO_IN_GPIO_1_FX_L, SDVX_IO_OUT_GPIO_FX_L); + gpio_lights |= + check_assign_gpio(gpio1, SDVX_IO_IN_GPIO_1_FX_R, SDVX_IO_OUT_GPIO_FX_R); + + sdvx_io_set_gpio_lights(gpio_lights); +} + +void set_pwm_brightness(uint8_t wing_pwm, uint8_t controller_pwm) +{ + // 0-11 are the 4 wings + for (size_t i = 0; i < 12; ++i) { + sdvx_io_set_pwm_light(i, wing_pwm); + } + // 12-17 are the woofer / control deck + for (size_t i = 12; i < 18; ++i) { + sdvx_io_set_pwm_light(i, controller_pwm); + } +} + +int main(int argc, char **argv) +{ + log_to_writer(log_writer_stdout, NULL); + + struct vigem_sdvxio_config config; + get_vigem_sdvxio_config(&config); + + sdvx_io_set_loggers( + log_impl_misc, log_impl_info, log_impl_warning, log_impl_fatal); + + if (!sdvx_io_init(crt_thread_create, crt_thread_join, crt_thread_destroy)) { + log_warning("Initializing sdvxio failed"); + return -1; + } + + sdvx_io_set_amp_volume(config.amp_volume, config.amp_volume, config.amp_volume); + + PVIGEM_CLIENT client = vigem_helper_setup(); + if (!client) { + log_warning("client failed to connect failed"); + return -1; + } + + PVIGEM_TARGET pad = vigem_helper_add_pad(client); + if (!pad) { + log_warning("vigem_alloc pad 1 failed"); + return -1; + } + + bool loop = true; + + uint8_t sys; + uint16_t gpio0; + uint16_t gpio1; + uint16_t vol[2] = {0, 0}; + + XUSB_REPORT state; + + log_info("vigem init succeeded, beginnning poll loop"); + while (loop) { + sdvx_io_read_input(); + + sys = sdvx_io_get_input_gpio_sys(); + gpio0 = sdvx_io_get_input_gpio(0); + gpio1 = sdvx_io_get_input_gpio(1); + + vol[0] = sdvx_io_get_spinner_pos(0); + vol[1] = sdvx_io_get_spinner_pos(1); + + memset(&state, 0, sizeof(state)); + + state.sThumbLX = convert_analog_to_s16(vol[0]); + state.sThumbRX = convert_analog_to_s16(vol[1]); + + state.wButtons |= check_assign_key( + gpio0, SDVX_IO_IN_GPIO_0_START, XUSB_GAMEPAD_START); + state.wButtons |= + check_assign_key(sys, SDVX_IO_IN_GPIO_SYS_TEST, XUSB_GAMEPAD_BACK); + state.wButtons |= + check_assign_key(gpio0, SDVX_IO_IN_GPIO_0_A, XUSB_GAMEPAD_A); + state.wButtons |= + check_assign_key(gpio0, SDVX_IO_IN_GPIO_0_B, XUSB_GAMEPAD_B); + state.wButtons |= + check_assign_key(gpio0, SDVX_IO_IN_GPIO_0_C, XUSB_GAMEPAD_X); + state.wButtons |= + check_assign_key(gpio1, SDVX_IO_IN_GPIO_1_D, XUSB_GAMEPAD_Y); + state.wButtons |= check_assign_key( + gpio1, SDVX_IO_IN_GPIO_1_FX_L, XUSB_GAMEPAD_LEFT_SHOULDER); + state.wButtons |= check_assign_key( + gpio1, SDVX_IO_IN_GPIO_1_FX_R, XUSB_GAMEPAD_RIGHT_SHOULDER); + + vigem_target_x360_update(client, pad, state); + + if (config.enable_keylight) { + gpio_keylight(gpio0, gpio1); + } + + set_pwm_brightness(config.pwm_wings, config.pwm_controller); + sdvx_io_write_output(); + + if (check_key(sys, SDVX_IO_IN_GPIO_SYS_TEST) && + check_key(sys, SDVX_IO_IN_GPIO_SYS_SERVICE)) { + loop = false; + } + + // avoid banging + Sleep(1); + } + + vigem_target_remove(client, pad); + vigem_target_free(pad); + + sdvx_io_set_amp_volume(96, 96, 96); + Sleep(1000); + + sdvx_io_fini(); + + return 0; +} diff --git a/src/main/vigemstub/Module.mk b/src/main/vigemstub/Module.mk new file mode 100644 index 0000000..cdb94fb --- /dev/null +++ b/src/main/vigemstub/Module.mk @@ -0,0 +1,12 @@ +libs += vigemstub +imps += ViGEmClient + +cppflags_vigemstub := \ + -I src/imports \ + +libs_vigemstub := \ + +src_vigemstub := \ + helper.c \ + +# This is a dummy module so that the client only gets import libraries generated once diff --git a/src/main/vigemstub/helper.c b/src/main/vigemstub/helper.c new file mode 100644 index 0000000..9f2df6c --- /dev/null +++ b/src/main/vigemstub/helper.c @@ -0,0 +1,45 @@ + +#include +#include + +#include "ViGEm/Client.h" + +#include "vigemstub/helper.h" + +PVIGEM_CLIENT vigem_helper_setup(void) +{ + PVIGEM_CLIENT client = vigem_alloc(); + + if (client == NULL) { + printf("vigem_alloc failed\n"); + return NULL; + } + + VIGEM_ERROR retval = vigem_connect(client); + + if (!VIGEM_SUCCESS(retval)) { + printf("ViGEm Bus connection failed with error code: 0x%x\n", retval); + return NULL; + } + + return client; +} + +PVIGEM_TARGET vigem_helper_add_pad(PVIGEM_CLIENT client) +{ + PVIGEM_TARGET pad = vigem_target_x360_alloc(); + + if (pad == NULL) { + printf("vigem_target_x360_alloc failed\n"); + return NULL; + } + + VIGEM_ERROR pir = vigem_target_add(client, pad); + + if (!VIGEM_SUCCESS(pir)) { + printf("Target plugin failed with error code: 0x%x\n", pir); + return NULL; + } + + return pad; +} \ No newline at end of file diff --git a/src/main/vigemstub/helper.h b/src/main/vigemstub/helper.h new file mode 100644 index 0000000..a53f60b --- /dev/null +++ b/src/main/vigemstub/helper.h @@ -0,0 +1,9 @@ +#ifndef VIGEMSTUB_HELPER_H +#define VIGEMSTUB_HELPER_H + +#include "ViGEm/Client.h" + +PVIGEM_CLIENT vigem_helper_setup(); +PVIGEM_TARGET vigem_helper_add_pad(PVIGEM_CLIENT client); + +#endif