diff --git a/plugins/LilyPad/Linux/Config.cpp b/plugins/LilyPad/Linux/Config.cpp
new file mode 100644
index 0000000000..ec506baf44
--- /dev/null
+++ b/plugins/LilyPad/Linux/Config.cpp
@@ -0,0 +1,278 @@
+/* LilyPad - Pad plugin for PS2 Emulator
+ * Copyright (C) 2002-2014 PCSX2 Dev Team/ChickenLiver
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Found- ation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * PCSX2 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 PCSX2. If not, see .
+ */
+
+#include "Global.h"
+
+#include "InputManager.h"
+#include "Config.h"
+#include "DeviceEnumerator.h"
+
+GeneralConfig config;
+u8 ps2e = 0;
+
+int LoadSettings(int force, wchar_t *file) {
+ if (dm && !force) return 0;
+
+#if 0
+ if( createIniDir )
+ {
+ CreateDirectory(L"inis", 0);
+ createIniDir = false;
+ }
+#endif
+
+ // Could just do ClearDevices() instead, but if I ever add any extra stuff,
+ // this will still work.
+ UnloadConfigs();
+ dm = new InputDeviceManager();
+
+#ifdef _MSC_VER
+ if (!file) {
+ file = iniFile;
+ GetPrivateProfileStringW(L"General Settings", L"Last Config Path", L"inis", config.lastSaveConfigPath, sizeof(config.lastSaveConfigPath), file);
+ GetPrivateProfileStringW(L"General Settings", L"Last Config Name", L"LilyPad.lily", config.lastSaveConfigFileName, sizeof(config.lastSaveConfigFileName), file);
+ }
+ else {
+ wchar_t *c = wcsrchr(file, '\\');
+ if (c) {
+ *c = 0;
+ wcscpy(config.lastSaveConfigPath, file);
+ wcscpy(config.lastSaveConfigFileName, c+1);
+ *c = '\\';
+ WritePrivateProfileStringW(L"General Settings", L"Last Config Path", config.lastSaveConfigPath, iniFile);
+ WritePrivateProfileStringW(L"General Settings", L"Last Config Name", config.lastSaveConfigFileName, iniFile);
+ }
+ }
+
+ for (int i=0; i= 100) break;
+ continue;
+ }
+ wchar_t *id2 = 0;
+ if (GetPrivateProfileStringW(id, L"Product ID", 0, temp4, 1000, file) && temp4[0])
+ id2 = temp4;
+
+ int api = GetPrivateProfileIntW(id, L"API", 0, file);
+ int type = GetPrivateProfileIntW(id, L"Type", 0, file);
+ if (!api || !type) continue;
+
+ Device *dev = new Device((DeviceAPI)api, (DeviceType)type, temp2, temp3, id2);
+ dev->attached = 0;
+ dm->AddDevice(dev);
+ int j = 0;
+ int last = 0;
+ while (1) {
+ wsprintfW(temp, L"Binding %i", j++);
+ if (!GetPrivateProfileStringW(id, temp, 0, temp2, 1000, file)) {
+ if (j >= 100) {
+ if (!last) break;
+ last = 0;
+ }
+ continue;
+ }
+ last = 1;
+ unsigned int uid;
+ int port, command, sensitivity, turbo, slot = 0, deadZone = 0;
+ int w = 0;
+ char string[1000];
+ while (temp2[w]) {
+ string[w] = (char)temp2[w];
+ w++;
+ }
+ string[w] = 0;
+ int len = sscanf(string, " %i , %i , %i , %i , %i , %i , %i", &uid, &port, &command, &sensitivity, &turbo, &slot, &deadZone);
+ if (len >= 5 && type) {
+ VirtualControl *c = dev->GetVirtualControl(uid);
+ if (!c) c = dev->AddVirtualControl(uid, -1);
+ if (c) {
+ BindCommand(dev, uid, port, slot, command, sensitivity, turbo, deadZone);
+ }
+ }
+ }
+ j = 0;
+ while (1) {
+ wsprintfW(temp, L"FF Binding %i", j++);
+ if (!GetPrivateProfileStringW(id, temp, 0, temp2, 1000, file)) {
+ if (j >= 10) {
+ if (!last) break;
+ last = 0;
+ }
+ continue;
+ }
+ last = 1;
+ int port, slot, motor;
+ int w = 0;
+ char string[1000];
+ char effect[1000];
+ while (temp2[w]) {
+ string[w] = (char)temp2[w];
+ w++;
+ }
+ string[w] = 0;
+ // wcstok not in ntdll. More effore than its worth to shave off
+ // whitespace without it.
+ if (sscanf(string, " %s %i , %i , %i", effect, &port, &motor, &slot) == 4) {
+ char *s = strchr(strchr(strchr(string, ',')+1, ',')+1, ',');
+ if (!s) continue;
+ s++;
+ w = 0;
+ while (effect[w]) {
+ temp2[w] = effect[w];
+ w++;
+ }
+ temp2[w] = 0;
+ ForceFeedbackEffectType *eff = dev->GetForcefeedbackEffect(temp2);
+ if (!eff) {
+ // At the moment, don't record effect types.
+ // Only used internally, anyways, so not an issue.
+ dev->AddFFEffectType(temp2, temp2, EFFECT_CONSTANT);
+ // eff = &dev->ffEffectTypes[dev->numFFEffectTypes-1];
+ }
+ ForceFeedbackBinding *b;
+ CreateEffectBinding(dev, temp2, port, slot, motor, &b);
+ if (b) {
+ while (1) {
+ int axisID = atoi(s);
+ if (!(s = strchr(s, ','))) break;
+ s++;
+ int force = atoi(s);
+ int i;
+ for (i=0; inumFFAxes; i++) {
+ if (axisID == dev->ffAxes[i].id) break;
+ }
+ if (i == dev->numFFAxes) {
+ dev->AddFFAxis(L"?", axisID);
+ }
+ b->axes[i].force = force;
+ if (!(s = strchr(s, ','))) break;
+ s++;
+ }
+ }
+ }
+ }
+ }
+ config.multipleBinding = multipleBinding;
+
+ RefreshEnabledDevicesAndDisplay(1);
+#endif
+
+ return 0;
+}
+
+void UnloadConfigs() {
+ if (dm) {
+ delete dm;
+ dm = 0;
+ }
+}
+
+void RefreshEnabledDevices(int updateDeviceList) {
+ // Clears all device state.
+ static int lastXInputState = -1;
+ if (updateDeviceList || lastXInputState != config.gameApis.xInput) {
+ EnumDevices(config.gameApis.xInput);
+ lastXInputState = config.gameApis.xInput;
+ }
+
+ for (int i=0; inumDevices; i++) {
+ Device *dev = dm->devices[i];
+
+ if (!dev->attached && dev->displayName[0] != '[') {
+ wchar_t *newName = (wchar_t*) malloc(sizeof(wchar_t) * (wcslen(dev->displayName) + 12));
+ wsprintfW(newName, L"[Detached] %s", dev->displayName);
+ free(dev->displayName);
+ dev->displayName = newName;
+ }
+
+ if ((dev->type == KEYBOARD && dev->api == IGNORE_KEYBOARD) ||
+ (dev->type == KEYBOARD && dev->api == config.keyboardApi) ||
+ (dev->type == MOUSE && dev->api == config.mouseApi) ||
+ (dev->type == OTHER &&
+ ((dev->api == DI && config.gameApis.directInput) ||
+ (dev->api == DS3 && config.gameApis.dualShock3) ||
+ (dev->api == XINPUT && config.gameApis.xInput)))) {
+ if (config.gameApis.dualShock3 && dev->api == DI && dev->displayName &&
+ !wcsicmp(dev->displayName, L"DX PLAYSTATION(R)3 Controller")) {
+ dm->DisableDevice(i);
+ }
+ else {
+ dm->EnableDevice(i);
+ }
+ }
+ else {
+ dm->DisableDevice(i);
+ }
+ }
+}
+
+void Configure() {
+ // Can end up here without PADinit() being called first.
+ LoadSettings();
+ // Can also end up here after running emulator a bit, and possibly
+ // disabling some devices due to focus changes, or releasing mouse.
+ RefreshEnabledDevices(0);
+}
diff --git a/plugins/LilyPad/Linux/KeyboardQueue.cpp b/plugins/LilyPad/Linux/KeyboardQueue.cpp
new file mode 100644
index 0000000000..3718834f3c
--- /dev/null
+++ b/plugins/LilyPad/Linux/KeyboardQueue.cpp
@@ -0,0 +1,68 @@
+/* LilyPad - Pad plugin for PS2 Emulator
+ * Copyright (C) 2002-2014 PCSX2 Dev Team/ChickenLiver
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Found- ation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * PCSX2 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 PCSX2. If not, see .
+ */
+
+#include "Global.h"
+// This is undoubtedly completely unnecessary.
+#include "KeyboardQueue.h"
+
+// What MS calls a single process Mutex. Faster, supposedly.
+// More importantly, can be abbreviated, amusingly, as cSection.
+static std::mutex cSection;
+
+#define EVENT_QUEUE_LEN 16
+// Actually points one beyond the last queued event.
+static u8 lastQueuedEvent = 0;
+static u8 nextQueuedEvent = 0;
+static keyEvent queuedEvents[EVENT_QUEUE_LEN];
+
+void QueueKeyEvent(int key, int event) {
+ std::lock_guard lock(cSection);
+
+ // Don't queue events if escape is on top of queue. This is just for safety
+ // purposes when a game is killing the emulator for whatever reason.
+ if (nextQueuedEvent == lastQueuedEvent ||
+ queuedEvents[nextQueuedEvent].key != VK_ESCAPE ||
+ queuedEvents[nextQueuedEvent].evt != KEYPRESS) {
+ // Clear queue on escape down, bringing escape to front. May do something
+ // with shift/ctrl/alt and F-keys, later.
+ if (event == KEYPRESS && key == VK_ESCAPE) {
+ nextQueuedEvent = lastQueuedEvent;
+ }
+
+ queuedEvents[lastQueuedEvent].key = key;
+ queuedEvents[lastQueuedEvent].evt = event;
+
+ lastQueuedEvent = (lastQueuedEvent + 1) % EVENT_QUEUE_LEN;
+ // If queue wrapped around, remove last element.
+ if (nextQueuedEvent == lastQueuedEvent) {
+ nextQueuedEvent = (nextQueuedEvent + 1) % EVENT_QUEUE_LEN;
+ }
+ }
+}
+
+int GetQueuedKeyEvent(keyEvent *event) {
+ if (lastQueuedEvent == nextQueuedEvent) return 0;
+
+ std::lock_guard lock(cSection);
+ *event = queuedEvents[nextQueuedEvent];
+ nextQueuedEvent = (nextQueuedEvent + 1) % EVENT_QUEUE_LEN;
+ return 1;
+}
+
+void ClearKeyQueue() {
+ lastQueuedEvent = nextQueuedEvent;
+}
diff --git a/plugins/LilyPad/Linux/LilyPad.cpp b/plugins/LilyPad/Linux/LilyPad.cpp
new file mode 100644
index 0000000000..51d6a7b7a8
--- /dev/null
+++ b/plugins/LilyPad/Linux/LilyPad.cpp
@@ -0,0 +1,1275 @@
+/* LilyPad - Pad plugin for PS2 Emulator
+ * Copyright (C) 2002-2014 PCSX2 Dev Team/ChickenLiver
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Found- ation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * PCSX2 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 PCSX2. If not, see .
+ */
+
+#include "Global.h"
+
+// For escape timer, so as not to break GSDX+DX9.
+#include
+#include "resource.h"
+#include "InputManager.h"
+#include "Config.h"
+
+#define PADdefs
+
+#include "DeviceEnumerator.h"
+#include "KeyboardQueue.h"
+#include "svnrev.h"
+#include "DualShock3.h"
+#include "HidDevice.h"
+
+#define WMA_FORCE_UPDATE (WM_APP + 0x537)
+#define FORCE_UPDATE_WPARAM ((WPARAM)0x74328943)
+#define FORCE_UPDATE_LPARAM ((LPARAM)0x89437437)
+
+// LilyPad version.
+#define VERSION ((0<<8) | 11 | (0<<24))
+
+// Keeps the various sources for Update polling (PADpoll, PADupdate, etc) from wreaking
+// havoc on each other...
+#ifdef _MSC_VER
+CRITICAL_SECTION updateLock;
+#else
+static std::mutex updateLock;
+#endif
+
+// Used to toggle mouse listening.
+u8 miceEnabled;
+
+// 2 when both pads are initialized, 1 for one pad, etc.
+int openCount = 0;
+
+int activeWindow = 0;
+int windowThreadId = 0;
+int updateQueued = 0;
+
+int bufSize = 0;
+unsigned char outBuf[50];
+unsigned char inBuf[50];
+
+// windowThreadId = GetWindowThreadProcessId(hWnd, 0);
+
+#define MODE_DIGITAL 0x41
+#define MODE_ANALOG 0x73
+#define MODE_DS2_NATIVE 0x79
+
+
+void DEBUG_TEXT_OUT(const char *text) {
+#ifdef _MSC_VER
+ if (config.debug) {
+ HANDLE hFile = CreateFileA("logs\\padLog.txt", FILE_APPEND_DATA, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0);
+ if (hFile != INVALID_HANDLE_VALUE) {
+ DWORD junk;
+ WriteFile(hFile, text, strlen(text), &junk, 0);
+ CloseHandle(hFile);;
+ }
+ }
+#endif
+}
+
+void DEBUG_NEW_SET() {
+#ifdef _MSC_VER
+ if (config.debug && bufSize>1) {
+ HANDLE hFile = CreateFileA("logs\\padLog.txt", FILE_APPEND_DATA, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0);
+ if (hFile != INVALID_HANDLE_VALUE) {
+ int i;
+ char temp[1500];
+ char *end = temp;
+ sprintf(end, "%02X (%02X) ", inBuf[0], inBuf[1]);
+ end += 8;
+ for (i=2; i 0).
+ u8 enabled;
+} pads[2][4];
+
+// Active slots for each port.
+int slots[2];
+// Which ports we're running on.
+int portInitialized[2];
+
+// Force value to be from 0 to 255.
+u8 Cap (int i) {
+ if (i<0) return 0;
+ if (i>255) return 255;
+ return (u8) i;
+}
+
+inline void ReleaseModifierKeys() {
+ QueueKeyEvent(VK_SHIFT, KEYRELEASE);
+ QueueKeyEvent(VK_MENU, KEYRELEASE);
+ QueueKeyEvent(VK_CONTROL, KEYRELEASE);
+}
+
+// RefreshEnabledDevices() enables everything that can potentially
+// be bound to, as well as the "Ignore keyboard" device.
+//
+// This enables everything that input should be read from while the
+// emulator is running. Takes into account mouse and focus state
+// and which devices have bindings for enabled pads. Releases
+// keyboards if window is not focused. Releases game devices if
+// background monitoring is not checked.
+// 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.
+ for (int port = 0; port<2; port++) {
+ for (int slot = 0; slot<4; slot++) {
+ if (slot && !config.multitap[port]) {
+ pads[port][slot].enabled = 0;
+ }
+ else {
+ pads[port][slot].enabled = pads[port][slot].initialized && config.padConfigs[port][slot].type != DisabledPad;
+ }
+ }
+ }
+ 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.vistaVolume && (config.keyboardApi == NO_API || !dev->pads[0][0].numBindings)) || !activeWindow) {
+ 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 port=0; port<2; port++) {
+ for (int slot=0; slot<4; slot++) {
+ if (pads[port][slot].enabled) {
+ numActiveBindings += dev->pads[port][slot].numBindings + dev->pads[port][slot].numFFBindings;
+ }
+ }
+ }
+ if (!numActiveBindings)
+ dm->DisableDevice(i);
+ }
+ }
+}
+
+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) {
+ if (value < b->deadZone || !value) return;
+
+#ifdef _MSC_VER
+ if ( config.turboKeyHack == 1 ){ // send a tabulator keypress to emulator
+ //printf("%x\n", b->command);
+ if ( b->command == 0x11 ){ // L3 button
+ static unsigned int LastCheck = 0;
+ unsigned int t = timeGetTime();
+ if (t - LastCheck < 300 ) return;
+ QueueKeyEvent(VK_TAB, KEYPRESS);
+ LastCheck = t;
+ }
+ }
+#endif
+
+ int sensitivity = b->sensitivity;
+ if (sensitivity < 0) {
+ sensitivity = -sensitivity;
+ value = (1<<16)-value;
+ }
+ if (value < 0) return;
+
+ /* Note: Value ranges of FULLY_DOWN, and sensitivity of
+ * BASE_SENSITIVITY corresponds to an axis/button being exactly fully down.
+ * Math in next line takes care of those two conditions, rounding as necessary.
+ * Done using __int64s because overflows will occur when
+ * sensitivity > BASE_SENSITIVITY and/or value > FULLY_DOWN. Latter only happens
+ * for relative axis.
+ */
+ int force = (int)((((sensitivity*(255*(__int64)value)) + BASE_SENSITIVITY/2)/BASE_SENSITIVITY + FULLY_DOWN/2)/FULLY_DOWN);
+ AddForce(sum, b->command, force);
+}
+
+// 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 = std::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]);
+ }
+}
+
+// Counter similar to stateUpdated for each pad, except used for PADkeyEvent instead.
+// Only matters when GS thread updates is disabled (Just like summed pad values
+// for pads beyond the first slot).
+
+// Values, in order, correspond to PADkeyEvent, PADupdate(0), PADupdate(1), and
+// WndProc(WMA_FORCE_UPDATE). Last is always 0.
+char padReadKeyUpdated[4] = {0, 0, 0, 0};
+
+#define LOCK_DIRECTION 2
+#define LOCK_BUTTONS 4
+#define LOCK_BOTH 1
+
+void Update(unsigned int port, unsigned int slot) {
+ char *stateUpdated;
+ if (port < 2) {
+ stateUpdated = &pads[port][slot].stateUpdated;
+ }
+ else if (port < 6) {
+ stateUpdated = padReadKeyUpdated+port-2;
+ }
+ else return;
+
+ if (*stateUpdated > 0) {
+ stateUpdated[0] --;
+ return;
+ }
+
+ // Lock prior to timecheck code to avoid pesky race conditions.
+ std::lock_guard lock(updateLock);
+
+#ifdef _MSC_VER
+ static unsigned int LastCheck = 0;
+ unsigned int t = timeGetTime();
+ if (t - LastCheck < 15 || !openCount) return;
+#endif
+
+#ifdef _MSC_VER
+ if (windowThreadId != GetCurrentThreadId()) {
+ if (stateUpdated[0] < 0) {
+ if (!updateQueued) {
+ updateQueued = 1;
+ PostMessage(hWnd, WMA_FORCE_UPDATE, FORCE_UPDATE_WPARAM, FORCE_UPDATE_LPARAM);
+ }
+ } else
+ {
+ stateUpdated[0] --;
+ }
+ return;
+ }
+
+ LastCheck = t;
+#endif
+
+ int i;
+ ButtonSum s[2][4];
+ u8 lockStateChanged[2][4];
+ memset(lockStateChanged, 0, sizeof(lockStateChanged));
+
+ for (i=0; i<8; i++) {
+ s[i&1][i>>1] = pads[i&1][i>>1].lockedSum;
+ }
+#ifdef _MSC_VER
+ InitInfo info = {
+ 0, 0, hWndTop, &hWndGSProc
+ };
+
+ dm->Update(&info);
+#endif
+ 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->active) continue;
+ for (int port=0; port<2; port++) {
+ for (int slot=0; slot<4; slot++) {
+ if (config.padConfigs[port][slot].type == DisabledPad || !pads[port][slot].initialized) continue;
+ for (int j=0; jpads[port][slot].numBindings; j++) {
+ Binding *b = dev->pads[port][slot].bindings+j;
+ int cmd = b->command;
+ int state = dev->virtualControlState[b->controlIndex];
+ if (!(turbo & b->turbo)) {
+ if (cmd > 0x0F && cmd != 0x28) {
+ ProcessButtonBinding(b, &s[port][slot], state);
+ }
+ else if ((state>>15) && !(dev->oldVirtualControlState[b->controlIndex]>>15)) {
+ if (cmd == 0x0F) {
+ miceEnabled = !miceEnabled;
+ UpdateEnabledDevices();
+ }
+ else if (cmd == 0x0C) {
+ lockStateChanged[port][slot] |= LOCK_BUTTONS;
+ }
+ else if (cmd == 0x0E) {
+ lockStateChanged[port][slot] |= LOCK_DIRECTION;
+ }
+ else if (cmd == 0x0D) {
+ lockStateChanged[port][slot] |= LOCK_BOTH;
+ }
+ else if (cmd == 0x28) {
+ if (!pads[port][slot].modeLock) {
+ if (pads[port][slot].mode != MODE_DIGITAL)
+ pads[port][slot].mode = MODE_DIGITAL;
+ else
+ pads[port][slot].mode = MODE_ANALOG;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ dm->PostRead();
+
+ {
+ for (int port=0; port<2; port++) {
+ for (int slot=0; slot<4; slot++) {
+ for (int motor=0; motor<2; motor++) {
+ // TODO: Probably be better to send all of these at once.
+ if (pads[port][slot].nextVibrate[motor] | pads[port][slot].currentVibrate[motor]) {
+ pads[port][slot].currentVibrate[motor] = pads[port][slot].nextVibrate[motor];
+ dm->SetEffect(port,slot, motor, pads[port][slot].nextVibrate[motor]);
+ }
+ }
+ }
+ }
+
+ for (int port=0; port<2; port++) {
+ for (int slot=0; slot<4; slot++) {
+ pads[port][slot].stateUpdated = 1;
+ if (config.padConfigs[port][slot].type == DisabledPad || !pads[port][slot].initialized) continue;
+ if (config.padConfigs[port][slot].type == GuitarPad) {
+ if (!config.GH2) {
+ s[port][slot].sticks[1].vert = -s[port][slot].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[port][slot].buttons[id];
+ s[port][slot].buttons[id] = 0;
+ }
+ s[port][slot].buttons[ID_TRIANGLE-0x1104] = values[1];
+ for (i=0; i<5; i++) {
+ int id = idList[i] - 0x1104;
+ s[port][slot].buttons[id] = values[i];
+ }
+ if (abs(s[port][slot].sticks[0].vert) <= 48) {
+ for (int i=0; i<5; i++) {
+ unsigned int id = idList[i] - 0x1104;
+ if (pads[port][slot].sum.buttons[id] < s[port][slot].buttons[id]) {
+ s[port][slot].buttons[id] = pads[port][slot].sum.buttons[id];
+ }
+ }
+ }
+ else if (abs(pads[port][slot].sum.sticks[0].vert) <= 48) {
+ for (int i=0; i<5; i++) {
+ unsigned int id = idList[i] - 0x1104;
+ if (pads[port][slot].sum.buttons[id]) {
+ s[port][slot].buttons[id] = 0;
+ }
+ }
+ }
+ }
+ }
+
+ if (pads[port][slot].mode == 0x41) {
+ for (int i=1; i<=2; i++) {
+ if (abs(s[port][slot].sticks[i].horiz) >= 100)
+ s[port][slot].sticks[0].horiz += s[port][slot].sticks[i].horiz;
+ if (abs(s[port][slot].sticks[i].vert) >= 100)
+ s[port][slot].sticks[0].vert += s[port][slot].sticks[i].vert;
+ }
+ }
+
+ CapSum(&s[port][slot]);
+ if (lockStateChanged[port][slot]) {
+ if (lockStateChanged[port][slot] & LOCK_BOTH) {
+ if (pads[port][slot].lockedState != (LOCK_DIRECTION | LOCK_BUTTONS)) {
+ // Enable the one that's not enabled.
+ lockStateChanged[port][slot] ^= pads[port][slot].lockedState^(LOCK_DIRECTION | LOCK_BUTTONS);
+ }
+ else {
+ // Disable both
+ lockStateChanged[port][slot] ^= LOCK_DIRECTION | LOCK_BUTTONS;
+ }
+ }
+ if (lockStateChanged[port][slot] & LOCK_DIRECTION) {
+ if (pads[port][slot].lockedState & LOCK_DIRECTION) {
+ memset(pads[port][slot].lockedSum.sticks, 0, sizeof(pads[port][slot].lockedSum.sticks));
+ }
+ else {
+ memcpy(pads[port][slot].lockedSum.sticks, s[port][slot].sticks, sizeof(pads[port][slot].lockedSum.sticks));
+ }
+ pads[port][slot].lockedState ^= LOCK_DIRECTION;
+ }
+ if (lockStateChanged[port][slot] & LOCK_BUTTONS) {
+ if (pads[port][slot].lockedState & LOCK_BUTTONS) {
+ memset(pads[port][slot].lockedSum.buttons, 0, sizeof(pads[port][slot].lockedSum.buttons));
+ }
+ else {
+ memcpy(pads[port][slot].lockedSum.buttons, s[port][slot].buttons, sizeof(pads[port][slot].lockedSum.buttons));
+ }
+ pads[port][slot].lockedState ^= LOCK_BUTTONS;
+ }
+ for (i=0; i>1].sum = s[i&1][i>>1];
+ }
+
+ padReadKeyUpdated[0] = padReadKeyUpdated[1] = padReadKeyUpdated[2] = 1;
+
+ if( stateUpdated[0] > 0 )
+ --stateUpdated[0];
+}
+
+void CALLBACK PADupdate(int port) {
+ Update(port+3, 0);
+}
+
+inline void SetVibrate(int port, int slot, int motor, u8 val) {
+ pads[port][slot].nextVibrate[motor] = val;
+}
+
+u32 CALLBACK PS2EgetLibType(void) {
+ ps2e = 1;
+ return PS2E_LT_PAD;
+}
+
+u32 CALLBACK PS2EgetLibVersion2(u32 type) {
+ ps2e = 1;
+ if (type == PS2E_LT_PAD)
+ return (PS2E_PAD_VERSION<<16) | VERSION;
+ return 0;
+}
+
+char* CALLBACK PSEgetLibName() {
+#if defined(PCSX2_DEBUG)
+ static char version[50];
+ sprintf(version, "LilyPad Debug (%lld)", SVN_REV);
+ return version;
+#else
+ static char version[50];
+ sprintf(version, "LilyPad (%lld)", SVN_REV);
+ return version;
+#endif
+}
+
+char* CALLBACK PS2EgetLibName(void) {
+ ps2e = 1;
+ return PSEgetLibName();
+}
+
+//void CALLBACK PADgsDriverInfo(GSdriverInfo *info) {
+// info=info;
+//}
+
+void CALLBACK PADshutdown() {
+ DEBUG_TEXT_OUT("LilyPad shutdown.\n\n");
+ for (int i=0; i<8; i++)
+ pads[i&1][i>>1].initialized = 0;
+ portInitialized[0] = portInitialized[1] = 0;
+ UnloadConfigs();
+}
+
+inline void StopVibrate() {
+ for (int i=0; i<8; i++) {
+ SetVibrate(i&1, i>>1, 0, 0);
+ SetVibrate(i&1, i>>1, 1, 0);
+ }
+}
+
+inline void ResetVibrate(int port, int slot) {
+ SetVibrate(port, slot, 0, 0);
+ SetVibrate(port, slot, 1, 0);
+ ((int*)(pads[port][slot].vibrate))[0] = 0xFFFFFF5A;
+ ((int*)(pads[port][slot].vibrate))[1] = 0xFFFFFFFF;
+}
+
+void ResetPad(int port, int slot) {
+ // Lines before memset currently don't do anything useful,
+ // but allow this function to be called at any time.
+
+ // Need to backup, so can be called at any point.
+ u8 enabled = pads[port][slot].enabled;
+
+ // Currently should never do anything.
+ SetVibrate(port, slot, 0, 0);
+ SetVibrate(port, slot, 1, 0);
+
+ memset(&pads[port][slot], 0, sizeof(pads[0][0]));
+ pads[port][slot].mode = MODE_DIGITAL;
+ pads[port][slot].umask[0] = pads[port][slot].umask[1] = 0xFF;
+ // Sets up vibrate variable.
+ ResetVibrate(port, slot);
+ if (config.padConfigs[port][slot].autoAnalog && !ps2e) {
+ pads[port][slot].mode = MODE_ANALOG;
+ }
+ pads[port][slot].initialized = 1;
+
+ pads[port][slot].enabled = enabled;
+}
+
+
+struct QueryInfo {
+ u8 port;
+ u8 slot;
+ u8 lastByte;
+ u8 currentCommand;
+ u8 numBytes;
+ u8 queryDone;
+ u8 response[42];
+} query = {0,0,0,0, 0,0xFF, 0xF3};
+
+s32 CALLBACK PADinit(u32 flags) {
+ // Note: Won't load settings if already loaded.
+ if (LoadSettings() < 0) {
+ return -1;
+ }
+ int port = (flags & 3);
+ if (port == 3) {
+ if (PADinit(1) == -1) return -1;
+ return PADinit(2);
+ }
+
+ #if defined(PCSX2_DEBUG) && defined(_MSC_VER)
+ int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
+ tmpFlag |= _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF;
+ _CrtSetDbgFlag( tmpFlag );
+ #endif
+
+ port --;
+
+ for (int i=0; i<4; i++) {
+ ResetPad(port, i);
+ }
+ slots[port] = 0;
+ portInitialized[port] = 1;
+
+ query.lastByte = 1;
+ query.numBytes = 0;
+ ClearKeyQueue();
+ // Just in case, when resuming emulation.
+ ReleaseModifierKeys();
+
+ DEBUG_TEXT_OUT("LilyPad initialized\n\n");
+ 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};
+
+void CALLBACK PADconfigure() {
+ if (openCount) {
+ return;
+ }
+ Configure();
+}
+
+s32 CALLBACK PADopen(void *pDsp) {
+ if (openCount++) return 0;
+ DEBUG_TEXT_OUT("LilyPad opened\n\n");
+
+ miceEnabled = !config.mouseUnfocus;
+
+ for (int port=0; port<2; port++) {
+ for (int slot=0; slot<4; slot++) {
+ memset(&pads[port][slot].sum, 0, sizeof(pads[port][slot].sum));
+ memset(&pads[port][slot].lockedSum, 0, sizeof(pads[port][slot].lockedSum));
+ pads[port][slot].lockedState = 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));
+ activeWindow = 1;
+ UpdateEnabledDevices();
+ return 0;
+}
+
+void CALLBACK PADclose() {
+ if (openCount && !--openCount) {
+ DEBUG_TEXT_OUT("LilyPad closed\n\n");
+ updateQueued = 0;
+ ClearKeyQueue();
+ }
+}
+
+u8 CALLBACK PADstartPoll(int port) {
+ DEBUG_NEW_SET();
+ port--;
+ if ((unsigned int)port <= 1 && pads[port][slots[port]].enabled) {
+ query.queryDone = 0;
+ query.port = port;
+ query.slot = slots[port];
+ query.numBytes = 2;
+ query.lastByte = 0;
+ DEBUG_IN(port);
+ DEBUG_OUT(0xFF);
+ DEBUG_IN(slots[port]);
+ DEBUG_OUT(pads[port][slots[port]].enabled);
+ return 0xFF;
+ }
+ else {
+ query.queryDone = 1;
+ query.numBytes = 0;
+ query.lastByte = 1;
+ DEBUG_IN(0);
+ DEBUG_OUT(0);
+ DEBUG_IN(port);
+ DEBUG_OUT(0);
+ return 0;
+ }
+}
+
+inline int IsDualshock2(u8 port, u8 slot) {
+ return config.padConfigs[query.port][query.slot].type == Dualshock2Pad ||
+ (config.padConfigs[query.port][query.slot].type == GuitarPad && config.GH2);
+}
+
+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.port][query.slot];
+ 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;
+ {
+ Update(query.port, query.slot);
+ ButtonSum *sum = &pad->sum;
+
+ u8 b1 = 0xFF, b2 = 0xFF;
+ for (i = 0; i<4; i++) {
+ b1 -= (sum->buttons[i] > 0) << i;
+ }
+ for (i = 0; i<8; i++) {
+ b2 -= (sum->buttons[i+4] > 0) << i;
+ }
+ if (config.padConfigs[query.port][query.slot].type == GuitarPad && !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 < 0) << 4);
+ b1 -= ((sum->sticks[0].horiz > 0) << 5);
+ b1 -= ((sum->sticks[0].vert > 0) << 6);
+ b1 -= ((sum->sticks[0].horiz < 0) << 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:
+ // Right? Wrong? No clue.
+ 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(query.port, query.slot);
+ break;
+ // QUERY_MODEL_AND_MODE
+ case 0x45:
+ if (IsDualshock2(query.port, query.slot)) {
+ SET_FINAL_RESULT(queryModelDS2)
+ }
+ else {
+ SET_FINAL_RESULT(queryModelDS1);
+ }
+ // Not digital mode.
+ query.response[5] = (pad->mode & 0xF) != 1;
+ 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(query.port, query.slot);
+ break;
+ // SET_DS2_NATIVE_MODE
+ case 0x4F:
+ if (IsDualshock2(query.port, query.slot)) {
+ SET_RESULT(setNativeMode);
+ }
+ else {
+ SET_FINAL_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(query.port, query.slot, 1, 255*(0!=value));
+ }
+ else if (query.lastByte == pad->vibrateI[1]) {
+ SetVibrate(query.port, query.slot, 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.padConfigs[query.port][query.slot].autoAnalog && !ps2e) {
+ 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;
+ // SET_DS2_NATIVE_MODE
+ 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 PADabout() {
+}
+
+s32 CALLBACK PADtest() {
+ return 0;
+}
+
+keyEvent* CALLBACK PADkeyEvent() {
+ // If running both pads, ignore every other call. So if two keys pressed in same interval...
+ static char eventCount = 0;
+ eventCount++;
+ if (eventCount < openCount) {
+ return 0;
+ }
+ eventCount = 0;
+
+ Update(2, 0);
+ static keyEvent ev;
+ if (!GetQueuedKeyEvent(&ev)) return 0;
+
+ return &ev;
+}
+
+struct PadPluginFreezeData {
+ char format[8];
+ // Currently all different versions are incompatible.
+ // May split into major/minor with some compatibility rules.
+ u32 version;
+ // So when loading, know which plugin's settings I'm loading.
+ // Not a big deal. Use a static variable when saving to figure it out.
+ u8 port;
+ // active slot for port
+ u8 slot[2];
+ PadFreezeData padData[2][4];
+ QueryInfo query;
+};
+
+s32 CALLBACK PADfreeze(int mode, freezeData *data) {
+ if (!data)
+ {
+ printf("LilyPad savestate null pointer!\n");
+ return -1;
+ }
+
+ if (mode == FREEZE_SIZE) {
+ data->size = sizeof(PadPluginFreezeData);
+ }
+ else if (mode == FREEZE_LOAD) {
+ PadPluginFreezeData &pdata = *(PadPluginFreezeData*)(data->data);
+ StopVibrate();
+ if (data->size != sizeof(PadPluginFreezeData) ||
+ pdata.version != PAD_SAVE_STATE_VERSION ||
+ strcmp(pdata.format, "PadMode")) return 0;
+
+ if( pdata.port >= 2 ) return 0;
+
+ query = pdata.query;
+ if (pdata.query.slot < 4) {
+ query = pdata.query;
+ }
+
+ // Tales of the Abyss - pad fix
+ // - restore data for both ports
+ for (int port=0; port<2; port++) {
+ for (int slot=0; slot<4; slot++) {
+ u8 mode = pdata.padData[port][slot].mode;
+
+ if (mode != MODE_DIGITAL && mode != MODE_ANALOG && mode != MODE_DS2_NATIVE) {
+ break;
+ }
+
+ // Not sure if the cast is strictly necessary, but feel safest with it there...
+ *(PadFreezeData*)&pads[port][slot] = pdata.padData[port][slot];
+ }
+
+ if (pdata.slot[port] < 4)
+ slots[port] = pdata.slot[port];
+ }
+ }
+ else if (mode == FREEZE_SAVE) {
+ if (data->size != sizeof(PadPluginFreezeData)) return 0;
+ PadPluginFreezeData &pdata = *(PadPluginFreezeData*)(data->data);
+
+
+ // Tales of the Abyss - pad fix
+ // - PCSX2 only saves port0 (save #1), then port1 (save #2)
+
+ memset(&pdata, 0, sizeof(pdata));
+ strcpy(pdata.format, "PadMode");
+ pdata.version = PAD_SAVE_STATE_VERSION;
+ pdata.port = 0;
+ pdata.query = query;
+
+ for (int port=0; port<2; port++) {
+ for (int slot=0; slot<4; slot++) {
+ pdata.padData[port][slot] = pads[port][slot];
+ }
+
+ pdata.slot[port] = slots[port];
+ }
+ }
+ else return -1;
+ return 0;
+}
+
+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);
+}
+
+s32 CALLBACK PADqueryMtap(u8 port) {
+ port--;
+ if (port > 1) return 0;
+ return config.multitap[port];
+}
+
+s32 CALLBACK PADsetSlot(u8 port, u8 slot) {
+ port--;
+ slot--;
+ if (port > 1 || slot > 3) {
+ return 0;
+ }
+ // Even if no pad there, record the slot, as it is the active slot regardless.
+ slots[port] = slot;
+ // First slot always allowed.
+ // return pads[port][slot].enabled | !slot;
+ return 1;
+}
+
+// Little funkiness to handle rounding floating points to ints without the C runtime.
+// Unfortunately, means I can't use /GL optimization option when NO_CRT is defined.
+#ifdef NO_CRT
+extern "C" long _cdecl _ftol();
+extern "C" long _cdecl _ftol2_sse() {
+ return _ftol();
+}
+extern "C" long _cdecl _ftol2() {
+ return _ftol();
+}
+#endif
+