mirror of https://github.com/PCSX2/pcsx2.git
PAD: Add platform independent implementation
This commit is contained in:
parent
935f880fa7
commit
6ee029e14b
|
@ -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));
|
||||
}
|
||||
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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);
|
Loading…
Reference in New Issue