diff --git a/pcsx2/USB/device_init.cpp b/pcsx2/USB/device_init.cpp index b8afa97121..3a4034054b 100644 --- a/pcsx2/USB/device_init.cpp +++ b/pcsx2/USB/device_init.cpp @@ -36,6 +36,10 @@ void RegisterDevice::Register() inst.Add(DEVTYPE_HIDMOUSE, new DeviceProxy()); inst.Add(DEVTYPE_RBKIT, new DeviceProxy()); inst.Add(DEVTYPE_BUZZ, new DeviceProxy()); +#ifdef _WIN32 + inst.Add(DEVTYPE_GAMETRAK, new DeviceProxy()); + inst.Add(DEVTYPE_REALPLAY, new DeviceProxy()); +#endif inst.Add(DEVTYPE_EYETOY, new DeviceProxy()); inst.Add(DEVTYPE_BEATMANIA_DADADA, new DeviceProxy()); inst.Add(DEVTYPE_SEGA_SEAMIC, new DeviceProxy()); diff --git a/pcsx2/USB/deviceproxy.h b/pcsx2/USB/deviceproxy.h index 2a66aea874..88609d5ccc 100644 --- a/pcsx2/USB/deviceproxy.h +++ b/pcsx2/USB/deviceproxy.h @@ -44,6 +44,8 @@ enum DeviceType DEVTYPE_HIDMOUSE, DEVTYPE_RBKIT, DEVTYPE_BUZZ, + DEVTYPE_GAMETRAK, + DEVTYPE_REALPLAY, DEVTYPE_EYETOY, DEVTYPE_BEATMANIA_DADADA, DEVTYPE_SEGA_SEAMIC, diff --git a/pcsx2/USB/usb-pad/dx/dinput-config.cpp b/pcsx2/USB/usb-pad/dx/dinput-config.cpp index ba428ea747..870c011cf4 100644 --- a/pcsx2/USB/usb-pad/dx/dinput-config.cpp +++ b/pcsx2/USB/usb-pad/dx/dinput-config.cpp @@ -1468,6 +1468,14 @@ namespace usb_pad { return DialogBoxParam(h.hInst, MAKEINTRESOURCE(IDD_DLG_KEYBOARDMANIA), h.hWnd, DxDialogProc, (LPARAM)&s); } + if (strcmp(dev_type, GametrakDevice::TypeName()) == 0) + { + return DialogBoxParam(h.hInst, MAKEINTRESOURCE(IDD_DLG_GAMETRAK), h.hWnd, DxDialogProc, (LPARAM)&s); + } + if (strcmp(dev_type, RealPlayDevice::TypeName()) == 0) + { + return DialogBoxParam(h.hInst, MAKEINTRESOURCE(IDD_DLG_REALPLAY), h.hWnd, DxDialogProc, (LPARAM)&s); + } return DialogBoxParam(h.hInst, MAKEINTRESOURCE(IDD_DIALOG1), h.hWnd, DxDialogProc, (LPARAM)&s); } diff --git a/pcsx2/USB/usb-pad/dx/dinput.cpp b/pcsx2/USB/usb-pad/dx/dinput.cpp index 7c02bbe631..99d90a7f7a 100644 --- a/pcsx2/USB/usb-pad/dx/dinput.cpp +++ b/pcsx2/USB/usb-pad/dx/dinput.cpp @@ -1002,6 +1002,24 @@ namespace usb_pad return ReadAxisFiltered(port, id); } + int32_t GetAxisControlUnfiltered(int port, ControlID id) + { + InputMapped im; + if (!GetInputMap(port, id, im)) + return 0; + + assert(im.index < g_pJoysticks.size()); + if (im.index >= g_pJoysticks.size()) + return 0; + + LONG value = 0; + if (im.type == MT_AXIS) + { + value = g_pJoysticks[im.index]->GetAxis(im.mapped); + } + return value; + } + //set left/right ffb torque HRESULT SetConstantForce(int port, LONG magnitude) { diff --git a/pcsx2/USB/usb-pad/dx/dx.h b/pcsx2/USB/usb-pad/dx/dx.h index 29d048249c..138fc94861 100644 --- a/pcsx2/USB/usb-pad/dx/dx.h +++ b/pcsx2/USB/usb-pad/dx/dx.h @@ -218,6 +218,7 @@ namespace usb_pad LONG GetAxisValueFromOffset(int axis, const DIJOYSTATE2& j); bool GetControl(int port, int id); float GetAxisControl(int port, ControlID id); + int32_t GetAxisControlUnfiltered(int port, ControlID id); void CreateFFB(int port, LPDIRECTINPUTDEVICE8 device, DWORD axis); bool FindFFDevice(int port); bool UpdateFFBSettings(int port, LPDIRECTINPUTDEVICE8 device); diff --git a/pcsx2/USB/usb-pad/dx/usb-pad-dx.cpp b/pcsx2/USB/usb-pad/dx/usb-pad-dx.cpp index 5468098a38..e53f2f3575 100644 --- a/pcsx2/USB/usb-pad/dx/usb-pad-dx.cpp +++ b/pcsx2/USB/usb-pad/dx/usb-pad-dx.cpp @@ -57,8 +57,34 @@ namespace usb_pad pad_copy_data(mType, buf, mWheelData); return 5; } - - if (mType == WT_KEYBOARDMANIA_CONTROLLER) + else if (mType == WT_GAMETRAK_CONTROLLER) + { + mWheelData.buttons |= GetControl(mPort, CID_HATLEFT) << 4; + mWheelData.clutch = GetAxisControlUnfiltered(mPort, CID_STEERING) >> 4; + mWheelData.throttle = GetAxisControlUnfiltered(mPort, CID_STEERING_R) >> 4; + mWheelData.brake = GetAxisControlUnfiltered(mPort, CID_THROTTLE) >> 4; + mWheelData.hatswitch = GetAxisControlUnfiltered(mPort, CID_BRAKE) >> 4; + mWheelData.hat_horz = GetAxisControlUnfiltered(mPort, CID_HATUP) >> 4; + mWheelData.hat_vert = GetAxisControlUnfiltered(mPort, CID_HATDOWN) >> 4; + pad_copy_data(mType, buf, mWheelData); + return 16; + } + else if (mType >= WT_REALPLAY_RACING && mType <= WT_REALPLAY_POOL) + { + for (int i = 0; i < 8; i++) + { + if (GetControl(mPort, i)) + { + mWheelData.buttons |= 1 << i; + } + } + mWheelData.clutch = GetAxisControlUnfiltered(mPort, CID_SQUARE) >> 4; + mWheelData.throttle = GetAxisControlUnfiltered(mPort, CID_TRIANGLE) >> 4; + mWheelData.brake = GetAxisControlUnfiltered(mPort, CID_CROSS) >> 4; + pad_copy_data(mType, buf, mWheelData); + return 19; + } + else if (mType == WT_KEYBOARDMANIA_CONTROLLER) { for (int i = 0; i < 31; i++) { diff --git a/pcsx2/USB/usb-pad/dx/versionproxy.h b/pcsx2/USB/usb-pad/dx/versionproxy.h index f8f74b4ebe..412ef1f1d3 100644 --- a/pcsx2/USB/usb-pad/dx/versionproxy.h +++ b/pcsx2/USB/usb-pad/dx/versionproxy.h @@ -6,6 +6,8 @@ #define IDOK2 2 #define IDD_DLG_BUZZ 114 #define IDD_DLG_KEYBOARDMANIA 115 +#define IDD_DLG_GAMETRAK 116 +#define IDD_DLG_REALPLAY 120 #define IDD_DIALOG1 202 #define IDC_DEL0 1001 #define IDC_ASS0 1002 @@ -194,7 +196,7 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 117 +#define _APS_NEXT_RESOURCE_VALUE 121 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1137 #define _APS_NEXT_SYMED_VALUE 101 diff --git a/pcsx2/USB/usb-pad/dx/versionproxy.rc b/pcsx2/USB/usb-pad/dx/versionproxy.rc index 96a466892d..74b2c5f52d 100644 --- a/pcsx2/USB/usb-pad/dx/versionproxy.rc +++ b/pcsx2/USB/usb-pad/dx/versionproxy.rc @@ -327,6 +327,60 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,525,140,50,15 END +IDD_DLG_GAMETRAK DIALOGEX 0, 0, 260, 115 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Gametrak" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Left X",IDC_ASS0,10,40,50,14 + PUSHBUTTON "Left Y",IDC_ASS1,70,40,50,14 + PUSHBUTTON "Left Z",IDC_ASS2,40,10,50,14 + PUSHBUTTON "Right X",IDC_ASS3,140,40,50,14 + PUSHBUTTON "Right Y",IDC_ASS4,200,40,50,14 + PUSHBUTTON "Right Z",IDC_ASS5,170,10,50,14 + PUSHBUTTON "Foot Mat",IDC_ASS6,105,70,50,14 + LTEXT "1/1/1/1",IDC_LABEL0,10,55,55,8 + LTEXT "1/1/1/1",IDC_LABEL1,70,55,55,8 + LTEXT "1/1/1/1",IDC_LABEL2,40,25,55,8 + LTEXT "1/1/1/1",IDC_LABEL3,140,55,55,8 + LTEXT "1/1/1/1",IDC_LABEL4,200,55,55,8 + LTEXT "1/1/1/1",IDC_LABEL5,170,25,55,8 + LTEXT "1/1/1/1",IDC_LABEL6,105,85,55,8 + DEFPUSHBUTTON "OK",IDOK,145,95,50,14 + PUSHBUTTON "Cancel",IDCANCEL,200,95,50,14 +END + +IDD_DLG_REALPLAY DIALOGEX 0, 0, 260, 151 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "RealPlay" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Up",IDC_ASS0,40,10,50,14 + PUSHBUTTON "Down",IDC_ASS1,40,70,50,14 + PUSHBUTTON "Left",IDC_ASS2,10,40,50,14 + PUSHBUTTON "Right",IDC_ASS3,70,40,50,14 + PUSHBUTTON "Red",IDC_ASS4,170,10,50,14 + PUSHBUTTON "Green",IDC_ASS5,170,70,50,14 + PUSHBUTTON "Yellow",IDC_ASS6,140,40,50,14 + PUSHBUTTON "Blue",IDC_ASS7,200,40,50,14 + PUSHBUTTON "Accel X",IDC_ASS8,45,100,50,14 + PUSHBUTTON "Accel Y",IDC_ASS9,105,100,50,14 + PUSHBUTTON "Accel Z",IDC_ASS10,165,100,50,14 + LTEXT "1/1/1/1",IDC_LABEL0,40,25,55,8 + LTEXT "1/1/1/1",IDC_LABEL1,40,85,55,8 + LTEXT "1/1/1/1",IDC_LABEL2,10,55,55,8 + LTEXT "1/1/1/1",IDC_LABEL3,70,55,55,8 + LTEXT "1/1/1/1",IDC_LABEL4,170,25,55,8 + LTEXT "1/1/1/1",IDC_LABEL5,170,85,55,8 + LTEXT "1/1/1/1",IDC_LABEL6,140,55,55,8 + LTEXT "1/1/1/1",IDC_LABEL7,200,55,55,8 + LTEXT "1/1/1/1",IDC_LABEL8,45,115,55,8 + LTEXT "1/1/1/1",IDC_LABEL9,105,115,55,8 + LTEXT "1/1/1/1",IDC_LABEL10,165,115,55,8 + DEFPUSHBUTTON "OK",IDOK,145,130,50,14 + PUSHBUTTON "Cancel",IDCANCEL,200,130,50,14 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -351,6 +405,14 @@ BEGIN IDD_DLG_KEYBOARDMANIA, DIALOG BEGIN END + + IDD_DLG_GAMETRAK, DIALOG + BEGIN + END + + IDD_DLG_REALPLAY, DIALOG + BEGIN + END END #endif // APSTUDIO_INVOKED @@ -375,6 +437,16 @@ BEGIN 0 END +IDD_DLG_GAMETRAK AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_DLG_REALPLAY AFX_DIALOG_LAYOUT +BEGIN + 0 +END + #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/pcsx2/USB/usb-pad/usb-pad.cpp b/pcsx2/USB/usb-pad/usb-pad.cpp index e360e6d9db..94ce73bdf8 100644 --- a/pcsx2/USB/usb-pad/usb-pad.cpp +++ b/pcsx2/USB/usb-pad/usb-pad.cpp @@ -59,6 +59,16 @@ namespace usb_pad "", "KONAMI"}; + static const USBDescStrings gametrak_desc_strings = { + "", + "In2Games Ltd.", + "Game-Trak V1.3"}; + + static const USBDescStrings realplay_desc_strings = { + "", + "In2Games", + "Real Play"}; + std::list PadDevice::ListAPIs() { return RegisterPad::instance().Names(); @@ -102,6 +112,26 @@ namespace usb_pad return PadDevice::LongAPIName(name); } + std::list GametrakDevice::ListAPIs() + { + return PadDevice::ListAPIs(); + } + + const TCHAR* GametrakDevice::LongAPIName(const std::string& name) + { + return PadDevice::LongAPIName(name); + } + + std::list RealPlayDevice::ListAPIs() + { + return PadDevice::ListAPIs(); + } + + const TCHAR* RealPlayDevice::LongAPIName(const std::string& name) + { + return PadDevice::LongAPIName(name); + } + #ifdef _DEBUG void PrintBits(void* data, int size) { @@ -123,6 +153,19 @@ namespace usb_pad #define DbgPrint(...) #endif //_DEBUG + uint32_t gametrak_compute_key(uint32_t* key) + { + uint32_t ret = 0; + ret = *key << 2 & 0xFC0000; + ret |= *key << 17 & 0x020000; + ret ^= *key << 16 & 0xFE0000; + ret |= *key & 0x010000; + ret |= *key >> 9 & 0x007F7F; + ret |= *key << 7 & 0x008080; + *key = ret; + return ret >> 16; + }; + typedef struct PADState { USBDevice dev; @@ -134,6 +177,9 @@ namespace usb_pad { int dev_subtype; } f; + uint8_t gametrak_state; + uint32_t gametrak_key; + uint8_t realplay_state; } PADState; typedef struct u_wheel_data_t @@ -192,6 +238,38 @@ namespace usb_pad case USB_TOKEN_IN: if (devep == 1 && s->pad) { + if (s->pad->Type() == WT_GAMETRAK_CONTROLLER) + { + if (s->gametrak_state == 0) + { + s->gametrak_state = 1; + const unsigned char secret[] = "Gametrak\0\0\0\0\0\0\0\0"; + memcpy(data, secret, sizeof(secret)); + usb_packet_copy(p, data, 16); + break; + } + else if (s->gametrak_state == 1) + { + s->pad->TokenIn(data, p->iov.size); + data[0x00] |= s->gametrak_key >> 16 & 1; + data[0x02] |= s->gametrak_key >> 17 & 1; + data[0x04] |= s->gametrak_key >> 18 & 1; + data[0x06] |= s->gametrak_key >> 19 & 1; + data[0x08] |= s->gametrak_key >> 20 & 1; + data[0x0A] |= s->gametrak_key >> 21 & 1; + usb_packet_copy(p, data, 16); + break; + } + } + else if (s->pad->Type() >= WT_REALPLAY_RACING && s->pad->Type() <= WT_REALPLAY_POOL) + { + s->pad->TokenIn(data, p->iov.size); + // simulate a slight move to avoid a game "protection" : controller disconnected + s->realplay_state = !s->realplay_state; + data[0] |= s->realplay_state; + usb_packet_copy(p, data, 19); + break; + } ret = s->pad->TokenIn(data, p->iov.size); if (ret > 0) usb_packet_copy(p, data, MIN(ret, (int)sizeof(data))); @@ -260,11 +338,26 @@ namespace usb_pad ret = sizeof(kbm_hid_report_descriptor); memcpy(data, kbm_hid_report_descriptor, ret); } - else + else if (t == WT_GENERIC) { ret = sizeof(pad_driving_force_hid_separate_report_descriptor); memcpy(data, pad_driving_force_hid_separate_report_descriptor, ret); } + else if (t == WT_BUZZ_CONTROLLER) + { + ret = sizeof(buzz_hid_report_descriptor); + memcpy(data, buzz_hid_report_descriptor, ret); + } + else if (t == WT_GAMETRAK_CONTROLLER) + { + ret = sizeof(gametrak_hid_report_descriptor); + memcpy(data, gametrak_hid_report_descriptor, ret); + } + else if (t >= WT_REALPLAY_RACING && t <= WT_REALPLAY_POOL) + { + ret = sizeof(realplay_hid_report_descriptor); + memcpy(data, realplay_hid_report_descriptor, ret); + } p->actual_length = ret; break; default: @@ -273,6 +366,30 @@ namespace usb_pad break; /* hid specific requests */ case SET_REPORT: + if (t == WT_GAMETRAK_CONTROLLER) + { + const char secret[] = "Gametrak"; + if (length == 8 && memcmp(data, secret, 8) == 0) + { + s->gametrak_state = 0; + s->gametrak_key = 0; + } + else if (length == 2) + { + if (data[0] == 0x45) + { + s->gametrak_key = data[1] << 16; + } + if ((s->gametrak_key >> 16) == data[1]) + { + gametrak_compute_key(&s->gametrak_key); + } + else + { + fprintf(stderr, "gametrak error : own key = %02x, recv key = %02x\n", s->gametrak_key >> 16, data[1]); + } + } + } // no idea, Rock Band 2 keeps spamming this if (length > 0) { @@ -419,6 +536,38 @@ namespace usb_pad buf[3] = (data.buttons >> 8) & 0xff; buf[4] = 0xf0 | ((data.buttons >> 16) & 0xf); break; + + case WT_GAMETRAK_CONTROLLER: + memset(buf, 0, 16); + buf[0] = data.clutch & 0xfe; + buf[1] = data.clutch >> 8; + buf[2] = data.throttle & 0xfe; + buf[3] = data.throttle >> 8; + buf[4] = data.brake & 0xfe; + buf[5] = data.brake >> 8; + buf[6] = data.hatswitch & 0xfe; + buf[7] = data.hatswitch >> 8; + buf[8] = data.hat_horz & 0xfe; + buf[9] = data.hat_horz >> 8; + buf[10] = data.hat_vert & 0xfe; + buf[11] = data.hat_vert >> 8; + buf[12] = data.buttons; + break; + + case WT_REALPLAY_RACING: + case WT_REALPLAY_SPHERE: + case WT_REALPLAY_GOLF: + case WT_REALPLAY_POOL: + memset(buf, 0, 19); + buf[0] = data.clutch & 0xfe; + buf[1] = data.clutch >> 8; + buf[2] = data.throttle & 0xff; + buf[3] = data.throttle >> 8; + buf[4] = data.brake & 0xff; + buf[5] = data.brake >> 8; + buf[14] = data.buttons; + break; + case WT_SEGA_SEAMIC: buf[0] = data.steering & 0xFF; buf[1] = data.throttle & 0xFF; @@ -745,13 +894,13 @@ namespace usb_pad if (!proxy) { Console.WriteLn("Buzz: Invalid input API.\n"); - return NULL; + return nullptr; } Pad* pad = proxy->CreateObject(port, TypeName()); if (!pad) - return NULL; + return nullptr; pad->Type(WT_BUZZ_CONTROLLER); PADState* s = new PADState(); @@ -843,4 +992,156 @@ namespace usb_pad return PadDevice::Freeze(mode, dev, data); } + // ---- Gametrak ---- + + USBDevice* GametrakDevice::CreateDevice(int port) + { + std::string varApi; +#ifdef _WIN32 + std::wstring tmp; + LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, tmp); + varApi = wstr_to_str(tmp); +#else + LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, varApi); +#endif + PadProxyBase* proxy = RegisterPad::instance().Proxy(varApi); + if (!proxy) + { + Console.WriteLn("Gametrak: Invalid input API."); + return nullptr; + } + + Pad* pad = proxy->CreateObject(port, TypeName()); + + if (!pad) + return nullptr; + + pad->Type(WT_GAMETRAK_CONTROLLER); + PADState* s = new PADState(); + + s->desc.full = &s->desc_dev; + s->desc.str = gametrak_desc_strings; + + if (usb_desc_parse_dev(gametrak_dev_descriptor, sizeof(gametrak_dev_descriptor), s->desc, s->desc_dev) < 0) + goto fail; + if (usb_desc_parse_config(gametrak_config_descriptor, sizeof(gametrak_config_descriptor), s->desc_dev) < 0) + goto fail; + + s->f.dev_subtype = pad->Type(); + s->pad = pad; + s->port = port; + s->dev.speed = USB_SPEED_FULL; + s->dev.klass.handle_attach = usb_desc_attach; + s->dev.klass.handle_reset = pad_handle_reset; + s->dev.klass.handle_control = pad_handle_control; + s->dev.klass.handle_data = pad_handle_data; + s->dev.klass.unrealize = pad_handle_destroy; + s->dev.klass.open = pad_open; + s->dev.klass.close = pad_close; + s->dev.klass.usb_desc = &s->desc; + s->dev.klass.product_desc = s->desc.str[2]; + + usb_desc_init(&s->dev); + usb_ep_init(&s->dev); + pad_handle_reset((USBDevice*)s); + + return (USBDevice*)s; + + fail: + pad_handle_destroy((USBDevice*)s); + return nullptr; + } + + int GametrakDevice::Configure(int port, const std::string& api, void* data) + { + auto proxy = RegisterPad::instance().Proxy(api); + if (proxy) + return proxy->Configure(port, TypeName(), data); + return RESULT_CANCELED; + } + + int GametrakDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) + { + return PadDevice::Freeze(mode, dev, data); + } + + // ---- RealPlay ---- + + USBDevice* RealPlayDevice::CreateDevice(int port) + { + std::string varApi; +#ifdef _WIN32 + std::wstring tmp; + LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, tmp); + varApi = wstr_to_str(tmp); +#else + LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, varApi); +#endif + PadProxyBase* proxy = RegisterPad::instance().Proxy(varApi); + if (!proxy) + { + Console.WriteLn("RealPlay: Invalid input API"); + return nullptr; + } + + Pad* pad = proxy->CreateObject(port, TypeName()); + + if (!pad) + return nullptr; + + pad->Type((PS2WheelTypes)(WT_REALPLAY_RACING + GetSelectedSubtype(std::make_pair(port, TypeName())))); + PADState* s = new PADState(); + + s->desc.full = &s->desc_dev; + s->desc.str = realplay_desc_strings; + + if (pad->Type() == WT_REALPLAY_RACING && usb_desc_parse_dev(realplay_racing_dev_descriptor, sizeof(realplay_racing_dev_descriptor), s->desc, s->desc_dev) < 0) + goto fail; + if (pad->Type() == WT_REALPLAY_SPHERE && usb_desc_parse_dev(realplay_sphere_dev_descriptor, sizeof(realplay_sphere_dev_descriptor), s->desc, s->desc_dev) < 0) + goto fail; + if (pad->Type() == WT_REALPLAY_GOLF && usb_desc_parse_dev(realplay_golf_dev_descriptor, sizeof(realplay_golf_dev_descriptor), s->desc, s->desc_dev) < 0) + goto fail; + if (pad->Type() == WT_REALPLAY_POOL && usb_desc_parse_dev(realplay_pool_dev_descriptor, sizeof(realplay_pool_dev_descriptor), s->desc, s->desc_dev) < 0) + goto fail; + if (usb_desc_parse_config(realplay_config_descriptor, sizeof(realplay_config_descriptor), s->desc_dev) < 0) + goto fail; + + s->f.dev_subtype = pad->Type(); + s->pad = pad; + s->port = port; + s->dev.speed = USB_SPEED_FULL; + s->dev.klass.handle_attach = usb_desc_attach; + s->dev.klass.handle_reset = pad_handle_reset; + s->dev.klass.handle_control = pad_handle_control; + s->dev.klass.handle_data = pad_handle_data; + s->dev.klass.unrealize = pad_handle_destroy; + s->dev.klass.open = pad_open; + s->dev.klass.close = pad_close; + s->dev.klass.usb_desc = &s->desc; + s->dev.klass.product_desc = s->desc.str[1]; + + usb_desc_init(&s->dev); + usb_ep_init(&s->dev); + pad_handle_reset((USBDevice*)s); + + return (USBDevice*)s; + + fail: + pad_handle_destroy((USBDevice*)s); + return nullptr; + } + + int RealPlayDevice::Configure(int port, const std::string& api, void* data) + { + auto proxy = RegisterPad::instance().Proxy(api); + if (proxy) + return proxy->Configure(port, TypeName(), data); + return RESULT_CANCELED; + } + + int RealPlayDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) + { + return PadDevice::Freeze(mode, dev, data); + } + } // namespace usb_pad diff --git a/pcsx2/USB/usb-pad/usb-pad.h b/pcsx2/USB/usb-pad/usb-pad.h index 3654a9bb42..2fe461198b 100644 --- a/pcsx2/USB/usb-pad/usb-pad.h +++ b/pcsx2/USB/usb-pad/usb-pad.h @@ -111,6 +111,54 @@ namespace usb_pad } }; + class GametrakDevice + { + public: + virtual ~GametrakDevice() {} + static USBDevice* CreateDevice(int port); + static const TCHAR* Name() + { + return TEXT("Gametrak Device"); + } + static const char* TypeName() + { + return "gametrak_device"; + } + static std::list ListAPIs(); + static const TCHAR* LongAPIName(const std::string& name); + static int Configure(int port, const std::string& api, void* data); + static int Freeze(FreezeAction mode, USBDevice* dev, void* data); + static std::vector SubTypes() + { + return std::vector(); + } + static void Initialize(); + }; + + class RealPlayDevice + { + public: + virtual ~RealPlayDevice() {} + static USBDevice* CreateDevice(int port); + static const TCHAR* Name() + { + return TEXT("RealPlay Device"); + } + static const char* TypeName() + { + return "realplay_device"; + } + static std::list ListAPIs(); + static const TCHAR* LongAPIName(const std::string& name); + static int Configure(int port, const std::string& api, void* data); + static int Freeze(FreezeAction mode, USBDevice* dev, void* data); + static std::vector SubTypes() + { + return {"RealPlay Racing", "RealPlay Sphere", "RealPlay Golf", "RealPlay Pool"}; + } + static void Initialize(); + }; + class SeamicDevice { public: @@ -205,6 +253,11 @@ namespace usb_pad WT_GT_FORCE, //formula gp WT_ROCKBAND1_DRUMKIT, WT_BUZZ_CONTROLLER, + WT_GAMETRAK_CONTROLLER, + WT_REALPLAY_RACING, + WT_REALPLAY_SPHERE, + WT_REALPLAY_GOLF, + WT_REALPLAY_POOL, WT_SEGA_SEAMIC, WT_KEYBOARDMANIA_CONTROLLER, }; @@ -1455,6 +1508,318 @@ namespace usb_pad 0xc0 // END_COLLECTION }; + ////////////// + // GameTrak // + ////////////// + + // Product ID : + // 0x0982 - PlayStation 2 + // 0x0984 - ??? + + static uint8_t gametrak_dev_descriptor[] = { + 0x12, // bLength + 0x01, // bDescriptorType (Device) + 0x10, 0x01, // bcdUSB 1.10 + 0x00, // bDeviceClass (Use class information in the Interface Descriptors) + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + 0x08, // bMaxPacketSize0 8 + 0xB7, 0x14, // idVendor 0x14B7 + 0x82, 0x09, // idProduct 0x0982 + 0x01, 0x00, // bcdDevice 0.01 + 0x01, // iManufacturer (String Index) + 0x02, // iProduct (String Index) + 0x00, // iSerialNumber (String Index) + 0x01, // bNumConfigurations 1 + }; + + static const uint8_t gametrak_config_descriptor[] = { + 0x09, // bLength + 0x02, // bDescriptorType (Configuration) + 0x22, 0x00, // wTotalLength 34 + 0x01, // bNumInterfaces 1 + 0x01, // bConfigurationValue + 0x00, // iConfiguration (String Index) + 0x80, // bmAttributes + 0x0A, // bMaxPower 20mA + + 0x09, // bLength + 0x04, // bDescriptorType (Interface) + 0x00, // bInterfaceNumber 0 + 0x00, // bAlternateSetting + 0x01, // bNumEndpoints 1 + 0x03, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0x00, // iInterface (String Index) + + 0x09, // bLength + 0x21, // bDescriptorType (HID) + 0x01, 0x01, // bcdHID 1.01 + 0x00, // bCountryCode + 0x01, // bNumDescriptors + 0x22, // bDescriptorType[0] (HID) + 0x7A, 0x00, // wDescriptorLength[0] 122 + + 0x07, // bLength + 0x05, // bDescriptorType (Endpoint) + 0x81, // bEndpointAddress (IN/D2H) + 0x03, // bmAttributes (Interrupt) + 0x10, 0x00, // wMaxPacketSize 16 + 0x0A, // bInterval 10 (unit depends on device speed) + }; + + static const uint8_t gametrak_hid_report_descriptor[] = { + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x04, // Usage (Joystick) + 0xA1, 0x01, // Collection (Application) + 0x09, 0x01, // Usage (Pointer) + 0xA1, 0x00, // Collection (Physical) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x09, 0x32, // Usage (Z) + 0x09, 0x33, // Usage (Rx) + 0x09, 0x34, // Usage (Ry) + 0x09, 0x35, // Usage (Rz) + 0x16, 0x00, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x0F, // Logical Maximum (4095) + 0x36, 0x00, 0x00, // Physical Minimum (0) + 0x46, 0xFF, 0x0F, // Physical Maximum (4095) + 0x66, 0x00, 0x00, // Unit (None) + 0x75, 0x10, // Report Size (16) + 0x95, 0x06, // Report Count (6) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, // End Collection + 0x09, 0x39, // Usage (Hat switch) + 0x15, 0x01, // Logical Minimum (1) + 0x25, 0x08, // Logical Maximum (8) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0x3B, 0x01, // Physical Maximum (315) + 0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter) + 0x75, 0x04, // Report Size (4) + 0x95, 0x01, // Report Count (1) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (0x01) + 0x29, 0x0C, // Usage Maximum (0x0C) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x0C, // Report Count (12) + 0x55, 0x00, // Unit Exponent (0) + 0x65, 0x00, // Unit (None) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x75, 0x08, // Report Size (8) + 0x95, 0x02, // Report Count (2) + 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x08, // Usage Page (LEDs) + 0x09, 0x43, // Usage (Slow Blink On Time) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0xFF, 0x00, // Physical Maximum (255) + 0x75, 0x08, // Report Size (8) + 0x95, 0x01, // Report Count (1) + 0x91, 0x82, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) + 0x09, 0x44, // Usage (Slow Blink Off Time) + 0x91, 0x82, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) + 0x09, 0x45, // Usage (Fast Blink On Time) + 0x91, 0x82, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) + 0x09, 0x46, // Usage (Fast Blink Off Time) + 0x91, 0x82, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) + 0xC0, // End Collection + }; + + ////////////// + // RealPlay // + ////////////// + + // Product ID : + // Racing - 0x09B2 + // Sphere - 0x09B3 + // Golf - 0x09B5 + // Pool - 0x09B6 + + // RealPlay Golf is dumped from a real controller. + // The others were force-brutted to be accepted by games - they may be inaccurate. + + static const uint8_t realplay_racing_dev_descriptor[] = { + 0x12, // bLength + 0x01, // bDescriptorType (Device) + 0x00, 0x02, // bcdUSB 2.00 + 0x00, // bDeviceClass (Use class information in the Interface Descriptors) + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + 0x40, // bMaxPacketSize0 64 + 0xB7, 0x14, // idVendor 0x14B7 + 0xB2, 0x09, // idProduct 0x09B2 + 0x00, 0x01, // bcdDevice 2.00 + 0x01, // iManufacturer (String Index) + 0x02, // iProduct (String Index) + 0x00, // iSerialNumber (String Index) + 0x01, // bNumConfigurations 1 + }; + + static const uint8_t realplay_sphere_dev_descriptor[] = { + 0x12, // bLength + 0x01, // bDescriptorType (Device) + 0x00, 0x02, // bcdUSB 2.00 + 0x00, // bDeviceClass (Use class information in the Interface Descriptors) + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + 0x40, // bMaxPacketSize0 64 + 0xB7, 0x14, // idVendor 0x14B7 + 0xB3, 0x09, // idProduct 0x09B3 + 0x00, 0x01, // bcdDevice 2.00 + 0x01, // iManufacturer (String Index) + 0x02, // iProduct (String Index) + 0x00, // iSerialNumber (String Index) + 0x01, // bNumConfigurations 1 + }; + + static const uint8_t realplay_golf_dev_descriptor[] = { + 0x12, // bLength + 0x01, // bDescriptorType (Device) + 0x00, 0x02, // bcdUSB 2.00 + 0x00, // bDeviceClass (Use class information in the Interface Descriptors) + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + 0x40, // bMaxPacketSize0 64 + 0xB7, 0x14, // idVendor 0x14B7 + 0xB5, 0x09, // idProduct 0x09B5 + 0x00, 0x01, // bcdDevice 2.00 + 0x01, // iManufacturer (String Index) + 0x02, // iProduct (String Index) + 0x00, // iSerialNumber (String Index) + 0x01, // bNumConfigurations 1 + }; + + static const uint8_t realplay_pool_dev_descriptor[] = { + 0x12, // bLength + 0x01, // bDescriptorType (Device) + 0x00, 0x02, // bcdUSB 2.00 + 0x00, // bDeviceClass (Use class information in the Interface Descriptors) + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + 0x40, // bMaxPacketSize0 64 + 0xB7, 0x14, // idVendor 0x14B7 + 0xB6, 0x09, // idProduct 0x09B6 + 0x00, 0x01, // bcdDevice 2.00 + 0x01, // iManufacturer (String Index) + 0x02, // iProduct (String Index) + 0x00, // iSerialNumber (String Index) + 0x01, // bNumConfigurations 1 + }; + + static const uint8_t realplay_config_descriptor[] = { + 0x09, // bLength + 0x02, // bDescriptorType (Configuration) + 0x29, 0x00, // wTotalLength 41 + 0x01, // bNumInterfaces 1 + 0x01, // bConfigurationValue + 0x00, // iConfiguration (String Index) + 0x80, // bmAttributes + 0x32, // bMaxPower 100mA + + 0x09, // bLength + 0x04, // bDescriptorType (Interface) + 0x00, // bInterfaceNumber 0 + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints 2 + 0x03, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0x00, // iInterface (String Index) + + 0x09, // bLength + 0x21, // bDescriptorType (HID) + 0x11, 0x01, // bcdHID 1.11 + 0x00, // bCountryCode + 0x01, // bNumDescriptors + 0x22, // bDescriptorType[0] (HID) + 0x85, 0x00, // wDescriptorLength[0] 133 + + 0x07, // bLength + 0x05, // bDescriptorType (Endpoint) + 0x02, // bEndpointAddress (OUT/H2D) + 0x03, // bmAttributes (Interrupt) + 0x40, 0x00, // wMaxPacketSize 64 + 0x0A, // bInterval 10 (unit depends on device speed) + + 0x07, // bLength + 0x05, // bDescriptorType (Endpoint) + 0x81, // bEndpointAddress (IN/D2H) + 0x03, // bmAttributes (Interrupt) + 0x40, 0x00, // wMaxPacketSize 64 + 0x0A, // bInterval 10 (unit depends on device speed) + }; + + static const uint8_t realplay_hid_report_descriptor[] = { + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x05, // Usage (Game Pad) + 0xA1, 0x01, // Collection (Application) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x0F, // Logical Maximum (4095) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0xFF, 0x0F, // Physical Maximum (4095) + 0x09, 0x30, // Usage (X) + 0x75, 0x0C, // Report Size (12) + 0x95, 0x01, // Report Count (1) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x75, 0x01, // Report Size (1) + 0x95, 0x04, // Report Count (4) + 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x09, 0x31, // Usage (Y) + 0x75, 0x0C, // Report Size (12) + 0x95, 0x01, // Report Count (1) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x75, 0x01, // Report Size (1) + 0x95, 0x04, // Report Count (4) + 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x09, 0x32, // Usage (Z) + 0x75, 0x0C, // Report Size (12) + 0x95, 0x01, // Report Count (1) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x75, 0x01, // Report Size (1) + 0x95, 0x04, // Report Count (4) + 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) + 0x09, 0x20, // Usage (0x20) + 0x09, 0x21, // Usage (0x21) + 0x09, 0x22, // Usage (0x22) + 0x09, 0x23, // Usage (0x23) + 0x09, 0x24, // Usage (0x24) + 0x09, 0x25, // Usage (0x25) + 0x09, 0x26, // Usage (0x26) + 0x09, 0x27, // Usage (0x27) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x46, 0xFF, 0x00, // Physical Maximum (255) + 0x75, 0x08, // Report Size (8) + 0x95, 0x08, // Report Count (8) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x35, 0x00, // Physical Minimum (0) + 0x45, 0x01, // Physical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x08, // Report Count (8) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (0x01) + 0x29, 0x08, // Usage Maximum (0x08) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) + 0x09, 0x28, // Usage (0x28) + 0x09, 0x29, // Usage (0x29) + 0x09, 0x2A, // Usage (0x2A) + 0x09, 0x2B, // Usage (0x2B) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x46, 0xFF, 0x00, // Physical Maximum (255) + 0x75, 0x08, // Report Size (8) + 0x95, 0x04, // Report Count (4) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, // End Collection + }; + struct dfp_buttons_t { uint16_t cross : 1;