cellGem: Allow to use multiple controllers as fake PS-Move

- Allows to use the last 4 ports/players as PS Move in fake mode
- Should in theory react to re/disconnected controllers
- Should allow to use player 1-3 as normal controller simultaneously
This commit is contained in:
Megamouse 2024-01-31 22:49:50 +01:00
parent d5ce4e0f6b
commit dda0b8af45
2 changed files with 94 additions and 22 deletions

View File

@ -114,6 +114,20 @@ void fmt_class_string<CellGemVideoConvertFormatEnum>::format(std::string& out, u
});
}
// last 4 out of 7 ports (7,6,5,4). index starts at 1
static u32 port_num(u32 gem_num)
{
ensure(gem_num < CELL_GEM_MAX_NUM);
return CELL_PAD_MAX_PORT_NUM - gem_num;
}
// last 4 out of 7 ports (6,5,4,3). index starts at 0
static u32 pad_num(u32 gem_num)
{
ensure(gem_num < CELL_GEM_MAX_NUM);
return (CELL_PAD_MAX_PORT_NUM - 1) - gem_num;
}
// **********************
// * HLE helper structs *
// **********************
@ -278,12 +292,38 @@ public:
return;
}
bool is_connected = false;
switch (g_cfg.io.move)
{
case move_handler::fake:
{
connected_controllers = 0;
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler();
for (u32 i = 0; i < std::min<u32>(attribute.max_connect, CELL_GEM_MAX_NUM); i++)
{
const auto& pad = ::at32(handler->GetPads(), pad_num(i));
if (pad && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
{
connected_controllers++;
if (gem_num == i)
{
is_connected = true;
}
}
}
break;
}
case move_handler::mouse:
{
connected_controllers = 1;
if (gem_num == 0)
{
is_connected = true;
}
break;
}
#ifdef HAVE_LIBEVDEV
@ -291,8 +331,13 @@ public:
{
gun_thread& gun = g_fxo->get<gun_thread>();
std::scoped_lock lock(gun.handler.mutex);
connected_controllers = gun.handler.init() ? gun.handler.get_num_guns() : 0;
gun.num_devices = connected_controllers;
gun.num_devices = gun.handler.init() ? gun.handler.get_num_guns() : 0;
connected_controllers = std::min<u32>(std::min<u32>(attribute.max_connect, CELL_GEM_MAX_NUM), gun.num_devices);
if (gem_num < connected_controllers)
{
is_connected = true;
}
break;
}
#endif
@ -306,10 +351,10 @@ public:
controller.sphere_rgb = gem_color::get_default_color(gem_num);
// Assign status and port number
if (gem_num < connected_controllers)
if (is_connected)
{
controller.status = CELL_GEM_STATUS_READY;
controller.port = CELL_PAD_MAX_PORT_NUM - gem_num;
controller.port = port_num(gem_num);
}
}
@ -638,12 +683,12 @@ extern bool is_input_allowed();
/**
* \brief Maps Move controller data (digital buttons, and analog Trigger data) to DS3 pad input.
* Unavoidably buttons conflict with DS3 mappings, which is problematic for some games.
* \param port_no DS3 port number to use
* \param gem_num gem index to use
* \param digital_buttons Bitmask filled with CELL_GEM_CTRL_* values
* \param analog_t Analog value of Move's Trigger. Currently mapped to R2.
* \return true on success, false if port_no controller is invalid
* \return true on success, false if controller is disconnected
*/
static void ds3_input_to_pad(const u32 port_no, be_t<u16>& digital_buttons, be_t<u16>& analog_t)
static void ds3_input_to_pad(const u32 gem_num, be_t<u16>& digital_buttons, be_t<u16>& analog_t)
{
digital_buttons = 0;
analog_t = 0;
@ -656,14 +701,14 @@ static void ds3_input_to_pad(const u32 port_no, be_t<u16>& digital_buttons, be_t
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler();
const auto& pad = ::at32(handler->GetPads(), port_no);
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
{
return;
}
const auto& cfg = ::at32(g_cfg_gem.players, port_no);
const auto& cfg = ::at32(g_cfg_gem.players, gem_num);
cfg->handle_input(pad, true, [&](gem_btn btn, u16 value, bool pressed)
{
if (!pressed)
@ -707,12 +752,12 @@ static void ds3_input_to_pad(const u32 port_no, be_t<u16>& digital_buttons, be_t
constexpr u16 ds3_max_x = 255;
constexpr u16 ds3_max_y = 255;
static inline void ds3_get_stick_values(u32 port_no, const std::shared_ptr<Pad>& pad, s32& x_pos, s32& y_pos)
static inline void ds3_get_stick_values(u32 gem_num, const std::shared_ptr<Pad>& pad, s32& x_pos, s32& y_pos)
{
x_pos = 0;
y_pos = 0;
const auto& cfg = ::at32(g_cfg_gem.players, port_no);
const auto& cfg = ::at32(g_cfg_gem.players, gem_num);
cfg->handle_input(pad, true, [&](gem_btn btn, u16 value, bool pressed)
{
if (!pressed)
@ -733,7 +778,7 @@ static inline void ds3_get_stick_values(u32 port_no, const std::shared_ptr<Pad>&
}
template <typename T>
static void ds3_pos_to_gem_state(const u32 port_no, const gem_config::gem_controller& controller, T& gem_state)
static void ds3_pos_to_gem_state(u32 gem_num, const gem_config::gem_controller& controller, T& gem_state)
{
if (!gem_state || !is_input_allowed())
{
@ -743,7 +788,7 @@ static void ds3_pos_to_gem_state(const u32 port_no, const gem_config::gem_contro
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler();
const auto& pad = ::at32(handler->GetPads(), port_no);
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
{
@ -751,15 +796,15 @@ static void ds3_pos_to_gem_state(const u32 port_no, const gem_config::gem_contro
}
s32 ds3_pos_x, ds3_pos_y;
ds3_get_stick_values(port_no, pad, ds3_pos_x, ds3_pos_y);
ds3_get_stick_values(gem_num, pad, ds3_pos_x, ds3_pos_y);
if constexpr (std::is_same<T, vm::ptr<CellGemState>>::value)
{
pos_to_gem_state(port_no, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
pos_to_gem_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
}
else if constexpr (std::is_same<T, vm::ptr<CellGemImageState>>::value)
{
pos_to_gem_image_state(port_no, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
pos_to_gem_image_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
}
}
@ -767,11 +812,11 @@ static void ds3_pos_to_gem_state(const u32 port_no, const gem_config::gem_contro
* \brief Maps external Move controller data to DS3 input. (This can be input from any physical pad, not just the DS3)
* Implementation detail: CellGemExtPortData's digital/analog fields map the same way as
* libPad, so no translation is needed.
* \param port_no DS3 port number to use
* \param gem_num gem index to use
* \param ext External data to modify
* \return true on success, false if port_no controller is invalid
* \return true on success, false if controller is disconnected
*/
static void ds3_input_to_ext(const u32 port_no, const gem_config::gem_controller& controller, CellGemExtPortData& ext)
static void ds3_input_to_ext(const u32 gem_num, const gem_config::gem_controller& controller, CellGemExtPortData& ext)
{
ext = {};
@ -783,7 +828,7 @@ static void ds3_input_to_ext(const u32 port_no, const gem_config::gem_controller
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler();
const auto& pad = ::at32(handler->GetPads(), port_no);
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
{
@ -1460,6 +1505,33 @@ error_code cellGemGetInfo(vm::ptr<CellGemInfo> info)
}
// TODO: Support connecting PlayStation Move controllers
if (g_cfg.io.move == move_handler::fake)
{
gem.connected_controllers = 0;
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler();
for (u32 i = 0; i < CELL_GEM_MAX_NUM; i++)
{
const auto& pad = ::at32(handler->GetPads(), pad_num(i));
const bool connected = (pad && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) && i < gem.attribute.max_connect);
if (connected)
{
gem.connected_controllers++;
gem.controllers[i].status = CELL_GEM_STATUS_READY;
gem.controllers[i].port = port_num(i);
}
else
{
gem.controllers[i].status = CELL_GEM_STATUS_DISCONNECTED;
gem.controllers[i].port = 0;
}
}
}
info->max_connect = gem.attribute.max_connect;
info->now_connect = gem.connected_controllers;

View File

@ -36,9 +36,9 @@ struct cfg_gem final : public emulated_pad_config<gem_btn>
cfg_pad_btn<gem_btn> y_axis{ this, "Y-Axis", gem_btn::y_axis, pad_button::ls_y };
};
struct cfg_gems final : public emulated_pads_config<cfg_gem, 7>
struct cfg_gems final : public emulated_pads_config<cfg_gem, 4>
{
cfg_gems() : emulated_pads_config<cfg_gem, 7>("gem") {};
cfg_gems() : emulated_pads_config<cfg_gem, 4>("gem") {};
};
extern cfg_gems g_cfg_gem;