mirror of
https://github.com/RPCSX/rpcsx.git
synced 2025-12-06 07:12:14 +01:00
- Used IDs were not from the Guitar Hero instruments but in fact from the Rock Band ones. Sets the correct Guitar Hero IDs and adds the Rock Band ones on comments. TODO: Allow selecting the specific devices on the PAD Settings. - Adds DJ Hero Turntable VID/PID. - Adds Dance Dance Revolution Mat VID/PID.
961 lines
26 KiB
C++
961 lines
26 KiB
C++
#include "stdafx.h"
|
|
#include "Emu/System.h"
|
|
#include "Emu/IdManager.h"
|
|
#include "Emu/Cell/PPUModule.h"
|
|
|
|
#include "pad_thread.h"
|
|
#include "cellPad.h"
|
|
|
|
extern logs::channel sys_io;
|
|
|
|
template<>
|
|
void fmt_class_string<CellPadError>::format(std::string& out, u64 arg)
|
|
{
|
|
format_enum(out, arg, [](auto error)
|
|
{
|
|
switch (error)
|
|
{
|
|
STR_CASE(CELL_PAD_ERROR_FATAL);
|
|
STR_CASE(CELL_PAD_ERROR_INVALID_PARAMETER);
|
|
STR_CASE(CELL_PAD_ERROR_ALREADY_INITIALIZED);
|
|
STR_CASE(CELL_PAD_ERROR_UNINITIALIZED);
|
|
STR_CASE(CELL_PAD_ERROR_RESOURCE_ALLOCATION_FAILED);
|
|
STR_CASE(CELL_PAD_ERROR_DATA_READ_FAILED);
|
|
STR_CASE(CELL_PAD_ERROR_NO_DEVICE);
|
|
STR_CASE(CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD);
|
|
STR_CASE(CELL_PAD_ERROR_TOO_MANY_DEVICES);
|
|
STR_CASE(CELL_PAD_ERROR_EBUSY);
|
|
}
|
|
|
|
return unknown;
|
|
});
|
|
}
|
|
|
|
error_code cellPadInit(u32 max_connect)
|
|
{
|
|
sys_io.warning("cellPadInit(max_connect=%d)", max_connect);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
if (fxm::check<pad_t>())
|
|
return CELL_PAD_ERROR_ALREADY_INITIALIZED;
|
|
|
|
if (max_connect == 0 || max_connect > CELL_MAX_PADS)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
fxm::make<pad_t>(std::min(max_connect, (u32)CELL_PAD_MAX_PORT_NUM));
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code cellPadEnd()
|
|
{
|
|
sys_io.notice("cellPadEnd()");
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
if (!fxm::remove<pad_t>())
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code cellPadClearBuf(u32 port_no)
|
|
{
|
|
sys_io.trace("cellPadClearBuf(port_no=%d)", port_no);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
const auto config = fxm::check<pad_t>();
|
|
|
|
if (!config)
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
if (port_no >= CELL_MAX_PADS)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
const auto& pads = handler->GetPads();
|
|
|
|
if (port_no >= config->max_connect)
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
const auto pad = pads[port_no];
|
|
|
|
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
// Set 'm_buffer_cleared' to force a resend of everything
|
|
// might as well also reset everything in our pad 'buffer' to nothing as well
|
|
|
|
pad->m_buffer_cleared = true;
|
|
pad->m_analog_left_x = pad->m_analog_left_y = pad->m_analog_right_x = pad->m_analog_right_y = 128;
|
|
|
|
pad->m_digital_1 = pad->m_digital_2 = 0;
|
|
pad->m_press_right = pad->m_press_left = pad->m_press_up = pad->m_press_down = 0;
|
|
pad->m_press_triangle = pad->m_press_circle = pad->m_press_cross = pad->m_press_square = 0;
|
|
pad->m_press_L1 = pad->m_press_L2 = pad->m_press_R1 = pad->m_press_R2 = 0;
|
|
|
|
// ~399 on sensor y is a level non moving controller
|
|
pad->m_sensor_y = 399;
|
|
pad->m_sensor_x = pad->m_sensor_z = pad->m_sensor_g = 512;
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code cellPadGetData(u32 port_no, vm::ptr<CellPadData> data)
|
|
{
|
|
sys_io.trace("cellPadGetData(port_no=%d, data=*0x%x)", port_no, data);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
const auto config = fxm::check<pad_t>();
|
|
|
|
if (!config)
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
if (port_no >= CELL_MAX_PADS || !data)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
const auto& pads = handler->GetPads();
|
|
|
|
if (port_no >= config->max_connect)
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
const auto pad = pads[port_no];
|
|
const auto setting = config->port_setting[port_no];
|
|
|
|
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
u16 d1Initial, d2Initial;
|
|
d1Initial = pad->m_digital_1;
|
|
d2Initial = pad->m_digital_2;
|
|
bool btnChanged = false;
|
|
|
|
for (Button& button : pad->m_buttons)
|
|
{
|
|
// here we check btns, and set pad accordingly,
|
|
// if something changed, set btnChanged
|
|
|
|
if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1)
|
|
{
|
|
if (button.m_pressed) pad->m_digital_1 |= button.m_outKeyCode;
|
|
else pad->m_digital_1 &= ~button.m_outKeyCode;
|
|
|
|
switch (button.m_outKeyCode)
|
|
{
|
|
case CELL_PAD_CTRL_LEFT:
|
|
if (pad->m_press_left != button.m_value) btnChanged = true;
|
|
pad->m_press_left = button.m_value;
|
|
break;
|
|
case CELL_PAD_CTRL_DOWN:
|
|
if (pad->m_press_down != button.m_value) btnChanged = true;
|
|
pad->m_press_down = button.m_value;
|
|
break;
|
|
case CELL_PAD_CTRL_RIGHT:
|
|
if (pad->m_press_right != button.m_value) btnChanged = true;
|
|
pad->m_press_right = button.m_value;
|
|
break;
|
|
case CELL_PAD_CTRL_UP:
|
|
if (pad->m_press_up != button.m_value) btnChanged = true;
|
|
pad->m_press_up = button.m_value;
|
|
break;
|
|
// These arent pressure btns
|
|
case CELL_PAD_CTRL_R3:
|
|
case CELL_PAD_CTRL_L3:
|
|
case CELL_PAD_CTRL_START:
|
|
case CELL_PAD_CTRL_SELECT:
|
|
default: break;
|
|
}
|
|
}
|
|
else if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2)
|
|
{
|
|
if (button.m_pressed) pad->m_digital_2 |= button.m_outKeyCode;
|
|
else pad->m_digital_2 &= ~button.m_outKeyCode;
|
|
|
|
switch (button.m_outKeyCode)
|
|
{
|
|
case CELL_PAD_CTRL_SQUARE:
|
|
if (pad->m_press_square != button.m_value) btnChanged = true;
|
|
pad->m_press_square = button.m_value;
|
|
break;
|
|
case CELL_PAD_CTRL_CROSS:
|
|
if (pad->m_press_cross != button.m_value) btnChanged = true;
|
|
pad->m_press_cross = button.m_value;
|
|
break;
|
|
case CELL_PAD_CTRL_CIRCLE:
|
|
if (pad->m_press_circle != button.m_value) btnChanged = true;
|
|
pad->m_press_circle = button.m_value;
|
|
break;
|
|
case CELL_PAD_CTRL_TRIANGLE:
|
|
if (pad->m_press_triangle != button.m_value) btnChanged = true;
|
|
pad->m_press_triangle = button.m_value;
|
|
break;
|
|
case CELL_PAD_CTRL_R1:
|
|
if (pad->m_press_R1 != button.m_value) btnChanged = true;
|
|
pad->m_press_R1 = button.m_value;
|
|
break;
|
|
case CELL_PAD_CTRL_L1:
|
|
if (pad->m_press_L1 != button.m_value) btnChanged = true;
|
|
pad->m_press_L1 = button.m_value;
|
|
break;
|
|
case CELL_PAD_CTRL_R2:
|
|
if (pad->m_press_R2 != button.m_value) btnChanged = true;
|
|
pad->m_press_R2 = button.m_value;
|
|
break;
|
|
case CELL_PAD_CTRL_L2:
|
|
if (pad->m_press_L2 != button.m_value) btnChanged = true;
|
|
pad->m_press_L2 = button.m_value;
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
if (button.m_flush)
|
|
{
|
|
button.m_pressed = false;
|
|
button.m_flush = false;
|
|
button.m_value = 0;
|
|
}
|
|
}
|
|
|
|
for (const AnalogStick& stick : pad->m_sticks)
|
|
{
|
|
switch (stick.m_offset)
|
|
{
|
|
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X:
|
|
if (pad->m_analog_left_x != stick.m_value) btnChanged = true;
|
|
pad->m_analog_left_x = stick.m_value;
|
|
break;
|
|
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y:
|
|
if (pad->m_analog_left_y != stick.m_value) btnChanged = true;
|
|
pad->m_analog_left_y = stick.m_value;
|
|
break;
|
|
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X:
|
|
if (pad->m_analog_right_x != stick.m_value) btnChanged = true;
|
|
pad->m_analog_right_x = stick.m_value;
|
|
break;
|
|
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y:
|
|
if (pad->m_analog_right_y != stick.m_value) btnChanged = true;
|
|
pad->m_analog_right_y = stick.m_value;
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
if (setting & CELL_PAD_SETTING_SENSOR_ON)
|
|
{
|
|
for (const AnalogSensor& sensor : pad->m_sensors)
|
|
{
|
|
switch (sensor.m_offset)
|
|
{
|
|
case CELL_PAD_BTN_OFFSET_SENSOR_X:
|
|
if (pad->m_sensor_x != sensor.m_value) btnChanged = true;
|
|
pad->m_sensor_x = sensor.m_value;
|
|
break;
|
|
case CELL_PAD_BTN_OFFSET_SENSOR_Y:
|
|
if (pad->m_sensor_y != sensor.m_value) btnChanged = true;
|
|
pad->m_sensor_y = sensor.m_value;
|
|
break;
|
|
case CELL_PAD_BTN_OFFSET_SENSOR_Z:
|
|
if (pad->m_sensor_z != sensor.m_value) btnChanged = true;
|
|
pad->m_sensor_z = sensor.m_value;
|
|
break;
|
|
case CELL_PAD_BTN_OFFSET_SENSOR_G:
|
|
if (pad->m_sensor_g != sensor.m_value) btnChanged = true;
|
|
pad->m_sensor_g = sensor.m_value;
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (d1Initial != pad->m_digital_1 || d2Initial != pad->m_digital_2)
|
|
{
|
|
btnChanged = true;
|
|
}
|
|
|
|
if (setting & CELL_PAD_SETTING_SENSOR_ON)
|
|
{
|
|
// report back new data every ~10 ms even if the input doesn't change
|
|
// this is observed behaviour when using a Dualshock 3 controller
|
|
static std::chrono::time_point<steady_clock> last_update[CELL_PAD_MAX_PORT_NUM] = { };
|
|
const std::chrono::time_point<steady_clock> now = steady_clock::now();
|
|
|
|
if (btnChanged || pad->m_buffer_cleared || (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update[port_no]).count() >= 10))
|
|
{
|
|
data->len = CELL_PAD_LEN_CHANGE_SENSOR_ON;
|
|
last_update[port_no] = now;
|
|
}
|
|
else
|
|
{
|
|
data->len = CELL_PAD_LEN_NO_CHANGE;
|
|
}
|
|
}
|
|
else if (btnChanged || pad->m_buffer_cleared)
|
|
{
|
|
// only give back valid data if a controller state changed
|
|
data->len = (setting & CELL_PAD_SETTING_PRESS_ON) ? CELL_PAD_LEN_CHANGE_PRESS_ON : CELL_PAD_LEN_CHANGE_DEFAULT;
|
|
}
|
|
else
|
|
{
|
|
// report no state changes
|
|
data->len = CELL_PAD_LEN_NO_CHANGE;
|
|
}
|
|
|
|
pad->m_buffer_cleared = false;
|
|
|
|
// only update parts of the output struct depending on the controller setting
|
|
if (data->len > CELL_PAD_LEN_NO_CHANGE)
|
|
{
|
|
data->button[0] = 0x0; // always 0
|
|
// bits 15-8 reserved, 7-4 = 0x7, 3-0: data->len/2;
|
|
data->button[1] = (0x7 << 4) | std::min(data->len / 2, 15);
|
|
|
|
data->button[CELL_PAD_BTN_OFFSET_DIGITAL1] = pad->m_digital_1;
|
|
data->button[CELL_PAD_BTN_OFFSET_DIGITAL2] = pad->m_digital_2;
|
|
data->button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X] = pad->m_analog_right_x;
|
|
data->button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y] = pad->m_analog_right_y;
|
|
data->button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X] = pad->m_analog_left_x;
|
|
data->button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y] = pad->m_analog_left_y;
|
|
|
|
if (setting & CELL_PAD_SETTING_PRESS_ON)
|
|
{
|
|
data->button[CELL_PAD_BTN_OFFSET_PRESS_RIGHT] = pad->m_press_right;
|
|
data->button[CELL_PAD_BTN_OFFSET_PRESS_LEFT] = pad->m_press_left;
|
|
data->button[CELL_PAD_BTN_OFFSET_PRESS_UP] = pad->m_press_up;
|
|
data->button[CELL_PAD_BTN_OFFSET_PRESS_DOWN] = pad->m_press_down;
|
|
data->button[CELL_PAD_BTN_OFFSET_PRESS_TRIANGLE] = pad->m_press_triangle;
|
|
data->button[CELL_PAD_BTN_OFFSET_PRESS_CIRCLE] = pad->m_press_circle;
|
|
data->button[CELL_PAD_BTN_OFFSET_PRESS_CROSS] = pad->m_press_cross;
|
|
data->button[CELL_PAD_BTN_OFFSET_PRESS_SQUARE] = pad->m_press_square;
|
|
data->button[CELL_PAD_BTN_OFFSET_PRESS_L1] = pad->m_press_L1;
|
|
data->button[CELL_PAD_BTN_OFFSET_PRESS_L2] = pad->m_press_L2;
|
|
data->button[CELL_PAD_BTN_OFFSET_PRESS_R1] = pad->m_press_R1;
|
|
data->button[CELL_PAD_BTN_OFFSET_PRESS_R2] = pad->m_press_R2;
|
|
}
|
|
else
|
|
{
|
|
// Clear area if setting is not used
|
|
constexpr u32 area_lengh = (CELL_PAD_LEN_CHANGE_PRESS_ON - CELL_PAD_LEN_CHANGE_DEFAULT) * sizeof(u16);
|
|
std::memset(&data->button[CELL_PAD_LEN_CHANGE_DEFAULT], 0, area_lengh);
|
|
}
|
|
|
|
if (data->len == CELL_PAD_LEN_CHANGE_SENSOR_ON)
|
|
{
|
|
data->button[CELL_PAD_BTN_OFFSET_SENSOR_X] = pad->m_sensor_x;
|
|
data->button[CELL_PAD_BTN_OFFSET_SENSOR_Y] = pad->m_sensor_y;
|
|
data->button[CELL_PAD_BTN_OFFSET_SENSOR_Z] = pad->m_sensor_z;
|
|
data->button[CELL_PAD_BTN_OFFSET_SENSOR_G] = pad->m_sensor_g;
|
|
}
|
|
}
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code cellPadPeriphGetInfo(vm::ptr<CellPadPeriphInfo> info)
|
|
{
|
|
sys_io.trace("cellPadPeriphGetInfo(info=*0x%x)", info);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
const auto config = fxm::check<pad_t>();
|
|
|
|
if (!config)
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
if (!info)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
const PadInfo& rinfo = handler->GetInfo();
|
|
|
|
std::memset(info.get_ptr(), 0, sizeof(CellPadPeriphInfo));
|
|
|
|
info->max_connect = config->max_connect;
|
|
info->now_connect = rinfo.now_connect;
|
|
info->system_info = rinfo.system_info;
|
|
|
|
const auto& pads = handler->GetPads();
|
|
|
|
// TODO: Support other types of controllers
|
|
for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; ++i)
|
|
{
|
|
if (i >= config->max_connect)
|
|
break;
|
|
|
|
info->port_status[i] = pads[i]->m_port_status;
|
|
pads[i]->m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES;
|
|
info->port_setting[i] = config->port_setting[i];
|
|
info->device_capability[i] = pads[i]->m_device_capability;
|
|
info->device_type[i] = pads[i]->m_device_type;
|
|
info->pclass_type[i] = pads[i]->m_class_type;
|
|
info->pclass_profile[i] = 0x0;
|
|
}
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code cellPadPeriphGetData(u32 port_no, vm::ptr<CellPadPeriphData> data)
|
|
{
|
|
sys_io.trace("cellPadPeriphGetData(port_no=%d, data=*0x%x)", port_no, data);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
const auto config = fxm::check<pad_t>();
|
|
|
|
if (!config)
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
// port_no can only be 0-6 in this function
|
|
if (port_no >= CELL_PAD_MAX_PORT_NUM || !data)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
const auto& pads = handler->GetPads();
|
|
|
|
if (port_no >= config->max_connect)
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
const auto pad = pads[port_no];
|
|
|
|
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
// todo: support for 'unique' controllers, which goes in offsets 24+ in padData
|
|
data->pclass_type = pad->m_class_type;
|
|
data->pclass_profile = 0x0;
|
|
|
|
return cellPadGetData(port_no, vm::get_addr(&data->cellpad_data));
|
|
}
|
|
|
|
error_code cellPadGetRawData(u32 port_no, vm::ptr<CellPadData> data)
|
|
{
|
|
sys_io.todo("cellPadGetRawData(port_no=%d, data=*0x%x)", port_no, data);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
const auto config = fxm::check<pad_t>();
|
|
|
|
if (!config)
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
if (port_no >= CELL_MAX_PADS || !data)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
const auto& pads = handler->GetPads();
|
|
|
|
if (port_no >= config->max_connect)
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
const auto pad = pads[port_no];
|
|
|
|
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
// ?
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code cellPadGetDataExtra(u32 port_no, vm::ptr<u32> device_type, vm::ptr<CellPadData> data)
|
|
{
|
|
sys_io.trace("cellPadGetDataExtra(port_no=%d, device_type=*0x%x, data=*0x%x)", port_no, device_type, data);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
const auto config = fxm::check<pad_t>();
|
|
|
|
if (!config)
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
if (port_no >= CELL_MAX_PADS || !data)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
const auto& pads = handler->GetPads();
|
|
|
|
if (port_no >= config->max_connect)
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
const auto pad = pads[port_no];
|
|
|
|
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
// TODO: This is used just to get data from a BD/CEC remote,
|
|
// but if the port isnt a remote, device type is set to 0 and just regular cellPadGetData is returned
|
|
|
|
if (device_type) // no error is returned on NULL
|
|
{
|
|
*device_type = 0;
|
|
}
|
|
|
|
// set BD data before just incase
|
|
data->button[24] = 0x0;
|
|
data->button[25] = 0x0;
|
|
|
|
return cellPadGetData(port_no, data);
|
|
}
|
|
|
|
error_code cellPadSetActDirect(u32 port_no, vm::ptr<CellPadActParam> param)
|
|
{
|
|
sys_io.trace("cellPadSetActDirect(port_no=%d, param=*0x%x)", port_no, param);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
const auto config = fxm::check<pad_t>();
|
|
|
|
if (!config)
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
if (port_no >= CELL_MAX_PADS || !param)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
const auto& pads = handler->GetPads();
|
|
|
|
if (port_no >= config->max_connect)
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
const auto pad = pads[port_no];
|
|
|
|
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
// TODO: find out if this is checked here or later or at all
|
|
if (!(pad->m_device_capability & CELL_PAD_CAPABILITY_ACTUATOR))
|
|
return CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD;
|
|
|
|
// make sure reserved bits are 0. Looks like this happens after checking the pad status
|
|
for (int i = 0; i < 6; i++)
|
|
if (param->reserved[i])
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
handler->SetRumble(port_no, param->motor[1], param->motor[0] > 0);
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code cellPadGetInfo(vm::ptr<CellPadInfo> info)
|
|
{
|
|
sys_io.trace("cellPadGetInfo(info=*0x%x)", info);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
const auto config = fxm::check<pad_t>();
|
|
|
|
if (!config)
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
if (!info)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
std::memset(info.get_ptr(), 0, sizeof(CellPadInfo));
|
|
|
|
const PadInfo& rinfo = handler->GetInfo();
|
|
info->max_connect = config->max_connect;
|
|
info->now_connect = rinfo.now_connect;
|
|
info->system_info = rinfo.system_info;
|
|
|
|
const auto& pads = handler->GetPads();
|
|
|
|
for (u32 i = 0; i < CELL_MAX_PADS; ++i)
|
|
{
|
|
if (i >= config->max_connect)
|
|
break;
|
|
|
|
pads[i]->m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; // TODO: should ASSIGN flags be cleared here?
|
|
info->status[i] = pads[i]->m_port_status;
|
|
|
|
// TODO: Allow selecting different product IDs
|
|
switch (pads[i]->m_class_type)
|
|
{
|
|
case CELL_PAD_PCLASS_TYPE_GUITAR:
|
|
// Sony Computer Entertainment America
|
|
info->vendor_id[i] = 0x12BA;
|
|
// RedOctane Guitar (Guitar Hero)
|
|
info->product_id[i] = 0x0100;
|
|
// Harmonix Guitar (Rock Band)
|
|
// info->product_id[i] = 0x0200;
|
|
break;
|
|
case CELL_PAD_PCLASS_TYPE_DRUM:
|
|
// Sony Computer Entertainment America
|
|
info->vendor_id[i] = 0x12BA;
|
|
// RedOctane Drum Kit (Guitar Hero)
|
|
info->product_id[i] = 0x0120;
|
|
// Harmonix Drum Kit (Rock Band)
|
|
// info->product_id[i] = 0x0210;
|
|
break;
|
|
case CELL_PAD_PCLASS_TYPE_DJ:
|
|
// Sony Computer Entertainment America
|
|
info->vendor_id[i] = 0x12BA;
|
|
// DJ Hero Turntable
|
|
info->product_id[i] = 0x0140;
|
|
break;
|
|
case CELL_PAD_PCLASS_TYPE_DANCEMAT:
|
|
// Konami Digital Entertainment
|
|
info->vendor_id[i] = 0x1CCF;
|
|
// Dance Dance Revolution Mat
|
|
info->product_id[i] = 0x0140;
|
|
break;
|
|
default:
|
|
// Sony Corp.
|
|
info->vendor_id[i] = 0x054C;
|
|
// PlayStation 3 Controller
|
|
info->product_id[i] = 0x0268;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code cellPadGetInfo2(vm::ptr<CellPadInfo2> info)
|
|
{
|
|
sys_io.trace("cellPadGetInfo2(info=*0x%x)", info);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
const auto config = fxm::check<pad_t>();
|
|
|
|
if (!config)
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
if (!info)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
std::memset(info.get_ptr(), 0, sizeof(CellPadInfo2));
|
|
|
|
const PadInfo& rinfo = handler->GetInfo();
|
|
info->max_connect = config->max_connect;
|
|
info->now_connect = rinfo.now_connect;
|
|
info->system_info = rinfo.system_info;
|
|
|
|
const auto& pads = handler->GetPads();
|
|
|
|
for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; ++i)
|
|
{
|
|
if (i >= config->max_connect)
|
|
break;
|
|
|
|
info->port_status[i] = pads[i]->m_port_status;
|
|
pads[i]->m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES;
|
|
info->port_setting[i] = config->port_setting[i];
|
|
info->device_capability[i] = pads[i]->m_device_capability;
|
|
info->device_type[i] = pads[i]->m_device_type;
|
|
}
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code cellPadGetCapabilityInfo(u32 port_no, vm::ptr<CellPadCapabilityInfo> info)
|
|
{
|
|
sys_io.trace("cellPadGetCapabilityInfo(port_no=%d, data_addr:=0x%x)", port_no, info.addr());
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
const auto config = fxm::check<pad_t>();
|
|
|
|
if (!config)
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
if (port_no >= CELL_MAX_PADS || !info)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
const auto& pads = handler->GetPads();
|
|
|
|
if (port_no >= config->max_connect)
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
const auto pad = pads[port_no];
|
|
|
|
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
// Should return the same as device capability mask, psl1ght has it backwards in pad->h
|
|
memset(info->info, 0, CELL_PAD_MAX_CAPABILITY_INFO * sizeof(u32));
|
|
info->info[0] = pad->m_device_capability;
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code cellPadSetPortSetting(u32 port_no, u32 port_setting)
|
|
{
|
|
sys_io.trace("cellPadSetPortSetting(port_no=%d, port_setting=0x%x)", port_no, port_setting);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
const auto config = fxm::check<pad_t>();
|
|
|
|
if (!config)
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
if (port_no >= CELL_MAX_PADS)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
// CELL_PAD_ERROR_NO_DEVICE is not returned in this case.
|
|
if (port_no >= CELL_PAD_MAX_PORT_NUM)
|
|
return CELL_OK;
|
|
|
|
config->port_setting[port_no] = port_setting;
|
|
|
|
// can also return CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
s32 cellPadInfoPressMode(u32 port_no)
|
|
{
|
|
sys_io.trace("cellPadInfoPressMode(port_no=%d)", port_no);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
const auto config = fxm::check<pad_t>();
|
|
|
|
if (!config)
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
if (port_no >= CELL_MAX_PADS)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
const auto& pads = handler->GetPads();
|
|
|
|
if (port_no >= config->max_connect)
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
const auto pad = pads[port_no];
|
|
|
|
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
return (pad->m_device_capability & CELL_PAD_CAPABILITY_PRESS_MODE) > 0;
|
|
}
|
|
|
|
s32 cellPadInfoSensorMode(u32 port_no)
|
|
{
|
|
sys_io.trace("cellPadInfoSensorMode(port_no=%d)", port_no);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
const auto config = fxm::check<pad_t>();
|
|
|
|
if (!config)
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
if (port_no >= CELL_MAX_PADS)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
const auto& pads = handler->GetPads();
|
|
|
|
if (port_no >= config->max_connect)
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
const auto pad = pads[port_no];
|
|
|
|
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
|
return CELL_PAD_ERROR_NO_DEVICE;
|
|
|
|
return (pad->m_device_capability & CELL_PAD_CAPABILITY_SENSOR_MODE) > 0;
|
|
}
|
|
|
|
error_code cellPadSetPressMode(u32 port_no, u32 mode)
|
|
{
|
|
sys_io.trace("cellPadSetPressMode(port_no=%d, mode=%d)", port_no, mode);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
const auto config = fxm::check<pad_t>();
|
|
|
|
if (!config)
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
if (port_no >= CELL_PAD_MAX_PORT_NUM)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
const auto& pads = handler->GetPads();
|
|
|
|
// CELL_PAD_ERROR_NO_DEVICE is not returned in this case.
|
|
if (port_no >= CELL_PAD_MAX_PORT_NUM)
|
|
return CELL_OK;
|
|
|
|
const auto pad = pads[port_no];
|
|
|
|
// TODO: find out if this is checked here or later or at all
|
|
if (!(pad->m_device_capability & CELL_PAD_CAPABILITY_PRESS_MODE))
|
|
return CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD;
|
|
|
|
if (mode)
|
|
config->port_setting[port_no] |= CELL_PAD_SETTING_PRESS_ON;
|
|
else
|
|
config->port_setting[port_no] &= ~CELL_PAD_SETTING_PRESS_ON;
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code cellPadSetSensorMode(u32 port_no, u32 mode)
|
|
{
|
|
sys_io.trace("cellPadSetSensorMode(port_no=%d, mode=%d)", port_no, mode);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
const auto config = fxm::check<pad_t>();
|
|
|
|
if (!config)
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
if (port_no >= CELL_MAX_PADS)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
const auto& pads = handler->GetPads();
|
|
|
|
// CELL_PAD_ERROR_NO_DEVICE is not returned in this case.
|
|
if (port_no >= CELL_PAD_MAX_PORT_NUM)
|
|
return CELL_OK;
|
|
|
|
const auto pad = pads[port_no];
|
|
|
|
// TODO: find out if this is checked here or later or at all
|
|
if (!(pad->m_device_capability & CELL_PAD_CAPABILITY_SENSOR_MODE))
|
|
return CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD;
|
|
|
|
if (mode)
|
|
config->port_setting[port_no] |= CELL_PAD_SETTING_SENSOR_ON;
|
|
else
|
|
config->port_setting[port_no] &= ~CELL_PAD_SETTING_SENSOR_ON;
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code cellPadLddRegisterController()
|
|
{
|
|
sys_io.todo("cellPadLddRegisterController()");
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
if (!fxm::check<pad_t>())
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
// can return CELL_PAD_ERROR_TOO_MANY_DEVICES
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code cellPadLddDataInsert(s32 handle, vm::ptr<CellPadData> data)
|
|
{
|
|
sys_io.todo("cellPadLddDataInsert(handle=%d, data=*0x%x)", handle, data);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
if (!fxm::check<pad_t>())
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
if (handle < 0 || !data) // data == NULL stalls on decr
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
// can return CELL_PAD_ERROR_NO_DEVICE
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
error_code cellPadLddGetPortNo(s32 handle)
|
|
{
|
|
sys_io.todo("cellPadLddGetPortNo(handle=%d)", handle);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
if (!fxm::check<pad_t>())
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
if (handle < 0)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
return CELL_PAD_ERROR_EBUSY; // or CELL_PAD_ERROR_FATAL or CELL_EBUSY
|
|
}
|
|
|
|
error_code cellPadLddUnregisterController(s32 handle)
|
|
{
|
|
sys_io.todo("cellPadLddUnregisterController(handle=%d)", handle);
|
|
|
|
std::lock_guard lock(pad::g_pad_mutex);
|
|
|
|
if (!fxm::check<pad_t>())
|
|
return CELL_PAD_ERROR_UNINITIALIZED;
|
|
|
|
const auto handler = pad::get_current_handler();
|
|
|
|
if (handle < 0)
|
|
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
|
|
|
// can return CELL_PAD_ERROR_NO_DEVICE
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
s32 sys_io_3733EA3C(u32 port_no, vm::ptr<u32> device_type, vm::ptr<CellPadData> data)
|
|
{
|
|
// Used by the ps1 emulator built into the firmware
|
|
// Seems to call the same function that getdataextra does
|
|
sys_io.trace("sys_io_3733EA3C(port_no=%d, device_type=*0x%x, data=*0x%x)", port_no, device_type, data);
|
|
return cellPadGetDataExtra(port_no, device_type, data);
|
|
}
|
|
|
|
void cellPad_init()
|
|
{
|
|
REG_FUNC(sys_io, cellPadInit);
|
|
REG_FUNC(sys_io, cellPadEnd);
|
|
REG_FUNC(sys_io, cellPadClearBuf);
|
|
REG_FUNC(sys_io, cellPadGetData);
|
|
REG_FUNC(sys_io, cellPadGetRawData); //
|
|
REG_FUNC(sys_io, cellPadGetDataExtra);
|
|
REG_FUNC(sys_io, cellPadSetActDirect);
|
|
REG_FUNC(sys_io, cellPadGetInfo); //
|
|
REG_FUNC(sys_io, cellPadGetInfo2);
|
|
REG_FUNC(sys_io, cellPadPeriphGetInfo);
|
|
REG_FUNC(sys_io, cellPadPeriphGetData);
|
|
REG_FUNC(sys_io, cellPadSetPortSetting);
|
|
REG_FUNC(sys_io, cellPadInfoPressMode); //
|
|
REG_FUNC(sys_io, cellPadInfoSensorMode); //
|
|
REG_FUNC(sys_io, cellPadSetPressMode); //
|
|
REG_FUNC(sys_io, cellPadSetSensorMode); //
|
|
REG_FUNC(sys_io, cellPadGetCapabilityInfo); //
|
|
REG_FUNC(sys_io, cellPadLddRegisterController);
|
|
REG_FUNC(sys_io, cellPadLddDataInsert);
|
|
REG_FUNC(sys_io, cellPadLddGetPortNo);
|
|
REG_FUNC(sys_io, cellPadLddUnregisterController);
|
|
|
|
REG_FNID(sys_io, 0x3733EA3C, sys_io_3733EA3C);
|
|
}
|