Qt: Implement multitap

This commit is contained in:
Connor McLaughlin 2022-06-08 00:16:37 +10:00 committed by refractionpcsx2
parent d1a235272e
commit 59412b1673
16 changed files with 205 additions and 131 deletions

View File

@ -29,8 +29,13 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.enableSDLSource, "InputSources", "SDL", true);
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.enableSDLEnhancedMode, "InputSources", "SDLControllerEnhancedMode", false);
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.enableXInputSource, "InputSources", "XInput", false);
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.multitapPort1, "EmuCore", "MultitapPort0_Enabled", false);
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.multitapPort2, "EmuCore", "MultitapPort1_Enabled", false);
connect(m_ui.enableSDLSource, &QCheckBox::stateChanged, this, &ControllerGlobalSettingsWidget::updateSDLOptionsEnabled);
for (QCheckBox* cb : {m_ui.multitapPort1, m_ui.multitapPort2})
connect(cb, &QCheckBox::stateChanged, this, [this]() { emit multitapModeChanged(); });
updateSDLOptionsEnabled();
}

View File

@ -35,6 +35,9 @@ public:
void addDeviceToList(const QString& identifier, const QString& name);
void removeDeviceFromList(const QString& identifier);
Q_SIGNALS:
void multitapModeChanged();
private:
void updateSDLOptionsEnabled();

View File

@ -96,7 +96,7 @@
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>The multitap enables up to 8 controllers to be connected to the console. Each multitap provides 4 ports. Multitap is not supported by all games. (NOT YET IMPLEMENTED)</string>
<string>The multitap enables up to 8 controllers to be connected to the console. Each multitap provides 4 ports. Multitap is not supported by all games.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@ -105,9 +105,6 @@
</item>
<item>
<widget class="QCheckBox" name="multitapPort1">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Multitap on Console Port 1</string>
</property>
@ -115,9 +112,6 @@
</item>
<item>
<widget class="QCheckBox" name="multitapPort2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Multitap on Console Port 2</string>
</property>

View File

