From 6ee029e14b31245b66c3bc00aae604876ff86b95 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sun, 19 Dec 2021 00:34:42 +1000 Subject: [PATCH] PAD: Add platform independent implementation --- pcsx2/PAD/Host/Global.h | 65 ++++ pcsx2/PAD/Host/KeyStatus.cpp | 149 +++++++++ pcsx2/PAD/Host/KeyStatus.h | 48 +++ pcsx2/PAD/Host/PAD.cpp | 220 +++++++++++++ pcsx2/PAD/Host/PAD.h | 49 +++ pcsx2/PAD/Host/StateManagement.cpp | 488 +++++++++++++++++++++++++++++ pcsx2/PAD/Host/StateManagement.h | 131 ++++++++ 7 files changed, 1150 insertions(+) create mode 100644 pcsx2/PAD/Host/Global.h create mode 100644 pcsx2/PAD/Host/KeyStatus.cpp create mode 100644 pcsx2/PAD/Host/KeyStatus.h create mode 100644 pcsx2/PAD/Host/PAD.cpp create mode 100644 pcsx2/PAD/Host/PAD.h create mode 100644 pcsx2/PAD/Host/StateManagement.cpp create mode 100644 pcsx2/PAD/Host/StateManagement.h diff --git a/pcsx2/PAD/Host/Global.h b/pcsx2/PAD/Host/Global.h new file mode 100644 index 0000000000..e3f94343d8 --- /dev/null +++ b/pcsx2/PAD/Host/Global.h @@ -0,0 +1,65 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 PCSX2 Dev Team + * + * 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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/Pcsx2Defs.h" + +static const u32 GAMEPAD_NUMBER = 2; +static const u32 MAX_KEYS = 24; + +enum gamePadValues +{ + PAD_UP, // Directional pad ↑ + PAD_RIGHT, // Directional pad → + PAD_DOWN, // Directional pad ↓ + PAD_LEFT, // Directional pad ← + PAD_TRIANGLE, // Triangle button ▲ + PAD_CIRCLE, // Circle button ● + PAD_CROSS, // Cross button ✖ + PAD_SQUARE, // Square button ■ + PAD_SELECT, // Select button + PAD_START, // Start button + PAD_L1, // L1 button + PAD_L2, // L2 button + PAD_R1, // R1 button + PAD_R2, // R2 button + PAD_L3, // Left joystick button (L3) + PAD_R3, // Right joystick button (R3) + PAD_L_UP, // Left joystick (Up) ↑ + PAD_L_RIGHT, // Left joystick (Right) → + PAD_L_DOWN, // Left joystick (Down) ↓ + PAD_L_LEFT, // Left joystick (Left) ← + PAD_R_UP, // Right joystick (Up) ↑ + PAD_R_RIGHT, // Right joystick (Right) → + PAD_R_DOWN, // Right joystick (Down) ↓ + PAD_R_LEFT // Right joystick (Left) ← +}; + +static bool IsAnalogKey(int index) +{ + return ((index >= PAD_L_UP) && (index <= PAD_R_LEFT)); +} + diff --git a/pcsx2/PAD/Host/KeyStatus.cpp b/pcsx2/PAD/Host/KeyStatus.cpp new file mode 100644 index 0000000000..e94d70f30f --- /dev/null +++ b/pcsx2/PAD/Host/KeyStatus.cpp @@ -0,0 +1,149 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 PCSX2 Dev Team + * + * 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 "PrecompiledHeader.h" + +#include "PAD/Host/KeyStatus.h" + +KeyStatus::KeyStatus() +{ + Init(); + + for (u32 pad = 0; pad < GAMEPAD_NUMBER; pad++) + { + m_axis_scale[pad] = 1.0f; + } +} + +void KeyStatus::Init() +{ + for (u32 pad = 0; pad < GAMEPAD_NUMBER; pad++) + { + m_button[pad] = 0xFFFF; + + for (u32 index = 0; index < MAX_KEYS; index++) + m_button_pressure[pad][index] = 0; + + m_analog[pad].lx = m_analog_released_val; + m_analog[pad].ly = m_analog_released_val; + m_analog[pad].rx = m_analog_released_val; + m_analog[pad].ry = m_analog_released_val; + } +} + +void KeyStatus::Set(u32 pad, u32 index, float value) +{ + m_button_pressure[pad][index] = static_cast(std::clamp(value * m_axis_scale[pad] * 255.0f, 0.0f, 255.0f)); + + if (IsAnalogKey(index)) + { + // Left -> -- -> Right + // Value range : FFFF8002 -> 0 -> 7FFE + // Force range : 80 -> 0 -> 7F + // Normal mode : expect value 0 -> 80 -> FF + // Reverse mode: expect value FF -> 7F -> 0 + + // merge left/right or up/down into rx or ry + +#define MERGE(pad, pos, neg) ((m_button_pressure[pad][pos] != 0) ? (127u + ((m_button_pressure[pad][pos] + 1u) / 2u)) : (127u - (m_button_pressure[pad][neg] / 2u))) + + switch (index) + { + case PAD_R_LEFT: + case PAD_R_RIGHT: + m_analog[pad].rx = MERGE(pad, PAD_R_RIGHT, PAD_R_LEFT); + break; + + case PAD_R_DOWN: + case PAD_R_UP: + m_analog[pad].ry = MERGE(pad, PAD_R_DOWN, PAD_R_UP); + break; + + case PAD_L_LEFT: + case PAD_L_RIGHT: + m_analog[pad].lx = MERGE(pad, PAD_L_RIGHT, PAD_L_LEFT); + break; + + case PAD_L_DOWN: + case PAD_L_UP: + m_analog[pad].ly = MERGE(pad, PAD_L_DOWN, PAD_L_UP); + break; + + default: + break; + } + +#undef MERGE + } + else + { + // Since we reordered the buttons for better UI, we need to remap them here. + static constexpr std::array bitmask_mapping = { { + 12, // PAD_UP + 13, // PAD_RIGHT + 14, // PAD_DOWN + 15, // PAD_LEFT + 4, // PAD_TRIANGLE + 5, // PAD_CIRCLE + 6, // PAD_CROSS + 7, // PAD_SQUARE + 8, // PAD_SELECT + 11, // PAD_START + 2, // PAD_L1 + 0, // PAD_L2 + 3, // PAD_R1 + 1, // PAD_R2 + 9, // PAD_L3 + 10, // PAD_R3 + // remainder are analogs and not used here + } }; + + // TODO: Deadzone here? + if (value > 0.0f) + m_button[pad] &= ~(1u << bitmask_mapping[index]); + else + m_button[pad] |= (1u << bitmask_mapping[index]); + } +} + +u16 KeyStatus::GetButtons(u32 pad) +{ + return m_button[pad]; +} + +u8 KeyStatus::GetPressure(u32 pad, u32 index) +{ + switch (index) + { + case PAD_R_LEFT: + case PAD_R_RIGHT: + return m_analog[pad].rx; + + case PAD_R_DOWN: + case PAD_R_UP: + return m_analog[pad].ry; + + case PAD_L_LEFT: + case PAD_L_RIGHT: + return m_analog[pad].lx; + + case PAD_L_DOWN: + case PAD_L_UP: + return m_analog[pad].ly; + + default: + return m_button_pressure[pad][index]; + } +} diff --git a/pcsx2/PAD/Host/KeyStatus.h b/pcsx2/PAD/Host/KeyStatus.h new file mode 100644 index 0000000000..ab3e59b2a0 --- /dev/null +++ b/pcsx2/PAD/Host/KeyStatus.h @@ -0,0 +1,48 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 PCSX2 Dev Team + * + * 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 . + */ + +#pragma once + +#include "PAD/Host/Global.h" + +class KeyStatus +{ +private: + static constexpr u8 m_analog_released_val = 0x7F; + + struct PADAnalog + { + u8 lx, ly; + u8 rx, ry; + }; + + u16 m_button[GAMEPAD_NUMBER]; + u8 m_button_pressure[GAMEPAD_NUMBER][MAX_KEYS]; + PADAnalog m_analog[GAMEPAD_NUMBER]; + float m_axis_scale[GAMEPAD_NUMBER]; + +public: + KeyStatus(); + void Init(); + + void Set(u32 pad, u32 index, float value); + + void SetAxisScale(u32 pad, float scale) { m_axis_scale[pad] = scale; } + + u16 GetButtons(u32 pad); + u8 GetPressure(u32 pad, u32 index); +}; + +extern KeyStatus g_key_status; diff --git a/pcsx2/PAD/Host/PAD.cpp b/pcsx2/PAD/Host/PAD.cpp new file mode 100644 index 0000000000..b911bec8b8 --- /dev/null +++ b/pcsx2/PAD/Host/PAD.cpp @@ -0,0 +1,220 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 PCSX2 Dev Team + * + * 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 "PrecompiledHeader.h" + +#include "common/StringUtil.h" +#include "common/SettingsInterface.h" + +#include "HostSettings.h" + +#include "PAD/Host/Global.h" +#include "PAD/Host/PAD.h" +#include "PAD/Host/KeyStatus.h" +#include "PAD/Host/StateManagement.h" + +const u32 revision = 3; +const u32 build = 0; // increase that with each version +#define PAD_SAVE_STATE_VERSION ((revision << 8) | (build << 0)) + +KeyStatus g_key_status; + +s32 PADinit() +{ + Pad::reset_all(); + + query.reset(); + + for (int port = 0; port < 2; port++) + slots[port] = 0; + + return 0; +} + +void PADshutdown() +{ +} + +s32 PADopen(const WindowInfo& wi) +{ + g_key_status.Init(); + return 0; +} + +void PADclose() +{ +} + +s32 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; + + return 1; +} + +s32 PADfreeze(FreezeAction mode, freezeData* data) +{ + if (!data) + return -1; + + if (mode == FreezeAction::Size) + { + data->size = sizeof(PadFullFreezeData); + } + else if (mode == FreezeAction::Load) + { + PadFullFreezeData* pdata = (PadFullFreezeData*)(data->data); + + Pad::stop_vibrate_all(); + + if (data->size != sizeof(PadFullFreezeData) || pdata->version != PAD_SAVE_STATE_VERSION || + strncmp(pdata->format, "LinPad", sizeof(pdata->format))) + 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; + } + + memcpy(&pads[port][slot], &pdata->padData[port][slot], sizeof(PadFreezeData)); + } + + if (pdata->slot[port] < 4) + slots[port] = pdata->slot[port]; + } + } + else if (mode == FreezeAction::Save) + { + if (data->size != sizeof(PadFullFreezeData)) + return 0; + + PadFullFreezeData* pdata = (PadFullFreezeData*)(data->data); + + // Tales of the Abyss - pad fix + // - PCSX2 only saves port0 (save #1), then port1 (save #2) + + memset(pdata, 0, data->size); + strncpy(pdata->format, "LinPad", sizeof(pdata->format)); + pdata->version = PAD_SAVE_STATE_VERSION; + 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; +} + +u8 PADstartPoll(int pad) +{ + return pad_start_poll(pad); +} + +u8 PADpoll(u8 value) +{ + return pad_poll(value); +} + +void PAD::LoadConfig(const SettingsInterface& si) +{ + // This is where we would load controller types, if onepad supported them. + + for (u32 i = 0; i < GAMEPAD_NUMBER; i++) + { + const std::string section(StringUtil::StdStringFromFormat("Pad%u", i + 1u)); + const float axis_scale = si.GetFloatValue(section.c_str(), "AxisScale", 1.0f); + + g_key_status.SetAxisScale(i, axis_scale); + } +} + +std::vector PAD::GetControllerTypeNames() +{ + return {"DualShock2"}; +} + +std::vector PAD::GetControllerBinds(const std::string_view& type) +{ + if (type == "DualShock2") + { + return { + "Up", + "Right", + "Down", + "Left", + "Triangle", + "Circle", + "Cross", + "Square", + "Select", + "Start", + "L1", + "L2", + "R1", + "R2", + "L3", + "R3", + "LUp", + "LRight", + "LDown", + "LLeft", + "RUp", + "RRight", + "RDown", + "RLeft"}; + } + + return {}; +} + +void PAD::SetControllerState(u32 controller, u32 bind, float value) +{ + if (controller >= GAMEPAD_NUMBER || bind >= MAX_KEYS) + return; + + g_key_status.Set(controller, bind, value); +} diff --git a/pcsx2/PAD/Host/PAD.h b/pcsx2/PAD/Host/PAD.h new file mode 100644 index 0000000000..0d62f31542 --- /dev/null +++ b/pcsx2/PAD/Host/PAD.h @@ -0,0 +1,49 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 PCSX2 Dev Team + * + * 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 . + */ + +#pragma once + +#include +#include + +#include "PAD/Host/Global.h" +#include "SaveState.h" + +class SettingsInterface; +struct WindowInfo; + +s32 PADinit(); +void PADshutdown(); +s32 PADopen(const WindowInfo& wi); +void PADclose(); +s32 PADsetSlot(u8 port, u8 slot); +s32 PADfreeze(FreezeAction mode, freezeData* data); +u8 PADstartPoll(int pad); +u8 PADpoll(u8 value); + +namespace PAD +{ + /// Reloads configuration. + void LoadConfig(const SettingsInterface& si); + + /// Returns a list of controller type names. + std::vector GetControllerTypeNames(); + + /// Returns the list of binds for the specified controller type. + std::vector GetControllerBinds(const std::string_view& type); + + /// Sets the specified bind on a controller to the specified pressure (normalized to 0..1). + void SetControllerState(u32 controller, u32 bind, float value); +} // namespace PAD diff --git a/pcsx2/PAD/Host/StateManagement.cpp b/pcsx2/PAD/Host/StateManagement.cpp new file mode 100644 index 0000000000..62e27d9e34 --- /dev/null +++ b/pcsx2/PAD/Host/StateManagement.cpp @@ -0,0 +1,488 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 PCSX2 Dev Team + * + * 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 "PrecompiledHeader.h" + +#include "PAD/Host/StateManagement.h" +#include "PAD/Host/KeyStatus.h" + +template +static bool __fi test_bit(T& value, int bit) +{ + return (value & (1 << bit)); +} + +// Typical packet response on the bus +static const u8 ConfigExit[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static const u8 noclue[7] = {0x5A, 0x00, 0x00, 0x02, 0x00, 0x00, 0x5A}; +static const u8 setMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static const u8 queryModelDS2[7] = {0x5A, 0x03, 0x02, 0x00, 0x02, 0x01, 0x00}; +static const u8 queryModelDS1[7] = {0x5A, 0x01, 0x02, 0x00, 0x02, 0x01, 0x00}; +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}; + +static u8 queryMaskMode[7] = {0x5A, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x5A}; + +static const u8 queryAct[2][7] = { + {0x5A, 0x00, 0x00, 0x01, 0x02, 0x00, 0x0A}, + {0x5A, 0x00, 0x00, 0x01, 0x01, 0x01, 0x14}}; + +QueryInfo query; +Pad pads[2][4]; +int slots[2] = {0, 0}; + +////////////////////////////////////////////////////////////////////// +// QueryInfo implementation +////////////////////////////////////////////////////////////////////// + +void QueryInfo::reset() +{ + port = 0; + slot = 0; + lastByte = 1; + currentCommand = 0; + numBytes = 0; + queryDone = 1; + memset(response, 0xF3, sizeof(response)); +} + +u8 QueryInfo::start_poll(int _port) +{ + if (port > 1) + { + reset(); + return 0; + } + + queryDone = 0; + port = _port; + slot = slots[port]; + numBytes = 2; + lastByte = 0; + + return 0xFF; +} + +////////////////////////////////////////////////////////////////////// +// Pad implementation +////////////////////////////////////////////////////////////////////// + +void Pad::set_mode(int _mode) +{ + mode = _mode; +} + +void Pad::set_vibrate(int motor, u8 val) +{ + nextVibrate[motor] = val; +} + +void Pad::reset_vibrate() +{ + set_vibrate(0, 0); + set_vibrate(1, 0); + memset(vibrate, 0xFF, sizeof(vibrate)); + vibrate[0] = 0x5A; +} + +void Pad::reset() +{ + memset(this, 0, sizeof(PadFreezeData)); + + set_mode(MODE_DIGITAL); + umask[0] = 0xFF; + umask[1] = 0xFF; + umask[2] = 0x03; + + // Sets up vibrate variable. + reset_vibrate(); +} + +void Pad::rumble(unsigned port) +{ + for (unsigned motor = 0; motor < 2; motor++) + { + // TODO: Probably be better to send all of these at once. + if (nextVibrate[motor] | currentVibrate[motor]) + { + currentVibrate[motor] = nextVibrate[motor]; + + // TODO: Implement in InputManager + // Device::DoRumble(motor, port); + } + } +} + +void Pad::stop_vibrate_all() +{ +#if 0 + for (int i=0; i<8; i++) { + SetVibrate(i&1, i>>1, 0, 0); + SetVibrate(i&1, i>>1, 1, 0); + } +#endif + // FIXME equivalent ? + for (int port = 0; port < 2; port++) + for (int slot = 0; slot < 4; slot++) + pads[port][slot].reset_vibrate(); +} + +void Pad::reset_all() +{ + for (int port = 0; port < 2; port++) + for (int slot = 0; slot < 4; slot++) + pads[port][slot].reset(); +} + +void Pad::rumble_all() +{ + for (unsigned port = 0; port < 2; port++) + for (unsigned slot = 0; slot < 4; slot++) + pads[port][slot].rumble(port); +} + +////////////////////////////////////////////////////////////////////// +// Pad implementation +////////////////////////////////////////////////////////////////////// + +inline bool IsDualshock2() +{ +// FIXME +#if 0 + return config.padConfigs[query.port][query.slot].type == Dualshock2Pad || + (config.padConfigs[query.port][query.slot].type == GuitarPad && config.GH2); +#else + return true; +#endif +} + +u8 pad_start_poll(u8 pad) +{ + return query.start_poll(pad - 1); +} + +u8 pad_poll(u8 value) +{ + if (query.lastByte + 1 >= query.numBytes) + { + return 0; + } + if (query.lastByte && query.queryDone) + { + return query.response[++query.lastByte]; + } + + Pad* pad = &pads[query.port][query.slot]; + + if (query.lastByte == 0) + { + query.lastByte++; + query.currentCommand = value; + + switch (value) + { + case CMD_CONFIG_MODE: + if (pad->config) + { + // In config mode. Might not actually be leaving it. + query.set_result(ConfigExit); + return 0xF3; + } + [[fallthrough]]; // fallthrough on purpose (but I don't know why) + + case CMD_READ_DATA_AND_VIBRATE: + { + query.response[2] = 0x5A; +#if 0 + int i; + 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; + } +#endif + +// FIXME +#if 0 + if (config.padConfigs[query.port][query.slot].type == GuitarPad && !config.GH2) { + sum->buttons[15] = 255; + // Not sure about this. Forces wammy to be from 0 to 0x7F. + // if (sum->sticks[2].vert > 0) sum->sticks[2].vert = 0; + } +#endif + +#if 0 + for (i = 4; i<8; i++) { + b1 -= (sum->buttons[i+8] > 0) << i; + } +#endif + +// FIXME +#if 0 + //Left, Right and Down are always pressed on Pop'n Music controller. + if (config.padConfigs[query.port][query.slot].type == PopnPad) + b1=b1 & 0x1f; +#endif + + uint16_t buttons = g_key_status.GetButtons(query.port); + + query.numBytes = 5; + + query.response[3] = (buttons >> 8) & 0xFF; + query.response[4] = (buttons >> 0) & 0xFF; + + if (pad->mode != MODE_DIGITAL) + { // ANALOG || DS2 native + query.numBytes = 9; + + query.response[5] = g_key_status.GetPressure(query.port, PAD_R_RIGHT); + query.response[6] = g_key_status.GetPressure(query.port, PAD_R_UP); + query.response[7] = g_key_status.GetPressure(query.port, PAD_L_RIGHT); + query.response[8] = g_key_status.GetPressure(query.port, PAD_L_UP); + + if (pad->mode != MODE_ANALOG) + { // DS2 native + query.numBytes = 21; + + query.response[9] = !test_bit(buttons, 13) ? g_key_status.GetPressure(query.port, PAD_RIGHT) : 0; + query.response[10] = !test_bit(buttons, 15) ? g_key_status.GetPressure(query.port, PAD_LEFT) : 0; + query.response[11] = !test_bit(buttons, 12) ? g_key_status.GetPressure(query.port, PAD_UP) : 0; + query.response[12] = !test_bit(buttons, 14) ? g_key_status.GetPressure(query.port, PAD_DOWN) : 0; + + query.response[13] = !test_bit(buttons, 4) ? g_key_status.GetPressure(query.port, PAD_TRIANGLE) : 0; + query.response[14] = !test_bit(buttons, 5) ? g_key_status.GetPressure(query.port, PAD_CIRCLE) : 0; + query.response[15] = !test_bit(buttons, 6) ? g_key_status.GetPressure(query.port, PAD_CROSS) : 0; + query.response[16] = !test_bit(buttons, 7) ? g_key_status.GetPressure(query.port, PAD_SQUARE) : 0; + query.response[17] = !test_bit(buttons, 2) ? g_key_status.GetPressure(query.port, PAD_L1) : 0; + query.response[18] = !test_bit(buttons, 3) ? g_key_status.GetPressure(query.port, PAD_R1) : 0; + query.response[19] = !test_bit(buttons, 0) ? g_key_status.GetPressure(query.port, PAD_L2) : 0; + query.response[20] = !test_bit(buttons, 1) ? g_key_status.GetPressure(query.port, PAD_R2) : 0; + } + } + +#if 0 + query.response[3] = b1; + query.response[4] = b2; + + query.numBytes = 5; + if (pad->mode != MODE_DIGITAL) { + query.response[5] = Cap((sum->sticks[0].horiz+255)/2); + query.response[6] = Cap((sum->sticks[0].vert+255)/2); + query.response[7] = Cap((sum->sticks[1].horiz+255)/2); + query.response[8] = Cap((sum->sticks[1].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]; + + // No need to cap these, already done int CapSum(). + query.response[9] = (unsigned char)sum->buttons[13]; //D-pad right + query.response[10] = (unsigned char)sum->buttons[15]; //D-pad left + query.response[11] = (unsigned char)sum->buttons[12]; //D-pad up + query.response[12] = (unsigned char)sum->buttons[14]; //D-pad down + + 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; + } + } +#endif + } + + query.lastByte = 1; + return pad->mode; + + case CMD_SET_VREF_PARAM: + query.set_final_result(noclue); + break; + + case CMD_QUERY_DS2_ANALOG_MODE: + // 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] = pad->umask[2]; + // Not entirely sure about this. + //queryMaskMode[3] = 0x01 | (pad->mode == MODE_DS2_NATIVE)*2; + queryMaskMode[6] = 0x5A; + } + query.set_final_result(queryMaskMode); + break; + + case CMD_SET_MODE_AND_LOCK: + query.set_result(setMode); + pad->reset_vibrate(); + break; + + case CMD_QUERY_MODEL_AND_MODE: + if (IsDualshock2()) + { + query.set_final_result(queryModelDS2); + } + else + { + query.set_final_result(queryModelDS1); + } + // Not digital mode. + query.response[5] = (pad->mode & 0xF) != 1; + break; + + case CMD_QUERY_ACT: + query.set_result(queryAct[0]); + break; + + case CMD_QUERY_COMB: + query.set_final_result(queryComb); + break; + + case CMD_QUERY_MODE: + query.set_result(queryMode); + break; + + case CMD_VIBRATION_TOGGLE: + memcpy(query.response + 2, pad->vibrate, 7); + query.numBytes = 9; + //query.set_result(pad->vibrate); // warning copy 7b not 8 (but it is really important?) + pad->reset_vibrate(); + break; + + case CMD_SET_DS2_NATIVE_MODE: + if (IsDualshock2()) + { + query.set_result(setNativeMode); + } + else + { + query.set_final_result(setNativeMode); + } + break; + + default: + query.numBytes = 0; + query.queryDone = 1; + break; + } + + return 0xF3; + } + else + { + query.lastByte++; + + switch (query.currentCommand) + { + case CMD_READ_DATA_AND_VIBRATE: + if (query.lastByte == pad->vibrateI[0]) + pad->set_vibrate(1, 255 * (value & 1)); + else if (query.lastByte == pad->vibrateI[1]) + pad->set_vibrate(0, value); + + break; + + case CMD_CONFIG_MODE: + if (query.lastByte == 3) + { + query.queryDone = 1; + pad->config = value; + } + break; + + case CMD_SET_MODE_AND_LOCK: + if (query.lastByte == 3 && value < 2) + { + pad->set_mode(value ? MODE_ANALOG : MODE_DIGITAL); + } + else if (query.lastByte == 4) + { + if (value == 3) + pad->modeLock = 3; + else + pad->modeLock = 0; + + query.queryDone = 1; + } + break; + + case CMD_QUERY_ACT: + if (query.lastByte == 3) + { + if (value < 2) + query.set_result(queryAct[value]); + // bunch of 0's + // else query.set_result(setMode); + query.queryDone = 1; + } + break; + + case CMD_QUERY_MODE: + if (query.lastByte == 3 && value < 2) + { + query.response[6] = 4 + value * 3; + query.queryDone = 1; + } + // bunch of 0's + //else data = setMode; + break; + + case CMD_VIBRATION_TOGGLE: + 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 CMD_SET_DS2_NATIVE_MODE: + if (query.lastByte > 2 && query.lastByte < 6) + { + pad->umask[query.lastByte - 3] = value; + } + pad->set_mode(MODE_DS2_NATIVE); + break; + + default: + return 0; + } + + return query.response[query.lastByte]; + } +} diff --git a/pcsx2/PAD/Host/StateManagement.h b/pcsx2/PAD/Host/StateManagement.h new file mode 100644 index 0000000000..997786779d --- /dev/null +++ b/pcsx2/PAD/Host/StateManagement.h @@ -0,0 +1,131 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 PCSX2 Dev Team + * + * 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 . + */ + +#pragma once + +#include "common/Pcsx2Defs.h" +#include "PAD/Host/Global.h" + +#define MODE_DIGITAL 0x41 +#define MODE_ANALOG 0x73 +#define MODE_DS2_NATIVE 0x79 + +enum PadCommands +{ + CMD_SET_VREF_PARAM = 0x40, + CMD_QUERY_DS2_ANALOG_MODE = 0x41, + CMD_READ_DATA_AND_VIBRATE = 0x42, + CMD_CONFIG_MODE = 0x43, + CMD_SET_MODE_AND_LOCK = 0x44, + CMD_QUERY_MODEL_AND_MODE = 0x45, + CMD_QUERY_ACT = 0x46, // ?? + CMD_QUERY_COMB = 0x47, // ?? + CMD_QUERY_MODE = 0x4C, // QUERY_MODE ?? + CMD_VIBRATION_TOGGLE = 0x4D, + CMD_SET_DS2_NATIVE_MODE = 0x4F // SET_DS2_NATIVE_MODE +}; + +// The state of the PS2 bus +struct QueryInfo +{ + u8 port; + u8 slot; + u8 lastByte; + u8 currentCommand; + u8 numBytes; + u8 queryDone; + u8 response[42]; + + void reset(); + u8 start_poll(int port); + + template + void set_result(const u8 (&rsp)[S]) + { + memcpy(response + 2, rsp, S); + numBytes = 2 + S; + } + + template + void set_final_result(const u8 (&rsp)[S]) + { + set_result(rsp); + queryDone = 1; + } +}; + +// Freeze data, for a single pad. Basically has all pad state that +// a PS2 can set. +struct PadFreezeData +{ + // Digital / Analog / DS2 Native + u8 mode; + + u8 modeLock; + + // In config mode + u8 config; + + u8 vibrate[8]; + u8 umask[3]; + + // Vibration indices. + u8 vibrateI[2]; + + // Last vibration value sent to controller. + // Only used so as not to call vibration + // functions when old and new values are both 0. + u8 currentVibrate[2]; + + // Next vibrate val to send to controller. If next and current are + // both 0, nothing is sent to the controller. Otherwise, it's sent + // on every update. + u8 nextVibrate[2]; +}; + +class Pad : public PadFreezeData +{ +public: + // Lilypad store here the state of PC pad + + void rumble(unsigned port); + void set_vibrate(int motor, u8 val); + void reset_vibrate(); + void reset(); + + void set_mode(int mode); + + static void reset_all(); + static void stop_vibrate_all(); + static void rumble_all(); +}; + +// Full state to manage save state +struct PadFullFreezeData +{ + char format[8]; + u32 version; + // active slot for port + u8 slot[2]; + PadFreezeData padData[2][4]; + QueryInfo query; +}; + +extern QueryInfo query; +extern Pad pads[2][4]; +extern int slots[2]; + +extern u8 pad_start_poll(u8 pad); +extern u8 pad_poll(u8 value);