#include "Global.h" #include #include #include #define PADdefs #include "PS2Etypes.h" #include "PS2Edefs.h" #include "Config.h" #include "InputManager.h" #include "DeviceEnumerator.h" #include "WndProcEater.h" #include "KeyboardQueue.h" #include "resource.h" // Used to prevent reading input and cleaning up input devices at the same time. // Only an issue when not reading input in GS thread and disabling devices due to // lost focus. CRITICAL_SECTION readInputCriticalSection; HINSTANCE hInst; HWND hWnd; // Used to toggle mouse binding. u8 miceEnabled; // 2 when both pads are initialized, 1 for one pad, etc. int openCount = 0; int activeWindow = 0; int bufSize = 0; static unsigned char outBuf[50]; static unsigned char inBuf[50]; #define MODE_DIGITAL 0x41 #define MODE_ANALOG 0x73 #define MODE_DS2_NATIVE 0x79 int IsWindowMaximized (HWND hWnd) { RECT rect; if (GetWindowRect(hWnd, &rect)) { POINT p; p.x = rect.left; p.y = rect.top; MONITORINFO info; memset(&info, 0, sizeof(info)); info.cbSize = sizeof(info); HMONITOR hMonitor; if ((hMonitor = MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY)) && GetMonitorInfo(hMonitor, &info) && memcmp(&info.rcMonitor, &rect, sizeof(rect)) == 0) { return 1; } } return 0; } void DEBUG_NEW_SET() { if (config.debug) { HANDLE hFile = CreateFileA("logs\\padLog.txt", GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0); if (hFile != INVALID_HANDLE_VALUE) { int i; char temp[1500]; char *end = temp; for (i=0; i255) return 255; return (u8) i; } // Just like RefreshEnabledDevices(), but takes into account // mouse and focus state and which devices have bindings for // enabled pads. Also releases keyboards if window is not focused. // And releases games if not focused and config.background is not set. void UpdateEnabledDevices(int updateList = 0) { // Enable all devices I might want. Can ignore the rest. RefreshEnabledDevices(updateList); // Figure out which pads I'm getting input for. int padsEnabled[2] = { pads[0].initialized && !config.disablePad[0], pads[1].initialized && !config.disablePad[1] }; for (int i=0; inumDevices; i++) { Device *dev = dm->devices[i]; if (!dev->enabled) continue; if (!dev->attached) { dm->DisableDevice(i); continue; } // Disable ignore keyboard if don't have focus or there are no keys to ignore. if (dev->api == IGNORE_KEYBOARD) { if (config.keyboardApi == NO_API || !activeWindow || !dev->pads[0].numBindings) { dm->DisableDevice(i); } continue; } // Keep for PCSX2 keyboard shotcuts, unless unfocused. if (dev->type == KEYBOARD) { if (!activeWindow) dm->DisableDevice(i); } // Keep for cursor hiding consistency, unless unfocused. // miceEnabled tracks state of mouse enable/disable button, not if mouse API is set to disabled. else if (dev->type == MOUSE) { if (!miceEnabled || !activeWindow) dm->DisableDevice(i); } else if (!activeWindow && !config.background) dm->DisableDevice(i); else { int numActiveBindings = 0; for (int pad=0; pad<2; pad++) { if (padsEnabled[pad]) { numActiveBindings += dev->pads[pad].numBindings + dev->pads[pad].numFFBindings; } } if (!numActiveBindings) dm->DisableDevice(i); } } } BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, void* lpvReserved) { hInst = hInstance; if (fdwReason == DLL_PROCESS_ATTACH) { InitializeCriticalSection(&readInputCriticalSection); DisableThreadLibraryCalls(hInstance); } else if (fdwReason == DLL_PROCESS_DETACH) { DeleteCriticalSection(&readInputCriticalSection); while (openCount) PADclose(); PADshutdown(); } return 1; } BOOL WINAPI MyDllMainCRTStartup(HANDLE hDllHandle, DWORD dwReason, LPVOID lpReserved) { return DllMain((HINSTANCE) hDllHandle, dwReason, lpReserved); } void AddForce(ButtonSum *sum, u8 cmd, int delta = 255) { if (!delta) return; if (cmd<0x14) { sum->buttons[cmd-0x10] += delta; } // D-pad. Command numbering is based on ordering of digital values. else if (cmd < 0x18) { if (cmd == 0x14) { sum->sticks[0].vert -= delta; } else if (cmd == 0x15) { sum->sticks[0].horiz += delta; } else if (cmd == 0x16) { sum->sticks[0].vert += delta; } else if (cmd == 0x17) { sum->sticks[0].horiz -= delta; } } else if (cmd < 0x20) { sum->buttons[cmd-0x10-4] += delta; } // Left stick. else if (cmd < 0x24) { if (cmd == 32) { sum->sticks[2].vert -= delta; } else if (cmd == 33) { sum->sticks[2].horiz += delta; } else if (cmd == 34) { sum->sticks[2].vert += delta; } else if (cmd == 35) { sum->sticks[2].horiz -= delta; } } // Right stick. else if (cmd < 0x28) { if (cmd == 36) { sum->sticks[1].vert -= delta; } else if (cmd == 37) { sum->sticks[1].horiz += delta; } else if (cmd == 38) { sum->sticks[1].vert += delta; } else if (cmd == 39) { sum->sticks[1].horiz -= delta; } } } void ProcessButtonBinding(Binding *b, ButtonSum *sum, int value) { int sensitivity = b->sensitivity; if (sensitivity < 0) { sensitivity = -sensitivity; value = (1<<16)-value; } if (value > 0) { AddForce(sum, b->command, (int)((((sensitivity*(255*(__int64)value)) + BASE_SENSITIVITY/2)/BASE_SENSITIVITY + FULLY_DOWN/2)/FULLY_DOWN)); } } // Restricts d-pad/analog stick values to be from -255 to 255 and button values to be from 0 to 255. // With D-pad in DS2 native mode, the negative and positive ranges are both independently from 0 to 255, // which is why I use 9 bits of all sticks. For left and right sticks, I have to remove a bit before sending. void CapSum(ButtonSum *sum) { int i; for (i=0; i<3; i++) { int div = max(abs(sum->sticks[i].horiz), abs(sum->sticks[i].vert)); if (div > 255) { sum->sticks[i].horiz = sum->sticks[i].horiz * 255 / div; sum->sticks[i].vert = sum->sticks[i].vert * 255 / div; } } for (i=0; i<12; i++) { sum->buttons[i] = Cap(sum->buttons[i]); } } // Counters for when to next update pad state. // Read all devices at once, so don't need to read them again // for pad 2 immediately after pad 1. 3rd counter is for // when neither pad is being read, so still respond to // key press info requests. int summed[3] = {0, 0, 0}; int lockStateChanged[2] = {0,0}; #define LOCK_DIRECTION 2 #define LOCK_BUTTONS 4 #define LOCK_BOTH 1 extern HWND hWndStealing; void Update(int pad) { if ((unsigned int)pad > 2) return; if (summed[pad] > 0) { summed[pad]--; return; } int i; ButtonSum s[2]; s[0] = pads[0].lockedSum; s[1] = pads[1].lockedSum; InitInfo info = { 0, hWnd, hWnd, 0 }; if (!config.GSThreadUpdates) { EnterCriticalSection(&readInputCriticalSection); } dm->Update(&info); static int turbo = 0; turbo++; for (i=0; inumDevices; i++) { Device *dev = dm->devices[i]; // Skip both disabled devices and inactive enabled devices. // Shouldn't be any of the latter, in general, but just in case... if (!dev->virtualControlState) continue; for (int pad=0; pad<2; pad++) { if (config.disablePad[pad]) continue; for (int j=0; jpads[pad].numBindings; j++) { Binding *b = dev->pads[pad].bindings+j; int cmd = b->command; int state = dev->virtualControlState[b->controlIndex]; if (!(turbo & b->turbo)) { if (cmd > 0x0F && cmd != 0x28) { ProcessButtonBinding(b, s+pad, state); } else if ((state>>15) && !(dev->oldVirtualControlState[b->controlIndex]>>15)) { if (cmd == 0x0F) { miceEnabled = !miceEnabled; UpdateEnabledDevices(); } else if (cmd == 0x0C) { lockStateChanged[pad] |= LOCK_BUTTONS; } else if (cmd == 0x0E) { lockStateChanged[pad] |= LOCK_DIRECTION; } else if (cmd == 0x0D) { lockStateChanged[pad] |= LOCK_BOTH; } else if (cmd == 0x28) { if (!pads[pad].modeLock) { if (pads[pad].mode != MODE_DIGITAL) pads[pad].mode = MODE_DIGITAL; else pads[pad].mode = MODE_ANALOG; } } } } } } } dm->PostRead(); if (!config.GSThreadUpdates) { LeaveCriticalSection(&readInputCriticalSection); } for (int currentPad = 0; currentPad<2; currentPad++) { if (config.guitar[currentPad]) { if (!config.GH2) { s[currentPad].sticks[1].vert = -s[currentPad].sticks[1].vert; } // GH2 hack. else if (config.GH2) { const unsigned int oldIdList[5] = {ID_R2, ID_CIRCLE, ID_TRIANGLE, ID_CROSS, ID_SQUARE}; const unsigned int idList[5] = {ID_L2, ID_L1, ID_R1, ID_R2, ID_CROSS}; int values[5]; int i; for (i=0; i<5; i++) { int id = oldIdList[i] - 0x1104; values[i] = s[currentPad].buttons[id]; s[currentPad].buttons[id] = 0; } s[currentPad].buttons[ID_TRIANGLE-0x1104] = values[1]; for (i=0; i<5; i++) { int id = idList[i] - 0x1104; s[currentPad].buttons[id] = values[i]; } if (abs(s[currentPad].sticks[0].vert) <= 48) { for (int i=0; i<5; i++) { unsigned int id = idList[i] - 0x1104; if (pads[currentPad].sum.buttons[id] < s[currentPad].buttons[id]) { s[currentPad].buttons[id] = pads[currentPad].sum.buttons[id]; } } } else if (abs(pads[currentPad].sum.sticks[0].vert) <= 48) { for (int i=0; i<5; i++) { unsigned int id = idList[i] - 0x1104; if (pads[currentPad].sum.buttons[id]) { s[currentPad].buttons[id] = 0; } } } } } if (pads[currentPad].mode == 0x41) { s[currentPad].sticks[0].horiz += s[currentPad].sticks[1].horiz + s[currentPad].sticks[2].horiz; s[currentPad].sticks[0].vert += s[currentPad].sticks[1].vert + s[currentPad].sticks[2].vert; } CapSum(&s[currentPad]); if (lockStateChanged[currentPad]) { if (lockStateChanged[currentPad] & LOCK_BOTH) { if (pads[currentPad].lockedState != (LOCK_DIRECTION | LOCK_BUTTONS)) { // Enable the one that's not enabled. lockStateChanged[currentPad] ^= pads[currentPad].lockedState^(LOCK_DIRECTION | LOCK_BUTTONS); } else { // Disable both lockStateChanged[currentPad] ^= LOCK_DIRECTION | LOCK_BUTTONS; } } if (lockStateChanged[currentPad] & LOCK_DIRECTION) { if (pads[currentPad].lockedState & LOCK_DIRECTION) { memset(pads[currentPad].lockedSum.sticks, 0, sizeof(pads[currentPad].lockedSum.sticks)); } else { memcpy(pads[currentPad].lockedSum.sticks, s[currentPad].sticks, sizeof(pads[currentPad].lockedSum.sticks)); } pads[currentPad].lockedState ^= LOCK_DIRECTION; } if (lockStateChanged[currentPad] & LOCK_BUTTONS) { if (pads[currentPad].lockedState & LOCK_BUTTONS) { memset(pads[currentPad].lockedSum.buttons, 0, sizeof(pads[currentPad].lockedSum.buttons)); } else { memcpy(pads[currentPad].lockedSum.buttons, s[currentPad].buttons, sizeof(pads[currentPad].lockedSum.buttons)); } pads[currentPad].lockedState ^= LOCK_BUTTONS; } for (i=0; ivibrateVal[motor]) { dm->SetEffect(pad - pads, motor, val); pad->vibrateVal[motor] = val; } } u32 CALLBACK PS2EgetLibType(void) { ps2e = 1; return PS2E_LT_PAD; } #define VERSION ((0<<8) | 9 | (10<<24)) u32 CALLBACK PS2EgetLibVersion2(u32 type) { ps2e = 1; if (type == PS2E_LT_PAD) return (PS2E_PAD_VERSION<<16) | VERSION; return 0; } char* CALLBACK PSEgetLibName() { #ifdef _DEBUG return "LilyPad Debug"; #else return "LilyPad"; #endif } char* CALLBACK PS2EgetLibName(void) { ps2e = 1; return PSEgetLibName(); } //void CALLBACK PADgsDriverInfo(GSdriverInfo *info) { // info=info; //} void CALLBACK PADshutdown() { pads[0].initialized = 0; pads[1].initialized = 0; UnloadConfigs(); } #ifdef _DEBUG #include "crtdbg.h" #endif inline void StopVibrate() { for (int i=0; i<4; i++) { SetVibrate(&pads[i/2], i&1, 0); } } inline void ResetVibrate(Pad *pad) { SetVibrate(pad, 0, 0); SetVibrate(pad, 1, 0); ((int*)(pad->vibrate))[0] = 0xFFFFFF5A; ((int*)(pad->vibrate))[1] = 0xFFFFFFFF; } s32 CALLBACK PADinit(u32 flags) { // Note: Won't load settings if already loaded. if (LoadSettings() < 0) { return -1; } int pad = (flags & 3); if (pad == 3) { if (PADinit(1)) return -1; return PADinit(2); } #ifdef _DEBUG int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ); tmpFlag |= _CRTDBG_LEAK_CHECK_DF; _CrtSetDbgFlag( tmpFlag ); #endif pad --; memset(&pads[pad], 0, sizeof(pads[0])); pads[pad].mode = MODE_DIGITAL; pads[pad].umask[0] = pads[pad].umask[1] = 0xFF; ResetVibrate(pads+pad); if (config.AutoAnalog[pad]) { pads[pad].mode = MODE_ANALOG; } pads[pad].initialized = 1; return 0; } // Note to self: Has to be a define for the sizeof() to work right. // Note to self 2: All are the same size, anyways, except for longer full DS2 response // and shorter digital mode response. #define SET_RESULT(a) { \ memcpy(query.response+2, a, sizeof(a)); \ query.numBytes = 2+sizeof(a); \ } #define SET_FINAL_RESULT(a) { \ memcpy(query.response+2, a, sizeof(a));\ query.numBytes = 2+sizeof(a); \ query.queryDone = 1; \ } static const u8 ConfigExit[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; //static const u8 ConfigExit[7] = {0x5A, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}; static const u8 noclue[7] = {0x5A, 0x00, 0x00, 0x02, 0x00, 0x00, 0x5A}; static u8 queryMaskMode[7] = {0x5A, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x5A}; //static const u8 DSNonNativeMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static const u8 setMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // DS2 static const u8 queryModelDS2[7] = {0x5A, 0x03, 0x02, 0x00, 0x02, 0x01, 0x00}; // DS1 static const u8 queryModelDS1[7] = {0x5A, 0x01, 0x02, 0x00, 0x02, 0x01, 0x00}; static const u8 queryAct[2][7] = {{0x5A, 0x00, 0x00, 0x01, 0x02, 0x00, 0x0A}, {0x5A, 0x00, 0x00, 0x01, 0x01, 0x01, 0x14}}; static const u8 queryComb[7] = {0x5A, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00}; static const u8 queryMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static const u8 setNativeMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A}; struct QueryInfo { u8 pad; u8 lastByte; u8 currentCommand; u8 numBytes; u8 queryDone; u8 response[22]; } query = {0,0,0,0, 0,0xFF, 0xF3}; int saveStateIndex = 0; ExtraWndProcResult HackWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { switch (uMsg) { case WM_SETTEXT: if (config.saveStateTitle) { wchar_t text[200]; int len; if (IsWindowUnicode(hWnd)) { len = wcslen((wchar_t*) lParam); if (len < sizeof(text)/sizeof(wchar_t)) wcscpy(text, (wchar_t*) lParam); } else { len = MultiByteToWideChar(CP_ACP, 0, (char*) lParam, -1, text, sizeof(text)/sizeof(wchar_t)); } if (len > 0 && len < 150 && !wcsstr(text, L" | State ")) { wsprintfW(text+len, L" | State %i", saveStateIndex); SetWindowText(hWnd, text); return NO_WND_PROC; } } break; case WM_DEVICECHANGE: if (wParam == DBT_DEVNODES_CHANGED) { // Need to do this when not reading input from gs thread. // Checking for that case not worth the effort. EnterCriticalSection(&readInputCriticalSection); UpdateEnabledDevices(1); LeaveCriticalSection(&readInputCriticalSection); } break; case WM_ACTIVATEAPP: // Need to do this when not reading input from gs thread. // Checking for that case not worth the effort. EnterCriticalSection(&readInputCriticalSection); if (!wParam) { activeWindow = 0; UpdateEnabledDevices(); } else { activeWindow = 1; UpdateEnabledDevices(); } LeaveCriticalSection(&readInputCriticalSection); break; case WM_CLOSE: if (config.closeHacks & 1) { QueueKeyEvent(VK_ESCAPE, KEYPRESS); return NO_WND_PROC; } else if (config.closeHacks & 2) { ExitProcess(0); return NO_WND_PROC; } break; case WM_SYSCOMMAND: if ((wParam == SC_SCREENSAVE || wParam == SC_MONITORPOWER) && config.disableScreenSaver) return NO_WND_PROC; break; case WM_DESTROY: QueueKeyEvent(VK_ESCAPE, KEYPRESS); break; default: break; } return CONTINUE_BLISSFULLY; } // All that's needed to force hiding the cursor in the proper thread. // Could have a special case elsewhere, but this make sure it's called // only once, rather than repeatedly. ExtraWndProcResult HideCursorProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { ShowCursor(0); return CONTINUE_BLISSFULLY_AND_RELEASE_PROC; } s32 CALLBACK PADopen(void *pDsp) { if (openCount++) return 0; // Not really needed, shouldn't do anything. if (LoadSettings()) return -1; miceEnabled = !config.mouseUnfocus; if (!hWnd) { if (IsWindow((HWND)pDsp)) { hWnd = (HWND) pDsp; } else if (pDsp && !IsBadReadPtr(pDsp, 4) && IsWindow(*(HWND*) pDsp)) { hWnd = *(HWND*) pDsp; } else { openCount = 0; return -1; } while (GetWindowLong (hWnd, GWL_STYLE) & WS_CHILD) hWnd = GetParent (hWnd); // Implements most hacks, as well as enabling/disabling mouse // capture when focus changes. if (!EatWndProc(hWnd, HackWndProc)) { openCount = 0; return -1; } if (config.forceHide) { EatWndProc(hWnd, HideCursorProc); } } memset(&pads[0].sum, 0, sizeof(pads[0].sum)); memset(&pads[0].lockedSum, 0, sizeof(pads[0].lockedSum)); pads[0].lockedState = 0; memset(&pads[1].sum, 0, sizeof(pads[0].sum)); memset(&pads[1].lockedSum, 0, sizeof(pads[0].lockedSum)); pads[1].lockedState = 0; query.lastByte = 1; query.numBytes = 0; // I'd really rather use this line, but GetActiveWindow() does not have complete specs. // It *seems* to return null when no window from this thread has focus, but the // Microsoft specs seem to imply it returns the window from this thread that would have focus, // if any window did (topmost in this thread?). Which isn't what I want, and doesn't seem // to be what it actually does. // activeWindow = GetActiveWindow() == hWnd; activeWindow = (GetAncestor(hWnd, GA_ROOT) == GetAncestor(GetForegroundWindow(), GA_ROOT)); UpdateEnabledDevices(); return 0; } void CALLBACK PADclose() { if (openCount && !--openCount) { dm->ReleaseInput(); ReleaseEatenProc(); hWnd = 0; ClearKeyQueue(); } } u8 CALLBACK PADstartPoll(int pad) { DEBUG_NEW_SET(); pad--; if ((unsigned int)pad <= 1) { query.queryDone = 0; query.pad = pad; query.numBytes = 2; query.lastByte = 0; DEBUG_IN(pad); DEBUG_OUT(0xFF); return 0xFF; } else { query.queryDone = 1; query.numBytes = 0; query.lastByte = 1; DEBUG_IN(pad); DEBUG_OUT(0); return 0; } } u8 CALLBACK PADpoll(u8 value) { DEBUG_IN(value); if (query.lastByte+1 >= query.numBytes) { DEBUG_OUT(0); return 0; } if (query.lastByte && query.queryDone) { DEBUG_OUT(query.response[1+query.lastByte]); return query.response[++query.lastByte]; } int i; Pad *pad = &pads[query.pad]; if (query.lastByte == 0) { query.lastByte++; query.currentCommand = value; switch(value) { // CONFIG_MODE case 0x43: if (pad->config) { // In config mode. Might not actually be leaving it. SET_RESULT(ConfigExit); DEBUG_OUT(0xF3); return 0xF3; } // READ_DATA_AND_VIBRATE case 0x42: query.response[2] = 0x5A; { if (!config.GSThreadUpdates) { Update(pad != pads); } ButtonSum *sum = &pad->sum; u8 b1 = 0xFF, b2 = 0xFF; for (i = 0; i<4; i++) { b1 -= (sum->buttons[i]>=128) << i; } for (i = 0; i<8; i++) { b2 -= (sum->buttons[i+4]>=128) << i; } if (config.guitar[query.pad] && !config.GH2) { sum->sticks[0].horiz = -255; // Not sure about this. Forces wammy to be from 0 to 0x7F. // if (sum->sticks[2].vert > 0) sum->sticks[2].vert = 0; } b1 -= ((sum->sticks[0].vert<=-128) << 4); b1 -= ((sum->sticks[0].horiz>=128) << 5); b1 -= ((sum->sticks[0].vert>=128) << 6); b1 -= ((sum->sticks[0].horiz<=-128) << 7); query.response[3] = b1; query.response[4] = b2; query.numBytes = 5; if (pad->mode != MODE_DIGITAL) { query.response[5] = Cap((sum->sticks[1].horiz+255)/2); query.response[6] = Cap((sum->sticks[1].vert+255)/2); query.response[7] = Cap((sum->sticks[2].horiz+255)/2); query.response[8] = Cap((sum->sticks[2].vert+255)/2); query.numBytes = 9; if (pad->mode != MODE_ANALOG) { // Good idea? No clue. //query.response[3] &= pad->mask[0]; //query.response[4] &= pad->mask[1]; // Each value is from -255 to 255, so have to use cap to convert // negative values to 0. query.response[9] = Cap(sum->sticks[0].horiz); query.response[10] = Cap(-sum->sticks[0].horiz); query.response[11] = Cap(-sum->sticks[0].vert); query.response[12] = Cap(sum->sticks[0].vert); // No need to cap these, already done int CapSum(). query.response[13] = (unsigned char) sum->buttons[8]; query.response[14] = (unsigned char) sum->buttons[9]; query.response[15] = (unsigned char) sum->buttons[10]; query.response[16] = (unsigned char) sum->buttons[11]; query.response[17] = (unsigned char) sum->buttons[6]; query.response[18] = (unsigned char) sum->buttons[7]; query.response[19] = (unsigned char) sum->buttons[4]; query.response[20] = (unsigned char) sum->buttons[5]; query.numBytes = 21; } } } query.lastByte=1; DEBUG_OUT(pad->mode); return pad->mode; // SET_VREF_PARAM case 0x40: SET_FINAL_RESULT(noclue); break; // QUERY_DS2_ANALOG_MODE case 0x41: if (pad->mode == MODE_DIGITAL) { queryMaskMode[1] = queryMaskMode[2] = queryMaskMode[3] = 0; queryMaskMode[6] = 0x00; } else { queryMaskMode[1] = pad->umask[0]; queryMaskMode[2] = pad->umask[1]; queryMaskMode[3] = 0x03; // Not entirely sure about this. //queryMaskMode[3] = 0x01 | (pad->mode == MODE_DS2_NATIVE)*2; queryMaskMode[6] = 0x5A; } SET_FINAL_RESULT(queryMaskMode); break; // SET_MODE_AND_LOCK case 0x44: SET_RESULT(setMode); ResetVibrate(pad); break; // QUERY_MODEL_AND_MODE case 0x45: if (!config.guitar[query.pad] || config.GH2) SET_FINAL_RESULT(queryModelDS2) else SET_FINAL_RESULT(queryModelDS1); query.response[5] = pad->mode != MODE_DIGITAL; break; // QUERY_ACT case 0x46: SET_RESULT(queryAct[0]); break; // QUERY_COMB case 0x47: SET_FINAL_RESULT(queryComb); break; // QUERY_MODE case 0x4C: SET_RESULT(queryMode); break; // VIBRATION_TOGGLE case 0x4D: memcpy(query.response+2, pad->vibrate, 7); query.numBytes = 9; ResetVibrate(pad); break; // SET_DS2_NATIVE_MODE case 0x4F: SET_RESULT(setNativeMode); break; default: query.numBytes = 0; query.queryDone = 1; break; } DEBUG_OUT(0xF3); return 0xF3; } else { query.lastByte++; switch (query.currentCommand) { // READ_DATA_AND_VIBRATE case 0x42: if (query.lastByte == pad->vibrateI[0]) { SetVibrate(pad, 1, 255*(0!=value)); } else if (query.lastByte == pad->vibrateI[1]) { SetVibrate(pad, 0, value); } break; // CONFIG_MODE case 0x43: if (query.lastByte == 3) { query.queryDone = 1; pad->config = value; } break; // SET_MODE_AND_LOCK case 0x44: if (query.lastByte == 3 && value < 2) { static const u8 modes[2] = {MODE_DIGITAL, MODE_ANALOG}; pad->mode = modes[value]; } else if (query.lastByte == 4) { if (value == 3) { pad->modeLock = 3; } else { pad->modeLock = 0; if (pad->mode == MODE_DIGITAL && config.AutoAnalog[query.pad]) { pad->mode = MODE_ANALOG; } } query.queryDone = 1; } break; // QUERY_ACT case 0x46: if (query.lastByte == 3) { if (value<2) SET_RESULT(queryAct[value]) // bunch of 0's // else SET_RESULT(setMode); query.queryDone = 1; } break; // QUERY_MODE case 0x4C: if (query.lastByte == 3 && value<2) { query.response[6] = 4+value*3; query.queryDone = 1; } // bunch of 0's //else data = setMode; break; // VIBRATION_TOGGLE case 0x4D: if (query.lastByte>=3) { if (value == 0) { pad->vibrateI[0] = (u8)query.lastByte; } else if (value == 1) { pad->vibrateI[1] = (u8)query.lastByte; } pad->vibrate[query.lastByte-2] = value; } break; case 0x4F: if (query.lastByte == 3 || query.lastByte == 4) { pad->umask[query.lastByte-3] = value; } else if (query.lastByte == 5) { if (!(value & 1)) { pad->mode = MODE_DIGITAL; } else if (!(value & 2)) { pad->mode = MODE_ANALOG; } else { pad->mode = MODE_DS2_NATIVE; } } break; default: DEBUG_OUT(0); return 0; } DEBUG_OUT(query.response[query.lastByte]); return query.response[query.lastByte]; } } // returns: 1 if supports pad1 // 2 if supports pad2 // 3 if both are supported u32 CALLBACK PADquery() { return 3; } //void CALLBACK PADgsDriverInfo(GSdriverInfo *info) { //} INT_PTR CALLBACK AboutDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_COMMAND && LOWORD(wParam) == IDOK) { EndDialog(hwndDlg, 0); return 1; } return 0; } void CALLBACK PADabout() { DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUT), 0, AboutDialogProc); } s32 CALLBACK PADtest() { return 0; } // For escape fullscreen hack. This doesn't work when called from another thread, for some reason. // That includes a new thread, independent of GS and PCSX2 thread, so use this to make sure it's // called from the right spot. ExtraWndProcResult KillFullScreenProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { // Prevent infinite recursion. Could also just remove this function from the list, // but CONTINUE_BLISSFULLY_AND_RELEASE_PROC is a safer way to do that. static int inFunction = 0; if (!inFunction) { inFunction = 1; ShowWindow(hWnd, 0); inFunction = 0; } return CONTINUE_BLISSFULLY_AND_RELEASE_PROC; } DWORD WINAPI RenameWindowThreadProc(void *lpParameter) { wchar_t newTitle[200]; if (hWnd) { int len = GetWindowTextW(hWnd, newTitle, 200); if (len > 0 && len < 199) { wchar_t *end; if (end = wcsstr(newTitle, L" | State ")) *end = 0; SetWindowTextW(hWnd, newTitle); } } return 0; } keyEvent* CALLBACK PADkeyEvent() { if (!config.GSThreadUpdates) { Update(2); } static int shiftDown = 0; static keyEvent ev; if (!GetQueuedKeyEvent(&ev)) return 0; if (ev.key == VK_ESCAPE && ev.evt == KEYPRESS && config.escapeFullscreenHack) { if (IsWindowMaximized(hWnd)) { EatWndProc(hWnd, KillFullScreenProc); return 0; } } if (ev.key == VK_F2 && ev.evt == KEYPRESS) { saveStateIndex += 1 - 2*shiftDown; saveStateIndex = (saveStateIndex+10)%10; if (config.saveStateTitle) { // GSDX only checks its window's message queue at certain points or something, so // have to do this in another thread to prevent lockup. HANDLE hThread = CreateThread(0, 0, RenameWindowThreadProc, 0, 0, 0); if (hThread) CloseHandle(hThread); } } if (ev.key == VK_LSHIFT || ev.key == VK_RSHIFT || ev.key == VK_SHIFT) { ev.key = VK_SHIFT; if (ev.evt == KEYPRESS) shiftDown = 1; else shiftDown = 0; } else if (ev.key == VK_LCONTROL || ev.key == VK_RCONTROL) { ev.key = VK_CONTROL; } else if (ev.key == VK_LMENU || ev.key == VK_RMENU) { ev.key = VK_MENU; } return &ev; } typedef struct { unsigned char controllerType; unsigned short buttonStatus; unsigned char rightJoyX, rightJoyY, leftJoyX, leftJoyY; unsigned char moveX, moveY; unsigned char reserved[91]; } PadDataS; u32 CALLBACK PADreadPort1 (PadDataS* pads) { PADstartPoll(1); PADpoll(0x42); memcpy(pads, query.response+1, 7); pads->controllerType = pads[0].controllerType>>4; memset (pads+7, 0, sizeof(PadDataS)-7); return 0; } u32 CALLBACK PADreadPort2 (PadDataS* pads) { PADstartPoll(2); PADpoll(0x42); memcpy(pads, query.response+1, 7); pads->controllerType = pads->controllerType>>4; memset (pads+7, 0, sizeof(PadDataS)-7); return 0; } u32 CALLBACK PSEgetLibType() { return 8; } u32 CALLBACK PSEgetLibVersion() { return (VERSION & 0xFFFFFF); } // Little funkiness to handle rounding floating points to ints without the C runtime. // Unfortunately, means I can't use /GL optimization option. #ifdef NO_CRT extern "C" long _cdecl _ftol(); extern "C" long _cdecl _ftol2_sse() { return _ftol(); } extern "C" long _cdecl _ftol2() { return _ftol(); } #endif