/* * Copyright (C) 2007 Gabest * http://www.gabest.org * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA USA. * http://www.gnu.org/copyleft/gpl.html * */ #include "stdafx.h" #include "xpad.h" #include #undef _WIN32_WINNT #define _WIN32_WINNT 0x0602 // Required for XInputEnable definition #include static HMODULE s_hModule; static HMODULE s_xInputDll; static decltype(&XInputEnable) pXInputEnable; static decltype(&XInputGetState) pXInputGetState; static decltype(&XInputSetState) pXInputSetState; BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch(ul_reason_for_call) { case DLL_PROCESS_ATTACH: s_hModule = hModule; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } // static struct { static const UINT32 revision = 0; static const UINT32 build = 2; static const UINT32 minor = 0; } s_ver; static bool s_ps2 = false; EXPORT_C_(UINT32) PSEgetLibType() { return PSE_LT_PAD; } EXPORT_C_(char*) PSEgetLibName() { return "XPad"; } EXPORT_C_(UINT32) PSEgetLibVersion() { return (s_ver.minor << 16) | (s_ver.revision << 8) | s_ver.build; } EXPORT_C_(UINT32) PS2EgetLibType() { return PS2E_LT_PAD; } EXPORT_C_(char*) PS2EgetLibName() { return "XPad"; } EXPORT_C_(UINT32) PS2EgetLibVersion2(UINT32 type) { s_ps2 = true; return (s_ver.build << 0) | (s_ver.revision << 8) | (PS2E_PAD_VERSION << 16) | (s_ver.minor << 24); } // struct XPadButton { enum { Select = 0x0001, L3 = 0x0002, R3 = 0x0004, Start = 0x0008, Up = 0x0010, Right = 0x0020, Down = 0x0040, Left = 0x0080, L2 = 0x0100, R2 = 0x0200, L1 = 0x0400, R1 = 0x0800, Triangle = 0x1000, Circle = 0x2000, Cross = 0x4000, Square = 0x8000, }; }; class XInput { int m_pad; bool m_connected; XINPUT_STATE m_state; XINPUT_VIBRATION m_vibration; clock_t m_lastpoll; public: XInput(int pad) : m_pad(pad) , m_connected(false) , m_lastpoll(0) { memset(&m_vibration, 0, sizeof(m_vibration)); } bool GetState(XINPUT_STATE& state) { clock_t now = clock(); clock_t delay = m_connected ? 16 : 1000; // poll once per frame (16 ms is about 60 fps) if(now > m_lastpoll + delay) { memset(&m_state, 0, sizeof(m_state)); m_connected = pXInputGetState(m_pad, &m_state) == S_OK; // ERROR_DEVICE_NOT_CONNECTED is not an error, SUCCEEDED(...) won't work here m_lastpoll = now; } memcpy(&state, &m_state, sizeof(state)); return m_connected; } void SetState(XINPUT_VIBRATION& vibration) { if(m_vibration.wLeftMotorSpeed != vibration.wLeftMotorSpeed || m_vibration.wRightMotorSpeed != vibration.wRightMotorSpeed) { pXInputSetState(m_pad, &vibration); m_vibration = vibration; } } bool IsConnected() { return m_connected; } }; class XPad { public: int m_pad; XInput m_xinput; bool m_connected; bool m_ds2native; bool m_analog; bool m_locked; bool m_vibration; BYTE m_small; BYTE m_large; WORD m_buttons; struct {BYTE x, y;} m_left; struct {BYTE x, y;} m_right; void SetButton(WORD buttons, WORD mask, int flag) { if(buttons & mask) { m_buttons &= ~flag; } else { m_buttons |= flag; } } void SetAnalog(short src, BYTE& dst, short deadzone) { if(abs(src) < deadzone) src = 0; dst = (src >> 8) + 128; } public: XPad(int pad) : m_xinput(pad) , m_ds2native(false) , m_analog(!s_ps2) // defaults to analog off for ps2 , m_locked(false) , m_vibration(true) , m_small(0) , m_large(0) , m_buttons(0xffff) { } virtual ~XPad() { } BYTE GetId() { return m_analog ? (m_ds2native ? 0x79 : 0x73) : 0x41; } BYTE ReadData(int index) { if(index == 0) { XINPUT_STATE state; if(m_xinput.GetState(state)) { SetButton(state.Gamepad.wButtons, XINPUT_GAMEPAD_BACK, XPadButton::Select); SetButton(state.Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_THUMB, XPadButton::L3); SetButton(state.Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB, XPadButton::R3); SetButton(state.Gamepad.wButtons, XINPUT_GAMEPAD_START, XPadButton::Start); SetButton(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP, XPadButton::Up); SetButton(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT, XPadButton::Right); SetButton(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN, XPadButton::Down); SetButton(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT, XPadButton::Left); SetButton(state.Gamepad.bLeftTrigger, 0xe0, XPadButton::L2); SetButton(state.Gamepad.bRightTrigger, 0xe0, XPadButton::R2); SetButton(state.Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER, XPadButton::L1); SetButton(state.Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER, XPadButton::R1); SetButton(state.Gamepad.wButtons, XINPUT_GAMEPAD_Y, XPadButton::Triangle); SetButton(state.Gamepad.wButtons, XINPUT_GAMEPAD_B, XPadButton::Circle); SetButton(state.Gamepad.wButtons, XINPUT_GAMEPAD_A, XPadButton::Cross); SetButton(state.Gamepad.wButtons, XINPUT_GAMEPAD_X, XPadButton::Square); SetAnalog(state.Gamepad.sThumbLX, m_left.x, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); SetAnalog(~state.Gamepad.sThumbLY, m_left.y, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); SetAnalog(state.Gamepad.sThumbRX, m_right.x, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE); SetAnalog(~state.Gamepad.sThumbRY, m_right.y, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE); } else { m_buttons = 0xffff; m_left.x = 128; m_left.y = 128; m_right.x = 128; m_right.y = 128; } } if(index == 1) { if(m_xinput.IsConnected()) { XINPUT_VIBRATION vibraton; memset(&vibraton, 0, sizeof(vibraton)); if(m_vibration && (m_small || m_large)) { vibraton.wLeftMotorSpeed = m_large << 8; vibraton.wRightMotorSpeed = m_small << 8; } m_xinput.SetState(vibraton); } } switch(index) { case 0: return (BYTE)(m_buttons & 0xff); case 1: return (BYTE)(m_buttons >> 8); case 2: return m_right.x; case 3: return m_right.y; case 4: return m_left.x; case 5: return m_left.y; } return 0xff; } }; static class XPadPlugin { vector m_pads; XPad* m_pad; int m_index; bool m_cfgreaddata; BYTE m_unknown1; BYTE m_unknown3; typedef BYTE (XPadPlugin::*CommandHandler)(int, BYTE); CommandHandler m_handlers[256]; CommandHandler m_handler; BYTE DS2Enabler(int index, BYTE value) { switch(index) { case 2: return 0x02; case 5: return 'Z'; } return 0; } BYTE QueryDS2AnalogMode(int index, BYTE value) { if(m_pad->m_ds2native) { switch(index) { case 0: return 0xff; case 1: return 0xff; case 2: return 3; case 3: return 0; case 4: return 0; case 5: return 'Z'; } } return 0; } BYTE ReadDataAndVibrate(int index, BYTE value) { switch(index) { case 0: m_pad->m_small = (value&1) * 128; break; case 1: m_pad->m_large = value; break; } return m_pad->ReadData(index); } BYTE ConfigMode(int index, BYTE value) { switch(index) { case 0: switch(value) { case 0: m_cfgreaddata = false; break; case 1: m_cfgreaddata = true; break; } break; } if(m_cfgreaddata) { return m_pad->ReadData(index); } return 0; } BYTE SetModeAndLock(int index, BYTE value) { switch(index) { case 0: // if(!m_pad->m_locked) ? m_pad->m_analog = value == 1; break; case 1: m_pad->m_locked = value == 3; break; } return 0; } BYTE QueryModelAndMode(int index, BYTE value) { switch(index) { case 0: return 3; case 1: return 2; case 2: return m_pad->m_analog ? 1 : 0; case 3: return m_pad->m_ds2native ? 1 : 2; case 4: return 1; } return 0; } BYTE QueryUnknown1(int index, BYTE value) { switch(index) { case 0: m_unknown1 = value; return 0; case 1: return 0; case 2: return 1; case 3: return m_unknown1 ? 0x01 : 0x02; case 4: return m_unknown1 ? 0x01 : 0x00; case 5: return m_unknown1 ? 0x14 : 0x0a; } return 0; } BYTE QueryUnknown2(int index, BYTE value) { switch(index) { case 2: return 2; case 4: return 1; } return 0; } BYTE QueryUnknown3(int index, BYTE value) { switch(index) { case 0: m_unknown3 = value; return 0; case 3: return m_unknown3 ? 0x07 : 0x04; } return 0; } BYTE ConfigVibration(int index, BYTE value) { switch(index) { case 0: return value; case 1: m_pad->m_vibration = value == 1; return value; } return 0xff; } BYTE SetDS2NativeMode(int index, BYTE value) { switch(index) { case 5: m_pad->m_ds2native = true; return 'Z'; } return 0; } BYTE m_cmd; BYTE UnknownCommand(int index, BYTE value) { // printf("Unknown command %02x (%d, %02x)\n", m_cmd, index, value); return 0; } public: XPadPlugin() : m_pad(NULL) , m_index(-1) , m_cfgreaddata(false) , m_handler(NULL) { m_pads.push_back(new XPad(0)); m_pads.push_back(new XPad(1)); for(int i = 0; i < countof(m_handlers); i++) { m_handlers[i] = &XPadPlugin::UnknownCommand; } m_handlers['@'] = &XPadPlugin::DS2Enabler; m_handlers['A'] = &XPadPlugin::QueryDS2AnalogMode; m_handlers['B'] = &XPadPlugin::ReadDataAndVibrate; m_handlers['C'] = &XPadPlugin::ConfigMode; m_handlers['D'] = &XPadPlugin::SetModeAndLock; m_handlers['E'] = &XPadPlugin::QueryModelAndMode; m_handlers['F'] = &XPadPlugin::QueryUnknown1; m_handlers['G'] = &XPadPlugin::QueryUnknown2; m_handlers['L'] = &XPadPlugin::QueryUnknown3; m_handlers['M'] = &XPadPlugin::ConfigVibration; m_handlers['O'] = &XPadPlugin::SetDS2NativeMode; } virtual ~XPadPlugin() { for(vector::iterator i = m_pads.begin(); i != m_pads.end(); i++) { delete *i; } m_pads.clear(); } void StartPoll(int pad) { m_pad = m_pads[pad & 1]; m_index = 0; } BYTE Poll(BYTE value) { BYTE ret = 0; switch(++m_index) { case 1: m_cmd = value; m_handler = m_handlers[value]; ret = (value == 'B' || value == 'C') ? m_pad->GetId() : 0xf3; break; case 2: assert(value == 0); ret = 'Z'; break; default: ret = (this->*m_handler)(m_index - 3, value); break; } return ret; } } s_padplugin; // static int s_nRefs = 0; static HWND s_hWnd = NULL; static WNDPROC s_GSWndProc = NULL; static class CKeyEventList : protected list, protected CCritSec { public: void Push(UINT32 event, UINT32 key) { CAutoLock cAutoLock(this); KeyEvent e; e.event = event; e.key = key; push_back(e); } bool Pop(KeyEvent& e) { CAutoLock cAutoLock(this); if(empty()) return false; e = front(); pop_front(); return true; } } s_event; LRESULT WINAPI PADwndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_KEYDOWN: if(lParam & 0x40000000) return TRUE; s_event.Push(KEYPRESS, wParam); return TRUE; case WM_KEYUP: s_event.Push(KEYRELEASE, wParam); return TRUE; case WM_DESTROY: case WM_QUIT: s_event.Push(KEYPRESS, VK_ESCAPE); break; } return CallWindowProc(s_GSWndProc, hWnd, msg, wParam, lParam); } // EXPORT_C_(UINT32) PADinit(UINT32 flags) { // Use LoadLibrary and not LoadLibraryEx for XInput 1.3, since some // Windows 7 systems have issues with it. // TODO: Only load XInput 1.4 for Windows 8+? Or implement the SCP extension? s_xInputDll = LoadLibrary(L"xinput1_3.dll"); if (s_xInputDll == nullptr && IsWindows8OrGreater()) { s_xInputDll = LoadLibraryEx(L"XInput1_4.dll", nullptr, LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32); } if (s_xInputDll == nullptr) { return 1; } if (!(pXInputEnable = reinterpret_cast(GetProcAddress(s_xInputDll, "XInputEnable"))) || !(pXInputSetState = reinterpret_cast(GetProcAddress(s_xInputDll, "XInputSetState"))) || !(pXInputGetState = reinterpret_cast(GetProcAddress(s_xInputDll, "XInputGetState")))) { FreeLibrary(s_xInputDll); s_xInputDll = nullptr; return 1; } return 0; } EXPORT_C PADshutdown() { if (s_xInputDll) { FreeLibrary(s_xInputDll); s_xInputDll = nullptr; } } EXPORT_C_(UINT32) PADopen(void* pDsp) { pXInputEnable(TRUE); if(s_nRefs == 0) { if(s_ps2) { s_hWnd = *(HWND*)pDsp; s_GSWndProc = (WNDPROC)SetWindowLongPtr(s_hWnd, GWLP_WNDPROC, (LPARAM)PADwndProc); } } s_nRefs++; return 0; } EXPORT_C PADclose() { s_nRefs--; if(s_nRefs == 0) { if(s_ps2) { SetWindowLongPtr(s_hWnd, GWLP_WNDPROC, (LPARAM)s_GSWndProc); } } pXInputEnable(FALSE); } EXPORT_C_(UINT32) CALLBACK PADquery() { return 3; } EXPORT_C_(BYTE) PADstartPoll(int pad) { s_padplugin.StartPoll(pad - 1); return 0xff; } EXPORT_C_(BYTE) PADpoll(BYTE value) { return s_padplugin.Poll(value); } EXPORT_C_(UINT32) PADreadPort(int port, PadDataS* data) { PADstartPoll(port + 1); data->type = PADpoll('B') >> 4; PADpoll(0); data->status = PADpoll(0) | (PADpoll(0) << 8); data->rightJoyX = data->moveX = PADpoll(0); data->rightJoyY = data->moveY = PADpoll(0); data->leftJoyX = PADpoll(0); data->leftJoyY = PADpoll(0); return 0; } EXPORT_C_(UINT32) PADreadPort1(PadDataS* ppds) { return PADreadPort(0, ppds); } EXPORT_C_(UINT32) PADreadPort2(PadDataS* ppds) { return PADreadPort(1, ppds); } EXPORT_C_(KeyEvent*) PADkeyEvent() { static KeyEvent e; return s_event.Pop(e) ? &e : NULL; } EXPORT_C PADconfigure() { } EXPORT_C PADabout() { } EXPORT_C_(UINT32) PADtest() { return 0; }