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 * // * HLE helper structs *
// ********************** // **********************
@ -278,12 +292,38 @@ public:
return; return;
} }
bool is_connected = false;
switch (g_cfg.io.move) switch (g_cfg.io.move)
{ {
case move_handler::fake: 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: case move_handler::mouse:
{ {
connected_controllers = 1; connected_controllers = 1;
if (gem_num == 0)
{
is_connected = true;
}
break; break;
} }
#ifdef HAVE_LIBEVDEV #ifdef HAVE_LIBEVDEV
@ -291,8 +331,13 @@ public:
{ {
gun_thread& gun = g_fxo->get<gun_thread>(); gun_thread& gun = g_fxo->get<gun_thread>();
std::scoped_lock lock(gun.handler.mutex); std::scoped_lock lock(gun.handler.mutex);
connected_controllers = gun.handler.init() ? gun.handler.get_num_guns() : 0; gun.num_devices = gun.handler.init() ? gun.handler.get_num_guns() : 0;
gun.num_devices = connected_controllers; 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; break;
} }
#endif #endif
@ -306,10 +351,10 @@ public:
controller.sphere_rgb = gem_color::get_default_color(gem_num); controller.sphere_rgb = gem_color::get_default_color(gem_num);
// Assign status and port number // Assign status and port number
if (gem_num < connected_controllers) if (is_connected)
{ {
controller.status = CELL_GEM_STATUS_READY; 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. * \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. * 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 digital_buttons Bitmask filled with CELL_GEM_CTRL_* values
* \param analog_t Analog value of Move's Trigger. Currently mapped to R2. * \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; digital_buttons = 0;
analog_t = 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); std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler(); 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)) if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
{ {
return; 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) cfg->handle_input(pad, true, [&](gem_btn btn, u16 value, bool pressed)
{ {
if (!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_x = 255;
constexpr u16 ds3_max_y = 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; x_pos = 0;
y_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) cfg->handle_input(pad, true, [&](gem_btn btn, u16 value, bool pressed)
{ {
if (!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> 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()) 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); std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler(); 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)) 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; 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) 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) 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) * \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 * Implementation detail: CellGemExtPortData's digital/analog fields map the same way as
* libPad, so no translation is needed. * 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 * \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 = {}; 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); std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler(); 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)) 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 // 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->max_connect = gem.attribute.max_connect;
info->now_connect = gem.connected_controllers; 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 }; 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; extern cfg_gems g_cfg_gem;