@ -22,6 +22,11 @@
#include "Settings/ControllerBindingWidgets.h"
#include "Settings/HotkeySettingsWidget.h"
#include "pcsx2/Sio.h"
#include "common/Assertions.h"
#include <array>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QTextEdit>
@ -32,17 +37,14 @@ ControllerSettingsDialog::ControllerSettingsDialog(QWidget* parent /* = nullptr
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
// These are preset in the ui file.
m_global_settings = new ControllerGlobalSettingsWidget(m_ui.settingsContainer, this);
m_ui.settingsContainer->insertWidget(0, m_global_settings);
for (u32 i = 0; i < MAX_PORTS; i++)
{
m_port_bindings[i] = new ControllerBindingWidget(m_ui.settingsContainer, this, i);
m_ui.settingsContainer->insertWidget(i + 1, m_port_bindings[i]);
}
m_ui.settingsContainer->addWidget(m_global_settings);
m_hotkey_settings = new HotkeySettingsWidget(m_ui.settingsContainer, this);
m_ui.settingsContainer->insertWidget(3, m_hotkey_settings);
m_ui.settingsContainer->addWidget(m_hotkey_settings);
// add remainder of ports
createPortWidgets();
m_ui.settingsCategory->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
connect(m_ui.settingsCategory, &QListWidget::currentRowChanged, this, &ControllerSettingsDialog::onCategoryCurrentRowChanged);
@ -53,6 +55,8 @@ ControllerSettingsDialog::ControllerSettingsDialog(QWidget* parent /* = nullptr
connect(g_emu_thread, &EmuThread::onInputDeviceDisconnected, this, &ControllerSettingsDialog::onInputDeviceDisconnected);
connect(g_emu_thread, &EmuThread::onVibrationMotorsEnumerated, this, &ControllerSettingsDialog::onVibrationMotorsEnumerated);
connect(m_global_settings, &ControllerGlobalSettingsWidget::multitapModeChanged, this, &ControllerSettingsDialog::createPortWidgets);
// trigger a device enumeration to populate the device list
g_emu_thread->enumerateInputDevices();
g_emu_thread->enumerateVibrationMotors();
@ -128,3 +132,61 @@ void ControllerSettingsDialog::onVibrationMotorsEnumerated(const QList<InputBind
m_vibration_motors.push_back(QString::fromStdString(key_str));
}
}
void ControllerSettingsDialog::createPortWidgets()
{
// shouldn't mess with it with something else visible
{
QSignalBlocker sb(m_ui.settingsContainer);
QSignalBlocker sb2(m_ui.settingsCategory);
m_ui.settingsContainer->setCurrentIndex(0);
m_ui.settingsCategory->setCurrentRow(0);
}
// remove all except global and hotkeys (i.e. first and last)
pxAssert(m_ui.settingsCategory->count() == m_ui.settingsContainer->count());
while (m_ui.settingsContainer->count() > 2)
{
delete m_ui.settingsCategory->takeItem(1);
QWidget* widget = m_ui.settingsContainer->widget(1);
m_ui.settingsContainer->removeWidget(widget);
delete widget;
}
// because we can't insert and shuffle everything forward, we need to temporarily remove hotkeys
QListWidgetItem* const hotkeys_row = m_ui.settingsCategory->takeItem(1);
QWidget* const hotkeys_widget = m_ui.settingsContainer->widget(1);
m_ui.settingsContainer->removeWidget(hotkeys_widget);
// load mtap settings
const std::array<bool, 2> mtap_enabled = {{Host::GetBaseBoolSettingValue("EmuCore", "MultitapPort0_Enabled", false),
Host::GetBaseBoolSettingValue("EmuCore", "MultitapPort1_Enabled", false)}};
// we reorder things a little to make it look less silly for mtap
static constexpr const std::array<char, 4> mtap_slot_names = {{'A', 'B', 'C', 'D'}};
static constexpr const std::array<u32, MAX_PORTS> mtap_port_order = {{0, 2, 3, 4, 1, 5, 6, 7}};
// create the ports
for (u32 global_slot : mtap_port_order)
{
const bool is_mtap_port = sioPadIsMultitapSlot(global_slot);
const auto [port, slot] = sioConvertPadToPortAndSlot(global_slot);
if (is_mtap_port && !mtap_enabled[port])
continue;
QListWidgetItem* item = new QListWidgetItem();
item->setText(mtap_enabled[port] ?
(tr("Controller Port %1%2").arg(port + 1).arg(mtap_slot_names[slot])) :
tr("Controller Port %1").arg(port + 1));
item->setIcon(QIcon::fromTheme("gamepad-line"));
m_ui.settingsCategory->addItem(item);
m_port_bindings[global_slot] = new ControllerBindingWidget(m_ui.settingsContainer, this, global_slot);
m_ui.settingsContainer->addWidget(m_port_bindings[global_slot]);
}
// and re-insert hotkeys
m_ui.settingsCategory->addItem(hotkeys_row);
m_ui.settingsContainer->addWidget(hotkeys_widget);
}

View File

@ -42,7 +42,7 @@ public:
enum : u32
{
MAX_PORTS = 2
MAX_PORTS = 8
};
ControllerSettingsDialog(QWidget* parent = nullptr);
@ -64,6 +64,8 @@ private Q_SLOTS:
void onInputDeviceDisconnected(const QString& identifier);
void onVibrationMotorsEnumerated(const QList<InputBindingKey>& motors);
void createPortWidgets();
private:
Ui::ControllerSettingsDialog m_ui;

View File

@ -58,24 +58,6 @@
<normaloff>.</normaloff>.</iconset>
</property>
</item>
<item>
<property name="text">
<string>Controller 1</string>
</property>
<property name="icon">
<iconset theme="gamepad-line">
<normaloff>.</normaloff>.</iconset>
</property>
</item>
<item>
<property name="text">
<string>Controller 2</string>
</property>
<property name="icon">
<iconset theme="gamepad-line">
<normaloff>.</normaloff>.</iconset>
</property>
</item>
<item>
<property name="text">
<string>Hotkeys</string>
@ -95,15 +77,6 @@
<height>620</height>
</size>
</property>
<property name="currentIndex">
<number>4</number>
</property>
<widget class="QWidget" name="page"/>
<widget class="QWidget" name="page_3"/>
<widget class="QWidget" name="page_4"/>
<widget class="QWidget" name="page_5"/>
<widget class="QWidget" name="page_6"/>
<widget class="QWidget" name="page_2"/>
</widget>
</item>
<item row="1" column="0" colspan="2">

View File

@ -808,12 +808,6 @@ bool InputManager::DoEventHook(InputBindingKey key, float value)
// Binding Updater
// ------------------------------------------------------------------------
// TODO(Stenzek): Find a better place for this. Maybe in PAD?
static constexpr std::array<const char*, InputManager::MAX_PAD_NUMBER> s_default_pad_types = {{
"DualShock2", // Pad 1
"None" // Pad 2
}};
void InputManager::ReloadBindings(SettingsInterface& si)
{
PauseVibration();
@ -825,8 +819,8 @@ void InputManager::ReloadBindings(SettingsInterface& si)
AddHotkeyBindings(si);
for (u32 pad = 0; pad < MAX_PAD_NUMBER; pad++)
AddPadBindings(si, pad, s_default_pad_types[pad]);
for (u32 pad = 0; pad < PAD::NUM_CONTROLLER_PORTS; pad++)
AddPadBindings(si, pad, PAD::GetDefaultPadType(pad));
}
// ------------------------------------------------------------------------

View File

@ -174,9 +174,6 @@ class InputSource;
namespace InputManager
{
/// Number of emulated pads. TODO: Multitap support.
static constexpr u32 MAX_PAD_NUMBER = 2;
/// Minimum interval between vibration updates when the effect is continuous.
static constexpr double VIBRATION_UPDATE_INTERVAL_SECONDS = 0.5; // 500ms

View File

@ -15,19 +15,8 @@
#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 = 25;
enum gamePadValues

View File

@ -16,12 +16,17 @@
#include "PrecompiledHeader.h"
#include "PAD/Host/KeyStatus.h"
#include "PAD/Host/Global.h"
#include <array>
using namespace PAD;
KeyStatus::KeyStatus()
{
Init();
for (u32 pad = 0; pad < GAMEPAD_NUMBER; pad++)
for (u32 pad = 0; pad < NUM_CONTROLLER_PORTS; pad++)
{
m_axis_scale[pad] = 1.0f;
}
@ -29,7 +34,7 @@ KeyStatus::KeyStatus()
void KeyStatus::Init()
{
for (u32 pad = 0; pad < GAMEPAD_NUMBER; pad++)
for (u32 pad = 0; pad < NUM_CONTROLLER_PORTS; pad++)
{
m_button[pad] = 0xFFFFFFFF;

View File

@ -15,46 +15,46 @@
#pragma once
#include "PAD/Host/Global.h"
#include "PAD/Host/PAD.h"
namespace PAD
{
enum class ControllerType : u8;
}
enum class ControllerType : u8;
class KeyStatus
{
private:
static constexpr u8 m_analog_released_val = 0x7F;
struct PADAnalog
class KeyStatus
{
u8 lx, ly;
u8 rx, ry;
private:
static constexpr u8 m_analog_released_val = 0x7F;
struct PADAnalog
{
u8 lx, ly;
u8 rx, ry;
};
PAD::ControllerType m_type[NUM_CONTROLLER_PORTS] = {};
u32 m_button[NUM_CONTROLLER_PORTS];
u8 m_button_pressure[NUM_CONTROLLER_PORTS][MAX_KEYS];
PADAnalog m_analog[NUM_CONTROLLER_PORTS];
float m_axis_scale[NUM_CONTROLLER_PORTS];
float m_vibration_scale[NUM_CONTROLLER_PORTS][2];
public:
KeyStatus();
void Init();
void Set(u32 pad, u32 index, float value);
__fi PAD::ControllerType GetType(u32 pad) { return m_type[pad]; }
__fi void SetType(u32 pad, PAD::ControllerType type) { m_type[pad] = type; }
__fi void SetAxisScale(u32 pad, float scale) { m_axis_scale[pad] = scale; }
__fi float GetVibrationScale(u32 pad, u32 motor) const { return m_vibration_scale[pad][motor]; }
__fi void SetVibrationScale(u32 pad, u32 motor, float scale) { m_vibration_scale[pad][motor] = scale; }
u32 GetButtons(u32 pad);
u8 GetPressure(u32 pad, u32 index);
};
} // namespace PAD
PAD::ControllerType m_type[GAMEPAD_NUMBER] = {};
u32 m_button[GAMEPAD_NUMBER];
u8 m_button_pressure[GAMEPAD_NUMBER][MAX_KEYS];
PADAnalog m_analog[GAMEPAD_NUMBER];
float m_axis_scale[GAMEPAD_NUMBER];
float m_vibration_scale[GAMEPAD_NUMBER][2];
public:
KeyStatus();
void Init();
void Set(u32 pad, u32 index, float value);
__fi PAD::ControllerType GetType(u32 pad) { return m_type[pad]; }
__fi void SetType(u32 pad, PAD::ControllerType type) { m_type[pad] = type; }
__fi void SetAxisScale(u32 pad, float scale) { m_axis_scale[pad] = scale; }
__fi float GetVibrationScale(u32 pad, u32 motor) const { return m_vibration_scale[pad][motor]; }
__fi void SetVibrationScale(u32 pad, u32 motor, float scale) { m_vibration_scale[pad][motor] = scale; }
u32 GetButtons(u32 pad);
u8 GetPressure(u32 pad, u32 index);
};
extern KeyStatus g_key_status;
extern PAD::KeyStatus g_key_status;

View File

@ -26,11 +26,13 @@
#include "PAD/Host/KeyStatus.h"
#include "PAD/Host/StateManagement.h"
#include <array>
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;
PAD::KeyStatus g_key_status;
namespace PAD
{
@ -47,7 +49,7 @@ namespace PAD
static void ApplyMacroButton(u32 pad, const MacroButton& mb);
static void UpdateMacroButtons();
static std::array<std::array<MacroButton, NUM_MACRO_BUTTONS_PER_CONTROLLER>, GAMEPAD_NUMBER> s_macro_buttons;
static std::array<std::array<MacroButton, NUM_MACRO_BUTTONS_PER_CONTROLLER>, NUM_CONTROLLER_PORTS> s_macro_buttons;
} // namespace PAD
s32 PADinit()
@ -183,7 +185,7 @@ void PAD::LoadConfig(const SettingsInterface& si)
PAD::s_macro_buttons = {};
// This is where we would load controller types, if onepad supported them.
for (u32 i = 0; i < GAMEPAD_NUMBER; i++)
for (u32 i = 0; i < NUM_CONTROLLER_PORTS; i++)
{
const std::string section(StringUtil::StdStringFromFormat("Pad%u", i + 1u));
const std::string type(si.GetStringValue(section.c_str(), "Type", GetDefaultPadType(i)));
@ -221,7 +223,7 @@ void PAD::SetDefaultConfig(SettingsInterface& si)
{
si.ClearSection("InputSources");
for (u32 i = 0; i < GAMEPAD_NUMBER; i++)
for (u32 i = 0; i < NUM_CONTROLLER_PORTS; i++)
si.ClearSection(StringUtil::StdStringFromFormat("Pad%u", i + 1).c_str());
si.ClearSection("Hotkeys");
@ -455,7 +457,7 @@ bool PAD::MapController(SettingsInterface& si, u32 controller,
void PAD::SetControllerState(u32 controller, u32 bind, float value)
{
if (controller >= GAMEPAD_NUMBER || bind >= MAX_KEYS)
if (controller >= NUM_CONTROLLER_PORTS || bind >= MAX_KEYS)
return;
g_key_status.Set(controller, bind, value);
@ -502,7 +504,7 @@ void PAD::LoadMacroButtonConfig(const SettingsInterface& si, u32 pad, const std:
void PAD::SetMacroButtonState(u32 pad, u32 index, bool state)
{
if (pad >= GAMEPAD_NUMBER || index >= NUM_MACRO_BUTTONS_PER_CONTROLLER)
if (pad >= NUM_CONTROLLER_PORTS || index >= NUM_MACRO_BUTTONS_PER_CONTROLLER)
return;
MacroButton& mb = s_macro_buttons[pad][index];
@ -527,7 +529,7 @@ void PAD::ApplyMacroButton(u32 pad, const MacroButton& mb)
void PAD::UpdateMacroButtons()
{
for (u32 pad = 0; pad < GAMEPAD_NUMBER; pad++)
for (u32 pad = 0; pad < NUM_CONTROLLER_PORTS; pad++)
{
for (u32 index = 0; index < NUM_MACRO_BUTTONS_PER_CONTROLLER; index++)
{

View File

@ -16,6 +16,7 @@
#pragma once
#include <string>
#include <tuple>
#include <utility>
#include <vector>
@ -80,6 +81,9 @@ namespace PAD
PAD::VibrationCapabilities vibration_caps;
};
/// Total number of pad ports, across both multitaps.
static constexpr u32 NUM_CONTROLLER_PORTS = 8;
/// Number of macro buttons per controller.
static constexpr u32 NUM_MACRO_BUTTONS_PER_CONTROLLER = 4;

View File

@ -19,6 +19,7 @@
#include "PAD/Host/KeyStatus.h"
#include "PAD/Host/PAD.h"
#include "Frontend/InputManager.h"
#include "Sio.h"
template <class T>
static bool __fi test_bit(T& value, int bit)
@ -63,7 +64,7 @@ void QueryInfo::reset()
u8 QueryInfo::start_poll(int _port)
{
if (_port >= static_cast<int>(GAMEPAD_NUMBER))
if (_port >= 2)
{
reset();
return 0;
@ -72,7 +73,9 @@ u8 QueryInfo::start_poll(int _port)
port = _port;
slot = slots[port];
if (g_key_status.GetType(_port) == PAD::ControllerType::NotConnected)
const u32 ext_port = sioConvertPortAndSlotToPad(port, slot);
if (g_key_status.GetType(ext_port) == PAD::ControllerType::NotConnected)
{
queryDone = 1;
numBytes = 0;
@ -163,7 +166,7 @@ void Pad::rumble_all()
{
for (unsigned port = 0; port < 2; port++)
for (unsigned slot = 0; slot < 4; slot++)
pads[port][slot].rumble(port);
pads[port][slot].rumble(sioConvertPortAndSlotToPad(port, slot));
}
//////////////////////////////////////////////////////////////////////
@ -254,7 +257,8 @@ u8 pad_poll(u8 value)
b1=b1 & 0x1f;
#endif
uint32_t buttons = g_key_status.GetButtons(query.port);
const u32 ext_port = sioConvertPortAndSlotToPad(query.port, query.slot);
const u32 buttons = g_key_status.GetButtons(ext_port);
if (!test_bit(buttons, PAD_ANALOG) && !pad->modeLock)
{
switch (pad->mode)
@ -279,28 +283,28 @@ u8 pad_poll(u8 value)
{ // 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);
query.response[5] = g_key_status.GetPressure(ext_port, PAD_R_RIGHT);
query.response[6] = g_key_status.GetPressure(ext_port, PAD_R_UP);
query.response[7] = g_key_status.GetPressure(ext_port, PAD_L_RIGHT);
query.response[8] = g_key_status.GetPressure(ext_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[9] = !test_bit(buttons, 13) ? g_key_status.GetPressure(ext_port, PAD_RIGHT) : 0;
query.response[10] = !test_bit(buttons, 15) ? g_key_status.GetPressure(ext_port, PAD_LEFT) : 0;
query.response[11] = !test_bit(buttons, 12) ? g_key_status.GetPressure(ext_port, PAD_UP) : 0;
query.response[12] = !test_bit(buttons, 14) ? g_key_status.GetPressure(ext_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;
query.response[13] = !test_bit(buttons, 4) ? g_key_status.GetPressure(ext_port, PAD_TRIANGLE) : 0;
query.response[14] = !test_bit(buttons, 5) ? g_key_status.GetPressure(ext_port, PAD_CIRCLE) : 0;
query.response[15] = !test_bit(buttons, 6) ? g_key_status.GetPressure(ext_port, PAD_CROSS) : 0;
query.response[16] = !test_bit(buttons, 7) ? g_key_status.GetPressure(ext_port, PAD_SQUARE) : 0;
query.response[17] = !test_bit(buttons, 2) ? g_key_status.GetPressure(ext_port, PAD_L1) : 0;
query.response[18] = !test_bit(buttons, 3) ? g_key_status.GetPressure(ext_port, PAD_R1) : 0;
query.response[19] = !test_bit(buttons, 0) ? g_key_status.GetPressure(ext_port, PAD_L2) : 0;
query.response[20] = !test_bit(buttons, 1) ? g_key_status.GetPressure(ext_port, PAD_R2) : 0;
}
}

View File

@ -1055,3 +1055,33 @@ void SaveStateBase::sioFreeze()
}
}
}
std::tuple<u32, u32> sioConvertPadToPortAndSlot(u32 index)
{
if (index > 4) // [5,6,7]
return std::make_tuple(1, index - 4); // 2B,2C,2D
else if (index > 1) // [2,3,4]
return std::make_tuple(0, index - 1); // 1B,1C,1D
else // [0,1]
return std::make_tuple(index, 0); // 1A,2A
}
u32 sioConvertPortAndSlotToPad(u32 port, u32 slot)
{
if (slot == 0)
return port;
else if (port == 0) // slot=[0,1]
return slot + 1; // 2,3,4
else
return slot + 4; // 5,6,7
}
bool sioPadIsMultitapSlot(u32 index)
{
return (index >= 2);
}
bool sioPortAndSlotIsMultitap(u32 port, u32 slot)
{
return (slot != 0);
}

View File

@ -127,3 +127,13 @@ extern void ClearMcdEjectTimeoutNow();
extern void sioStatRead();
extern void sioSetGameSerial(const std::string& serial);
extern void sioNextFrame();
/// Converts a global pad index to a multitap port and slot.
extern std::tuple<u32, u32> sioConvertPadToPortAndSlot(u32 index);
/// Converts a multitap port and slot to a global pad index.
extern u32 sioConvertPortAndSlotToPad(u32 port, u32 slot);
/// Returns true if the given pad index is a multitap slot.
extern bool sioPadIsMultitapSlot(u32 index);
extern bool sioPortAndSlotIsMultitap(u32 port, u32 slot);