PAD: Add platform independent implementation

This commit is contained in:
Connor McLaughlin 2021-12-19 00:34:42 +10:00 committed by refractionpcsx2
parent 935f880fa7
commit 6ee029e14b
7 changed files with 1150 additions and 0 deletions

65
pcsx2/PAD/Host/Global.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdio.h>
#include <assert.h>
#include <array>
#include <vector>
#include <map>
#include <string>
#include <memory>
#include <mutex>
#include <queue>
#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));
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<u8>(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<u8, MAX_KEYS> 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];
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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;

220
pcsx2/PAD/Host/PAD.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<std::string> PAD::GetControllerTypeNames()
{
return {"DualShock2"};
}
std::vector<std::string> 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);
}

49
pcsx2/PAD/Host/PAD.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include <vector>
#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<std::string> GetControllerTypeNames();
/// Returns the list of binds for the specified controller type.
std::vector<std::string> 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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "PAD/Host/StateManagement.h"
#include "PAD/Host/KeyStatus.h"
template <class T>
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];
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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 <size_t S>
void set_result(const u8 (&rsp)[S])
{
memcpy(response + 2, rsp, S);
numBytes = 2 + S;
}
template <size_t S>
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);