diff --git a/.github/labeler.yml b/.github/labeler.yml
index cda83c3042..01c2407baf 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -115,11 +115,11 @@
- 'pcsx2/IPU/*'
- 'pcsx2/IPU/**/*'
'Memory Card':
- - 'pcsx2/gui/MemoryCard*'
- - 'pcsx2/gui/**/MemoryCard*'
+ - 'pcsx2/SIO/Memcard/*'
+ - 'pcsx2/SIO/Memcard/**/*'
'PAD':
- - 'pcsx2/PAD/*'
- - 'pcsx2/PAD/**/*'
+ - 'pcsx2/SIO/Pad/*'
+ - 'pcsx2/SIO/Pad/**/*'
'SPU2':
- 'pcsx2/SPU2/*'
- 'pcsx2/SPU2/**/*'
diff --git a/pcsx2-qt/CMakeLists.txt b/pcsx2-qt/CMakeLists.txt
index 708ce739b5..9128762519 100644
--- a/pcsx2-qt/CMakeLists.txt
+++ b/pcsx2-qt/CMakeLists.txt
@@ -55,6 +55,7 @@ target_sources(pcsx2-qt PRIVATE
Settings/BIOSSettingsWidget.ui
Settings/ControllerBindingWidget.ui
Settings/ControllerBindingWidget_DualShock2.ui
+ Settings/ControllerBindingWidget_Guitar.ui
Settings/ControllerBindingWidgets.cpp
Settings/ControllerBindingWidgets.h
Settings/ControllerLEDSettingsDialog.ui
diff --git a/pcsx2-qt/QtHost.cpp b/pcsx2-qt/QtHost.cpp
index 2a836562ea..c5fdda4d17 100644
--- a/pcsx2-qt/QtHost.cpp
+++ b/pcsx2-qt/QtHost.cpp
@@ -40,7 +40,6 @@
#include "pcsx2/Input/InputManager.h"
#include "pcsx2/LogSink.h"
#include "pcsx2/MTGS.h"
-#include "pcsx2/PAD/Host/PAD.h"
#include "pcsx2/PerformanceMetrics.h"
#include "pcsx2/SysForwardDefs.h"
#include "pcsx2/VMManager.h"
diff --git a/pcsx2-qt/Settings/ControllerBindingWidget_Guitar.ui b/pcsx2-qt/Settings/ControllerBindingWidget_Guitar.ui
new file mode 100644
index 0000000000..268d4a9d8b
--- /dev/null
+++ b/pcsx2-qt/Settings/ControllerBindingWidget_Guitar.ui
@@ -0,0 +1,317 @@
+
+
+ ControllerBindingWidget_Guitar
+
+
+
+ 0
+ 0
+ 1100
+ 500
+
+
+
+ Form
+
+
+
+
+ 0
+ 0
+ 1101
+ 322
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 800
+ 320
+
+
+
+
+
+
+ :/images/guitar.png
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+ 0
+ 320
+ 1101
+ 181
+
+
+
+ -
+
+
+ Yellow
+
+
+
+
+ 24
+ 30
+ 90
+ 40
+
+
+
+ PushButton
+
+
+
+
+ -
+
+
+ Start
+
+
+
+
+ 24
+ 30
+ 90
+ 40
+
+
+
+ PushButton
+
+
+
+
+ -
+
+
+ Red
+
+
+
+
+ 24
+ 30
+ 90
+ 40
+
+
+
+ PushButton
+
+
+
+
+ -
+
+
+ Green
+
+
+
+
+ 24
+ 30
+ 90
+ 40
+
+
+
+ PushButton
+
+
+
+
+ -
+
+
+ Orange
+
+
+
+
+ 24
+ 30
+ 90
+ 40
+
+
+
+ PushButton
+
+
+
+
+ -
+
+
+ Select
+
+
+
+
+ 24
+ 30
+ 90
+ 40
+
+
+
+ PushButton
+
+
+
+
+ -
+
+
+ Strum Up
+
+
+
+
+ 24
+ 30
+ 90
+ 40
+
+
+
+ PushButton
+
+
+
+
+ -
+
+
+ Strum Down
+
+
+
+
+ 24
+ 30
+ 90
+ 40
+
+
+
+ PushButton
+
+
+
+
+ -
+
+
+ Blue
+
+
+
+
+ 24
+ 30
+ 90
+ 40
+
+
+
+ PushButton
+
+
+
+
+ -
+
+
+ Whammy Bar
+
+
+
+
+ 24
+ 30
+ 90
+ 40
+
+
+
+ PushButton
+
+
+
+
+ -
+
+
+ Tilt
+
+
+
+
+ 20
+ 30
+ 90
+ 40
+
+
+
+ PushButton
+
+
+
+
+
+
+
+
+
+ InputBindingWidget
+ QPushButton
+ Settings/InputBindingWidget.h
+
+
+
+
+
+
+
diff --git a/pcsx2-qt/Settings/ControllerBindingWidgets.cpp b/pcsx2-qt/Settings/ControllerBindingWidgets.cpp
index 001a5d2b5c..8e1a536523 100644
--- a/pcsx2-qt/Settings/ControllerBindingWidgets.cpp
+++ b/pcsx2-qt/Settings/ControllerBindingWidgets.cpp
@@ -26,7 +26,7 @@
#include "common/StringUtil.h"
#include "pcsx2/Host.h"
-#include "pcsx2/PAD/Host/PAD.h"
+#include "pcsx2/SIO/Pad/PadConfig.h"
#include "Settings/ControllerBindingWidgets.h"
#include "Settings/ControllerSettingsDialog.h"
@@ -52,7 +52,7 @@ ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSett
onTypeChanged();
ControllerSettingWidgetBinder::BindWidgetToInputProfileString(
- m_dialog->getProfileSettingsInterface(), m_ui.controllerType, m_config_section, "Type", PAD::GetDefaultPadType(port));
+ m_dialog->getProfileSettingsInterface(), m_ui.controllerType, m_config_section, "Type", g_PadConfig.GetDefaultPadType(port));
connect(m_ui.controllerType, QOverload::of(&QComboBox::currentIndexChanged), this, &ControllerBindingWidget::onTypeChanged);
connect(m_ui.bindings, &QPushButton::clicked, this, &ControllerBindingWidget::onBindingsClicked);
@@ -71,14 +71,14 @@ QIcon ControllerBindingWidget::getIcon() const
void ControllerBindingWidget::populateControllerTypes()
{
- for (const auto& [name, display_name] : PAD::GetControllerTypeNames())
+ for (const auto& [name, display_name] : g_PadConfig.GetControllerTypeNames())
m_ui.controllerType->addItem(qApp->translate("Pad", display_name), QString::fromStdString(name));
}
void ControllerBindingWidget::onTypeChanged()
{
const bool is_initializing = (m_ui.stackedWidget->count() == 0);
- m_controller_type = m_dialog->getStringValue(m_config_section.c_str(), "Type", PAD::GetDefaultPadType(m_port_number));
+ m_controller_type = m_dialog->getStringValue(m_config_section.c_str(), "Type", g_PadConfig.GetDefaultPadType(m_port_number));
if (m_bindings_widget)
{
@@ -99,16 +99,24 @@ void ControllerBindingWidget::onTypeChanged()
m_macros_widget = nullptr;
}
- const PAD::ControllerInfo* cinfo = PAD::GetControllerInfo(m_controller_type);
+ const PadConfig::ControllerInfo* cinfo = g_PadConfig.GetControllerInfo(m_controller_type);
const bool has_settings = (cinfo && cinfo->num_settings > 0);
const bool has_macros = (cinfo && cinfo->num_bindings > 0);
m_ui.settings->setEnabled(has_settings);
m_ui.macros->setEnabled(has_macros);
if (m_controller_type == "DualShock2")
+ {
m_bindings_widget = ControllerBindingWidget_DualShock2::createInstance(this);
+ }
+ else if (m_controller_type == "Guitar")
+ {
+ m_bindings_widget = ControllerBindingWidget_Guitar::createInstance(this);
+ }
else
+ {
m_bindings_widget = new ControllerBindingWidget_Base(this);
+ }
m_ui.stackedWidget->addWidget(m_bindings_widget);
m_ui.stackedWidget->setCurrentWidget(m_bindings_widget);
@@ -210,13 +218,13 @@ void ControllerBindingWidget::onClearBindingsClicked()
{
{
auto lock = Host::GetSettingsLock();
- PAD::ClearPortBindings(*Host::Internal::GetBaseSettingsLayer(), m_port_number);
+ g_PadConfig.ClearPortBindings(*Host::Internal::GetBaseSettingsLayer(), m_port_number);
}
Host::CommitBaseSettingChanges();
}
else
{
- PAD::ClearPortBindings(*m_dialog->getProfileSettingsInterface(), m_port_number);
+ g_PadConfig.ClearPortBindings(*m_dialog->getProfileSettingsInterface(), m_port_number);
m_dialog->getProfileSettingsInterface()->Save();
}
@@ -240,14 +248,14 @@ void ControllerBindingWidget::doDeviceAutomaticBinding(const QString& device)
{
{
auto lock = Host::GetSettingsLock();
- result = PAD::MapController(*Host::Internal::GetBaseSettingsLayer(), m_port_number, mapping);
+ result = g_PadConfig.MapController(*Host::Internal::GetBaseSettingsLayer(), m_port_number, mapping);
}
if (result)
Host::CommitBaseSettingChanges();
}
else
{
- result = PAD::MapController(*m_dialog->getProfileSettingsInterface(), m_port_number, mapping);
+ result = g_PadConfig.MapController(*m_dialog->getProfileSettingsInterface(), m_port_number, mapping);
if (result)
{
m_dialog->getProfileSettingsInterface()->Save();
@@ -312,7 +320,7 @@ ControllerMacroEditWidget::ControllerMacroEditWidget(ControllerMacroWidget* pare
ControllerSettingsDialog* dialog = m_bwidget->getDialog();
const std::string& section = m_bwidget->getConfigSection();
- const PAD::ControllerInfo* cinfo = PAD::GetControllerInfo(m_bwidget->getControllerType());
+ const PadConfig::ControllerInfo* cinfo = g_PadConfig.GetControllerInfo(m_bwidget->getControllerType());
if (!cinfo)
{
// Shouldn't ever happen.
@@ -423,7 +431,7 @@ void ControllerMacroEditWidget::updateFrequencyText()
void ControllerMacroEditWidget::updateBinds()
{
ControllerSettingsDialog* dialog = m_bwidget->getDialog();
- const PAD::ControllerInfo* cinfo = PAD::GetControllerInfo(m_bwidget->getControllerType());
+ const PadConfig::ControllerInfo* cinfo = g_PadConfig.GetControllerInfo(m_bwidget->getControllerType());
if (!cinfo)
return;
@@ -793,7 +801,7 @@ QIcon ControllerBindingWidget_Base::getIcon() const
void ControllerBindingWidget_Base::initBindingWidgets()
{
- const PAD::ControllerInfo* cinfo = PAD::GetControllerInfo(getControllerType());
+ const PadConfig::ControllerInfo* cinfo = g_PadConfig.GetControllerInfo(getControllerType());
if (!cinfo)
return;
@@ -820,7 +828,7 @@ void ControllerBindingWidget_Base::initBindingWidgets()
switch (cinfo->vibration_caps)
{
- case PAD::VibrationCapabilities::LargeSmallMotors:
+ case Pad::VibrationCapabilities::LargeSmallMotors:
{
InputVibrationBindingWidget* widget = findChild(QStringLiteral("LargeMotor"));
if (widget)
@@ -832,7 +840,7 @@ void ControllerBindingWidget_Base::initBindingWidgets()
}
break;
- case PAD::VibrationCapabilities::SingleMotor:
+ case Pad::VibrationCapabilities::SingleMotor:
{
InputVibrationBindingWidget* widget = findChild(QStringLiteral("Motor"));
if (widget)
@@ -840,7 +848,7 @@ void ControllerBindingWidget_Base::initBindingWidgets()
}
break;
- case PAD::VibrationCapabilities::NoVibration:
+ case Pad::VibrationCapabilities::NoVibration:
default:
break;
}
@@ -867,6 +875,27 @@ ControllerBindingWidget_Base* ControllerBindingWidget_DualShock2::createInstance
return new ControllerBindingWidget_DualShock2(parent);
}
+ControllerBindingWidget_Guitar::ControllerBindingWidget_Guitar(ControllerBindingWidget* parent)
+ : ControllerBindingWidget_Base(parent)
+{
+ m_ui.setupUi(this);
+ initBindingWidgets();
+}
+
+ControllerBindingWidget_Guitar::~ControllerBindingWidget_Guitar()
+{
+}
+
+QIcon ControllerBindingWidget_Guitar::getIcon() const
+{
+ return QIcon::fromTheme("guitar-line");
+}
+
+ControllerBindingWidget_Base* ControllerBindingWidget_Guitar::createInstance(ControllerBindingWidget* parent)
+{
+ return new ControllerBindingWidget_Guitar(parent);
+}
+
//////////////////////////////////////////////////////////////////////////
USBDeviceWidget::USBDeviceWidget(QWidget* parent, ControllerSettingsDialog* dialog, u32 port)
diff --git a/pcsx2-qt/Settings/ControllerBindingWidgets.h b/pcsx2-qt/Settings/ControllerBindingWidgets.h
index 3cbf508e61..d3479e3cf4 100644
--- a/pcsx2-qt/Settings/ControllerBindingWidgets.h
+++ b/pcsx2-qt/Settings/ControllerBindingWidgets.h
@@ -15,7 +15,7 @@
#pragma once
-#include "PAD/Host/PAD.h"
+#include "pcsx2/SIO/Pad/PadMacros.h"
#include
@@ -23,6 +23,7 @@
#include "ui_ControllerBindingWidget.h"
#include "ui_ControllerBindingWidget_DualShock2.h"
+#include "ui_ControllerBindingWidget_Guitar.h"
#include "ui_ControllerMacroWidget.h"
#include "ui_ControllerMacroEditWidget.h"
#include "ui_USBDeviceWidget.h"
@@ -91,7 +92,7 @@ public:
void updateListItem(u32 index);
private:
- static constexpr u32 NUM_MACROS = PAD::NUM_MACRO_BUTTONS_PER_CONTROLLER;
+ static constexpr u32 NUM_MACROS = PadMacros::NUM_MACRO_BUTTONS_PER_CONTROLLER;
void createWidgets(ControllerBindingWidget* parent);
@@ -193,6 +194,22 @@ private:
Ui::ControllerBindingWidget_DualShock2 m_ui;
};
+class ControllerBindingWidget_Guitar final : public ControllerBindingWidget_Base
+{
+ Q_OBJECT
+
+public:
+ ControllerBindingWidget_Guitar(ControllerBindingWidget* parent);
+ ~ControllerBindingWidget_Guitar();
+
+ QIcon getIcon() const override;
+
+ static ControllerBindingWidget_Base* createInstance(ControllerBindingWidget* parent);
+
+private:
+ Ui::ControllerBindingWidget_Guitar m_ui;
+};
+
//////////////////////////////////////////////////////////////////////////
class USBDeviceWidget final : public QWidget
diff --git a/pcsx2-qt/Settings/ControllerSettingsDialog.cpp b/pcsx2-qt/Settings/ControllerSettingsDialog.cpp
index a42c32b711..2451832369 100644
--- a/pcsx2-qt/Settings/ControllerSettingsDialog.cpp
+++ b/pcsx2-qt/Settings/ControllerSettingsDialog.cpp
@@ -22,8 +22,8 @@
#include "Settings/HotkeySettingsWidget.h"
#include "pcsx2/INISettingsInterface.h"
-#include "pcsx2/PAD/Host/PAD.h"
-#include "pcsx2/Sio.h"
+#include "pcsx2/SIO/Pad/PadConfig.h"
+#include "pcsx2/SIO/Sio.h"
#include "pcsx2/VMManager.h"
#include "common/Assertions.h"
@@ -130,7 +130,7 @@ void ControllerSettingsDialog::onNewProfileClicked()
{
// from global
auto lock = Host::GetSettingsLock();
- PAD::CopyConfiguration(&temp_si, *Host::Internal::GetBaseSettingsLayer(), true, true, false);
+ g_PadConfig.CopyConfiguration(&temp_si, *Host::Internal::GetBaseSettingsLayer(), true, true, false);
USB::CopyConfiguration(&temp_si, *Host::Internal::GetBaseSettingsLayer(), true, true);
}
else
@@ -138,7 +138,7 @@ void ControllerSettingsDialog::onNewProfileClicked()
// from profile
const bool copy_hotkey_bindings = m_profile_interface->GetBoolValue("Pad", "UseProfileHotkeyBindings", false);
temp_si.SetBoolValue("Pad", "UseProfileHotkeyBindings", copy_hotkey_bindings);
- PAD::CopyConfiguration(&temp_si, *m_profile_interface, true, true, copy_hotkey_bindings);
+ g_PadConfig.CopyConfiguration(&temp_si, *m_profile_interface, true, true, copy_hotkey_bindings);
USB::CopyConfiguration(&temp_si, *m_profile_interface, true, true);
}
}
@@ -167,7 +167,7 @@ void ControllerSettingsDialog::onLoadProfileClicked()
{
auto lock = Host::GetSettingsLock();
- PAD::CopyConfiguration(Host::Internal::GetBaseSettingsLayer(), *m_profile_interface, true, true, false);
+ g_PadConfig.CopyConfiguration(Host::Internal::GetBaseSettingsLayer(), *m_profile_interface, true, true, false);
USB::CopyConfiguration(Host::Internal::GetBaseSettingsLayer(), *m_profile_interface, true, true);
}
Host::CommitBaseSettingChanges();
@@ -403,7 +403,7 @@ void ControllerSettingsDialog::createWidgets()
m_port_bindings[global_slot] = new ControllerBindingWidget(m_ui.settingsContainer, this, global_slot);
m_ui.settingsContainer->addWidget(m_port_bindings[global_slot]);
- const PAD::ControllerInfo* ci = PAD::GetControllerInfo(m_port_bindings[global_slot]->getControllerType());
+ const PadConfig::ControllerInfo* ci = g_PadConfig.GetControllerInfo(m_port_bindings[global_slot]->getControllerType());
const QString display_name(ci ? qApp->translate("Pad", ci->display_name) : QStringLiteral("Unknown"));
QListWidgetItem* item = new QListWidgetItem();
@@ -459,7 +459,7 @@ void ControllerSettingsDialog::updateListDescription(u32 global_slot, Controller
const auto [port, slot] = sioConvertPadToPortAndSlot(global_slot);
const bool mtap_enabled = getBoolValue("Pad", (port == 0) ? "MultitapPort1" : "MultitapPort2", false);
- const PAD::ControllerInfo* ci = PAD::GetControllerInfo(widget->getControllerType());
+ const PadConfig::ControllerInfo* ci = g_PadConfig.GetControllerInfo(widget->getControllerType());
const QString display_name(ci ? qApp->translate("Pad", ci->display_name) : QStringLiteral("Unknown"));
//: Controller Port is an official term from Sony. Find the official translation for your language inside the console's manual.
@@ -492,7 +492,7 @@ void ControllerSettingsDialog::updateListDescription(u32 port, USBDeviceWidget*
void ControllerSettingsDialog::refreshProfileList()
{
- const std::vector names(PAD::GetInputProfileNames());
+ const std::vector names(g_PadConfig.GetInputProfileNames());
QSignalBlocker sb(m_ui.currentProfile);
m_ui.currentProfile->clear();
diff --git a/pcsx2-qt/Settings/GameSummaryWidget.cpp b/pcsx2-qt/Settings/GameSummaryWidget.cpp
index dc958a8710..1b2b5ef847 100644
--- a/pcsx2-qt/Settings/GameSummaryWidget.cpp
+++ b/pcsx2-qt/Settings/GameSummaryWidget.cpp
@@ -15,6 +15,7 @@
#include "PrecompiledHeader.h"
+#include "pcsx2/SIO/Pad/PadConfig.h"
#include "GameSummaryWidget.h"
#include "SettingsDialog.h"
#include "MainWindow.h"
@@ -25,7 +26,6 @@
#include "pcsx2/CDVD/IsoHasher.h"
#include "pcsx2/GameDatabase.h"
#include "pcsx2/GameList.h"
-#include "pcsx2/PAD/Host/PAD.h"
#include "common/Error.h"
#include "common/MD5Digest.h"
@@ -69,7 +69,7 @@ GameSummaryWidget::~GameSummaryWidget() = default;
void GameSummaryWidget::populateInputProfiles()
{
- for (const std::string& name : PAD::GetInputProfileNames())
+ for (const std::string& name : g_PadConfig.GetInputProfileNames())
m_ui.inputProfile->addItem(QString::fromStdString(name));
}
diff --git a/pcsx2-qt/Settings/MemoryCardConvertDialog.h b/pcsx2-qt/Settings/MemoryCardConvertDialog.h
index fdbe0d3490..1d5616b212 100644
--- a/pcsx2-qt/Settings/MemoryCardConvertDialog.h
+++ b/pcsx2-qt/Settings/MemoryCardConvertDialog.h
@@ -23,7 +23,7 @@
#include "MemoryCardConvertWorker.h"
-#include "pcsx2/MemoryCardFile.h"
+#include "pcsx2/SIO/Memcard/MemoryCardFile.h"
class MemoryCardConvertDialog final : public QDialog
{
diff --git a/pcsx2-qt/Settings/MemoryCardConvertWorker.h b/pcsx2-qt/Settings/MemoryCardConvertWorker.h
index 5812c566a2..9413f86477 100644
--- a/pcsx2-qt/Settings/MemoryCardConvertWorker.h
+++ b/pcsx2-qt/Settings/MemoryCardConvertWorker.h
@@ -7,8 +7,8 @@
#include "QtProgressCallback.h"
-#include "pcsx2/MemoryCardFile.h"
-#include "pcsx2/MemoryCardFolder.h"
+#include "pcsx2/SIO/Memcard/MemoryCardFile.h"
+#include "pcsx2/SIO/Memcard/MemoryCardFolder.h"
class MemoryCardConvertWorker : public QtAsyncProgressThread
{
diff --git a/pcsx2-qt/Settings/MemoryCardCreateDialog.cpp b/pcsx2-qt/Settings/MemoryCardCreateDialog.cpp
index 48d74689ff..7843620f33 100644
--- a/pcsx2-qt/Settings/MemoryCardCreateDialog.cpp
+++ b/pcsx2-qt/Settings/MemoryCardCreateDialog.cpp
@@ -24,7 +24,7 @@
#include "Settings/MemoryCardCreateDialog.h"
-#include "pcsx2/MemoryCardFile.h"
+#include "pcsx2/SIO/Memcard/MemoryCardFile.h"
#include "pcsx2/System.h"
MemoryCardCreateDialog::MemoryCardCreateDialog(QWidget* parent /* = nullptr */)
diff --git a/pcsx2-qt/Settings/MemoryCardSettingsWidget.cpp b/pcsx2-qt/Settings/MemoryCardSettingsWidget.cpp
index 98566cbbd6..e4cdfe0e56 100644
--- a/pcsx2-qt/Settings/MemoryCardSettingsWidget.cpp
+++ b/pcsx2-qt/Settings/MemoryCardSettingsWidget.cpp
@@ -32,7 +32,7 @@
#include "SettingWidgetBinder.h"
#include "SettingsDialog.h"
-#include "pcsx2/MemoryCardFile.h"
+#include "pcsx2/SIO/Memcard/MemoryCardFile.h"
static constexpr const char* CONFIG_SECTION = "MemoryCards";
diff --git a/pcsx2-qt/SetupWizardDialog.cpp b/pcsx2-qt/SetupWizardDialog.cpp
index 7e08384cb3..e9ebe8d9b2 100644
--- a/pcsx2-qt/SetupWizardDialog.cpp
+++ b/pcsx2-qt/SetupWizardDialog.cpp
@@ -15,7 +15,7 @@
#include "PrecompiledHeader.h"
-#include "PAD/Host/PAD.h"
+#include "pcsx2/SIO/Pad/PadConfig.h"
#include "QtHost.h"
#include "QtUtils.h"
#include "SettingWidgetBinder.h"
@@ -414,10 +414,10 @@ void SetupWizardDialog::setupControllerPage()
const std::string section = fmt::format("Pad{}", port + 1);
const PadWidgets& w = pad_widgets[port];
- for (const auto& [name, display_name] : PAD::GetControllerTypeNames())
+ for (const auto& [name, display_name] : g_PadConfig.GetControllerTypeNames())
w.type_combo->addItem(qApp->translate("Pad", display_name), QString::fromStdString(name));
ControllerSettingWidgetBinder::BindWidgetToInputProfileString(
- nullptr, w.type_combo, section, "Type", PAD::GetDefaultPadType(port));
+ nullptr, w.type_combo, section, "Type", g_PadConfig.GetDefaultPadType(port));
w.mapping_result->setText((port == 0) ? tr("Default (Keyboard)") : tr("Default (None)"));
@@ -473,7 +473,7 @@ void SetupWizardDialog::doDeviceAutomaticBinding(u32 port, QLabel* update_label,
bool result;
{
auto lock = Host::GetSettingsLock();
- result = PAD::MapController(*Host::Internal::GetBaseSettingsLayer(), port, mapping);
+ result = g_PadConfig.MapController(*Host::Internal::GetBaseSettingsLayer(), port, mapping);
}
if (!result)
return;
diff --git a/pcsx2-qt/pcsx2-qt.vcxproj b/pcsx2-qt/pcsx2-qt.vcxproj
index 2c2dc0ccf4..562bda532b 100644
--- a/pcsx2-qt/pcsx2-qt.vcxproj
+++ b/pcsx2-qt/pcsx2-qt.vcxproj
@@ -306,6 +306,9 @@
Document
+
+ Document
+
Document
diff --git a/pcsx2-qt/pcsx2-qt.vcxproj.filters b/pcsx2-qt/pcsx2-qt.vcxproj.filters
index 43154da722..3cef1749b1 100644
--- a/pcsx2-qt/pcsx2-qt.vcxproj.filters
+++ b/pcsx2-qt/pcsx2-qt.vcxproj.filters
@@ -531,6 +531,9 @@
Settings
+
+ Settings
+
Settings
diff --git a/pcsx2-qt/resources/icons/black/svg/guitar-line.svg b/pcsx2-qt/resources/icons/black/svg/guitar-line.svg
new file mode 100644
index 0000000000..d941edee13
--- /dev/null
+++ b/pcsx2-qt/resources/icons/black/svg/guitar-line.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/pcsx2-qt/resources/icons/white/svg/guitar-line.svg b/pcsx2-qt/resources/icons/white/svg/guitar-line.svg
new file mode 100644
index 0000000000..e0498e9030
--- /dev/null
+++ b/pcsx2-qt/resources/icons/white/svg/guitar-line.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/pcsx2-qt/resources/images/guitar.png b/pcsx2-qt/resources/images/guitar.png
new file mode 100644
index 0000000000..708cd1c22b
Binary files /dev/null and b/pcsx2-qt/resources/images/guitar.png differ
diff --git a/pcsx2-qt/resources/resources.qrc b/pcsx2-qt/resources/resources.qrc
index ae2962aaba..4b10d1695d 100644
--- a/pcsx2-qt/resources/resources.qrc
+++ b/pcsx2-qt/resources/resources.qrc
@@ -1,147 +1,150 @@
-
- icons/AppIcon64.png
- icons/applications-system-24.png
- icons/black/index.theme
- icons/black/svg/arrow-left-right-line.svg
- icons/black/svg/artboard-2-line.svg
- icons/black/svg/at.svg
- icons/black/svg/band-aid-line.svg
- icons/black/svg/brush-line.svg
- icons/black/svg/cheats-line.svg
- icons/black/svg/checkbox-multiple-blank-line.svg
- icons/black/svg/chip-line.svg
- icons/black/svg/close-line.svg
- icons/black/svg/controller-line.svg
- icons/black/svg/controller-strike-line.svg
- icons/black/svg/delete-back-2-line.svg
- icons/black/svg/disc-2-line.svg
- icons/black/svg/disc-eject-line.svg
- icons/black/svg/discord.svg
- icons/black/svg/door-open-line.svg
- icons/black/svg/download-2-line.svg
- icons/black/svg/eject-line.svg
- icons/black/svg/emulation-line.svg
- icons/black/svg/file-add-line.svg
- icons/black/svg/file-line.svg
- icons/black/svg/file-list-line.svg
- icons/black/svg/file-reduce-line.svg
- icons/black/svg/file-search-line.svg
- icons/black/svg/file-settings-line.svg
- icons/black/svg/filter-line.svg
- icons/black/svg/flashlight-line.svg
- icons/black/svg/floppy-in-line.svg
- icons/black/svg/floppy-out-line.svg
- icons/black/svg/folder-add-line.svg
- icons/black/svg/folder-open-line.svg
- icons/black/svg/folder-reduce-line.svg
- icons/black/svg/folder-settings-line.svg
- icons/black/svg/fullscreen-line.svg
- icons/black/svg/function-line.svg
- icons/black/svg/github.svg
- icons/black/svg/global-line.svg
- icons/black/svg/heart-circle-line.svg
- icons/black/svg/heart-monitor-line.svg
- icons/black/svg/image-fill.svg
- icons/black/svg/interface-line.svg
- icons/black/svg/keyboard-line.svg
- icons/black/svg/lightbulb-line.svg
- icons/black/svg/list-check.svg
- icons/black/svg/login-box-line.svg
- icons/black/svg/memcard-line.svg
- icons/black/svg/mouse-line.svg
- icons/black/svg/pause-line.svg
- icons/black/svg/play-line.svg
- icons/black/svg/price-tag-3-line.svg
- icons/black/svg/refresh-line.svg
- icons/black/svg/restart-line.svg
- icons/black/svg/save-3-line.svg
- icons/black/svg/screenshot-2-line.svg
- icons/black/svg/settings-3-line.svg
- icons/black/svg/shut-down-line.svg
- icons/black/svg/tools-line.svg
- icons/black/svg/trophy-line.svg
- icons/black/svg/tv-2-line.svg
- icons/black/svg/usb-fill.svg
- icons/black/svg/volume-up-line.svg
- icons/black/svg/warning-line.svg
- icons/black/svg/window-2-line.svg
- icons/black/svg/zoom-in-line.svg
- icons/black/svg/zoom-out-line.svg
- icons/logo.png
- icons/media-optical-24.png
- icons/media-optical-gear-24.png
- icons/media-optical.png
- icons/QT.png
- icons/update.png
- icons/white/index.theme
- icons/white/svg/arrow-left-right-line.svg
- icons/white/svg/artboard-2-line.svg
- icons/white/svg/at.svg
- icons/white/svg/band-aid-line.svg
- icons/white/svg/brush-line.svg
- icons/white/svg/cheats-line.svg
- icons/white/svg/checkbox-multiple-blank-line.svg
- icons/white/svg/chip-line.svg
- icons/white/svg/close-line.svg
- icons/white/svg/controller-line.svg
- icons/white/svg/controller-strike-line.svg
- icons/white/svg/delete-back-2-line.svg
- icons/white/svg/disc-2-line.svg
- icons/white/svg/disc-eject-line.svg
- icons/white/svg/discord.svg
- icons/white/svg/door-open-line.svg
- icons/white/svg/download-2-line.svg
- icons/white/svg/eject-line.svg
- icons/white/svg/emulation-line.svg
- icons/white/svg/file-add-line.svg
- icons/white/svg/file-line.svg
- icons/white/svg/file-list-line.svg
- icons/white/svg/file-reduce-line.svg
- icons/white/svg/file-search-line.svg
- icons/white/svg/file-settings-line.svg
- icons/white/svg/filter-line.svg
- icons/white/svg/flashlight-line.svg
- icons/white/svg/floppy-in-line.svg
- icons/white/svg/floppy-out-line.svg
- icons/white/svg/folder-add-line.svg
- icons/white/svg/folder-open-line.svg
- icons/white/svg/folder-reduce-line.svg
- icons/white/svg/folder-settings-line.svg
- icons/white/svg/fullscreen-line.svg
- icons/white/svg/function-line.svg
- icons/white/svg/github.svg
- icons/white/svg/global-line.svg
- icons/white/svg/heart-circle-line.svg
- icons/white/svg/heart-monitor-line.svg
- icons/white/svg/image-fill.svg
- icons/white/svg/interface-line.svg
- icons/white/svg/keyboard-line.svg
- icons/white/svg/lightbulb-line.svg
- icons/white/svg/list-check.svg
- icons/white/svg/login-box-line.svg
- icons/white/svg/memcard-line.svg
- icons/white/svg/mouse-line.svg
- icons/white/svg/pause-line.svg
- icons/white/svg/play-line.svg
- icons/white/svg/price-tag-3-line.svg
- icons/white/svg/refresh-line.svg
- icons/white/svg/restart-line.svg
- icons/white/svg/save-3-line.svg
- icons/white/svg/screenshot-2-line.svg
- icons/white/svg/settings-3-line.svg
- icons/white/svg/shut-down-line.svg
- icons/white/svg/tools-line.svg
- icons/white/svg/trophy-line.svg
- icons/white/svg/tv-2-line.svg
- icons/white/svg/usb-fill.svg
- icons/white/svg/volume-up-line.svg
- icons/white/svg/warning-line.svg
- icons/white/svg/window-2-line.svg
- icons/white/svg/zoom-in-line.svg
- icons/white/svg/zoom-out-line.svg
- images/DrivingForce.png
- images/dualshock-2.png
- images/GTForce.png
-
+
+ icons/AppIcon64.png
+ icons/applications-system-24.png
+ icons/black/index.theme
+ icons/black/svg/arrow-left-right-line.svg
+ icons/black/svg/artboard-2-line.svg
+ icons/black/svg/at.svg
+ icons/black/svg/band-aid-line.svg
+ icons/black/svg/brush-line.svg
+ icons/black/svg/cheats-line.svg
+ icons/black/svg/checkbox-multiple-blank-line.svg
+ icons/black/svg/chip-line.svg
+ icons/black/svg/close-line.svg
+ icons/black/svg/controller-line.svg
+ icons/black/svg/controller-strike-line.svg
+ icons/black/svg/delete-back-2-line.svg
+ icons/black/svg/disc-2-line.svg
+ icons/black/svg/disc-eject-line.svg
+ icons/black/svg/discord.svg
+ icons/black/svg/door-open-line.svg
+ icons/black/svg/download-2-line.svg
+ icons/black/svg/eject-line.svg
+ icons/black/svg/emulation-line.svg
+ icons/black/svg/file-add-line.svg
+ icons/black/svg/file-line.svg
+ icons/black/svg/file-list-line.svg
+ icons/black/svg/file-reduce-line.svg
+ icons/black/svg/file-search-line.svg
+ icons/black/svg/file-settings-line.svg
+ icons/black/svg/filter-line.svg
+ icons/black/svg/flashlight-line.svg
+ icons/black/svg/floppy-in-line.svg
+ icons/black/svg/floppy-out-line.svg
+ icons/black/svg/folder-add-line.svg
+ icons/black/svg/folder-open-line.svg
+ icons/black/svg/folder-reduce-line.svg
+ icons/black/svg/guitar-line.svg
+ icons/black/svg/folder-settings-line.svg
+ icons/black/svg/fullscreen-line.svg
+ icons/black/svg/function-line.svg
+ icons/black/svg/github.svg
+ icons/black/svg/global-line.svg
+ icons/black/svg/heart-circle-line.svg
+ icons/black/svg/heart-monitor-line.svg
+ icons/black/svg/image-fill.svg
+ icons/black/svg/interface-line.svg
+ icons/black/svg/keyboard-line.svg
+ icons/black/svg/lightbulb-line.svg
+ icons/black/svg/list-check.svg
+ icons/black/svg/login-box-line.svg
+ icons/black/svg/memcard-line.svg
+ icons/black/svg/mouse-line.svg
+ icons/black/svg/pause-line.svg
+ icons/black/svg/play-line.svg
+ icons/black/svg/price-tag-3-line.svg
+ icons/black/svg/refresh-line.svg
+ icons/black/svg/restart-line.svg
+ icons/black/svg/save-3-line.svg
+ icons/black/svg/screenshot-2-line.svg
+ icons/black/svg/settings-3-line.svg
+ icons/black/svg/shut-down-line.svg
+ icons/black/svg/tools-line.svg
+ icons/black/svg/trophy-line.svg
+ icons/black/svg/tv-2-line.svg
+ icons/black/svg/usb-fill.svg
+ icons/black/svg/volume-up-line.svg
+ icons/black/svg/warning-line.svg
+ icons/black/svg/window-2-line.svg
+ icons/black/svg/zoom-in-line.svg
+ icons/black/svg/zoom-out-line.svg
+ icons/logo.png
+ icons/media-optical-24.png
+ icons/media-optical-gear-24.png
+ icons/media-optical.png
+ icons/QT.png
+ icons/update.png
+ icons/white/index.theme
+ icons/white/svg/arrow-left-right-line.svg
+ icons/white/svg/artboard-2-line.svg
+ icons/white/svg/at.svg
+ icons/white/svg/band-aid-line.svg
+ icons/white/svg/brush-line.svg
+ icons/white/svg/cheats-line.svg
+ icons/white/svg/checkbox-multiple-blank-line.svg
+ icons/white/svg/chip-line.svg
+ icons/white/svg/close-line.svg
+ icons/white/svg/controller-line.svg
+ icons/white/svg/controller-strike-line.svg
+ icons/white/svg/delete-back-2-line.svg
+ icons/white/svg/disc-2-line.svg
+ icons/white/svg/disc-eject-line.svg
+ icons/white/svg/discord.svg
+ icons/white/svg/door-open-line.svg
+ icons/white/svg/download-2-line.svg
+ icons/white/svg/eject-line.svg
+ icons/white/svg/emulation-line.svg
+ icons/white/svg/file-add-line.svg
+ icons/white/svg/file-line.svg
+ icons/white/svg/file-list-line.svg
+ icons/white/svg/file-reduce-line.svg
+ icons/white/svg/file-search-line.svg
+ icons/white/svg/file-settings-line.svg
+ icons/white/svg/filter-line.svg
+ icons/white/svg/flashlight-line.svg
+ icons/white/svg/floppy-in-line.svg
+ icons/white/svg/floppy-out-line.svg
+ icons/white/svg/folder-add-line.svg
+ icons/white/svg/folder-open-line.svg
+ icons/white/svg/folder-reduce-line.svg
+ icons/white/svg/folder-settings-line.svg
+ icons/white/svg/fullscreen-line.svg
+ icons/white/svg/function-line.svg
+ icons/white/svg/github.svg
+ icons/white/svg/global-line.svg
+ icons/white/svg/guitar-line.svg
+ icons/white/svg/heart-circle-line.svg
+ icons/white/svg/heart-monitor-line.svg
+ icons/white/svg/image-fill.svg
+ icons/white/svg/interface-line.svg
+ icons/white/svg/keyboard-line.svg
+ icons/white/svg/lightbulb-line.svg
+ icons/white/svg/list-check.svg
+ icons/white/svg/login-box-line.svg
+ icons/white/svg/memcard-line.svg
+ icons/white/svg/mouse-line.svg
+ icons/white/svg/pause-line.svg
+ icons/white/svg/play-line.svg
+ icons/white/svg/price-tag-3-line.svg
+ icons/white/svg/refresh-line.svg
+ icons/white/svg/restart-line.svg
+ icons/white/svg/save-3-line.svg
+ icons/white/svg/screenshot-2-line.svg
+ icons/white/svg/settings-3-line.svg
+ icons/white/svg/shut-down-line.svg
+ icons/white/svg/tools-line.svg
+ icons/white/svg/trophy-line.svg
+ icons/white/svg/tv-2-line.svg
+ icons/white/svg/usb-fill.svg
+ icons/white/svg/volume-up-line.svg
+ icons/white/svg/warning-line.svg
+ icons/white/svg/window-2-line.svg
+ icons/white/svg/zoom-in-line.svg
+ icons/white/svg/zoom-out-line.svg
+ images/DrivingForce.png
+ images/dualshock-2.png
+ images/GTForce.png
+ images/guitar.png
+
diff --git a/pcsx2/CDVD/CDVD.cpp b/pcsx2/CDVD/CDVD.cpp
index edf793a59b..d4d9d9bd31 100644
--- a/pcsx2/CDVD/CDVD.cpp
+++ b/pcsx2/CDVD/CDVD.cpp
@@ -30,7 +30,7 @@
#include "IopHw.h"
#include "IopDma.h"
#include "VMManager.h"
-#include "Sio.h"
+#include "SIO/Sio.h"
#include "common/Error.h"
#include "common/FileSystem.h"
diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt
index e71c9b0d73..cdfb9f3531 100644
--- a/pcsx2/CMakeLists.txt
+++ b/pcsx2/CMakeLists.txt
@@ -120,14 +120,10 @@ set(pcsx2Sources
PINE.cpp
Mdec.cpp
Memory.cpp
- MemoryCardFile.cpp
- MemoryCardFolder.cpp
- MemoryCardProtocol.cpp
MMI.cpp
MTGS.cpp
MTVU.cpp
MultipartFileReader.cpp
- MultitapProtocol.cpp
Patch.cpp
Pcsx2Config.cpp
PerformanceMetrics.cpp
@@ -144,7 +140,13 @@ set(pcsx2Sources
Sif0.cpp
Sif1.cpp
sif2.cpp
- Sio.cpp
+ SIO/Sio.cpp
+ SIO/Sio2.cpp
+ SIO/Sio0.cpp
+ SIO/Multitap/MultitapProtocol.cpp
+ SIO/Memcard/MemoryCardFile.cpp
+ SIO/Memcard/MemoryCardFolder.cpp
+ SIO/Memcard/MemoryCardProtocol.cpp
SourceLog.cpp
SPR.cpp
StateWrapper.cpp
@@ -205,11 +207,7 @@ set(pcsx2Headers
MTGS.h
MTVU.h
Memory.h
- MemoryCardFile.h
- MemoryCardFolder.h
- MemoryCardProtocol.h
MemoryTypes.h
- MultitapProtocol.h
Patch.h
PCSX2Base.h
PerformanceMetrics.h
@@ -222,7 +220,14 @@ set(pcsx2Headers
Sifcmd.h
Sif.h
SingleRegisterTypes.h
- Sio.h
+ SIO/Sio.h
+ SIO/Sio2.h
+ SIO/Sio0.h
+ SIO/SioTypes.h
+ SIO/Multitap/MultitapProtocol.h
+ SIO/Memcard/MemoryCardFile.h
+ SIO/Memcard/MemoryCardFolder.h
+ SIO/Memcard/MemoryCardProtocol.h
SPR.h
StateWrapper.h
SysForwardDefs.h
@@ -477,15 +482,25 @@ endif()
# Host PAD
set(pcsx2PADSources
- PAD/Host/KeyStatus.cpp
- PAD/Host/PAD.cpp
- PAD/Host/StateManagement.cpp
+ SIO/Pad/PadBase.cpp
+ SIO/Pad/PadConfig.cpp
+ SIO/Pad/PadDualshock2.cpp
+ SIO/Pad/PadGuitar.cpp
+ SIO/Pad/PadMacros.cpp
+ SIO/Pad/PadManager.cpp
+ SIO/Pad/PadNotConnected.cpp
)
set(pcsx2PADHeaders
- PAD/Host/Global.h
- PAD/Host/KeyStatus.h
- PAD/Host/PAD.h
- PAD/Host/StateManagement.h
+ SIO/Pad/PadBase.h
+ SIO/Pad/PadConfig.h
+ SIO/Pad/PadDualshock2.h
+ SIO/Pad/PadDualshock2Types.h
+ SIO/Pad/PadGuitar.h
+ SIO/Pad/PadGuitarTypes.h
+ SIO/Pad/PadMacros.h
+ SIO/Pad/PadManager.h
+ SIO/Pad/PadNotConnected.h
+ SIO/Pad/PadTypes.h
)
# GS sources
diff --git a/pcsx2/Counters.cpp b/pcsx2/Counters.cpp
index 306c338492..2c1d5232bb 100644
--- a/pcsx2/Counters.cpp
+++ b/pcsx2/Counters.cpp
@@ -30,9 +30,8 @@
#include "PerformanceMetrics.h"
#include "Patch.h"
#include "ps2/HwInternal.h"
-#include "Sio.h"
+#include "SIO/Sio.h"
#include "SPU2/spu2.h"
-#include "PAD/Host/PAD.h"
#include "Recording/InputRecording.h"
#include "VMManager.h"
#include "VUmicro.h"
@@ -608,7 +607,6 @@ static __fi void VSyncStart(u32 sCycle)
{
// Update vibration at the end of a frame.
VSyncUpdateCore();
- PAD::Update();
frameLimit(); // limit FPS
gsPostVsyncStart(); // MUST be after framelimit; doing so before causes funk with frame times!
diff --git a/pcsx2/Host.cpp b/pcsx2/Host.cpp
index fdc6c7f8f0..20221b60f2 100644
--- a/pcsx2/Host.cpp
+++ b/pcsx2/Host.cpp
@@ -19,8 +19,6 @@
#include "GS/Renderers/HW/GSTextureReplacements.h"
#include "Host.h"
#include "LayeredSettingsInterface.h"
-#include "MemoryCardFile.h"
-#include "Sio.h"
#include "VMManager.h"
#include "common/Assertions.h"
diff --git a/pcsx2/ImGui/FullscreenUI.cpp b/pcsx2/ImGui/FullscreenUI.cpp
index 691038d5eb..2d0d843c1f 100644
--- a/pcsx2/ImGui/FullscreenUI.cpp
+++ b/pcsx2/ImGui/FullscreenUI.cpp
@@ -29,10 +29,7 @@
#include "ImGui/ImGuiFullscreen.h"
#include "ImGui/ImGuiManager.h"
#include "Input/InputManager.h"
-#include "MemoryCardFile.h"
#include "MTGS.h"
-#include "PAD/Host/PAD.h"
-#include "Sio.h"
#include "USB/USB.h"
#include "VMManager.h"
#include "ps2/BiosTools.h"
@@ -50,6 +47,11 @@
#include "common/Threading.h"
#include "common/Timer.h"
+#include "SIO/Memcard/MemoryCardFile.h"
+#include "SIO/Pad/PadConfig.h"
+#include "SIO/Pad/PadMacros.h"
+#include "SIO/Sio.h"
+
#include "imgui.h"
#include "imgui_internal.h"
#include "IconsFontAwesome5.h"
@@ -2271,7 +2273,7 @@ void FullscreenUI::StartAutomaticBinding(u32 port)
Host::RunOnCPUThread([port, name = std::move(names[index])]() {
auto lock = Host::GetSettingsLock();
SettingsInterface* bsi = GetEditingSettingsInterface();
- const bool result = PAD::MapController(*bsi, port, InputManager::GetGenericBindingMapping(name));
+ const bool result = g_PadConfig.MapController(*bsi, port, InputManager::GetGenericBindingMapping(name));
SetSettingsChanged(bsi);
@@ -3648,7 +3650,7 @@ void FullscreenUI::CopyGlobalControllerSettingsToGame()
SettingsInterface* dsi = GetEditingSettingsInterface(true);
SettingsInterface* ssi = GetEditingSettingsInterface(false);
- PAD::CopyConfiguration(dsi, *ssi, true, true, false);
+ g_PadConfig.CopyConfiguration(dsi, *ssi, true, true, false);
USB::CopyConfiguration(dsi, *ssi, true, true);
SetSettingsChanged(dsi);
@@ -3659,15 +3661,15 @@ void FullscreenUI::ResetControllerSettings()
{
SettingsInterface* dsi = GetEditingSettingsInterface();
- PAD::SetDefaultControllerConfig(*dsi);
- PAD::SetDefaultHotkeyConfig(*dsi);
+ g_PadConfig.SetDefaultControllerConfig(*dsi);
+ g_PadConfig.SetDefaultHotkeyConfig(*dsi);
USB::SetDefaultConfiguration(dsi);
ShowToast(std::string(), "Controller settings reset to default.");
}
void FullscreenUI::DoLoadInputProfile()
{
- std::vector profiles(PAD::GetInputProfileNames());
+ std::vector profiles(g_PadConfig.GetInputProfileNames());
if (profiles.empty())
{
ShowToast(std::string(), "No input profiles available.");
@@ -3693,7 +3695,7 @@ void FullscreenUI::DoLoadInputProfile()
auto lock = Host::GetSettingsLock();
SettingsInterface* dsi = GetEditingSettingsInterface();
- PAD::CopyConfiguration(dsi, ssi, true, true, IsEditingGameSettings(dsi));
+ g_PadConfig.CopyConfiguration(dsi, ssi, true, true, IsEditingGameSettings(dsi));
USB::CopyConfiguration(dsi, ssi, true, true);
SetSettingsChanged(dsi);
ShowToast(std::string(), fmt::format("Input profile '{}' loaded.", title));
@@ -3707,7 +3709,7 @@ void FullscreenUI::DoSaveInputProfile(const std::string& name)
auto lock = Host::GetSettingsLock();
SettingsInterface* ssi = GetEditingSettingsInterface();
- PAD::CopyConfiguration(&dsi, *ssi, true, true, IsEditingGameSettings(ssi));
+ g_PadConfig.CopyConfiguration(&dsi, *ssi, true, true, IsEditingGameSettings(ssi));
USB::CopyConfiguration(&dsi, *ssi, true, true);
if (dsi.Save())
ShowToast(std::string(), fmt::format("Input profile '{}' saved.", name));
@@ -3717,7 +3719,7 @@ void FullscreenUI::DoSaveInputProfile(const std::string& name)
void FullscreenUI::DoSaveInputProfile()
{
- std::vector profiles(PAD::GetInputProfileNames());
+ std::vector profiles(g_PadConfig.GetInputProfileNames());
ImGuiFullscreen::ChoiceDialogOptions coptions;
coptions.reserve(profiles.size() + 1);
@@ -3832,8 +3834,8 @@ void FullscreenUI::DrawControllerSettingsPage()
// we reorder things a little to make it look less silly for mtap
static constexpr const std::array mtap_slot_names = {{'A', 'B', 'C', 'D'}};
- static constexpr const std::array mtap_port_order = {{0, 2, 3, 4, 1, 5, 6, 7}};
- static constexpr const std::array sections = {
+ static constexpr const std::array mtap_port_order = {{0, 2, 3, 4, 1, 5, 6, 7}};
+ static constexpr const std::array sections = {
{"Pad1", "Pad2", "Pad3", "Pad4", "Pad5", "Pad6", "Pad7", "Pad8"}};
// create the ports
@@ -3851,11 +3853,11 @@ void FullscreenUI::DrawControllerSettingsPage()
.c_str());
const char* section = sections[global_slot];
- const std::string type(bsi->GetStringValue(section, "Type", PAD::GetDefaultPadType(global_slot)));
- const PAD::ControllerInfo* ci = PAD::GetControllerInfo(type);
+ const std::string type(bsi->GetStringValue(section, "Type", g_PadConfig.GetDefaultPadType(global_slot)));
+ const PadConfig::ControllerInfo* ci = g_PadConfig.GetControllerInfo(type);
if (MenuButton(ICON_FA_GAMEPAD " Controller Type", ci ? ci->display_name : "Unknown"))
{
- const std::vector> raw_options = PAD::GetControllerTypeNames();
+ const std::vector> raw_options = g_PadConfig.GetControllerTypeNames();
ImGuiFullscreen::ChoiceDialogOptions options;
options.reserve(raw_options.size());
for (auto& it : raw_options)
@@ -3896,9 +3898,9 @@ void FullscreenUI::DrawControllerSettingsPage()
fmt::format(ICON_FA_MICROCHIP " Controller Port {} Macros", mtap_port + 1))
.c_str());
- static bool macro_button_expanded[PAD::NUM_CONTROLLER_PORTS][PAD::NUM_MACRO_BUTTONS_PER_CONTROLLER] = {};
+ static bool macro_button_expanded[Pad::NUM_CONTROLLER_PORTS][PadMacros::NUM_MACRO_BUTTONS_PER_CONTROLLER] = {};
- for (u32 macro_index = 0; macro_index < PAD::NUM_MACRO_BUTTONS_PER_CONTROLLER; macro_index++)
+ for (u32 macro_index = 0; macro_index < PadMacros::NUM_MACRO_BUTTONS_PER_CONTROLLER; macro_index++)
{
bool& expanded = macro_button_expanded[global_slot][macro_index];
expanded ^= MenuHeadingButton(fmt::format(ICON_FA_MICROCHIP " Macro Button {}", macro_index + 1).c_str(),
diff --git a/pcsx2/ImGui/ImGuiOverlays.cpp b/pcsx2/ImGui/ImGuiOverlays.cpp
index 2263898675..cb08ae12b0 100644
--- a/pcsx2/ImGui/ImGuiOverlays.cpp
+++ b/pcsx2/ImGui/ImGuiOverlays.cpp
@@ -28,12 +28,12 @@
#include "ImGui/ImGuiManager.h"
#include "ImGui/ImGuiOverlays.h"
#include "Input/InputManager.h"
-#include "PAD/Host/KeyStatus.h"
-#include "PAD/Host/PAD.h"
#include "PerformanceMetrics.h"
#include "USB/USB.h"
#include "VMManager.h"
#include "pcsx2/Recording/InputRecording.h"
+#include "SIO/Pad/PadConfig.h"
+#include "SIO/Pad/PadManager.h"
#include "common/BitUtils.h"
#include "common/StringUtil.h"
@@ -482,11 +482,21 @@ void ImGuiManager::DrawInputsOverlay()
ImDrawList* dl = ImGui::GetBackgroundDrawList();
u32 num_ports = 0;
- for (u32 port = 0; port < PAD::NUM_CONTROLLER_PORTS; port++)
+
+ for (u32 slot = 0; slot < Pad::NUM_CONTROLLER_PORTS; slot++)
{
- const PAD::ControllerType ctype = g_key_status.GetType(port);
- if (ctype != PAD::ControllerType::NotConnected)
- num_ports++;
+ const std::optional padOpt = g_PadManager.GetPad(slot);
+
+ if (padOpt.has_value())
+ {
+ PadBase* pad = padOpt.value();
+ const Pad::ControllerType ctype = pad->GetType();
+
+ if (ctype != Pad::ControllerType::NotConnected)
+ {
+ num_ports++;
+ }
+ }
}
for (u32 port = 0; port < USB::NUM_PORTS; port++)
@@ -503,61 +513,67 @@ void ImGuiManager::DrawInputsOverlay()
std::string text;
text.reserve(256);
- for (u32 port = 0; port < PAD::NUM_CONTROLLER_PORTS; port++)
+ for (u32 slot = 0; slot < Pad::NUM_CONTROLLER_PORTS; slot++)
{
- const PAD::ControllerType ctype = g_key_status.GetType(port);
- if (ctype == PAD::ControllerType::NotConnected)
- continue;
+ const std::optional padOpt = g_PadManager.GetPad(slot);
- const PAD::ControllerInfo* cinfo = PAD::GetControllerInfo(ctype);
- if (!cinfo)
- continue;
-
- text.clear();
- fmt::format_to(std::back_inserter(text), "P{} |", port + 1u);
-
- for (u32 bind = 0; bind < cinfo->num_bindings; bind++)
+ if (padOpt.has_value())
{
- const InputBindingInfo& bi = cinfo->bindings[bind];
- switch (bi.bind_type)
+ PadBase* pad = padOpt.value();
+ const Pad::ControllerType ctype = pad->GetType();
+
+ if (ctype == Pad::ControllerType::NotConnected)
+ continue;
+
+ const PadConfig::ControllerInfo* cinfo = g_PadConfig.GetControllerInfo(ctype);
+
+ if (!cinfo)
+ continue;
+
+ text.clear();
+ fmt::format_to(std::back_inserter(text), "P{} |", slot + 1u);
+
+ for (u32 bind = 0; bind < cinfo->num_bindings; bind++)
{
- case InputBindingInfo::Type::Axis:
- case InputBindingInfo::Type::HalfAxis:
+ const InputBindingInfo& bi = cinfo->bindings[bind];
+ switch (bi.bind_type)
{
- // axes are always shown
- const float value = static_cast(g_key_status.GetRawPressure(port, bind)) * (1.0f / 255.0f);
- if (value >= (254.0f / 255.0f))
- fmt::format_to(std::back_inserter(text), " {}", bi.name);
- else if (value > (1.0f / 255.0f))
- fmt::format_to(std::back_inserter(text), " {}: {:.2f}", bi.name, value);
- }
- break;
-
- case InputBindingInfo::Type::Button:
- {
- // buttons only shown when active
- const float value = static_cast(g_key_status.GetRawPressure(port, bind)) * (1.0f / 255.0f);
- if (value == 1.0f)
- fmt::format_to(std::back_inserter(text), " {}", bi.name);
- else if (value > 0.0f)
- fmt::format_to(std::back_inserter(text), " {}: {:.2f}", bi.name, value);
- }
- break;
-
- case InputBindingInfo::Type::Motor:
- case InputBindingInfo::Type::Macro:
- case InputBindingInfo::Type::Unknown:
- default:
+ case InputBindingInfo::Type::Axis:
+ case InputBindingInfo::Type::HalfAxis:
+ {
+ // axes are always shown
+ const float value = static_cast(pad->GetRawInput(bind)) * (1.0f / 255.0f);
+ if (value >= (254.0f / 255.0f))
+ fmt::format_to(std::back_inserter(text), " {}", bi.name);
+ else if (value > (1.0f / 255.0f))
+ fmt::format_to(std::back_inserter(text), " {}: {:.2f}", bi.name, value);
+ }
break;
+
+ case InputBindingInfo::Type::Button:
+ {
+ // buttons only shown when active
+ const float value = static_cast(pad->GetRawInput(bind)) * (1.0f / 255.0f);
+ if (value >= 0.5f)
+ fmt::format_to(std::back_inserter(text), " {}", bi.name);
+ }
+ break;
+
+ case InputBindingInfo::Type::Motor:
+ case InputBindingInfo::Type::Macro:
+ case InputBindingInfo::Type::Unknown:
+ default:
+ break;
+ }
}
+
+ dl->AddText(font, font->FontSize, ImVec2(current_x + shadow_offset, current_y + shadow_offset), shadow_color, text.c_str(),
+ text.c_str() + text.length(), 0.0f, &clip_rect);
+ dl->AddText(
+ font, font->FontSize, ImVec2(current_x, current_y), text_color, text.c_str(), text.c_str() + text.length(), 0.0f, &clip_rect);
+
+ current_y += font->FontSize + spacing;
}
-
- dl->AddText(font, font->FontSize, ImVec2(current_x + shadow_offset, current_y + shadow_offset), shadow_color, text.c_str(),
- text.c_str() + text.length(), 0.0f, &clip_rect);
- dl->AddText(
- font, font->FontSize, ImVec2(current_x, current_y), text_color, text.c_str(), text.c_str() + text.length(), 0.0f, &clip_rect);
-
- current_y += font->FontSize + spacing;
}
for (u32 port = 0; port < USB::NUM_PORTS; port++)
diff --git a/pcsx2/Input/InputManager.cpp b/pcsx2/Input/InputManager.cpp
index a77729b962..08950324bb 100644
--- a/pcsx2/Input/InputManager.cpp
+++ b/pcsx2/Input/InputManager.cpp
@@ -18,10 +18,13 @@
#include "ImGui/ImGuiManager.h"
#include "Input/InputManager.h"
#include "Input/InputSource.h"
-#include "PAD/Host/PAD.h"
+#include "SIO/Pad/PadConfig.h"
+#include "SIO/Pad/PadMacros.h"
+#include "SIO/Pad/PadManager.h"
#include "USB/USB.h"
#include "VMManager.h"
+#include "common/Assertions.h"
#include "common/StringUtil.h"
#include "common/Timer.h"
@@ -631,7 +634,7 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const ch
if (type.empty() || type == "None")
return;
- const PAD::ControllerInfo* cinfo = PAD::GetControllerInfo(type);
+ const PadConfig::ControllerInfo* cinfo = g_PadConfig.GetControllerInfo(type);
if (!cinfo)
return;
@@ -653,7 +656,7 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const ch
const float deadzone = si.GetFloatValue(section.c_str(), fmt::format("{}Deadzone", bi.name).c_str(), 0.0f);
AddBindings(
bindings, InputAxisEventHandler{[pad_index, bind_index = bi.bind_index, sensitivity, deadzone](float value) {
- PAD::SetControllerState(pad_index, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value));
+ g_PadManager.SetControllerState(pad_index, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value));
}});
}
}
@@ -666,18 +669,18 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const ch
}
}
- for (u32 macro_button_index = 0; macro_button_index < PAD::NUM_MACRO_BUTTONS_PER_CONTROLLER; macro_button_index++)
+ for (u32 macro_button_index = 0; macro_button_index < PadMacros::NUM_MACRO_BUTTONS_PER_CONTROLLER; macro_button_index++)
{
const std::vector bindings(si.GetStringList(section.c_str(), fmt::format("Macro{}", macro_button_index + 1).c_str()));
if (!bindings.empty())
{
AddBindings(bindings, InputButtonEventHandler{[pad_index, macro_button_index](bool state) {
- PAD::SetMacroButtonState(pad_index, macro_button_index, state);
+ g_PadMacros.SetMacroButtonState(pad_index, macro_button_index, state);
}});
}
}
- if (cinfo->vibration_caps != PAD::VibrationCapabilities::NoVibration)
+ if (cinfo->vibration_caps != Pad::VibrationCapabilities::NoVibration)
{
PadVibrationBinding vib;
vib.pad_index = pad_index;
@@ -685,7 +688,7 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const ch
bool has_any_bindings = false;
switch (cinfo->vibration_caps)
{
- case PAD::VibrationCapabilities::LargeSmallMotors:
+ case Pad::VibrationCapabilities::LargeSmallMotors:
{
if (const std::string large_binding(si.GetStringValue(section.c_str(), "LargeMotor")); !large_binding.empty())
has_any_bindings |= ParseBindingAndGetSource(large_binding, &vib.motors[0].binding, &vib.motors[0].source);
@@ -694,7 +697,7 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const ch
}
break;
- case PAD::VibrationCapabilities::SingleMotor:
+ case Pad::VibrationCapabilities::SingleMotor:
{
if (const std::string binding(si.GetStringValue(section.c_str(), "Motor")); !binding.empty())
has_any_bindings |= ParseBindingAndGetSource(binding, &vib.motors[0].binding, &vib.motors[0].source);
@@ -1289,8 +1292,8 @@ void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& bind
// If there's an input profile, we load pad bindings from it alone, rather than
// falling back to the base configuration.
- for (u32 pad = 0; pad < PAD::NUM_CONTROLLER_PORTS; pad++)
- AddPadBindings(binding_si, pad, PAD::GetDefaultPadType(pad));
+ for (u32 pad = 0; pad < Pad::NUM_CONTROLLER_PORTS; pad++)
+ AddPadBindings(binding_si, pad, g_PadConfig.GetDefaultPadType(pad));
constexpr float ui_ctrl_range = 100.0f;
constexpr float pointer_sensitivity = 0.05f;
diff --git a/pcsx2/IopDma.cpp b/pcsx2/IopDma.cpp
index 7701138d96..db90135a6d 100644
--- a/pcsx2/IopDma.cpp
+++ b/pcsx2/IopDma.cpp
@@ -20,7 +20,7 @@
#include "IopCounters.h"
#include "IopHw.h"
#include "IopDma.h"
-#include "Sio.h"
+#include "SIO/Sio2.h"
#include "Sif.h"
#include "DEV9/DEV9.h"
@@ -232,7 +232,7 @@ void psxDma11(u32 madr, u32 bcr, u32 chcr)
PSXDMA_LOG("*** DMA 11 - SIO2 in *** %lx addr = %lx size = %lx", chcr, madr, bcr);
// Set dmaBlockSize, so SIO2 knows to count based on the DMA block rather than SEND3 length.
// When SEND3 is written, SIO2 will automatically reset this to zero.
- sio2.dmaBlockSize = (bcr & 0xffff) * 4;
+ g_Sio2.dmaBlockSize = (bcr & 0xffff) * 4;
if (chcr != 0x01000201)
{
@@ -244,7 +244,7 @@ void psxDma11(u32 madr, u32 bcr, u32 chcr)
for (j = 0; j < ((bcr & 0xFFFF) * 4); j++)
{
const u8 data = iopMemRead8(madr);
- sio2.Write(data);
+ g_Sio2.Write(data);
madr++;
}
}
@@ -276,7 +276,7 @@ void psxDma12(u32 madr, u32 bcr, u32 chcr)
while (bcr > 0)
{
- const u8 data = sio2.Read();
+ const u8 data = g_Sio2.Read();
iopMemWrite8(madr, data);
bcr--;
madr++;
diff --git a/pcsx2/IopHw.cpp b/pcsx2/IopHw.cpp
index b8f91658b2..0538fc9ef2 100644
--- a/pcsx2/IopHw.cpp
+++ b/pcsx2/IopHw.cpp
@@ -16,6 +16,8 @@
#include "PrecompiledHeader.h"
#include "Common.h"
+#include "SIO/Sio2.h"
+#include "SIO/Sio0.h"
#include "CDVD/CDVD.h"
#include "CDVD/Ps1CD.h"
#include "IopCounters.h"
@@ -23,7 +25,6 @@
#include "IopHw.h"
#include "Mdec.h"
#include "R3000A.h"
-#include "Sio.h"
#include "x86/iR5900.h"
// NOTE: Any modifications to read/write fns should also go into their const counterparts
@@ -39,8 +40,6 @@ void psxHwReset() {
cdrReset();
cdvdReset();
psxRcntInit();
- sio0.FullReset();
- sio2.FullReset();
}
__fi u8 psxHw4Read8(u32 add)
diff --git a/pcsx2/PAD/Host/Global.h b/pcsx2/PAD/Host/Global.h
deleted file mode 100644
index 8ad6dd4944..0000000000
--- a/pcsx2/PAD/Host/Global.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/* 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 .
- */
-
-#pragma once
-
-#include "common/Pcsx2Defs.h"
-
-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_ANALOG, // Analog mode toggle
- PAD_PRESSURE, // Pressure modifier
- 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) ←
- MAX_KEYS,
-};
-
-static inline bool IsAnalogKey(int index)
-{
- return ((index >= PAD_L_UP) && (index <= PAD_R_LEFT));
-}
-
-static inline bool IsTriggerKey(int index)
-{
- return (index == PAD_L2 || index == PAD_R2);
-}
diff --git a/pcsx2/PAD/Host/KeyStatus.cpp b/pcsx2/PAD/Host/KeyStatus.cpp
deleted file mode 100644
index 069755d772..0000000000
--- a/pcsx2/PAD/Host/KeyStatus.cpp
+++ /dev/null
@@ -1,217 +0,0 @@
-/* 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 .
- */
-
-#include "PrecompiledHeader.h"
-
-#include "PAD/Host/KeyStatus.h"
-#include "PAD/Host/Global.h"
-
-#include
-#include
-
-using namespace PAD;
-
-KeyStatus::KeyStatus()
-{
- std::memset(&m_analog, 0, sizeof(m_analog));
-
- for (u32 pad = 0; pad < NUM_CONTROLLER_PORTS; pad++)
- {
- m_axis_scale[pad][0] = 0.0f;
- m_axis_scale[pad][1] = 1.0f;
- m_pressure_modifier[pad] = 0.5f;
- }
-
- Init();
-}
-
-void KeyStatus::Init()
-{
- for (u32 pad = 0; pad < NUM_CONTROLLER_PORTS; pad++)
- {
- m_button[pad] = 0xFFFFFFFF;
-
- 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)
-{
- // Since we reordered the buttons for better UI, we need to remap them here.
- static constexpr std::array 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
- 16, // PAD_ANALOG
- 17, // PAD_PRESSURE
- // remainder are analogs and not used here
- }};
-
- if (IsAnalogKey(index))
- {
- m_button_pressure[pad][index] = static_cast(std::clamp(value * m_axis_scale[pad][1] * 255.0f, 0.0f, 255.0f));
-
- // 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)))
- if (index <= PAD_L_LEFT)
- {
- // Left Stick
- m_analog[pad].lx = m_analog[pad].invert_lx ? MERGE(pad, PAD_L_LEFT, PAD_L_RIGHT) : MERGE(pad, PAD_L_RIGHT, PAD_L_LEFT);
- m_analog[pad].ly = m_analog[pad].invert_ly ? MERGE(pad, PAD_L_UP, PAD_L_DOWN) : MERGE(pad, PAD_L_DOWN, PAD_L_UP);
- }
- else
- {
- // Right Stick
- m_analog[pad].rx = m_analog[pad].invert_rx ? MERGE(pad, PAD_R_LEFT, PAD_R_RIGHT) : MERGE(pad, PAD_R_RIGHT, PAD_R_LEFT);
- m_analog[pad].ry = m_analog[pad].invert_ry ? MERGE(pad, PAD_R_UP, PAD_R_DOWN) : MERGE(pad, PAD_R_DOWN, PAD_R_UP);
- }
-#undef MERGE
-
- // Deadzone computation.
- const float dz = m_axis_scale[pad][0];
- if (dz > 0.0f)
- {
-#define MERGE_F(pad, pos, neg) ((m_button_pressure[pad][pos] != 0) ? (static_cast(m_button_pressure[pad][pos]) / 255.0f) : (static_cast(m_button_pressure[pad][neg]) / -255.0f))
- float pos_x, pos_y;
- if (index <= PAD_L_LEFT)
- {
- pos_x = m_analog[pad].invert_lx ? MERGE_F(pad, PAD_L_LEFT, PAD_L_RIGHT) : MERGE_F(pad, PAD_L_RIGHT, PAD_L_LEFT);
- pos_y = m_analog[pad].invert_ly ? MERGE_F(pad, PAD_L_UP, PAD_L_DOWN) : MERGE_F(pad, PAD_L_DOWN, PAD_L_UP);
- }
- else
- {
- pos_x = m_analog[pad].invert_rx ? MERGE_F(pad, PAD_R_LEFT, PAD_R_RIGHT) : MERGE_F(pad, PAD_R_RIGHT, PAD_R_LEFT);
- pos_y = m_analog[pad].invert_ry ? MERGE_F(pad, PAD_R_UP, PAD_R_DOWN) : MERGE_F(pad, PAD_R_DOWN, PAD_R_UP);
- }
-
- // No point checking if we're at dead center (usually keyboard with no buttons pressed).
- if (pos_x != 0.0f || pos_y != 0.0f)
- {
- // Compute the angle at the given position in the stick's square bounding box.
- const float theta = std::atan2(pos_y, pos_x);
-
- // Compute the position that the edge of the circle would be at, given the angle.
- const float dz_x = std::cos(theta) * dz;
- const float dz_y = std::sin(theta) * dz;
-
- // We're in the deadzone if our position is less than the circle edge.
- const bool in_x = (pos_x < 0.0f) ? (pos_x > dz_x) : (pos_x <= dz_x);
- const bool in_y = (pos_y < 0.0f) ? (pos_y > dz_y) : (pos_y <= dz_y);
- if (in_x && in_y)
- {
- // In deadzone. Set to 127 (center).
- if (index <= PAD_L_LEFT)
- m_analog[pad].lx = m_analog[pad].ly = 127;
- else
- m_analog[pad].rx = m_analog[pad].ry = 127;
- }
- }
-#undef MERGE_F
- }
- }
- else if (IsTriggerKey(index))
- {
- const float s_value = std::clamp(value * m_trigger_scale[pad][1], 0.0f, 1.0f);
- const float dz_value = (m_trigger_scale[pad][0] > 0.0f && s_value < m_trigger_scale[pad][0]) ? 0.0f : s_value;
- m_button_pressure[pad][index] = static_cast(dz_value * 255.0f);
- if (dz_value > 0.0f)
- m_button[pad] &= ~(1u << bitmask_mapping[index]);
- else
- m_button[pad] |= (1u << bitmask_mapping[index]);
- }
- else
- {
- // Don't affect L2/R2, since they are analog on most pads.
- const float pmod = ((m_button[pad] & (1u << PAD_PRESSURE)) == 0) ? m_pressure_modifier[pad] : 1.0f;
- const float dz_value = (value < m_button_deadzone[pad]) ? 0.0f : value;
- m_button_pressure[pad][index] = static_cast(std::clamp(dz_value * pmod * 255.0f, 0.0f, 255.0f));
-
- if (dz_value > 0.0f)
- m_button[pad] &= ~(1u << bitmask_mapping[index]);
- else
- m_button[pad] |= (1u << bitmask_mapping[index]);
-
- // Adjust pressure of all other face buttons which are active when pressure modifier is pressed..
- if (index == PAD_PRESSURE)
- {
- const float adjust_pmod = ((m_button[pad] & (1u << PAD_PRESSURE)) == 0) ? m_pressure_modifier[pad] : (1.0f / m_pressure_modifier[pad]);
- for (u32 i = 0; i < MAX_KEYS; i++)
- {
- if (i == index || IsAnalogKey(i) || IsTriggerKey(i))
- continue;
-
- // We add 0.5 here so that the round trip between 255->127->255 when applying works as expected.
- const float add = (m_button_pressure[pad][i] != 0) ? 0.5f : 0.0f;
- m_button_pressure[pad][i] = static_cast(std::clamp((static_cast(m_button_pressure[pad][i]) + add) * adjust_pmod, 0.0f, 255.0f));
- }
- }
- }
-}
-
-u32 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];
- }
-}
diff --git a/pcsx2/PAD/Host/KeyStatus.h b/pcsx2/PAD/Host/KeyStatus.h
deleted file mode 100644
index 605e129f4d..0000000000
--- a/pcsx2/PAD/Host/KeyStatus.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/* 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 .
- */
-
-#pragma once
-
-#include "PAD/Host/PAD.h"
-#include
-
-namespace PAD
-{
- enum class ControllerType : u8;
-
- class KeyStatus
- {
- private:
- static constexpr u8 m_analog_released_val = 0x7F;
-
- struct PADAnalog
- {
- u8 lx, ly;
- u8 rx, ry;
- u8 invert_lx, invert_ly;
- u8 invert_rx, invert_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][2];
- float m_trigger_scale[NUM_CONTROLLER_PORTS][2];
- float m_vibration_scale[NUM_CONTROLLER_PORTS][2];
- float m_pressure_modifier[NUM_CONTROLLER_PORTS];
- float m_button_deadzone[NUM_CONTROLLER_PORTS];
-
- public:
- KeyStatus();
- void Init();
-
- void Set(u32 pad, u32 index, float value);
-
- __fi void SetRawAnalogs(const u32 pad, const std::tuple left, const std::tuple right)
- {
- m_analog[pad].lx = std::get<0>(left);
- m_analog[pad].ly = std::get<1>(left);
- m_analog[pad].rx = std::get<0>(right);
- m_analog[pad].ry = std::get<1>(right);
- }
-
- __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 deadzone, float scale)
- {
- m_axis_scale[pad][0] = deadzone;
- m_axis_scale[pad][1] = scale;
- }
- __fi void SetTriggerScale(u32 pad, float deadzone, float scale)
- {
- m_trigger_scale[pad][0] = deadzone;
- m_trigger_scale[pad][1] = 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; }
- __fi float GetPressureModifier(u32 pad) const { return m_pressure_modifier[pad]; }
- __fi void SetPressureModifier(u32 pad, float mod) { m_pressure_modifier[pad] = mod; }
- __fi void SetButtonDeadzone(u32 pad, float deadzone) { m_button_deadzone[pad] = deadzone; }
- __fi void SetAnalogInvertL(u32 pad, bool x, bool y)
- {
- m_analog[pad].invert_lx = x;
- m_analog[pad].invert_ly = y;
- }
- __fi void SetAnalogInvertR(u32 pad, bool x, bool y)
- {
- m_analog[pad].invert_rx = x;
- m_analog[pad].invert_ry = y;
- }
-
- __fi u8 GetRawPressure(u32 pad, u32 index) const { return m_button_pressure[pad][index]; }
-
- __fi std::tuple GetRawLeftAnalog(u32 pad) const { return {m_analog[pad].lx, m_analog[pad].ly}; }
- __fi std::tuple GetRawRightAnalog(u32 pad) const { return {m_analog[pad].rx, m_analog[pad].ry}; }
-
- u32 GetButtons(u32 pad);
- u8 GetPressure(u32 pad, u32 index);
- };
-} // namespace PAD
-
-extern PAD::KeyStatus g_key_status;
diff --git a/pcsx2/PAD/Host/PAD.cpp b/pcsx2/PAD/Host/PAD.cpp
deleted file mode 100644
index ef94c620e4..0000000000
--- a/pcsx2/PAD/Host/PAD.cpp
+++ /dev/null
@@ -1,757 +0,0 @@
-/* PCSX2 - PS2 Emulator for PCs
- * Copyright (C) 2002-2023 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 .
- */
-
-#include "PrecompiledHeader.h"
-
-#include "Host.h"
-#include "Input/InputManager.h"
-#include "PAD/Host/Global.h"
-#include "PAD/Host/KeyStatus.h"
-#include "PAD/Host/PAD.h"
-#include "PAD/Host/StateManagement.h"
-
-#include "common/FileSystem.h"
-#include "common/Path.h"
-#include "common/SettingsInterface.h"
-#include "common/StringUtil.h"
-
-#include
-
-const u32 revision = 3;
-const u32 build = 0; // increase that with each version
-#define PAD_SAVE_STATE_VERSION ((revision << 8) | (build << 0))
-
-PAD::KeyStatus g_key_status;
-
-namespace PAD
-{
- struct MacroButton
- {
- std::vector buttons; ///< Buttons to activate.
- float pressure; ///< Pressure to apply when macro is active.
- u32 toggle_frequency; ///< Interval at which the buttons will be toggled, if not 0.
- u32 toggle_counter; ///< When this counter reaches zero, buttons will be toggled.
- bool toggle_state; ///< Current state for turbo.
- bool trigger_state; ///< Whether the macro button is active.
- };
-
- static std::string GetConfigSection(u32 pad_index);
- static void LoadMacroButtonConfig(const SettingsInterface& si, u32 pad, const std::string_view& type, const std::string& section);
- static void ApplyMacroButton(u32 pad, const MacroButton& mb);
- static void UpdateMacroButtons();
-
- static std::array, NUM_CONTROLLER_PORTS> s_macro_buttons;
-} // namespace PAD
-
-s32 PADinit()
-{
- Pad::reset_all();
-
- query.reset();
-
- for (int port = 0; port < 2; port++)
- slots[port] = 0;
-
- return 0;
-}
-
-void PADshutdown()
-{
-}
-
-s32 PADopen()
-{
- 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 _port, int _slot)
-{
- return pad_start_poll(_port, _slot);
-}
-
-u8 PADpoll(u8 value)
-{
- return pad_poll(value);
-}
-
-std::string PAD::GetConfigSection(u32 pad_index)
-{
- return fmt::format("Pad{}", pad_index + 1);
-}
-
-bool PADcomplete()
-{
- return pad_complete();
-}
-
-void PAD::LoadConfig(const SettingsInterface& si)
-{
- PAD::s_macro_buttons = {};
-
- EmuConfig.MultitapPort0_Enabled = si.GetBoolValue("Pad", "MultitapPort1", false);
- EmuConfig.MultitapPort1_Enabled = si.GetBoolValue("Pad", "MultitapPort2", false);
-
- // This is where we would load controller types, if onepad supported them.
- for (u32 i = 0; i < NUM_CONTROLLER_PORTS; i++)
- {
- const std::string section(GetConfigSection(i));
- const std::string type(si.GetStringValue(section.c_str(), "Type", GetDefaultPadType(i)));
-
- const ControllerInfo* ci = GetControllerInfo(type);
- if (!ci)
- {
- g_key_status.SetType(i, ControllerType::NotConnected);
- continue;
- }
-
- g_key_status.SetType(i, ci->type);
-
- const float axis_deadzone = si.GetFloatValue(section.c_str(), "Deadzone", DEFAULT_STICK_DEADZONE);
- const float axis_scale = si.GetFloatValue(section.c_str(), "AxisScale", DEFAULT_STICK_SCALE);
- const float trigger_deadzone = si.GetFloatValue(section.c_str(), "TriggerDeadzone", DEFAULT_TRIGGER_DEADZONE);
- const float trigger_scale = si.GetFloatValue(section.c_str(), "TriggerScale", DEFAULT_TRIGGER_SCALE);
- const float button_deadzone = si.GetFloatValue(section.c_str(), "ButtonDeadzone", DEFAULT_BUTTON_DEADZONE);
- g_key_status.SetAxisScale(i, axis_deadzone, axis_scale);
- g_key_status.SetTriggerScale(i, trigger_deadzone, trigger_scale);
- g_key_status.SetButtonDeadzone(i, button_deadzone);
-
- if (ci->vibration_caps != VibrationCapabilities::NoVibration)
- {
- const float large_motor_scale = si.GetFloatValue(section.c_str(), "LargeMotorScale", DEFAULT_MOTOR_SCALE);
- const float small_motor_scale = si.GetFloatValue(section.c_str(), "SmallMotorScale", DEFAULT_MOTOR_SCALE);
- g_key_status.SetVibrationScale(i, 0, large_motor_scale);
- g_key_status.SetVibrationScale(i, 1, small_motor_scale);
- }
-
- const float pressure_modifier = si.GetFloatValue(section.c_str(), "PressureModifier", 1.0f);
- g_key_status.SetPressureModifier(i, pressure_modifier);
-
- const int invert_l = si.GetIntValue(section.c_str(), "InvertL", 0);
- const int invert_r = si.GetIntValue(section.c_str(), "InvertR", 0);
- g_key_status.SetAnalogInvertL(i, (invert_l & 1) != 0, (invert_l & 2) != 0);
- g_key_status.SetAnalogInvertR(i, (invert_r & 1) != 0, (invert_r & 2) != 0);
-
- LoadMacroButtonConfig(si, i, type, section);
- }
-}
-
-const char* PAD::GetDefaultPadType(u32 pad)
-{
- return (pad == 0) ? "DualShock2" : "None";
-}
-
-void PAD::SetDefaultControllerConfig(SettingsInterface& si)
-{
- si.ClearSection("InputSources");
- si.ClearSection("Hotkeys");
- si.ClearSection("Pad");
-
- // PCSX2 Controller Settings - Global Settings
- for (u32 i = 0; i < static_cast(InputSourceType::Count); i++)
- {
- si.SetBoolValue("InputSources",
- InputManager::InputSourceToString(static_cast(i)),
- InputManager::GetInputSourceDefaultEnabled(static_cast(i)));
- }
-#ifdef SDL_BUILD
- si.SetBoolValue("InputSources", "SDLControllerEnhancedMode", false);
-#endif
- si.SetBoolValue("Pad", "MultitapPort1", false);
- si.SetBoolValue("Pad", "MultitapPort2", false);
- si.SetFloatValue("Pad", "PointerXSpeed", 40.0f);
- si.SetFloatValue("Pad", "PointerYSpeed", 40.0f);
- si.SetFloatValue("Pad", "PointerXDeadZone", 20.0f);
- si.SetFloatValue("Pad", "PointerYDeadZone", 20.0f);
- si.SetFloatValue("Pad", "PointerInertia", 10.0f);
-
- // PCSX2 Controller Settings - Default pad types and parameters.
- for (u32 i = 0; i < NUM_CONTROLLER_PORTS; i++)
- {
- const char* type = GetDefaultPadType(i);
- const std::string section(GetConfigSection(i));
- si.ClearSection(section.c_str());
- si.SetStringValue(section.c_str(), "Type", type);
-
- const ControllerInfo* ci = GetControllerInfo(type);
- if (ci)
- {
- for (u32 i = 0; i < ci->num_settings; i++)
- {
- const SettingInfo& csi = ci->settings[i];
- csi.SetDefaultValue(&si, section.c_str(), csi.name);
- }
- }
- }
-
- // PCSX2 Controller Settings - Controller 1 / Controller 2 / ...
- // Use the automapper to set this up.
- MapController(si, 0, InputManager::GetGenericBindingMapping("Keyboard"));
-}
-
-void PAD::SetDefaultHotkeyConfig(SettingsInterface& si)
-{
- // PCSX2 Controller Settings - Hotkeys
-
- // PCSX2 Controller Settings - Hotkeys - General
- si.SetStringValue("Hotkeys", "ToggleFullscreen", "Keyboard/Alt & Keyboard/Return");
-
- // PCSX2 Controller Settings - Hotkeys - Graphics
- si.SetStringValue("Hotkeys", "CycleAspectRatio", "Keyboard/F6");
- si.SetStringValue("Hotkeys", "CycleInterlaceMode", "Keyboard/F5");
- si.SetStringValue("Hotkeys", "CycleMipmapMode", "Keyboard/Insert");
- // si.SetStringValue("Hotkeys", "DecreaseUpscaleMultiplier", "Keyboard"); TBD
- // si.SetStringValue("Hotkeys", "IncreaseUpscaleMultiplier", "Keyboard"); TBD
- // si.SetStringValue("Hotkeys", "ReloadTextureReplacements", "Keyboard"); TBD
- si.SetStringValue("Hotkeys", "GSDumpMultiFrame", "Keyboard/Control & Keyboard/Shift & Keyboard/F8");
- si.SetStringValue("Hotkeys", "Screenshot", "Keyboard/F8");
- si.SetStringValue("Hotkeys", "GSDumpSingleFrame", "Keyboard/Shift & Keyboard/F8");
- si.SetStringValue("Hotkeys", "ToggleSoftwareRendering", "Keyboard/F9");
- // si.SetStringValue("Hotkeys", "ToggleTextureDumping", "Keyboard"); TBD
- // si.SetStringValue("Hotkeys", "ToggleTextureReplacements", "Keyboard"); TBD
- si.SetStringValue("Hotkeys", "ZoomIn", "Keyboard/Control & Keyboard/Plus");
- si.SetStringValue("Hotkeys", "ZoomOut", "Keyboard/Control & Keyboard/Minus");
- // Missing hotkey for resetting zoom back to 100 with Keyboard/Control & Keyboard/Asterisk
-
- // PCSX2 Controller Settings - Hotkeys - Input Recording
- si.SetStringValue("Hotkeys", "InputRecToggleMode", "Keyboard/Shift & Keyboard/R");
-
- // PCSX2 Controller Settings - Hotkeys - Save States
- si.SetStringValue("Hotkeys", "LoadStateFromSlot", "Keyboard/F3");
- si.SetStringValue("Hotkeys", "SaveStateToSlot", "Keyboard/F1");
- si.SetStringValue("Hotkeys", "NextSaveStateSlot", "Keyboard/F2");
- si.SetStringValue("Hotkeys", "PreviousSaveStateSlot", "Keyboard/Shift & Keyboard/F2");
-
- // PCSX2 Controller Settings - Hotkeys - System
- // si.SetStringValue("Hotkeys", "DecreaseSpeed", "Keyboard"); TBD
- // si.SetStringValue("Hotkeys", "FrameAdvance", "Keyboard"); TBD
- // si.SetStringValue("Hotkeys", "IncreaseSpeed", "Keyboard"); TBD
- // si.SetStringValue("Hotkeys", "ResetVM", "Keyboard"); TBD
- // si.SetStringValue("Hotkeys", "ShutdownVM", "Keyboard"); TBD
- si.SetStringValue("Hotkeys", "OpenPauseMenu", "Keyboard/Escape");
- si.SetStringValue("Hotkeys", "ToggleFrameLimit", "Keyboard/F4");
- si.SetStringValue("Hotkeys", "TogglePause", "Keyboard/Space");
- si.SetStringValue("Hotkeys", "ToggleSlowMotion", "Keyboard/Shift & Keyboard/Backtab");
- si.SetStringValue("Hotkeys", "ToggleTurbo", "Keyboard/Tab");
- si.SetStringValue("Hotkeys", "HoldTurbo", "Keyboard/Period");
-}
-
-void PAD::Update()
-{
- Pad::rumble_all();
- UpdateMacroButtons();
-}
-
-static const InputBindingInfo s_dualshock2_binds[] = {
- {"Up", TRANSLATE_NOOP("Pad", "D-Pad Up"), InputBindingInfo::Type::Button, PAD_UP, GenericInputBinding::DPadUp},
- {"Right", TRANSLATE_NOOP("Pad", "D-Pad Right"), InputBindingInfo::Type::Button, PAD_RIGHT, GenericInputBinding::DPadRight},
- {"Down", TRANSLATE_NOOP("Pad", "D-Pad Down"), InputBindingInfo::Type::Button, PAD_DOWN, GenericInputBinding::DPadDown},
- {"Left", TRANSLATE_NOOP("Pad", "D-Pad Left"), InputBindingInfo::Type::Button, PAD_LEFT, GenericInputBinding::DPadLeft},
- {"Triangle", TRANSLATE_NOOP("Pad", "Triangle"), InputBindingInfo::Type::Button, PAD_TRIANGLE, GenericInputBinding::Triangle},
- {"Circle", TRANSLATE_NOOP("Pad", "Circle"), InputBindingInfo::Type::Button, PAD_CIRCLE, GenericInputBinding::Circle},
- {"Cross", TRANSLATE_NOOP("Pad", "Cross"), InputBindingInfo::Type::Button, PAD_CROSS, GenericInputBinding::Cross},
- {"Square", TRANSLATE_NOOP("Pad", "Square"), InputBindingInfo::Type::Button, PAD_SQUARE, GenericInputBinding::Square},
- {"Select", TRANSLATE_NOOP("Pad", "Select"), InputBindingInfo::Type::Button, PAD_SELECT, GenericInputBinding::Select},
- {"Start", TRANSLATE_NOOP("Pad", "Start"), InputBindingInfo::Type::Button, PAD_START, GenericInputBinding::Start},
- {"L1", TRANSLATE_NOOP("Pad", "L1 (Left Bumper)"), InputBindingInfo::Type::Button, PAD_L1, GenericInputBinding::L1},
- {"L2", TRANSLATE_NOOP("Pad", "L2 (Left Trigger)"), InputBindingInfo::Type::HalfAxis, PAD_L2, GenericInputBinding::L2},
- {"R1", TRANSLATE_NOOP("Pad", "R1 (Right Bumper)"), InputBindingInfo::Type::Button, PAD_R1, GenericInputBinding::R1},
- {"R2", TRANSLATE_NOOP("Pad", "R2 (Right Trigger)"), InputBindingInfo::Type::HalfAxis, PAD_R2, GenericInputBinding::R2},
- {"L3", TRANSLATE_NOOP("Pad", "L3 (Left Stick Button)"), InputBindingInfo::Type::Button, PAD_L3, GenericInputBinding::L3},
- {"R3", TRANSLATE_NOOP("Pad", "R3 (Right Stick Button)"), InputBindingInfo::Type::Button, PAD_R3, GenericInputBinding::R3},
- {"Analog", TRANSLATE_NOOP("Pad", "Analog Toggle"), InputBindingInfo::Type::Button, PAD_ANALOG, GenericInputBinding::System},
- {"Pressure", TRANSLATE_NOOP("Pad", "Apply Pressure"), InputBindingInfo::Type::Button, PAD_PRESSURE, GenericInputBinding::Unknown},
- {"LUp", TRANSLATE_NOOP("Pad", "Left Stick Up"), InputBindingInfo::Type::HalfAxis, PAD_L_UP, GenericInputBinding::LeftStickUp},
- {"LRight", TRANSLATE_NOOP("Pad", "Left Stick Right"), InputBindingInfo::Type::HalfAxis, PAD_L_RIGHT, GenericInputBinding::LeftStickRight},
- {"LDown", TRANSLATE_NOOP("Pad", "Left Stick Down"), InputBindingInfo::Type::HalfAxis, PAD_L_DOWN, GenericInputBinding::LeftStickDown},
- {"LLeft", TRANSLATE_NOOP("Pad", "Left Stick Left"), InputBindingInfo::Type::HalfAxis, PAD_L_LEFT, GenericInputBinding::LeftStickLeft},
- {"RUp", TRANSLATE_NOOP("Pad", "Right Stick Up"), InputBindingInfo::Type::HalfAxis, PAD_R_UP, GenericInputBinding::RightStickUp},
- {"RRight", TRANSLATE_NOOP("Pad", "Right Stick Right"), InputBindingInfo::Type::HalfAxis, PAD_R_RIGHT, GenericInputBinding::RightStickRight},
- {"RDown", TRANSLATE_NOOP("Pad", "Right Stick Down"), InputBindingInfo::Type::HalfAxis, PAD_R_DOWN, GenericInputBinding::RightStickDown},
- {"RLeft", TRANSLATE_NOOP("Pad", "Right Stick Left"), InputBindingInfo::Type::HalfAxis, PAD_R_LEFT, GenericInputBinding::RightStickLeft},
- {"LargeMotor", TRANSLATE_NOOP("Pad", "Large (Low Frequency) Motor"), InputBindingInfo::Type::Motor, 0, GenericInputBinding::LargeMotor},
- {"SmallMotor", TRANSLATE_NOOP("Pad", "Small (High Frequency) Motor"), InputBindingInfo::Type::Motor, 0, GenericInputBinding::SmallMotor},
-};
-
-static const char* s_dualshock2_invert_entries[] = {
- TRANSLATE_NOOP("Pad", "Not Inverted"),
- TRANSLATE_NOOP("Pad", "Invert Left/Right"),
- TRANSLATE_NOOP("Pad", "Invert Up/Down"),
- TRANSLATE_NOOP("Pad", "Invert Left/Right + Up/Down"),
- nullptr};
-
-static const SettingInfo s_dualshock2_settings[] = {
- {SettingInfo::Type::IntegerList, "InvertL", TRANSLATE_NOOP("Pad", "Invert Left Stick"),
- TRANSLATE_NOOP("Pad", "Inverts the direction of the left analog stick."), "0", "0", "3", nullptr, nullptr,
- s_dualshock2_invert_entries, nullptr, 0.0f},
- {SettingInfo::Type::IntegerList, "InvertR", TRANSLATE_NOOP("Pad", "Invert Right Stick"),
- TRANSLATE_NOOP("Pad", "Inverts the direction of the right analog stick."), "0", "0", "3", nullptr, nullptr,
- s_dualshock2_invert_entries, nullptr, 0.0f},
- {SettingInfo::Type::Float, "Deadzone", TRANSLATE_NOOP("Pad", "Analog Deadzone"),
- TRANSLATE_NOOP("Pad",
- "Sets the analog stick deadzone, i.e. the fraction of the analog stick movement which will be ignored."),
- "0.00", "0.00", "1.00", "0.01", "%.0f%%", nullptr, nullptr, 100.0f},
- {SettingInfo::Type::Float, "AxisScale", TRANSLATE_NOOP("Pad", "Analog Sensitivity"),
- TRANSLATE_NOOP("Pad",
- "Sets the analog stick axis scaling factor. A value between 130% and 140% is recommended when using recent "
- "controllers, e.g. DualShock 4, Xbox One Controller."),
- "1.33", "0.01", "2.00", "0.01", "%.0f%%", nullptr, nullptr, 100.0f},
- {SettingInfo::Type::Float, "TriggerDeadzone", TRANSLATE_NOOP("Pad", "Trigger Deadzone"),
- TRANSLATE_NOOP("Pad",
- "Sets the deadzone for activating triggers, i.e. the fraction of the trigger press which will be ignored."),
- "0.00", "0.00", "1.00", "0.01", "%.0f%%", nullptr, nullptr, 100.0f},
- {SettingInfo::Type::Float, "TriggerScale", TRANSLATE_NOOP("Pad", "Trigger Sensitivity"),
- TRANSLATE_NOOP("Pad", "Sets the trigger scaling factor."), "1.00", "0.01", "2.00", "0.01", "%.0f%%", nullptr,
- nullptr, 100.0f},
- {SettingInfo::Type::Float, "LargeMotorScale", TRANSLATE_NOOP("Pad", "Large Motor Vibration Scale"),
- TRANSLATE_NOOP("Pad", "Increases or decreases the intensity of low frequency vibration sent by the game."),
- "1.00", "0.00", "2.00", "0.01", "%.0f%%", nullptr, nullptr, 100.0f},
- {SettingInfo::Type::Float, "SmallMotorScale", TRANSLATE_NOOP("Pad", "Small Motor Vibration Scale"),
- TRANSLATE_NOOP("Pad", "Increases or decreases the intensity of high frequency vibration sent by the game."),
- "1.00", "0.00", "2.00", "0.01", "%.0f%%", nullptr, nullptr, 100.0f},
- {SettingInfo::Type::Float, "ButtonDeadzone", TRANSLATE_NOOP("Pad", "Button Deadzone"),
- TRANSLATE_NOOP("Pad",
- "Sets the deadzone for activating buttons, i.e. the fraction of the button press which will be ignored."),
- "0.00", "0.00", "1.00", "0.01", "%.0f%%", nullptr, nullptr, 100.0f},
- {SettingInfo::Type::Float, "PressureModifier", TRANSLATE_NOOP("Pad", "Modifier Pressure"),
- TRANSLATE_NOOP("Pad", "Sets the pressure when the modifier button is held."), "0.50", "0.01", "1.00", "0.01",
- "%.0f%%", nullptr, nullptr, 100.0f},
-};
-
-static const PAD::ControllerInfo s_controller_info[] = {
- {PAD::ControllerType::NotConnected, "None", TRANSLATE_NOOP("Pad", "Not Connected"), nullptr, 0, nullptr, 0,
- PAD::VibrationCapabilities::NoVibration},
- {PAD::ControllerType::DualShock2, "DualShock2", TRANSLATE_NOOP("Pad", "DualShock 2"), s_dualshock2_binds,
- std::size(s_dualshock2_binds), s_dualshock2_settings, std::size(s_dualshock2_settings),
- PAD::VibrationCapabilities::LargeSmallMotors},
-};
-
-const PAD::ControllerInfo* PAD::GetControllerInfo(ControllerType type)
-{
- for (const ControllerInfo& info : s_controller_info)
- {
- if (type == info.type)
- return &info;
- }
-
- return nullptr;
-}
-
-const PAD::ControllerInfo* PAD::GetControllerInfo(const std::string_view& name)
-{
- for (const ControllerInfo& info : s_controller_info)
- {
- if (name == info.name)
- return &info;
- }
-
- return nullptr;
-}
-
-std::vector> PAD::GetControllerTypeNames()
-{
- std::vector> ret;
- for (const ControllerInfo& info : s_controller_info)
- ret.emplace_back(info.name, info.display_name);
-
- return ret;
-}
-
-std::vector PAD::GetControllerBinds(const std::string_view& type)
-{
- std::vector ret;
-
- const ControllerInfo* info = GetControllerInfo(type);
- if (info)
- {
- for (u32 i = 0; i < info->num_bindings; i++)
- {
- const InputBindingInfo& bi = info->bindings[i];
- if (bi.bind_type == InputBindingInfo::Type::Unknown || bi.bind_type == InputBindingInfo::Type::Motor)
- continue;
-
- ret.emplace_back(info->bindings[i].name);
- }
- }
-
- return ret;
-}
-
-void PAD::ClearPortBindings(SettingsInterface& si, u32 port)
-{
- const std::string section(StringUtil::StdStringFromFormat("Pad%u", port + 1));
- const std::string type(si.GetStringValue(section.c_str(), "Type", GetDefaultPadType(port)));
-
- const ControllerInfo* info = GetControllerInfo(type);
- if (!info)
- return;
-
- for (u32 i = 0; i < info->num_bindings; i++)
- {
- const InputBindingInfo& bi = info->bindings[i];
- si.DeleteValue(section.c_str(), bi.name);
- si.DeleteValue(section.c_str(), fmt::format("{}Scale", bi.name).c_str());
- si.DeleteValue(section.c_str(), fmt::format("{}Deadzone", bi.name).c_str());
- }
-}
-
-void PAD::CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface& src_si,
- bool copy_pad_config, bool copy_pad_bindings, bool copy_hotkey_bindings)
-{
- if (copy_pad_config)
- {
- dest_si->CopyBoolValue(src_si, "Pad", "MultitapPort1");
- dest_si->CopyBoolValue(src_si, "Pad", "MultitapPort2");
- dest_si->CopyBoolValue(src_si, "Pad", "MultitapPort1");
- dest_si->CopyBoolValue(src_si, "Pad", "MultitapPort2");
- dest_si->CopyFloatValue(src_si, "Pad", "PointerXSpeed");
- dest_si->CopyFloatValue(src_si, "Pad", "PointerYSpeed");
- dest_si->CopyFloatValue(src_si, "Pad", "PointerXDeadZone");
- dest_si->CopyFloatValue(src_si, "Pad", "PointerYDeadZone");
- dest_si->CopyFloatValue(src_si, "Pad", "PointerInertia");
- for (u32 i = 0; i < static_cast(InputSourceType::Count); i++)
- {
- dest_si->CopyBoolValue(src_si, "InputSources",
- InputManager::InputSourceToString(static_cast(i)));
- }
-#ifdef SDL_BUILD
- dest_si->CopyBoolValue(src_si, "InputSources", "SDLControllerEnhancedMode");
-#endif
- }
-
- for (u32 port = 0; port < NUM_CONTROLLER_PORTS; port++)
- {
- const std::string section(fmt::format("Pad{}", port + 1));
- const std::string type(src_si.GetStringValue(section.c_str(), "Type", GetDefaultPadType(port)));
- if (copy_pad_config)
- dest_si->SetStringValue(section.c_str(), "Type", type.c_str());
-
- const ControllerInfo* info = GetControllerInfo(type);
- if (!info)
- return;
-
- if (copy_pad_bindings)
- {
- for (u32 i = 0; i < info->num_bindings; i++)
- {
- const InputBindingInfo& bi = info->bindings[i];
- dest_si->CopyStringListValue(src_si, section.c_str(), bi.name);
- dest_si->CopyFloatValue(src_si, section.c_str(), fmt::format("{}Sensitivity", bi.name).c_str());
- dest_si->CopyFloatValue(src_si, section.c_str(), fmt::format("{}Deadzone", bi.name).c_str());
- }
-
- for (u32 i = 0; i < NUM_MACRO_BUTTONS_PER_CONTROLLER; i++)
- {
- dest_si->CopyStringListValue(src_si, section.c_str(), fmt::format("Macro{}", i + 1).c_str());
- dest_si->CopyStringValue(src_si, section.c_str(), fmt::format("Macro{}Binds", i + 1).c_str());
- dest_si->CopyUIntValue(src_si, section.c_str(), fmt::format("Macro{}Frequency", i + 1).c_str());
- dest_si->CopyFloatValue(src_si, section.c_str(), fmt::format("Macro{}Pressure", i + 1).c_str());
- }
- }
-
- if (copy_pad_config)
- {
- for (u32 i = 0; i < info->num_settings; i++)
- {
- const SettingInfo& csi = info->settings[i];
- csi.CopyValue(dest_si, src_si, section.c_str(), csi.name);
- }
- }
- }
-
- if (copy_hotkey_bindings)
- {
- std::vector hotkeys(InputManager::GetHotkeyList());
- for (const HotkeyInfo* hki : hotkeys)
- dest_si->CopyStringListValue(src_si, "Hotkeys", hki->name);
- }
-}
-
-static u32 TryMapGenericMapping(SettingsInterface& si, const std::string& section,
- const InputManager::GenericInputBindingMapping& mapping, InputBindingInfo::Type bind_type,
- GenericInputBinding generic_name, const char* bind_name)
-{
- // find the mapping it corresponds to
- const std::string* found_mapping = nullptr;
- for (const std::pair& it : mapping)
- {
- if (it.first == generic_name)
- {
- found_mapping = &it.second;
- break;
- }
- }
-
- // Remove previously-set binding scales.
- if (bind_type == InputBindingInfo::Type::Button || bind_type == InputBindingInfo::Type::Axis ||
- bind_type == InputBindingInfo::Type::HalfAxis)
- {
- si.DeleteValue(section.c_str(), fmt::format("{}Scale", bind_name).c_str());
- si.DeleteValue(section.c_str(), fmt::format("{}Deadzone", bind_name).c_str());
- }
-
- if (found_mapping)
- {
- Console.WriteLn("(MapController) Map %s/%s to '%s'", section.c_str(), bind_name, found_mapping->c_str());
- si.SetStringValue(section.c_str(), bind_name, found_mapping->c_str());
- return 1;
- }
- else
- {
- si.DeleteValue(section.c_str(), bind_name);
- return 0;
- }
-}
-
-
-bool PAD::MapController(SettingsInterface& si, u32 controller,
- const std::vector>& mapping)
-{
- const std::string section(StringUtil::StdStringFromFormat("Pad%u", controller + 1));
- const std::string type(si.GetStringValue(section.c_str(), "Type", GetDefaultPadType(controller)));
- const ControllerInfo* info = GetControllerInfo(type);
- if (!info)
- return false;
-
- u32 num_mappings = 0;
- for (u32 i = 0; i < info->num_bindings; i++)
- {
- const InputBindingInfo& bi = info->bindings[i];
- if (bi.generic_mapping == GenericInputBinding::Unknown)
- continue;
-
- num_mappings += TryMapGenericMapping(si, section, mapping, bi.bind_type, bi.generic_mapping, bi.name);
- }
- if (info->vibration_caps == VibrationCapabilities::LargeSmallMotors)
- {
- num_mappings += TryMapGenericMapping(si, section, mapping, InputBindingInfo::Type::Motor, GenericInputBinding::SmallMotor, "SmallMotor");
- num_mappings += TryMapGenericMapping(si, section, mapping, InputBindingInfo::Type::Motor, GenericInputBinding::LargeMotor, "LargeMotor");
- }
- else if (info->vibration_caps == VibrationCapabilities::SingleMotor)
- {
- if (TryMapGenericMapping(si, section, mapping, InputBindingInfo::Type::Motor, GenericInputBinding::LargeMotor, "Motor") == 0)
- num_mappings += TryMapGenericMapping(si, section, mapping, InputBindingInfo::Type::Motor, GenericInputBinding::SmallMotor, "Motor");
- else
- num_mappings++;
- }
-
- return (num_mappings > 0);
-}
-
-void PAD::SetControllerState(u32 controller, u32 bind, float value)
-{
- if (controller >= NUM_CONTROLLER_PORTS || bind > MAX_KEYS)
- return;
-
- g_key_status.Set(controller, bind, value);
-}
-
-void PAD::LoadMacroButtonConfig(const SettingsInterface& si, u32 pad, const std::string_view& type, const std::string& section)
-{
- // lazily initialized
- std::vector binds;
-
- for (u32 i = 0; i < NUM_MACRO_BUTTONS_PER_CONTROLLER; i++)
- {
- std::string binds_string;
- if (!si.GetStringValue(section.c_str(), fmt::format("Macro{}Binds", i + 1).c_str(), &binds_string))
- continue;
-
- const u32 frequency = si.GetUIntValue(section.c_str(), fmt::format("Macro{}Frequency", i + 1).c_str(), 0u);
- if (binds.empty())
- binds = GetControllerBinds(type);
-
- const float pressure = si.GetFloatValue(section.c_str(), fmt::format("Macro{}Pressure", i + 1).c_str(), 1.0f);
-
- // convert binds
- std::vector bind_indices;
- std::vector buttons_split(StringUtil::SplitString(binds_string, '&', true));
- if (buttons_split.empty())
- continue;
- for (const std::string_view& button : buttons_split)
- {
- auto it = std::find(binds.begin(), binds.end(), button);
- if (it == binds.end())
- {
- Console.Error("Invalid bind '%.*s' in macro button %u for pad %u", static_cast(button.size()), button.data(), pad, i);
- continue;
- }
-
- bind_indices.push_back(static_cast(std::distance(binds.begin(), it)));
- }
- if (bind_indices.empty())
- continue;
-
- s_macro_buttons[pad][i].buttons = std::move(bind_indices);
- s_macro_buttons[pad][i].toggle_frequency = frequency;
- s_macro_buttons[pad][i].pressure = pressure;
- }
-}
-
-void PAD::SetMacroButtonState(u32 pad, u32 index, bool state)
-{
- if (pad >= NUM_CONTROLLER_PORTS || index >= NUM_MACRO_BUTTONS_PER_CONTROLLER)
- return;
-
- MacroButton& mb = s_macro_buttons[pad][index];
- if (mb.buttons.empty() || mb.trigger_state == state)
- return;
-
- mb.toggle_counter = mb.toggle_frequency;
- mb.trigger_state = state;
- if (mb.toggle_state != state)
- {
- mb.toggle_state = state;
- ApplyMacroButton(pad, mb);
- }
-}
-
-std::vector PAD::GetInputProfileNames()
-{
- FileSystem::FindResultsArray results;
- FileSystem::FindFiles(EmuFolders::InputProfiles.c_str(), "*.ini",
- FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RELATIVE_PATHS,
- &results);
-
- std::vector ret;
- ret.reserve(results.size());
- for (FILESYSTEM_FIND_DATA& fd : results)
- ret.emplace_back(Path::GetFileTitle(fd.FileName));
- return ret;
-}
-
-void PAD::ApplyMacroButton(u32 pad, const MacroButton& mb)
-{
- const float value = mb.toggle_state ? mb.pressure : 0.0f;
- for (const u32 btn : mb.buttons)
- g_key_status.Set(pad, btn, value);
-}
-
-void PAD::UpdateMacroButtons()
-{
- for (u32 pad = 0; pad < NUM_CONTROLLER_PORTS; pad++)
- {
- for (u32 index = 0; index < NUM_MACRO_BUTTONS_PER_CONTROLLER; index++)
- {
- MacroButton& mb = s_macro_buttons[pad][index];
- if (!mb.trigger_state || mb.toggle_frequency == 0)
- continue;
-
- mb.toggle_counter--;
- if (mb.toggle_counter > 0)
- continue;
-
- mb.toggle_counter = mb.toggle_frequency;
- mb.toggle_state = !mb.toggle_state;
- ApplyMacroButton(pad, mb);
- }
- }
-}
diff --git a/pcsx2/PAD/Host/PAD.h b/pcsx2/PAD/Host/PAD.h
deleted file mode 100644
index 68feae00fc..0000000000
--- a/pcsx2/PAD/Host/PAD.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/* 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 .
- */
-
-#pragma once
-
-#include
-#include
-#include
-#include
-
-#include "Config.h"
-#include "PAD/Host/Global.h"
-#include "SaveState.h"
-
-class SettingsInterface;
-struct WindowInfo;
-enum class GenericInputBinding : u8;
-
-s32 PADinit();
-void PADshutdown();
-s32 PADopen();
-void PADclose();
-s32 PADsetSlot(u8 port, u8 slot);
-s32 PADfreeze(FreezeAction mode, freezeData* data);
-u8 PADstartPoll(int _port, int _slot);
-u8 PADpoll(u8 value);
-bool PADcomplete();
-
-namespace PAD
-{
- enum class ControllerType: u8
- {
- NotConnected,
- DualShock2,
- Count
- };
-
- enum class VibrationCapabilities : u8
- {
- NoVibration,
- LargeSmallMotors,
- SingleMotor,
- Count
- };
-
- struct ControllerInfo
- {
- ControllerType type;
- const char* name;
- const char* display_name;
- const InputBindingInfo* bindings;
- u32 num_bindings;
- const SettingInfo* settings;
- u32 num_settings;
- 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 = 16;
-
- /// Default stick deadzone/sensitivity.
- static constexpr float DEFAULT_STICK_DEADZONE = 0.0f;
- static constexpr float DEFAULT_STICK_SCALE = 1.33f;
- static constexpr float DEFAULT_TRIGGER_DEADZONE = 0.0f;
- static constexpr float DEFAULT_TRIGGER_SCALE = 1.0f;
- static constexpr float DEFAULT_MOTOR_SCALE = 1.0f;
- static constexpr float DEFAULT_PRESSURE_MODIFIER = 0.5f;
- static constexpr float DEFAULT_BUTTON_DEADZONE = 0.0f;
-
- /// Returns the default type for the specified port.
- const char* GetDefaultPadType(u32 pad);
-
- /// Reloads configuration.
- void LoadConfig(const SettingsInterface& si);
-
- /// Restores default configuration.
- void SetDefaultControllerConfig(SettingsInterface& si);
- void SetDefaultHotkeyConfig(SettingsInterface& si);
-
- /// Clears all bindings for a given port.
- void ClearPortBindings(SettingsInterface& si, u32 port);
-
- /// Copies pad configuration from one interface (ini) to another.
- void CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface& src_si,
- bool copy_pad_config = true, bool copy_pad_bindings = true, bool copy_hotkey_bindings = true);
-
- /// Updates vibration and other internal state. Called at the *end* of a frame.
- void Update();
-
- /// Returns a list of controller type names. Pair of [name, display name].
- std::vector> GetControllerTypeNames();
-
- /// Returns the list of binds for the specified controller type.
- std::vector GetControllerBinds(const std::string_view& type);
-
- /// Returns general information for the specified controller type.
- const ControllerInfo* GetControllerInfo(ControllerType type);
- const ControllerInfo* GetControllerInfo(const std::string_view& name);
-
- /// Performs automatic controller mapping with the provided list of generic mappings.
- bool MapController(SettingsInterface& si, u32 controller,
- const std::vector>& mapping);
-
- /// Sets the specified bind on a controller to the specified pressure (normalized to 0..1).
- void SetControllerState(u32 controller, u32 bind, float value);
-
- /// Sets the state of the specified macro button.
- void SetMacroButtonState(u32 pad, u32 index, bool state);
-
- /// Returns a list of input profiles available.
- std::vector GetInputProfileNames();
-} // namespace PAD
diff --git a/pcsx2/PAD/Host/StateManagement.cpp b/pcsx2/PAD/Host/StateManagement.cpp
deleted file mode 100644
index 85351fa19a..0000000000
--- a/pcsx2/PAD/Host/StateManagement.cpp
+++ /dev/null
@@ -1,523 +0,0 @@
-/* PCSX2 - PS2 Emulator for PCs
- * Copyright (C) 2002-2023 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 .
- */
-
-#include "PrecompiledHeader.h"
-
-#include "PAD/Host/StateManagement.h"
-#include "PAD/Host/KeyStatus.h"
-#include "PAD/Host/PAD.h"
-#include "Input/InputManager.h"
-#include "Sio.h"
-
-template
-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, int _slot)
-{
- if (_port >= 2)
- {
- reset();
- return 0;
- }
-
- port = _port;
- slot = _slot;
-
- const u32 ext_port = sioConvertPortAndSlotToPad(port, slot);
-
- if (g_key_status.GetType(ext_port) == PAD::ControllerType::NotConnected)
- {
- queryDone = 1;
- numBytes = 0;
- lastByte = 1;
-
- return 0;
- }
- else
- {
- queryDone = 0;
- 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)
-{
- if (nextVibrate[0] == currentVibrate[0] && nextVibrate[1] == currentVibrate[1])
- return;
-
- currentVibrate[0] = nextVibrate[0];
- currentVibrate[1] = nextVibrate[1];
- InputManager::SetPadVibrationIntensity(port,
- std::min(static_cast(currentVibrate[0]) * g_key_status.GetVibrationScale(port, 0) * (1.0f / 255.0f), 1.0f),
- std::min(static_cast(currentVibrate[1]) * g_key_status.GetVibrationScale(port, 1) * (1.0f / 255.0f), 1.0f)
- );
-}
-
-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(sioConvertPortAndSlotToPad(port, slot));
-}
-
-//////////////////////////////////////////////////////////////////////
-// 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 _port, u8 _slot)
-{
- return query.start_poll(_port, _slot);
-}
-
-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
-
- 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)
- {
- case MODE_ANALOG:
- case MODE_DS2_NATIVE:
- pad->set_mode(MODE_DIGITAL);
- break;
- case MODE_DIGITAL:
- default:
- pad->set_mode(MODE_ANALOG);
- break;
- }
- }
-
- 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(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(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(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;
- }
- }
-
-#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];
- }
-}
-
-bool pad_complete()
-{
- return query.queryDone;
-}
\ No newline at end of file
diff --git a/pcsx2/PAD/Host/StateManagement.h b/pcsx2/PAD/Host/StateManagement.h
deleted file mode 100644
index acca386607..0000000000
--- a/pcsx2/PAD/Host/StateManagement.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/* 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 .
- */
-
-#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, int _slot);
-
- template
- void set_result(const u8 (&rsp)[S])
- {
- memcpy(response + 2, rsp, S);
- numBytes = 2 + S;
- }
-
- template
- 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 _port, u8 _slot);
-extern u8 pad_poll(u8 value);
-extern bool pad_complete();
\ No newline at end of file
diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp
index 65b08a6a78..9237e5e9a8 100644
--- a/pcsx2/Pcsx2Config.cpp
+++ b/pcsx2/Pcsx2Config.cpp
@@ -23,7 +23,7 @@
#include "Config.h"
#include "GS.h"
#include "CDVD/CDVDcommon.h"
-#include "MemoryCardFile.h"
+#include "SIO/Memcard/MemoryCardFile.h"
#include "USB/USB.h"
#ifdef _WIN32
diff --git a/pcsx2/R3000A.cpp b/pcsx2/R3000A.cpp
index b0ceed78a0..ef95b1456e 100644
--- a/pcsx2/R3000A.cpp
+++ b/pcsx2/R3000A.cpp
@@ -18,7 +18,7 @@
#include "R3000A.h"
#include "Common.h"
-#include "Sio.h"
+#include "SIO/Sio0.h"
#include "Sif.h"
#include "DebugTools/Breakpoints.h"
#include "R5900OpcodeTables.h"
@@ -175,7 +175,7 @@ static __fi void Sio0TestEvent(IopEventId n)
if (psxTestCycle(psxRegs.sCycle[n], psxRegs.eCycle[n]))
{
psxRegs.interrupt &= ~(1 << n);
- sio0.Interrupt(Sio0Interrupt::TEST_EVENT);
+ g_Sio0.Interrupt(Sio0Interrupt::TEST_EVENT);
}
else
{
diff --git a/pcsx2/Recording/PadData.cpp b/pcsx2/Recording/PadData.cpp
index 54ef4fe145..577ebb34e3 100644
--- a/pcsx2/Recording/PadData.cpp
+++ b/pcsx2/Recording/PadData.cpp
@@ -17,20 +17,21 @@
#include "DebugTools/Debug.h"
#include "Recording/PadData.h"
+#include "SIO/Pad/PadManager.h"
+#include "SIO/Pad/PadDualshock2Types.h"
+#include "SIO/Sio.h"
#include
-#include "PAD/Host/KeyStatus.h"
-#include "Sio.h"
-
PadData::PadData(const int port, const int slot)
{
m_port = port;
m_slot = slot;
m_ext_port = sioConvertPortAndSlotToPad(m_port, m_slot);
+ PadBase* pad = g_PadManager.GetPad(m_ext_port);
// Get the state of the buttons
// TODO - for the new recording file format, allow informing max number of buttons per frame per controller as well (ie. the analog button)
- const u32 buttons = g_key_status.GetButtons(m_ext_port);
+ const u32 buttons = pad->GetButtons();
// - pressed group one
// - left
// - down
@@ -52,26 +53,26 @@ PadData::PadData(const int port, const int slot)
// - l2
m_compactPressFlagsGroupTwo = (buttons & 0b11111111);
// Get the analog values
- m_rightAnalog = g_key_status.GetRawRightAnalog(m_ext_port);
- m_leftAnalog = g_key_status.GetRawLeftAnalog(m_ext_port);
+ m_rightAnalog = pad->GetRawRightAnalog();
+ m_leftAnalog = pad->GetRawLeftAnalog();
// Get pressure bytes (12 of them)
- m_left = {(0b10000000 & m_compactPressFlagsGroupOne) == 0, g_key_status.GetRawPressure(m_ext_port, gamePadValues::PAD_LEFT)};
- m_down = {(0b01000000 & m_compactPressFlagsGroupOne) == 0, g_key_status.GetRawPressure(m_ext_port, gamePadValues::PAD_DOWN)};
- m_right = {(0b00100000 & m_compactPressFlagsGroupOne) == 0, g_key_status.GetRawPressure(m_ext_port, gamePadValues::PAD_RIGHT)};
- m_up = {(0b00010000 & m_compactPressFlagsGroupOne) == 0, g_key_status.GetRawPressure(m_ext_port, gamePadValues::PAD_UP)};
+ m_left = {(0b10000000 & m_compactPressFlagsGroupOne) == 0, pad->GetRawInput(Dualshock2::Inputs::PAD_LEFT)};
+ m_down = {(0b01000000 & m_compactPressFlagsGroupOne) == 0, pad->GetRawInput(Dualshock2::Inputs::PAD_DOWN)};
+ m_right = {(0b00100000 & m_compactPressFlagsGroupOne) == 0, pad->GetRawInput(Dualshock2::Inputs::PAD_RIGHT)};
+ m_up = {(0b00010000 & m_compactPressFlagsGroupOne) == 0, pad->GetRawInput(Dualshock2::Inputs::PAD_UP)};
m_start = (0b00001000 & m_compactPressFlagsGroupOne) == 0;
m_r3 = (0b00000100 & m_compactPressFlagsGroupOne) == 0;
m_l3 = (0b00000010 & m_compactPressFlagsGroupOne) == 0;
m_select = (0b00000001 & m_compactPressFlagsGroupOne) == 0;
- m_square = {(0b10000000 & m_compactPressFlagsGroupTwo) == 0, g_key_status.GetRawPressure(m_ext_port, gamePadValues::PAD_SQUARE)};
- m_cross = {(0b01000000 & m_compactPressFlagsGroupTwo) == 0, g_key_status.GetRawPressure(m_ext_port, gamePadValues::PAD_CROSS)};
- m_circle = {(0b00100000 & m_compactPressFlagsGroupTwo) == 0, g_key_status.GetRawPressure(m_ext_port, gamePadValues::PAD_CIRCLE)};
- m_triangle = {(0b00010000 & m_compactPressFlagsGroupTwo) == 0, g_key_status.GetRawPressure(m_ext_port, gamePadValues::PAD_TRIANGLE)};
- m_r1 = {(0b00001000 & m_compactPressFlagsGroupTwo) == 0, g_key_status.GetRawPressure(m_ext_port, gamePadValues::PAD_R1)};
- m_l1 = {(0b00000100 & m_compactPressFlagsGroupTwo) == 0, g_key_status.GetRawPressure(m_ext_port, gamePadValues::PAD_L1)};
- m_r2 = {(0b00000010 & m_compactPressFlagsGroupTwo) == 0, g_key_status.GetRawPressure(m_ext_port, gamePadValues::PAD_R2)};
- m_l2 = {(0b00000001 & m_compactPressFlagsGroupTwo) == 0, g_key_status.GetRawPressure(m_ext_port, gamePadValues::PAD_L2)};
+ m_square = {(0b10000000 & m_compactPressFlagsGroupTwo) == 0, pad->GetRawInput(Dualshock2::Inputs::PAD_SQUARE)};
+ m_cross = {(0b01000000 & m_compactPressFlagsGroupTwo) == 0, pad->GetRawInput(Dualshock2::Inputs::PAD_CROSS)};
+ m_circle = {(0b00100000 & m_compactPressFlagsGroupTwo) == 0, pad->GetRawInput(Dualshock2::Inputs::PAD_CIRCLE)};
+ m_triangle = {(0b00010000 & m_compactPressFlagsGroupTwo) == 0, pad->GetRawInput(Dualshock2::Inputs::PAD_TRIANGLE)};
+ m_r1 = {(0b00001000 & m_compactPressFlagsGroupTwo) == 0, pad->GetRawInput(Dualshock2::Inputs::PAD_R1)};
+ m_l1 = {(0b00000100 & m_compactPressFlagsGroupTwo) == 0, pad->GetRawInput(Dualshock2::Inputs::PAD_L1)};
+ m_r2 = {(0b00000010 & m_compactPressFlagsGroupTwo) == 0, pad->GetRawInput(Dualshock2::Inputs::PAD_R2)};
+ m_l2 = {(0b00000001 & m_compactPressFlagsGroupTwo) == 0, pad->GetRawInput(Dualshock2::Inputs::PAD_L2)};
}
PadData::PadData(const int port, const int slot, const std::array data)
@@ -107,26 +108,27 @@ PadData::PadData(const int port, const int slot, const std::array data)
void PadData::OverrideActualController() const
{
- g_key_status.SetRawAnalogs(m_ext_port, m_leftAnalog, m_rightAnalog);
+ PadBase* pad = g_PadManager.GetPad(m_ext_port);
+ pad->SetRawAnalogs(m_leftAnalog, m_rightAnalog);
- g_key_status.Set(m_ext_port, PAD_RIGHT, std::get<1>(m_right));
- g_key_status.Set(m_ext_port, PAD_LEFT, std::get<1>(m_left));
- g_key_status.Set(m_ext_port, PAD_UP, std::get<1>(m_up));
- g_key_status.Set(m_ext_port, PAD_DOWN, std::get<1>(m_down));
- g_key_status.Set(m_ext_port, PAD_START, m_start);
- g_key_status.Set(m_ext_port, PAD_SELECT, m_select);
- g_key_status.Set(m_ext_port, PAD_R3, m_r3);
- g_key_status.Set(m_ext_port, PAD_L3, m_l3);
+ pad->Set(Dualshock2::Inputs::PAD_RIGHT, std::get<1>(m_right));
+ pad->Set(Dualshock2::Inputs::PAD_LEFT, std::get<1>(m_left));
+ pad->Set(Dualshock2::Inputs::PAD_UP, std::get<1>(m_up));
+ pad->Set(Dualshock2::Inputs::PAD_DOWN, std::get<1>(m_down));
+ pad->Set(Dualshock2::Inputs::PAD_START, m_start);
+ pad->Set(Dualshock2::Inputs::PAD_SELECT, m_select);
+ pad->Set(Dualshock2::Inputs::PAD_R3, m_r3);
+ pad->Set(Dualshock2::Inputs::PAD_L3, m_l3);
- g_key_status.Set(m_ext_port, PAD_SQUARE, std::get<1>(m_square));
- g_key_status.Set(m_ext_port, PAD_CROSS, std::get<1>(m_cross));
- g_key_status.Set(m_ext_port, PAD_CIRCLE, std::get<1>(m_circle));
- g_key_status.Set(m_ext_port, PAD_TRIANGLE, std::get<1>(m_triangle));
+ pad->Set(Dualshock2::Inputs::PAD_SQUARE, std::get<1>(m_square));
+ pad->Set(Dualshock2::Inputs::PAD_CROSS, std::get<1>(m_cross));
+ pad->Set(Dualshock2::Inputs::PAD_CIRCLE, std::get<1>(m_circle));
+ pad->Set(Dualshock2::Inputs::PAD_TRIANGLE, std::get<1>(m_triangle));
- g_key_status.Set(m_ext_port, PAD_R1, std::get<1>(m_r1));
- g_key_status.Set(m_ext_port, PAD_L1, std::get<1>(m_l1));
- g_key_status.Set(m_ext_port, PAD_R2, std::get<1>(m_r2));
- g_key_status.Set(m_ext_port, PAD_L2, std::get<1>(m_l2));
+ pad->Set(Dualshock2::Inputs::PAD_R1, std::get<1>(m_r1));
+ pad->Set(Dualshock2::Inputs::PAD_L1, std::get<1>(m_l1));
+ pad->Set(Dualshock2::Inputs::PAD_R2, std::get<1>(m_r2));
+ pad->Set(Dualshock2::Inputs::PAD_L2, std::get<1>(m_l2));
}
void addButtonInfoToString(std::string label, std::string& str, std::tuple buttonInfo)
@@ -173,6 +175,6 @@ void PadData::LogPadData() const
const auto& [right_x, right_y] = m_rightAnalog;
const std::string analogs = fmt::format("Left: [{}, {}] | Right: [{}, {}]", left_x, left_y, right_x, right_y);
- const std::string finalLog = fmt::format("[PAD {}:{}:{}]\n\t[Buttons]: {}\n\t[Analogs]: {}\n", m_ext_port, m_port, m_slot, pressedButtons, analogs);
+ const std::string finalLog = fmt::format("[PAD {}:{}]\n\t[Buttons]: {}\n\t[Analogs]: {}\n", m_port, m_slot, pressedButtons, analogs);
controlLog(finalLog);
}
diff --git a/pcsx2/MemoryCardFile.cpp b/pcsx2/SIO/Memcard/MemoryCardFile.cpp
similarity index 92%
rename from pcsx2/MemoryCardFile.cpp
rename to pcsx2/SIO/Memcard/MemoryCardFile.cpp
index a8666b3510..1d54625ee3 100644
--- a/pcsx2/MemoryCardFile.cpp
+++ b/pcsx2/SIO/Memcard/MemoryCardFile.cpp
@@ -14,6 +14,12 @@
*/
#include "PrecompiledHeader.h"
+
+#include "SIO/Memcard/MemoryCardFile.h"
+
+#include "SIO/Memcard/MemoryCardFolder.h"
+#include "SIO/Sio.h"
+
#include "common/FileSystem.h"
#include "common/Path.h"
#include "common/StringUtil.h"
@@ -21,10 +27,6 @@
#include
#include
-#include "MemoryCardFile.h"
-#include "MemoryCardFolder.h"
-#include "Sio.h"
-
#include "System.h"
#include "Config.h"
#include "Host.h"
@@ -52,26 +54,26 @@ bool FileMcd_Open = false;
static u32 CalculateECC(u8* buf)
{
- const u8 parity_table[256] = {0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,
- 0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,
- 1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,1,
- 0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,
- 1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,
- 0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,
- 1,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,
- 1,0,1,1,0};
+ const u8 parity_table[256] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1,
+ 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0,
+ 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1,
+ 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0,
+ 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1,
+ 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0,
+ 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0,
+ 1, 0, 1, 1, 0};
- const u8 column_parity_mask[256] = {0,7,22,17,37,34,51,52,52,51,34,37,17,22,
- 7,0,67,68,85,82,102,97,112,119,119,112,97,102,82,85,68,67,82,85,68,67,119,112,
- 97,102,102,97,112,119,67,68,85,82,17,22,7,0,52,51,34,37,37,34,51,52,0,7,22,17,
- 97,102,119,112,68,67,82,85,85,82,67,68,112,119,102,97,34,37,52,51,7,0,17,22,
- 22,17,0,7,51,52,37,34,51,52,37,34,22,17,0,7,7,0,17,22,34,37,52,51,112,119,102,
- 97,85,82,67,68,68,67,82,85,97,102,119,112,112,119,102,97,85,82,67,68,68,67,82,
- 85,97,102,119,112,51,52,37,34,22,17,0,7,7,0,17,22,34,37,52,51,34,37,52,51,7,0,
- 17,22,22,17,0,7,51,52,37,34,97,102,119,112,68,67,82,85,85,82,67,68,112,119,102,
- 97,17,22,7,0,52,51,34,37,37,34,51,52,0,7,22,17,82,85,68,67,119,112,97,102,102,
- 97,112,119,67,68,85,82,67,68,85,82,102,97,112,119,119,112,97,102,82,85,68,67,
- 0,7,22,17,37,34,51,52,52,51,34,37,17,22,7,0};
+ const u8 column_parity_mask[256] = {0, 7, 22, 17, 37, 34, 51, 52, 52, 51, 34, 37, 17, 22,
+ 7, 0, 67, 68, 85, 82, 102, 97, 112, 119, 119, 112, 97, 102, 82, 85, 68, 67, 82, 85, 68, 67, 119, 112,
+ 97, 102, 102, 97, 112, 119, 67, 68, 85, 82, 17, 22, 7, 0, 52, 51, 34, 37, 37, 34, 51, 52, 0, 7, 22, 17,
+ 97, 102, 119, 112, 68, 67, 82, 85, 85, 82, 67, 68, 112, 119, 102, 97, 34, 37, 52, 51, 7, 0, 17, 22,
+ 22, 17, 0, 7, 51, 52, 37, 34, 51, 52, 37, 34, 22, 17, 0, 7, 7, 0, 17, 22, 34, 37, 52, 51, 112, 119, 102,
+ 97, 85, 82, 67, 68, 68, 67, 82, 85, 97, 102, 119, 112, 112, 119, 102, 97, 85, 82, 67, 68, 68, 67, 82,
+ 85, 97, 102, 119, 112, 51, 52, 37, 34, 22, 17, 0, 7, 7, 0, 17, 22, 34, 37, 52, 51, 34, 37, 52, 51, 7, 0,
+ 17, 22, 22, 17, 0, 7, 51, 52, 37, 34, 97, 102, 119, 112, 68, 67, 82, 85, 85, 82, 67, 68, 112, 119, 102,
+ 97, 17, 22, 7, 0, 52, 51, 34, 37, 37, 34, 51, 52, 0, 7, 22, 17, 82, 85, 68, 67, 119, 112, 97, 102, 102,
+ 97, 112, 119, 67, 68, 85, 82, 67, 68, 85, 82, 102, 97, 112, 119, 119, 112, 97, 102, 82, 85, 68, 67,
+ 0, 7, 22, 17, 37, 34, 51, 52, 52, 51, 34, 37, 17, 22, 7, 0};
u8 column_parity = 0x77;
u8 line_parity_0 = 0x7F;
@@ -343,14 +345,14 @@ void FileMemoryCard::Open()
// Translation note: detailed description should mention that the memory card will be disabled
// for the duration of this session.
Host::ReportFormattedErrorAsync("Memory Card", "Access denied to memory card: \n\n%s\n\n"
- "Another instance of PCSX2 may be using this memory card. Close any other instances of PCSX2, or restart your computer.%s",
+ "Another instance of PCSX2 may be using this memory card. Close any other instances of PCSX2, or restart your computer.%s",
fname.c_str(),
#ifdef WIN32
"\n\nIf your memory card is in a write-protected folder such as \"Program Files\" or \"Program Files (x86)\", move it to another folder, such as \"Documents\" or \"Desktop\"."
#else
""
#endif
- );
+ );
}
else // Load checksum
{
@@ -445,9 +447,9 @@ s32 FileMemoryCard::IsPresent(uint slot)
void FileMemoryCard::GetSizeInfo(uint slot, McdSizeInfo& outways)
{
- outways.SectorSize = 512; // 0x0200
+ outways.SectorSize = 512; // 0x0200
outways.EraseBlockSizeInSectors = 16; // 0x0010
- outways.Xor = 18; // 0x12, XOR 02 00 00 10
+ outways.Xor = 18; // 0x12, XOR 02 00 00 10
if (pxAssert(m_file[slot]))
outways.McdSizeInSectors = static_cast(FileSystem::FSize64(m_file[slot])) / (outways.SectorSize + outways.EraseBlockSizeInSectors);
@@ -618,7 +620,7 @@ uint FileMcd_ConvertToSlot(uint port, uint slot)
return port;
if (port == 0)
return slot + 1; // multitap 1
- return slot + 4; // multitap 2
+ return slot + 4; // multitap 2
}
void FileMcd_SetType()
@@ -645,10 +647,10 @@ void FileMcd_SetType()
void FileMcd_EmuOpen()
{
- if(FileMcd_Open)
+ if (FileMcd_Open)
return;
FileMcd_Open = true;
-
+
Mcd::impl.Open();
Mcd::implFolder.SetFiltering(EmuConfig.McdFolderAutoManage);
@@ -657,7 +659,7 @@ void FileMcd_EmuOpen()
void FileMcd_EmuClose()
{
- if(!FileMcd_Open)
+ if (!FileMcd_Open)
return;
FileMcd_Open = false;
Mcd::implFolder.Close();
@@ -808,7 +810,7 @@ int FileMcd_ReIndex(uint port, uint slot, const std::string& filter)
return -1;
break;
default:
- return -1;
+ return -1;
break;
}
@@ -1088,4 +1090,4 @@ bool FileMcd_DeleteCard(const std::string_view& name)
}
return true;
-}
+}
\ No newline at end of file
diff --git a/pcsx2/MemoryCardFile.h b/pcsx2/SIO/Memcard/MemoryCardFile.h
similarity index 97%
rename from pcsx2/MemoryCardFile.h
rename to pcsx2/SIO/Memcard/MemoryCardFile.h
index 91122538f4..d183ee1560 100644
--- a/pcsx2/MemoryCardFile.h
+++ b/pcsx2/SIO/Memcard/MemoryCardFile.h
@@ -64,4 +64,4 @@ std::vector FileMcd_GetAvailableCards(bool include_in_use_card
std::optional FileMcd_GetCardInfo(const std::string_view& name);
bool FileMcd_CreateNewCard(const std::string_view& name, MemoryCardType type, MemoryCardFileType file_type);
bool FileMcd_RenameCard(const std::string_view& name, const std::string_view& new_name);
-bool FileMcd_DeleteCard(const std::string_view& name);
+bool FileMcd_DeleteCard(const std::string_view& name);
\ No newline at end of file
diff --git a/pcsx2/MemoryCardFolder.cpp b/pcsx2/SIO/Memcard/MemoryCardFolder.cpp
similarity index 99%
rename from pcsx2/MemoryCardFolder.cpp
rename to pcsx2/SIO/Memcard/MemoryCardFolder.cpp
index 2f6d421f56..da7dbdedb1 100644
--- a/pcsx2/MemoryCardFolder.cpp
+++ b/pcsx2/SIO/Memcard/MemoryCardFolder.cpp
@@ -14,10 +14,11 @@
*/
#include "PrecompiledHeader.h"
-#include "common/Path.h"
-#include "MemoryCardFile.h"
-#include "MemoryCardFolder.h"
+#include "SIO/Memcard/MemoryCardFile.h"
+#include "SIO/Memcard/MemoryCardFolder.h"
+
+#include "common/Path.h"
#include "System.h"
#include "Config.h"
@@ -1914,7 +1915,7 @@ void FolderMemoryCard::DeleteFromIndex(const std::string& filePath, const std::s
if (yaml.has_value() && !yaml.value().empty())
{
ryml::NodeRef index = yaml.value().rootref();
-
+
if (index.has_child(c4::csubstr(entry.data(), entry.length())))
{
index.remove_child(c4::csubstr(entry.data(), entry.length()));
@@ -2358,7 +2359,6 @@ s32 FolderMemoryCardAggregator::Save(uint slot, const u8* src, u32 adr, int size
last = std::chrono::system_clock::now();
}
-
}
return saveResult;
@@ -2391,4 +2391,4 @@ bool FolderMemoryCardAggregator::ReIndex(uint slot, const bool enableFiltering,
SetFiltering(enableFiltering);
m_lastKnownFilter = filter;
return false;
-}
+}
\ No newline at end of file
diff --git a/pcsx2/MemoryCardFolder.h b/pcsx2/SIO/Memcard/MemoryCardFolder.h
similarity index 99%
rename from pcsx2/MemoryCardFolder.h
rename to pcsx2/SIO/Memcard/MemoryCardFolder.h
index 7d74d058ff..e8fff6e656 100644
--- a/pcsx2/MemoryCardFolder.h
+++ b/pcsx2/SIO/Memcard/MemoryCardFolder.h
@@ -572,4 +572,4 @@ public:
u64 GetCRC(uint slot);
void NextFrame(uint slot);
bool ReIndex(uint slot, const bool enableFiltering, const std::string& filter);
-};
+};
\ No newline at end of file
diff --git a/pcsx2/MemoryCardProtocol.cpp b/pcsx2/SIO/Memcard/MemoryCardProtocol.cpp
similarity index 78%
rename from pcsx2/MemoryCardProtocol.cpp
rename to pcsx2/SIO/Memcard/MemoryCardProtocol.cpp
index 0dd7061d61..4aa1c8a5be 100644
--- a/pcsx2/MemoryCardProtocol.cpp
+++ b/pcsx2/SIO/Memcard/MemoryCardProtocol.cpp
@@ -15,8 +15,11 @@
#include "PrecompiledHeader.h"
-#include "MemoryCardProtocol.h"
-#include "Sio.h"
+#include "SIO/Memcard/MemoryCardProtocol.h"
+
+#include "SIO/Sio.h"
+#include "SIO/Sio2.h"
+#include "SIO/Sio0.h"
#define MC_LOG_ENABLE 0
#define MC_LOG if (MC_LOG_ENABLE) DevCon
@@ -29,11 +32,11 @@ MemoryCardProtocol g_MemoryCardProtocol;
// If so, return dead air.
bool MemoryCardProtocol::PS1Fail()
{
- if (mcd->IsPSX() && sio2.commandLength > 0)
+ if (mcd->IsPSX() && g_Sio2.commandLength > 0)
{
- while (fifoOut.size() < sio2.commandLength)
+ while (g_Sio2FifoOut.size() < g_Sio2.commandLength)
{
- fifoOut.push_back(0x00);
+ g_Sio2FifoOut.push_back(0x00);
}
return true;
@@ -46,13 +49,13 @@ bool MemoryCardProtocol::PS1Fail()
// then end with 0x2b and terminator bytes. This function is a shortcut for that.
void MemoryCardProtocol::The2bTerminator(size_t length)
{
- while (fifoOut.size() < length - 2)
+ while (g_Sio2FifoOut.size() < length - 2)
{
- fifoOut.push_back(0x00);
+ g_Sio2FifoOut.push_back(0x00);
}
- fifoOut.push_back(0x2b);
- fifoOut.push_back(mcd->term);
+ g_Sio2FifoOut.push_back(0x2b);
+ g_Sio2FifoOut.push_back(mcd->term);
}
// After one read or write, the memcard is almost certainly going to be issued a new read or write
@@ -99,16 +102,16 @@ void MemoryCardProtocol::SetSector()
{
MC_LOG.WriteLn("%s", __FUNCTION__);
PS1_FAIL();
- const u8 sectorLSB = fifoIn.front();
- fifoIn.pop_front();
- const u8 sector2nd = fifoIn.front();
- fifoIn.pop_front();
- const u8 sector3rd = fifoIn.front();
- fifoIn.pop_front();
- const u8 sectorMSB = fifoIn.front();
- fifoIn.pop_front();
- const u8 expectedChecksum = fifoIn.front();
- fifoIn.pop_front();
+ const u8 sectorLSB = g_Sio2FifoIn.front();
+ g_Sio2FifoIn.pop_front();
+ const u8 sector2nd = g_Sio2FifoIn.front();
+ g_Sio2FifoIn.pop_front();
+ const u8 sector3rd = g_Sio2FifoIn.front();
+ g_Sio2FifoIn.pop_front();
+ const u8 sectorMSB = g_Sio2FifoIn.front();
+ g_Sio2FifoIn.pop_front();
+ const u8 expectedChecksum = g_Sio2FifoIn.front();
+ g_Sio2FifoIn.pop_front();
u8 computedChecksum = sectorLSB ^ sector2nd ^ sector3rd ^ sectorMSB;
mcd->goodSector = (computedChecksum == expectedChecksum);
@@ -135,89 +138,89 @@ void MemoryCardProtocol::GetSpecs()
//u8 checksum = 0x00;
McdSizeInfo info;
mcd->GetSizeInfo(info);
- fifoOut.push_back(0x2b);
+ g_Sio2FifoOut.push_back(0x2b);
const u8 sectorSizeLSB = (info.SectorSize & 0xff);
//checksum ^= sectorSizeLSB;
- fifoOut.push_back(sectorSizeLSB);
+ g_Sio2FifoOut.push_back(sectorSizeLSB);
const u8 sectorSizeMSB = (info.SectorSize >> 8);
//checksum ^= sectorSizeMSB;
- fifoOut.push_back(sectorSizeMSB);
+ g_Sio2FifoOut.push_back(sectorSizeMSB);
const u8 eraseBlockSizeLSB = (info.EraseBlockSizeInSectors & 0xff);
//checksum ^= eraseBlockSizeLSB;
- fifoOut.push_back(eraseBlockSizeLSB);
+ g_Sio2FifoOut.push_back(eraseBlockSizeLSB);
const u8 eraseBlockSizeMSB = (info.EraseBlockSizeInSectors >> 8);
//checksum ^= eraseBlockSizeMSB;
- fifoOut.push_back(eraseBlockSizeMSB);
+ g_Sio2FifoOut.push_back(eraseBlockSizeMSB);
const u8 sectorCountLSB = (info.McdSizeInSectors & 0xff);
//checksum ^= sectorCountLSB;
- fifoOut.push_back(sectorCountLSB);
+ g_Sio2FifoOut.push_back(sectorCountLSB);
const u8 sectorCount2nd = (info.McdSizeInSectors >> 8);
//checksum ^= sectorCount2nd;
- fifoOut.push_back(sectorCount2nd);
+ g_Sio2FifoOut.push_back(sectorCount2nd);
const u8 sectorCount3rd = (info.McdSizeInSectors >> 16);
//checksum ^= sectorCount3rd;
- fifoOut.push_back(sectorCount3rd);
+ g_Sio2FifoOut.push_back(sectorCount3rd);
const u8 sectorCountMSB = (info.McdSizeInSectors >> 24);
//checksum ^= sectorCountMSB;
- fifoOut.push_back(sectorCountMSB);
+ g_Sio2FifoOut.push_back(sectorCountMSB);
- fifoOut.push_back(info.Xor);
- fifoOut.push_back(mcd->term);
+ g_Sio2FifoOut.push_back(info.Xor);
+ g_Sio2FifoOut.push_back(mcd->term);
}
void MemoryCardProtocol::SetTerminator()
{
MC_LOG.WriteLn("%s", __FUNCTION__);
PS1_FAIL();
- const u8 newTerminator = fifoIn.front();
- fifoIn.pop_front();
+ const u8 newTerminator = g_Sio2FifoIn.front();
+ g_Sio2FifoIn.pop_front();
const u8 oldTerminator = mcd->term;
mcd->term = newTerminator;
- fifoOut.push_back(0x00);
- fifoOut.push_back(0x2b);
- fifoOut.push_back(oldTerminator);
+ g_Sio2FifoOut.push_back(0x00);
+ g_Sio2FifoOut.push_back(0x2b);
+ g_Sio2FifoOut.push_back(oldTerminator);
}
void MemoryCardProtocol::GetTerminator()
{
MC_LOG.WriteLn("%s", __FUNCTION__);
PS1_FAIL();
- fifoOut.push_back(0x2b);
- fifoOut.push_back(mcd->term);
- fifoOut.push_back(static_cast(Terminator::DEFAULT));
+ g_Sio2FifoOut.push_back(0x2b);
+ g_Sio2FifoOut.push_back(mcd->term);
+ g_Sio2FifoOut.push_back(static_cast(Terminator::DEFAULT));
}
void MemoryCardProtocol::WriteData()
{
MC_LOG.WriteLn("%s", __FUNCTION__);
PS1_FAIL();
- fifoOut.push_back(0x00);
- fifoOut.push_back(0x2b);
- const u8 writeLength = fifoIn.front();
- fifoIn.pop_front();
+ g_Sio2FifoOut.push_back(0x00);
+ g_Sio2FifoOut.push_back(0x2b);
+ const u8 writeLength = g_Sio2FifoIn.front();
+ g_Sio2FifoIn.pop_front();
u8 checksum = 0x00;
std::vector buf;
for (size_t writeCounter = 0; writeCounter < writeLength; writeCounter++)
{
- const u8 writeByte = fifoIn.front();
- fifoIn.pop_front();
+ const u8 writeByte = g_Sio2FifoIn.front();
+ g_Sio2FifoIn.pop_front();
checksum ^= writeByte;
buf.push_back(writeByte);
- fifoOut.push_back(0x00);
+ g_Sio2FifoOut.push_back(0x00);
}
mcd->Write(buf.data(), buf.size());
- fifoOut.push_back(checksum);
- fifoOut.push_back(mcd->term);
+ g_Sio2FifoOut.push_back(checksum);
+ g_Sio2FifoOut.push_back(mcd->term);
ReadWriteIncrement(writeLength);
}
@@ -226,10 +229,10 @@ void MemoryCardProtocol::ReadData()
{
MC_LOG.WriteLn("%s", __FUNCTION__);
PS1_FAIL();
- const u8 readLength = fifoIn.front();
- fifoIn.pop_front();
- fifoOut.push_back(0x00);
- fifoOut.push_back(0x2b);
+ const u8 readLength = g_Sio2FifoIn.front();
+ g_Sio2FifoIn.pop_front();
+ g_Sio2FifoOut.push_back(0x00);
+ g_Sio2FifoOut.push_back(0x2b);
std::vector buf;
buf.resize(readLength);
mcd->Read(buf.data(), buf.size());
@@ -238,11 +241,11 @@ void MemoryCardProtocol::ReadData()
for (const u8 readByte : buf)
{
checksum ^= readByte;
- fifoOut.push_back(readByte);
+ g_Sio2FifoOut.push_back(readByte);
}
- fifoOut.push_back(checksum);
- fifoOut.push_back(mcd->term);
+ g_Sio2FifoOut.push_back(checksum);
+ g_Sio2FifoOut.push_back(mcd->term);
ReadWriteIncrement(readLength);
}
@@ -299,10 +302,7 @@ u8 MemoryCardProtocol::PS1Read(u8 data)
break;
}
- if (sendAck)
- {
- sio0.Acknowledge();
- }
+ g_Sio0.SetAcknowledge(sendAck);
ps1McState.currentByte++;
return ret;
@@ -310,7 +310,7 @@ u8 MemoryCardProtocol::PS1Read(u8 data)
u8 MemoryCardProtocol::PS1State(u8 data)
{
- DevCon.Error("%s(%02X) I do not exist, please change that ASAP.", __FUNCTION__, data);
+ Console.Error("%s(%02X) I do not exist, please change that ASAP.", __FUNCTION__, data);
assert(false);
return 0x00;
}
@@ -378,10 +378,7 @@ u8 MemoryCardProtocol::PS1Write(u8 data)
break;
}
- if (sendAck)
- {
- sio0.Acknowledge();
- }
+ g_Sio0.SetAcknowledge(sendAck);
ps1McState.currentByte++;
return ret;
@@ -390,7 +387,7 @@ u8 MemoryCardProtocol::PS1Write(u8 data)
u8 MemoryCardProtocol::PS1Pocketstation(u8 data)
{
MC_LOG.WriteLn("%s", __FUNCTION__);
- sio2.SetRecv1(Recv1::DISCONNECTED);
+ g_Sio0.SetAcknowledge(false);
return 0x00;
}
@@ -420,8 +417,8 @@ void MemoryCardProtocol::AuthXor()
{
MC_LOG.WriteLn("%s", __FUNCTION__);
PS1_FAIL();
- const u8 modeByte = fifoIn.front();
- fifoIn.pop_front();
+ const u8 modeByte = g_Sio2FifoIn.front();
+ g_Sio2FifoIn.pop_front();
switch (modeByte)
{
@@ -435,20 +432,20 @@ void MemoryCardProtocol::AuthXor()
case 0x13:
{
// Long + XOR
- fifoOut.push_back(0x00);
- fifoOut.push_back(0x2b);
+ g_Sio2FifoOut.push_back(0x00);
+ g_Sio2FifoOut.push_back(0x2b);
u8 xorResult = 0x00;
for (size_t xorCounter = 0; xorCounter < 8; xorCounter++)
{
- const u8 toXOR = fifoIn.front();
- fifoIn.pop_front();
+ const u8 toXOR = g_Sio2FifoIn.front();
+ g_Sio2FifoIn.pop_front();
xorResult ^= toXOR;
- fifoOut.push_back(0x00);
+ g_Sio2FifoOut.push_back(0x00);
}
- fifoOut.push_back(xorResult);
- fifoOut.push_back(mcd->term);
+ g_Sio2FifoOut.push_back(xorResult);
+ g_Sio2FifoOut.push_back(mcd->term);
break;
}
// When encountered, the command length in RECV3 is guaranteed to be 5,
diff --git a/pcsx2/MemoryCardProtocol.h b/pcsx2/SIO/Memcard/MemoryCardProtocol.h
similarity index 98%
rename from pcsx2/MemoryCardProtocol.h
rename to pcsx2/SIO/Memcard/MemoryCardProtocol.h
index 6e8ed85094..0ec6cc0531 100644
--- a/pcsx2/MemoryCardProtocol.h
+++ b/pcsx2/SIO/Memcard/MemoryCardProtocol.h
@@ -24,7 +24,7 @@ struct PS1MemoryCardState
u8 sectorAddrLSB = 0;
u8 checksum = 0;
u8 expectedChecksum = 0;
- std::array buf;
+ std::array buf = {0};
};
// A global class which contains the behavior of each memory card command.
diff --git a/pcsx2/MultitapProtocol.cpp b/pcsx2/SIO/Multitap/MultitapProtocol.cpp
similarity index 70%
rename from pcsx2/MultitapProtocol.cpp
rename to pcsx2/SIO/Multitap/MultitapProtocol.cpp
index 0223887b0c..bb2aaa4471 100644
--- a/pcsx2/MultitapProtocol.cpp
+++ b/pcsx2/SIO/Multitap/MultitapProtocol.cpp
@@ -15,8 +15,10 @@
#include "PrecompiledHeader.h"
-#include "MultitapProtocol.h"
-#include "Sio.h"
+#include "SIO/Multitap/MultitapProtocol.h"
+
+#include "SIO/Sio2.h"
+#include "SIO/SioTypes.h"
#define MT_LOG_ENABLE 0
#define MT_LOG if (MT_LOG_ENABLE) DevCon
@@ -26,30 +28,30 @@ MultitapProtocol g_MultitapProtocol;
void MultitapProtocol::SupportCheck()
{
MT_LOG.WriteLn("%s", __FUNCTION__);
- fifoOut.push_back(0x5a);
- fifoOut.push_back(0x04);
- fifoOut.push_back(0x00);
- fifoOut.push_back(0x5a);
+ g_Sio2FifoOut.push_back(0x5a);
+ g_Sio2FifoOut.push_back(0x04);
+ g_Sio2FifoOut.push_back(0x00);
+ g_Sio2FifoOut.push_back(0x5a);
}
void MultitapProtocol::Select()
{
MT_LOG.WriteLn("%s", __FUNCTION__);
- const u8 newSlot = fifoIn.front();
- fifoIn.pop_front();
+ const u8 newSlot = g_Sio2FifoIn.front();
+ g_Sio2FifoIn.pop_front();
const bool isInBounds = (newSlot < SIO::SLOTS);
if (isInBounds)
{
- sio2.slot = newSlot;
- MT_LOG.WriteLn("Slot changed to %d", sio2.slot);
+ g_Sio2.slot = newSlot;
+ MT_LOG.WriteLn("Slot changed to %d", g_Sio2.slot);
}
- fifoOut.push_back(0x5a);
- fifoOut.push_back(0x00);
- fifoOut.push_back(0x00);
- fifoOut.push_back(isInBounds ? newSlot : 0xff);
- fifoOut.push_back(isInBounds ? 0x5a : 0x66);
+ g_Sio2FifoOut.push_back(0x5a);
+ g_Sio2FifoOut.push_back(0x00);
+ g_Sio2FifoOut.push_back(0x00);
+ g_Sio2FifoOut.push_back(isInBounds ? newSlot : 0xff);
+ g_Sio2FifoOut.push_back(isInBounds ? 0x5a : 0x66);
}
MultitapProtocol::MultitapProtocol() = default;
@@ -63,14 +65,14 @@ void MultitapProtocol::FullReset()
{
SoftReset();
- sio2.slot = 0;
+ g_Sio2.slot = 0;
}
void MultitapProtocol::SendToMultitap()
{
- const u8 commandByte = fifoIn.front();
- fifoIn.pop_front();
- fifoOut.push_back(0x80);
+ const u8 commandByte = g_Sio2FifoIn.front();
+ g_Sio2FifoIn.pop_front();
+ g_Sio2FifoOut.push_back(0x80);
switch (static_cast(commandByte))
{
diff --git a/pcsx2/MultitapProtocol.h b/pcsx2/SIO/Multitap/MultitapProtocol.h
similarity index 100%
rename from pcsx2/MultitapProtocol.h
rename to pcsx2/SIO/Multitap/MultitapProtocol.h
diff --git a/pcsx2/SIO/Pad/PadBase.cpp b/pcsx2/SIO/Pad/PadBase.cpp
new file mode 100644
index 0000000000..2c01fbbb73
--- /dev/null
+++ b/pcsx2/SIO/Pad/PadBase.cpp
@@ -0,0 +1,36 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#include "PrecompiledHeader.h"
+
+#include "SIO/Pad/PadBase.h"
+
+PadBase::PadBase(u8 unifiedSlot)
+{
+ this->unifiedSlot = unifiedSlot;
+}
+
+PadBase::~PadBase() = default;
+
+void PadBase::SoftReset()
+{
+ commandBytesReceived = 1;
+}
+
+void PadBase::FullReset()
+{
+ this->isInConfig = false;
+ this->currentMode = Pad::Mode::DIGITAL;
+}
diff --git a/pcsx2/SIO/Pad/PadBase.h b/pcsx2/SIO/Pad/PadBase.h
new file mode 100644
index 0000000000..2a39487323
--- /dev/null
+++ b/pcsx2/SIO/Pad/PadBase.h
@@ -0,0 +1,63 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#pragma once
+
+#include "SIO/Pad/PadTypes.h"
+
+#include "StateWrapper.h"
+
+#include
+
+class PadBase
+{
+protected:
+ std::array rawInputs;
+ u8 unifiedSlot;
+ bool isInConfig = false;
+ Pad::Mode currentMode = Pad::Mode::NOT_SET;
+ Pad::Command currentCommand = Pad::Command::NOT_SET;
+ size_t commandBytesReceived = 0;
+
+public: // Public members
+ PadBase(u8 unifiedSlot);
+ virtual ~PadBase();
+
+ void SoftReset();
+ void FullReset();
+
+ virtual void Init() = 0;
+ virtual Pad::ControllerType GetType() = 0;
+ virtual void Set(u32 index, float value) = 0;
+ virtual void SetRawAnalogs(const std::tuple left, const std::tuple right) = 0;
+ virtual void SetAxisScale(float deadzone, float scale) = 0;
+ virtual void SetTriggerScale(float deadzone, float scale) = 0;
+ virtual float GetVibrationScale(u32 motor) = 0;
+ virtual void SetVibrationScale(u32 motor, float scale) = 0;
+ virtual float GetPressureModifier() = 0;
+ virtual void SetPressureModifier(float mod) = 0;
+ virtual void SetButtonDeadzone(float deadzone) = 0;
+ virtual void SetAnalogInvertL(bool x, bool y) = 0;
+ virtual void SetAnalogInvertR(bool x, bool y) = 0;
+ virtual u8 GetRawInput(u32 index) = 0;
+ virtual std::tuple GetRawLeftAnalog() = 0;
+ virtual std::tuple GetRawRightAnalog() = 0;
+ virtual u32 GetButtons() = 0;
+ virtual u8 GetPressure(u32 index) = 0;
+
+ virtual void Freeze(StateWrapper& sw) = 0;
+
+ virtual u8 SendCommandByte(u8 commandByte) = 0;
+};
diff --git a/pcsx2/SIO/Pad/PadConfig.cpp b/pcsx2/SIO/Pad/PadConfig.cpp
new file mode 100644
index 0000000000..3e5fca68a1
--- /dev/null
+++ b/pcsx2/SIO/Pad/PadConfig.cpp
@@ -0,0 +1,511 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#include "PrecompiledHeader.h"
+
+#include "SIO/Pad/PadConfig.h"
+
+#include "SIO/Pad/PadManager.h"
+#include "SIO/Pad/PadMacros.h"
+#include "SIO/Pad/PadDualshock2Types.h"
+#include "SIO/Pad/PadGuitarTypes.h"
+
+#include "common/FileSystem.h"
+#include "common/Path.h"
+#include "common/StringUtil.h"
+#include "common/SettingsInterface.h"
+
+#include "Input/InputManager.h"
+
+PadConfig g_PadConfig;
+
+PadConfig::PadConfig() = default;
+PadConfig::~PadConfig() = default;
+
+void PadConfig::LoadConfig(const SettingsInterface& si)
+{
+ g_PadMacros.ClearMacros();
+
+ EmuConfig.MultitapPort0_Enabled = si.GetBoolValue("Pad", "MultitapPort1", false);
+ EmuConfig.MultitapPort1_Enabled = si.GetBoolValue("Pad", "MultitapPort2", false);
+
+ // This is where we would load controller types, if onepad supported them.
+ for (u32 i = 0; i < Pad::NUM_CONTROLLER_PORTS; i++)
+ {
+ const std::string section(GetConfigSection(i));
+ const std::string type(si.GetStringValue(section.c_str(), "Type", GetDefaultPadType(i)));
+ const ControllerInfo* ci = GetControllerInfo(type);
+ PadBase* pad = g_PadManager.GetPad(i);
+
+ // If a pad is not yet constructed, at minimum place a NotConnected pad in the slot.
+ // Do not abort the for loop - If there pad settings, we want those to be applied to the slot.
+ if (!pad)
+ {
+ pad = g_PadManager.ChangePadType(i, Pad::ControllerType::NotConnected);
+ }
+
+ if (!ci)
+ {
+ pad = g_PadManager.ChangePadType(i, Pad::ControllerType::NotConnected);
+ continue;
+ }
+
+
+ const Pad::ControllerType oldType = pad->GetType();
+
+ if (ci->type != oldType)
+ {
+ pad = g_PadManager.ChangePadType(i, ci->type);
+ }
+
+ const float axis_deadzone = si.GetFloatValue(section.c_str(), "Deadzone", Pad::DEFAULT_STICK_DEADZONE);
+ const float axis_scale = si.GetFloatValue(section.c_str(), "AxisScale", Pad::DEFAULT_STICK_SCALE);
+ const float trigger_deadzone = si.GetFloatValue(section.c_str(), "TriggerDeadzone", Pad::DEFAULT_TRIGGER_DEADZONE);
+ const float trigger_scale = si.GetFloatValue(section.c_str(), "TriggerScale", Pad::DEFAULT_TRIGGER_SCALE);
+ const float button_deadzone = si.GetFloatValue(section.c_str(), "ButtonDeadzone", Pad::DEFAULT_BUTTON_DEADZONE);
+ pad->SetAxisScale(axis_deadzone, axis_scale);
+ pad->SetTriggerScale(trigger_deadzone, trigger_scale);
+ pad->SetButtonDeadzone(button_deadzone);
+
+ if (ci->vibration_caps != Pad::VibrationCapabilities::NoVibration)
+ {
+ const float large_motor_scale = si.GetFloatValue(section.c_str(), "LargeMotorScale", Pad::DEFAULT_MOTOR_SCALE);
+ const float small_motor_scale = si.GetFloatValue(section.c_str(), "SmallMotorScale", Pad::DEFAULT_MOTOR_SCALE);
+ pad->SetVibrationScale(0, large_motor_scale);
+ pad->SetVibrationScale(1, small_motor_scale);
+ }
+
+ const float pressure_modifier = si.GetFloatValue(section.c_str(), "PressureModifier", 1.0f);
+ pad->SetPressureModifier(pressure_modifier);
+
+ const int invert_l = si.GetIntValue(section.c_str(), "InvertL", 0);
+ const int invert_r = si.GetIntValue(section.c_str(), "InvertR", 0);
+ pad->SetAnalogInvertL((invert_l & 1) != 0, (invert_l & 2) != 0);
+ pad->SetAnalogInvertR((invert_r & 1) != 0, (invert_r & 2) != 0);
+
+ LoadMacroButtonConfig(si, i, type, section);
+ }
+}
+
+const char* PadConfig::GetDefaultPadType(u32 pad)
+{
+ return (pad == 0) ? "DualShock2" : "None";
+}
+
+void PadConfig::SetDefaultControllerConfig(SettingsInterface& si)
+{
+ si.ClearSection("InputSources");
+ si.ClearSection("Hotkeys");
+ si.ClearSection("Pad");
+
+ // PCSX2 Controller Settings - Global Settings
+ for (u32 i = 0; i < static_cast(InputSourceType::Count); i++)
+ {
+ si.SetBoolValue("InputSources",
+ InputManager::InputSourceToString(static_cast(i)),
+ InputManager::GetInputSourceDefaultEnabled(static_cast(i)));
+ }
+#ifdef SDL_BUILD
+ si.SetBoolValue("InputSources", "SDLControllerEnhancedMode", false);
+#endif
+ si.SetBoolValue("Pad", "MultitapPort1", false);
+ si.SetBoolValue("Pad", "MultitapPort2", false);
+ si.SetFloatValue("Pad", "PointerXScale", 8.0f);
+ si.SetFloatValue("Pad", "PointerYScale", 8.0f);
+
+ // PCSX2 Controller Settings - Default pad types and parameters.
+ for (u32 i = 0; i < Pad::NUM_CONTROLLER_PORTS; i++)
+ {
+ const char* type = GetDefaultPadType(i);
+ const std::string section(GetConfigSection(i));
+ si.ClearSection(section.c_str());
+ si.SetStringValue(section.c_str(), "Type", type);
+
+ const ControllerInfo* ci = GetControllerInfo(type);
+ if (ci)
+ {
+ for (u32 i = 0; i < ci->num_settings; i++)
+ {
+ const SettingInfo& csi = ci->settings[i];
+ switch (csi.type)
+ {
+ case SettingInfo::Type::Boolean:
+ si.SetBoolValue(section.c_str(), csi.name, csi.BooleanDefaultValue());
+ break;
+ case SettingInfo::Type::Integer:
+ case SettingInfo::Type::IntegerList:
+ si.SetIntValue(section.c_str(), csi.name, csi.IntegerDefaultValue());
+ break;
+ case SettingInfo::Type::Float:
+ si.SetFloatValue(section.c_str(), csi.name, csi.FloatDefaultValue());
+ break;
+ case SettingInfo::Type::String:
+ case SettingInfo::Type::StringList:
+ case SettingInfo::Type::Path:
+ si.SetStringValue(section.c_str(), csi.name, csi.StringDefaultValue());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ // PCSX2 Controller Settings - Controller 1 / Controller 2 / ...
+ // Use the automapper to set this up.
+ MapController(si, 0, InputManager::GetGenericBindingMapping("Keyboard"));
+}
+
+void PadConfig::SetDefaultHotkeyConfig(SettingsInterface& si)
+{
+ // PCSX2 Controller Settings - Hotkeys
+
+ // PCSX2 Controller Settings - Hotkeys - General
+ si.SetStringValue("Hotkeys", "ToggleFullscreen", "Keyboard/Alt & Keyboard/Return");
+
+ // PCSX2 Controller Settings - Hotkeys - Graphics
+ si.SetStringValue("Hotkeys", "CycleAspectRatio", "Keyboard/F6");
+ si.SetStringValue("Hotkeys", "CycleInterlaceMode", "Keyboard/F5");
+ si.SetStringValue("Hotkeys", "CycleMipmapMode", "Keyboard/Insert");
+ // si.SetStringValue("Hotkeys", "DecreaseUpscaleMultiplier", "Keyboard"); TBD
+ // si.SetStringValue("Hotkeys", "IncreaseUpscaleMultiplier", "Keyboard"); TBD
+ // si.SetStringValue("Hotkeys", "ReloadTextureReplacements", "Keyboard"); TBD
+ si.SetStringValue("Hotkeys", "GSDumpMultiFrame", "Keyboard/Control & Keyboard/Shift & Keyboard/F8");
+ si.SetStringValue("Hotkeys", "Screenshot", "Keyboard/F8");
+ si.SetStringValue("Hotkeys", "GSDumpSingleFrame", "Keyboard/Shift & Keyboard/F8");
+ si.SetStringValue("Hotkeys", "ToggleSoftwareRendering", "Keyboard/F9");
+ // si.SetStringValue("Hotkeys", "ToggleTextureDumping", "Keyboard"); TBD
+ // si.SetStringValue("Hotkeys", "ToggleTextureReplacements", "Keyboard"); TBD
+ si.SetStringValue("Hotkeys", "ZoomIn", "Keyboard/Control & Keyboard/Plus");
+ si.SetStringValue("Hotkeys", "ZoomOut", "Keyboard/Control & Keyboard/Minus");
+ // Missing hotkey for resetting zoom back to 100 with Keyboard/Control & Keyboard/Asterisk
+
+ // PCSX2 Controller Settings - Hotkeys - Input Recording
+ si.SetStringValue("Hotkeys", "InputRecToggleMode", "Keyboard/Shift & Keyboard/R");
+
+ // PCSX2 Controller Settings - Hotkeys - Save States
+ si.SetStringValue("Hotkeys", "LoadStateFromSlot", "Keyboard/F3");
+ si.SetStringValue("Hotkeys", "SaveStateToSlot", "Keyboard/F1");
+ si.SetStringValue("Hotkeys", "NextSaveStateSlot", "Keyboard/F2");
+ si.SetStringValue("Hotkeys", "PreviousSaveStateSlot", "Keyboard/Shift & Keyboard/F2");
+
+ // PCSX2 Controller Settings - Hotkeys - System
+ // si.SetStringValue("Hotkeys", "DecreaseSpeed", "Keyboard"); TBD
+ // si.SetStringValue("Hotkeys", "FrameAdvance", "Keyboard"); TBD
+ // si.SetStringValue("Hotkeys", "IncreaseSpeed", "Keyboard"); TBD
+ // si.SetStringValue("Hotkeys", "ResetVM", "Keyboard"); TBD
+ // si.SetStringValue("Hotkeys", "ShutdownVM", "Keyboard"); TBD
+ si.SetStringValue("Hotkeys", "OpenPauseMenu", "Keyboard/Escape");
+ si.SetStringValue("Hotkeys", "ToggleFrameLimit", "Keyboard/F4");
+ si.SetStringValue("Hotkeys", "TogglePause", "Keyboard/Space");
+ si.SetStringValue("Hotkeys", "ToggleSlowMotion", "Keyboard/Shift & Keyboard/Backtab");
+ si.SetStringValue("Hotkeys", "ToggleTurbo", "Keyboard/Tab");
+ si.SetStringValue("Hotkeys", "HoldTurbo", "Keyboard/Period");
+}
+
+
+static const PadConfig::ControllerInfo s_controller_info[] = {
+ {Pad::ControllerType::NotConnected, "None", "Not Connected",
+ nullptr, 0,
+ nullptr, 0,
+ Pad::VibrationCapabilities::NoVibration},
+ {Pad::ControllerType::DualShock2, "DualShock2", "DualShock 2",
+ Dualshock2::defaultBindings, std::size(Dualshock2::defaultBindings),
+ Dualshock2::defaultSettings, std::size(Dualshock2::defaultSettings),
+ Pad::VibrationCapabilities::LargeSmallMotors},
+ {Pad::ControllerType::Guitar, "Guitar", "Guitar",
+ Guitar::defaultBindings, std::size(Guitar::defaultBindings),
+ Guitar::defaultSettings, std::size(Guitar::defaultSettings),
+ Pad::VibrationCapabilities::NoVibration},
+};
+
+const PadConfig::ControllerInfo* PadConfig::GetControllerInfo(Pad::ControllerType type)
+{
+ for (const ControllerInfo& info : s_controller_info)
+ {
+ if (type == info.type)
+ return &info;
+ }
+
+ return nullptr;
+}
+
+const PadConfig::ControllerInfo* PadConfig::GetControllerInfo(const std::string_view& name)
+{
+ for (const ControllerInfo& info : s_controller_info)
+ {
+ if (name == info.name)
+ return &info;
+ }
+
+ return nullptr;
+}
+
+const std::vector> PadConfig::GetControllerTypeNames()
+{
+ std::vector> ret;
+ for (const ControllerInfo& info : s_controller_info)
+ ret.emplace_back(info.name, info.display_name);
+
+ return ret;
+}
+
+std::vector PadConfig::GetControllerBinds(const std::string_view& type)
+{
+ std::vector ret;
+
+ const ControllerInfo* info = GetControllerInfo(type);
+ if (info)
+ {
+ for (u32 i = 0; i < info->num_bindings; i++)
+ {
+ const InputBindingInfo& bi = info->bindings[i];
+ if (bi.bind_type == InputBindingInfo::Type::Unknown || bi.bind_type == InputBindingInfo::Type::Motor)
+ continue;
+
+ ret.emplace_back(info->bindings[i].name);
+ }
+ }
+
+ return ret;
+}
+
+void PadConfig::ClearPortBindings(SettingsInterface& si, u32 port)
+{
+ const std::string section(StringUtil::StdStringFromFormat("Pad%u", port + 1));
+ const std::string type(si.GetStringValue(section.c_str(), "Type", GetDefaultPadType(port)));
+
+ const ControllerInfo* info = GetControllerInfo(type);
+ if (!info)
+ return;
+
+ for (u32 i = 0; i < info->num_bindings; i++)
+ si.DeleteValue(section.c_str(), info->bindings[i].name);
+}
+
+void PadConfig::CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface& src_si,
+ bool copy_pad_config, bool copy_pad_bindings, bool copy_hotkey_bindings)
+{
+ if (copy_pad_config)
+ {
+ dest_si->CopyBoolValue(src_si, "Pad", "MultitapPort1");
+ dest_si->CopyBoolValue(src_si, "Pad", "MultitapPort2");
+ dest_si->CopyBoolValue(src_si, "Pad", "MultitapPort1");
+ dest_si->CopyBoolValue(src_si, "Pad", "MultitapPort2");
+ dest_si->CopyFloatValue(src_si, "Pad", "PointerXScale");
+ dest_si->CopyFloatValue(src_si, "Pad", "PointerYScale");
+ for (u32 i = 0; i < static_cast(InputSourceType::Count); i++)
+ {
+ dest_si->CopyBoolValue(src_si, "InputSources",
+ InputManager::InputSourceToString(static_cast(i)));
+ }
+#ifdef SDL_BUILD
+ dest_si->CopyBoolValue(src_si, "InputSources", "SDLControllerEnhancedMode");
+#endif
+ }
+
+ for (u32 port = 0; port < Pad::NUM_CONTROLLER_PORTS; port++)
+ {
+ const std::string section(fmt::format("Pad{}", port + 1));
+ const std::string type(src_si.GetStringValue(section.c_str(), "Type", GetDefaultPadType(port)));
+ if (copy_pad_config)
+ dest_si->SetStringValue(section.c_str(), "Type", type.c_str());
+
+ const ControllerInfo* info = GetControllerInfo(type);
+ if (!info)
+ return;
+
+ if (copy_pad_bindings)
+ {
+ for (u32 i = 0; i < info->num_bindings; i++)
+ {
+ const InputBindingInfo& bi = info->bindings[i];
+ dest_si->CopyStringListValue(src_si, section.c_str(), bi.name);
+ }
+
+ for (u32 i = 0; i < PadMacros::NUM_MACRO_BUTTONS_PER_CONTROLLER; i++)
+ {
+ dest_si->CopyStringListValue(src_si, section.c_str(), fmt::format("Macro{}", i + 1).c_str());
+ dest_si->CopyStringValue(src_si, section.c_str(), fmt::format("Macro{}Binds", i + 1).c_str());
+ dest_si->CopyUIntValue(src_si, section.c_str(), fmt::format("Macro{}Frequency", i + 1).c_str());
+ }
+ }
+
+ if (copy_pad_config)
+ {
+ dest_si->CopyFloatValue(src_si, section.c_str(), "AxisScale");
+
+ if (info->vibration_caps != Pad::VibrationCapabilities::NoVibration)
+ {
+ dest_si->CopyFloatValue(src_si, section.c_str(), "LargeMotorScale");
+ dest_si->CopyFloatValue(src_si, section.c_str(), "SmallMotorScale");
+ }
+
+ for (u32 i = 0; i < info->num_settings; i++)
+ {
+ const SettingInfo& csi = info->settings[i];
+ switch (csi.type)
+ {
+ case SettingInfo::Type::Boolean:
+ dest_si->CopyBoolValue(src_si, section.c_str(), csi.name);
+ break;
+ case SettingInfo::Type::Integer:
+ case SettingInfo::Type::IntegerList:
+ dest_si->CopyIntValue(src_si, section.c_str(), csi.name);
+ break;
+ case SettingInfo::Type::Float:
+ dest_si->CopyFloatValue(src_si, section.c_str(), csi.name);
+ break;
+ case SettingInfo::Type::String:
+ case SettingInfo::Type::StringList:
+ case SettingInfo::Type::Path:
+ dest_si->CopyStringValue(src_si, section.c_str(), csi.name);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ if (copy_hotkey_bindings)
+ {
+ std::vector hotkeys(InputManager::GetHotkeyList());
+ for (const HotkeyInfo* hki : hotkeys)
+ dest_si->CopyStringListValue(src_si, "Hotkeys", hki->name);
+ }
+}
+
+static u32 TryMapGenericMapping(SettingsInterface& si, const std::string& section,
+ const InputManager::GenericInputBindingMapping& mapping, GenericInputBinding generic_name,
+ const char* bind_name)
+{
+ // find the mapping it corresponds to
+ const std::string* found_mapping = nullptr;
+ for (const std::pair& it : mapping)
+ {
+ if (it.first == generic_name)
+ {
+ found_mapping = &it.second;
+ break;
+ }
+ }
+
+ if (found_mapping)
+ {
+ Console.WriteLn("(MapController) Map %s/%s to '%s'", section.c_str(), bind_name, found_mapping->c_str());
+ si.SetStringValue(section.c_str(), bind_name, found_mapping->c_str());
+ return 1;
+ }
+ else
+ {
+ si.DeleteValue(section.c_str(), bind_name);
+ return 0;
+ }
+}
+
+
+bool PadConfig::MapController(SettingsInterface& si, u32 controller,
+ const std::vector>& mapping)
+{
+ const std::string section(StringUtil::StdStringFromFormat("Pad%u", controller + 1));
+ const std::string type(si.GetStringValue(section.c_str(), "Type", GetDefaultPadType(controller)));
+ const ControllerInfo* info = GetControllerInfo(type);
+ if (!info)
+ return false;
+
+ u32 num_mappings = 0;
+ for (u32 i = 0; i < info->num_bindings; i++)
+ {
+ const InputBindingInfo& bi = info->bindings[i];
+ if (bi.generic_mapping == GenericInputBinding::Unknown)
+ continue;
+
+ num_mappings += TryMapGenericMapping(si, section, mapping, bi.generic_mapping, bi.name);
+ }
+ if (info->vibration_caps == Pad::VibrationCapabilities::LargeSmallMotors)
+ {
+ num_mappings += TryMapGenericMapping(si, section, mapping, GenericInputBinding::SmallMotor, "SmallMotor");
+ num_mappings += TryMapGenericMapping(si, section, mapping, GenericInputBinding::LargeMotor, "LargeMotor");
+ }
+ else if (info->vibration_caps == Pad::VibrationCapabilities::SingleMotor)
+ {
+ if (TryMapGenericMapping(si, section, mapping, GenericInputBinding::LargeMotor, "Motor") == 0)
+ num_mappings += TryMapGenericMapping(si, section, mapping, GenericInputBinding::SmallMotor, "Motor");
+ else
+ num_mappings++;
+ }
+
+ return (num_mappings > 0);
+}
+
+std::vector PadConfig::GetInputProfileNames()
+{
+ FileSystem::FindResultsArray results;
+ FileSystem::FindFiles(EmuFolders::InputProfiles.c_str(), "*.ini",
+ FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RELATIVE_PATHS,
+ &results);
+
+ std::vector ret;
+ ret.reserve(results.size());
+ for (FILESYSTEM_FIND_DATA& fd : results)
+ ret.emplace_back(Path::GetFileTitle(fd.FileName));
+ return ret;
+}
+
+std::string PadConfig::GetConfigSection(u32 pad_index)
+{
+ return fmt::format("Pad{}", pad_index + 1);
+}
+
+void PadConfig::LoadMacroButtonConfig(const SettingsInterface& si, u32 pad, const std::string_view& type, const std::string& section)
+{
+ // lazily initialized
+ std::vector binds;
+
+ for (u32 i = 0; i < PadMacros::NUM_MACRO_BUTTONS_PER_CONTROLLER; i++)
+ {
+ std::string binds_string;
+ if (!si.GetStringValue(section.c_str(), StringUtil::StdStringFromFormat("Macro%uBinds", i + 1).c_str(), &binds_string))
+ continue;
+
+ const u32 frequency = si.GetUIntValue(section.c_str(), StringUtil::StdStringFromFormat("Macro%uFrequency", i + 1).c_str(), 0u);
+ if (binds.empty())
+ binds = GetControllerBinds(type);
+
+ // convert binds
+ std::vector bind_indices;
+ std::vector buttons_split(StringUtil::SplitString(binds_string, '&', true));
+ if (buttons_split.empty())
+ continue;
+ for (const std::string_view& button : buttons_split)
+ {
+ auto it = std::find(binds.begin(), binds.end(), button);
+ if (it == binds.end())
+ {
+ Console.Error("Invalid bind '%.*s' in macro button %u for pad %u", static_cast(button.size()), button.data(), pad, i);
+ continue;
+ }
+
+ bind_indices.push_back(static_cast(std::distance(binds.begin(), it)));
+ }
+ if (bind_indices.empty())
+ continue;
+
+ PadMacros::MacroButton& macro = g_PadMacros.GetMacroButton(pad, i);
+ macro.buttons = std::move(bind_indices);
+ macro.toggle_frequency = frequency;
+ }
+}
\ No newline at end of file
diff --git a/pcsx2/SIO/Pad/PadConfig.h b/pcsx2/SIO/Pad/PadConfig.h
new file mode 100644
index 0000000000..214b82ac74
--- /dev/null
+++ b/pcsx2/SIO/Pad/PadConfig.h
@@ -0,0 +1,72 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#pragma once
+
+#include "SIO/Pad/PadTypes.h"
+
+#include "Config.h"
+
+class SettingsInterface;
+enum class GenericInputBinding : u8;
+
+class PadConfig
+{
+public: // Constants
+ struct ControllerInfo
+ {
+ Pad::ControllerType type;
+ const char* name;
+ const char* display_name;
+ const InputBindingInfo* bindings;
+ u32 num_bindings;
+ const SettingInfo* settings;
+ u32 num_settings;
+ Pad::VibrationCapabilities vibration_caps;
+ };
+
+public: // Public members
+ PadConfig();
+ ~PadConfig();
+
+ // Returns the default type for the specified port.
+ const char* GetDefaultPadType(u32 pad);
+ // Reloads configuration.
+ void LoadConfig(const SettingsInterface& si);
+ // Restores default configuration.
+ void SetDefaultControllerConfig(SettingsInterface& si);
+ void SetDefaultHotkeyConfig(SettingsInterface& si);
+ // Clears all bindings for a given port.
+ void ClearPortBindings(SettingsInterface& si, u32 port);
+ // Copies pad configuration from one interface (ini) to another.
+ void CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface& src_si,
+ bool copy_pad_config = true, bool copy_pad_bindings = true, bool copy_hotkey_bindings = true);
+ // Returns a list of controller type names. Pair of [name, display name].
+ const std::vector> GetControllerTypeNames();
+ // Returns the list of binds for the specified controller type.
+ std::vector GetControllerBinds(const std::string_view& type);
+ // Returns general information for the specified controller type.
+ const ControllerInfo* GetControllerInfo(Pad::ControllerType type);
+ const ControllerInfo* GetControllerInfo(const std::string_view& name);
+ // Performs automatic controller mapping with the provided list of generic mappings.
+ bool MapController(SettingsInterface& si, u32 controller,
+ const std::vector>& mapping);
+ // Returns a list of input profiles available.
+ std::vector GetInputProfileNames();
+ std::string GetConfigSection(u32 pad_index);
+ void LoadMacroButtonConfig(const SettingsInterface& si, u32 pad, const std::string_view& type, const std::string& section);
+};
+
+extern PadConfig g_PadConfig;
diff --git a/pcsx2/SIO/Pad/PadDualshock2.cpp b/pcsx2/SIO/Pad/PadDualshock2.cpp
new file mode 100644
index 0000000000..73489c08b6
--- /dev/null
+++ b/pcsx2/SIO/Pad/PadDualshock2.cpp
@@ -0,0 +1,769 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#include "PrecompiledHeader.h"
+
+#include "SIO/Pad/PadDualshock2.h"
+
+#include "SIO/Pad/PadManager.h"
+#include "SIO/Sio.h"
+#include "SIO/Sio0.h"
+
+#include "Common.h"
+#include "Input/InputManager.h"
+#include "Host.h"
+
+u8 PadDualshock2::Mystery(u8 commandByte)
+{
+ switch (commandBytesReceived)
+ {
+ case 5:
+ return 0x02;
+ case 8:
+ return 0x5a;
+ default:
+ return 0x00;
+ }
+}
+
+u8 PadDualshock2::ButtonQuery(u8 commandByte)
+{
+ switch (commandBytesReceived)
+ {
+ case 3:
+ case 4:
+ return 0xff;
+ case 5:
+ return 0x03;
+ case 8:
+ g_Sio0.SetAcknowledge(false);
+ return 0x5a;
+ default:
+ return 0x00;
+ }
+}
+
+u8 PadDualshock2::Poll(u8 commandByte)
+{
+ PadBase* pad = g_PadManager.GetPad(this->unifiedSlot);
+ const u32 buttons = pad->GetButtons();
+
+ switch (commandBytesReceived)
+ {
+ case 3:
+ this->vibrationMotors.at(0) = commandByte;
+ return (buttons >> 8) & 0xff;
+ case 4:
+ this->vibrationMotors.at(1) = commandByte;
+ InputManager::SetPadVibrationIntensity(this->unifiedSlot,
+ std::min(static_cast(this->vibrationMotors.at(0)) * GetVibrationScale(0) * (1.0f / 255.0f), 1.0f),
+ std::min(static_cast(this->vibrationMotors.at(1)) * GetVibrationScale(1) * (1.0f / 255.0f), 1.0f)
+ );
+
+ // PS1 mode: If the controller is still in digital mode, it is time to stop acknowledging.
+ if (this->currentMode == Pad::Mode::DIGITAL)
+ {
+ g_Sio0.SetAcknowledge(false);
+ }
+
+ return buttons & 0xff;
+ case 5:
+ return pad->GetPressure(Dualshock2::Inputs::PAD_R_RIGHT);
+ case 6:
+ return pad->GetPressure(Dualshock2::Inputs::PAD_R_UP);
+ case 7:
+ return pad->GetPressure(Dualshock2::Inputs::PAD_L_RIGHT);
+ case 8:
+ // PS1 mode: If the controller reaches this byte, it is in analog mode and has irrefutably reached the last byte.
+ // There's simply nothing to check, we know it's done and time to stop acknowledgements.
+ g_Sio0.SetAcknowledge(false);
+ return pad->GetPressure(Dualshock2::Inputs::PAD_L_UP);
+ case 9:
+ return IsButtonBitSet(buttons, 13) ? pad->GetPressure(Dualshock2::Inputs::PAD_RIGHT) : 0;
+ case 10:
+ return IsButtonBitSet(buttons, 15) ? pad->GetPressure(Dualshock2::Inputs::PAD_LEFT) : 0;
+ case 11:
+ return IsButtonBitSet(buttons, 12) ? pad->GetPressure(Dualshock2::Inputs::PAD_UP) : 0;
+ case 12:
+ return IsButtonBitSet(buttons, 14) ? pad->GetPressure(Dualshock2::Inputs::PAD_DOWN) : 0;
+ case 13:
+ return IsButtonBitSet(buttons, 4) ? pad->GetPressure(Dualshock2::Inputs::PAD_TRIANGLE) : 0;
+ case 14:
+ return IsButtonBitSet(buttons, 5) ? pad->GetPressure(Dualshock2::Inputs::PAD_CIRCLE) : 0;
+ case 15:
+ return IsButtonBitSet(buttons, 6) ? pad->GetPressure(Dualshock2::Inputs::PAD_CROSS) : 0;
+ case 16:
+ return IsButtonBitSet(buttons, 7) ? pad->GetPressure(Dualshock2::Inputs::PAD_SQUARE) : 0;
+ case 17:
+ return IsButtonBitSet(buttons, 2) ? pad->GetPressure(Dualshock2::Inputs::PAD_L1) : 0;
+ case 18:
+ return IsButtonBitSet(buttons, 3) ? pad->GetPressure(Dualshock2::Inputs::PAD_R1) : 0;
+ case 19:
+ return IsButtonBitSet(buttons, 0) ? pad->GetPressure(Dualshock2::Inputs::PAD_L2) : 0;
+ case 20:
+ return IsButtonBitSet(buttons, 1) ? pad->GetPressure(Dualshock2::Inputs::PAD_R2) : 0;
+ }
+
+ Console.Warning("%s(%02X) Did not reach a valid return path! Returning zero as a failsafe!", __FUNCTION__, commandByte);
+ return 0x00;
+}
+
+u8 PadDualshock2::Config(u8 commandByte)
+{
+ if (commandBytesReceived == 3)
+ {
+ if (commandByte)
+ {
+ if (!this->isInConfig)
+ {
+ this->isInConfig = true;
+ }
+ else
+ {
+ Console.Warning("%s(%02X) Unexpected enter while already in config mode", __FUNCTION__, commandByte);
+ }
+ }
+ else
+ {
+ if (this->isInConfig)
+ {
+ this->isInConfig = false;
+ const auto [port, slot] = sioConvertPadToPortAndSlot(unifiedSlot);
+ Console.WriteLn(StringUtil::StdStringFromFormat("[Pad] Game finished pad setup for port %d / slot %d - Analogs: %s - Analog Button: %s - Pressure: %s",
+ port + 1,
+ slot + 1,
+ (this->analogLight ? "On" : "Off"),
+ (this->analogLocked ? "Locked" : "Usable"),
+ (this->responseBytes == static_cast(Pad::ResponseBytes::DUALSHOCK2) ? "On" : "Off")));
+ }
+ else
+ {
+ Console.Warning("%s(%02X) Unexpected exit while not in config mode", __FUNCTION__, commandByte);
+ }
+ }
+ }
+
+ // PS1 mode: Config mode would have been triggered by a prior byte in this command sequence;
+ // if we are now in config mode, check the current mode and if this is the last byte. If so,
+ // don't acknowledge.
+ if (this->isInConfig)
+ {
+ if ((this->currentMode == Pad::Mode::DIGITAL && this->commandBytesReceived == 4) || (this->currentMode == Pad::Mode::ANALOG && this->commandBytesReceived == 8))
+ {
+ g_Sio0.SetAcknowledge(false);
+ }
+ }
+
+ return 0x00;
+}
+
+// Changes the mode of the controller between digital and analog, and adjusts the analog LED accordingly.
+u8 PadDualshock2::ModeSwitch(u8 commandByte)
+{
+ switch (commandBytesReceived)
+ {
+ case 3:
+ this->analogLight = commandByte;
+
+ if (this->analogLight)
+ {
+ this->currentMode = Pad::Mode::ANALOG;
+ }
+ else
+ {
+ this->currentMode = Pad::Mode::DIGITAL;
+ }
+
+ break;
+ case 4:
+ this->analogLocked = (commandByte == 0x03);
+ break;
+ case 8:
+ g_Sio0.SetAcknowledge(false);
+ break;
+ default:
+ break;
+ }
+
+ return 0x00;
+}
+
+u8 PadDualshock2::StatusInfo(u8 commandByte)
+{
+ switch (commandBytesReceived)
+ {
+ case 3:
+ return static_cast(Pad::PhysicalType::STANDARD);
+ case 4:
+ return 0x02;
+ case 5:
+ return this->analogLight;
+ case 6:
+ return 0x02;
+ case 7:
+ return 0x01;
+ case 8:
+ g_Sio0.SetAcknowledge(false);
+ return 0x00;
+ default:
+ return 0x00;
+ }
+}
+
+u8 PadDualshock2::Constant1(u8 commandByte)
+{
+ static bool stage;
+
+ switch (commandBytesReceived)
+ {
+ case 3:
+ stage = commandByte;
+ return 0x00;
+ case 6:
+ if (stage)
+ {
+ return 0x00;
+ }
+ else
+ {
+ return 0x02;
+ }
+ case 8:
+ g_Sio0.SetAcknowledge(false);
+ return (stage ? 0x14 : 0x0a);
+ default:
+ return 0x00;
+ }
+}
+
+u8 PadDualshock2::Constant2(u8 commandByte)
+{
+ switch (commandBytesReceived)
+ {
+ case 5:
+ return 0x02;
+ case 8:
+ g_Sio0.SetAcknowledge(false);
+ return 0x00;
+ default:
+ return 0x00;
+ }
+}
+
+u8 PadDualshock2::Constant3(u8 commandByte)
+{
+ static bool stage;
+
+ switch (commandBytesReceived)
+ {
+ case 3:
+ stage = commandByte;
+ return 0x00;
+ case 6:
+ if (stage)
+ {
+ return 0x07;
+ }
+ else
+ {
+ return 0x04;
+ }
+ case 8:
+ g_Sio0.SetAcknowledge(false);
+ return 0x00;
+ default:
+ return 0x00;
+ }
+}
+
+u8 PadDualshock2::VibrationMap(u8 commandByte)
+{
+ switch (commandBytesReceived)
+ {
+ case 3:
+ return 0x00;
+ case 4:
+ return 0x01;
+ case 8:
+ g_Sio0.SetAcknowledge(false);
+ return 0xff;
+ default:
+ return 0xff;
+ }
+}
+
+u8 PadDualshock2::ResponseBytes(u8 commandByte)
+{
+ switch (commandBytesReceived)
+ {
+ case 3:
+ this->responseBytes = commandByte;
+ return 0x00;
+ case 4:
+ this->responseBytes |= (commandByte << 8);
+ return 0x00;
+ case 5:
+ this->responseBytes |= (commandByte << 16);
+
+ switch (static_cast(this->responseBytes))
+ {
+ case Pad::ResponseBytes::ANALOG:
+ this->analogLight = true;
+ this->currentMode = Pad::Mode::ANALOG;
+ break;
+ case Pad::ResponseBytes::DUALSHOCK2:
+ this->analogLight = true;
+ this->currentMode = Pad::Mode::DUALSHOCK2;
+ break;
+ default:
+ this->analogLight = false;
+ this->currentMode = Pad::Mode::DIGITAL;
+ break;
+ }
+
+ return 0x00;
+ case 8:
+ return 0x5a;
+ default:
+ return 0x00;
+ }
+}
+
+PadDualshock2::PadDualshock2(u8 unifiedSlot)
+ : PadBase(unifiedSlot)
+{
+ this->currentMode = Pad::Mode::DIGITAL;
+ Init();
+}
+
+PadDualshock2::~PadDualshock2() = default;
+
+void PadDualshock2::Init()
+{
+ this->buttons = 0xffffffff;
+ this->analogs.lx = Pad::ANALOG_NEUTRAL_POSITION;
+ this->analogs.ly = Pad::ANALOG_NEUTRAL_POSITION;
+ this->analogs.rx = Pad::ANALOG_NEUTRAL_POSITION;
+ this->analogs.ry = Pad::ANALOG_NEUTRAL_POSITION;
+ this->analogs.lxInvert = 0;
+ this->analogs.lyInvert = 0;
+ this->analogs.rxInvert = 0;
+ this->analogs.ryInvert = 0;
+ this->analogLight = false;
+ this->analogLocked = false;
+ this->analogPressed = false;
+ this->responseBytes = 0;
+
+ for (u8 i = 0; i < this->rawInputs.size(); i++)
+ {
+ this->rawInputs.at(i) = 0;
+ }
+
+ for (u8 i = 0; i < this->pressures.size(); i++)
+ {
+ this->pressures.at(i) = 0;
+ }
+
+ this->axisScale = 1.0f;
+ this->axisDeadzone = 0.0f;
+
+ this->vibrationScale.at(0) = 0.0f;
+ this->vibrationScale.at(1) = 1.0f;
+
+ this->pressureModifier = 0.5f;
+ this->buttonDeadzone = 0.0f;
+}
+
+Pad::ControllerType PadDualshock2::GetType()
+{
+ return Pad::ControllerType::DualShock2;
+}
+
+void PadDualshock2::Set(u32 index, float value)
+{
+ if (index > Dualshock2::Inputs::LENGTH)
+ {
+ return;
+ }
+
+ // Since we reordered the buttons for better UI, we need to remap them here.
+ static constexpr std::array bitmaskMapping = {{
+ 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
+ 16, // PAD_ANALOG
+ 17, // PAD_PRESSURE
+ // remainder are analogs and not used here
+ }};
+
+ if (IsAnalogKey(index))
+ {
+ this->rawInputs[index] = static_cast(std::clamp(value * this->axisScale * 255.0f, 0.0f, 255.0f));
+
+ // 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(pos, neg) ((this->rawInputs[pos] != 0) ? (127u + ((this->rawInputs[pos] + 1u) / 2u)) : (127u - (this->rawInputs[neg] / 2u)))
+ if (index <= Dualshock2::Inputs::PAD_L_LEFT)
+ {
+ // Left Stick
+ this->analogs.lx = this->analogs.lxInvert ? MERGE(Dualshock2::Inputs::PAD_L_LEFT, Dualshock2::Inputs::PAD_L_RIGHT) : MERGE(Dualshock2::Inputs::PAD_L_RIGHT, Dualshock2::Inputs::PAD_L_LEFT);
+ this->analogs.ly = this->analogs.lyInvert ? MERGE(Dualshock2::Inputs::PAD_L_UP, Dualshock2::Inputs::PAD_L_DOWN) : MERGE(Dualshock2::Inputs::PAD_L_DOWN, Dualshock2::Inputs::PAD_L_UP);
+ }
+ else
+ {
+ // Right Stick
+ this->analogs.rx = this->analogs.rxInvert ? MERGE(Dualshock2::Inputs::PAD_R_LEFT, Dualshock2::Inputs::PAD_R_RIGHT) : MERGE(Dualshock2::Inputs::PAD_R_RIGHT, Dualshock2::Inputs::PAD_R_LEFT);
+ this->analogs.ry = this->analogs.ryInvert ? MERGE(Dualshock2::Inputs::PAD_R_UP, Dualshock2::Inputs::PAD_R_DOWN) : MERGE(Dualshock2::Inputs::PAD_R_DOWN, Dualshock2::Inputs::PAD_R_UP);
+ }
+#undef MERGE
+
+ // Deadzone computation.
+ const float dz = this->axisDeadzone;
+
+ if (dz > 0.0f)
+ {
+#define MERGE_F(pos, neg) ((this->rawInputs[pos] != 0) ? (static_cast(this->rawInputs[pos]) / 255.0f) : (static_cast(this->rawInputs[neg]) / -255.0f))
+ float posX, posY;
+ if (index <= Dualshock2::Inputs::PAD_L_LEFT)
+ {
+ posX = this->analogs.lxInvert ? MERGE_F(Dualshock2::Inputs::PAD_L_LEFT, Dualshock2::Inputs::PAD_L_RIGHT) : MERGE_F(Dualshock2::Inputs::PAD_L_RIGHT, Dualshock2::Inputs::PAD_L_LEFT);
+ posY = this->analogs.lyInvert ? MERGE_F(Dualshock2::Inputs::PAD_L_UP, Dualshock2::Inputs::PAD_L_DOWN) : MERGE_F(Dualshock2::Inputs::PAD_L_DOWN, Dualshock2::Inputs::PAD_L_UP);
+ }
+ else
+ {
+ posX = this->analogs.rxInvert ? MERGE_F(Dualshock2::Inputs::PAD_R_LEFT, Dualshock2::Inputs::PAD_R_RIGHT) : MERGE_F(Dualshock2::Inputs::PAD_R_RIGHT, Dualshock2::Inputs::PAD_R_LEFT);
+ posY = this->analogs.ryInvert ? MERGE_F(Dualshock2::Inputs::PAD_R_UP, Dualshock2::Inputs::PAD_R_DOWN) : MERGE_F(Dualshock2::Inputs::PAD_R_DOWN, Dualshock2::Inputs::PAD_R_UP);
+ }
+
+ // No point checking if we're at dead center (usually keyboard with no buttons pressed).
+ if (posX != 0.0f || posY != 0.0f)
+ {
+ // Compute the angle at the given position in the stick's square bounding box.
+ const float theta = std::atan2(posY, posX);
+
+ // Compute the position that the edge of the circle would be at, given the angle.
+ const float dzX = std::cos(theta) * dz;
+ const float dzY = std::sin(theta) * dz;
+
+ // We're in the deadzone if our position is less than the circle edge.
+ const bool inX = (posX < 0.0f) ? (posX > dzX) : (posX <= dzX);
+ const bool inY = (posY < 0.0f) ? (posY > dzY) : (posY <= dzY);
+
+ if (inX && inY)
+ {
+ // In deadzone. Set to 127 (center).
+ if (index <= Dualshock2::Inputs::PAD_L_LEFT)
+ {
+ this->analogs.lx = this->analogs.ly = 127;
+ }
+ else
+ {
+ this->analogs.rx = this->analogs.ry = 127;
+ }
+ }
+ }
+#undef MERGE_F
+ }
+ }
+ else if (IsTriggerKey(index))
+ {
+ const float s_value = std::clamp(value * this->triggerScale, 0.0f, 1.0f);
+ const float dz_value = (this->triggerDeadzone > 0.0f && s_value < this->triggerDeadzone) ? 0.0f : s_value;
+ this->rawInputs[index] = static_cast(dz_value * 255.0f);
+ if (dz_value > 0.0f)
+ this->buttons &= ~(1u << bitmaskMapping[index]);
+ else
+ this->buttons |= (1u << bitmaskMapping[index]);
+ }
+ else
+ {
+ // Don't affect L2/R2, since they are analog on most pads.
+ const float pMod = ((this->buttons & (1u << Dualshock2::Inputs::PAD_PRESSURE)) == 0 && !IsTriggerKey(index)) ? this->pressureModifier : 1.0f;
+ const float dzValue = (value < this->buttonDeadzone) ? 0.0f : value;
+ this->rawInputs[index] = static_cast(std::clamp(dzValue * pMod * 255.0f, 0.0f, 255.0f));
+
+ if (dzValue > 0.0f)
+ {
+ this->buttons &= ~(1u << bitmaskMapping[index]);
+ }
+ else
+ {
+ this->buttons |= (1u << bitmaskMapping[index]);
+ }
+
+ // Adjust pressure of all other face buttons which are active when pressure modifier is pressed..
+ if (index == Dualshock2::Inputs::PAD_PRESSURE)
+ {
+ const float adjustPMod = ((this->buttons & (1u << Dualshock2::Inputs::PAD_PRESSURE)) == 0) ? this->pressureModifier : (1.0f / this->pressureModifier);
+
+ for (u32 i = 0; i < Dualshock2::Inputs::LENGTH; i++)
+ {
+ if (i == index || IsAnalogKey(i) || IsTriggerKey(i))
+ {
+ continue;
+ }
+
+ // We add 0.5 here so that the round trip between 255->127->255 when applying works as expected.
+ const float add = (this->rawInputs[i] != 0) ? 0.5f : 0.0f;
+ this->rawInputs[i] = static_cast(std::clamp((static_cast(this->rawInputs[i]) + add) * adjustPMod, 0.0f, 255.0f));
+ }
+ }
+
+ if (index == Dualshock2::Inputs::PAD_ANALOG && !this->analogPressed && value > 0)
+ {
+ this->analogPressed = true;
+
+ if (!this->analogLocked)
+ {
+ this->analogLight = !this->analogLight;
+
+ if (this->analogLight)
+ {
+ this->currentMode = Pad::Mode::ANALOG;
+ }
+ else
+ {
+ this->currentMode = Pad::Mode::DIGITAL;
+ }
+
+ const auto [port, slot] = sioConvertPadToPortAndSlot(unifiedSlot);
+
+ Host::AddKeyedOSDMessage(fmt::format("PadAnalogButtonChange{}{}", port, slot),
+ fmt::format(TRANSLATE_FS("Pad", "Analog light is now {} for port {} / slot {}"),
+ (this->analogLight ? "On" : "Off"),
+ port + 1,
+ slot + 1),
+ Host::OSD_INFO_DURATION);
+ }
+ }
+ else
+ {
+ this->analogPressed = false;
+ }
+ }
+}
+
+void PadDualshock2::SetRawAnalogs(const std::tuple left, const std::tuple right)
+{
+ this->analogs.lx = std::get<0>(left);
+ this->analogs.ly = std::get<1>(left);
+ this->analogs.rx = std::get<0>(right);
+ this->analogs.ry = std::get<1>(right);
+}
+
+void PadDualshock2::SetAxisScale(float deadzone, float scale)
+{
+ this->axisDeadzone = deadzone;
+ this->axisScale = scale;
+}
+
+void PadDualshock2::SetTriggerScale(float deadzone, float scale)
+{
+ this->triggerDeadzone = deadzone;
+ this->triggerScale = scale;
+}
+
+float PadDualshock2::GetVibrationScale(u32 motor)
+{
+ return this->vibrationScale[motor];
+}
+
+void PadDualshock2::SetVibrationScale(u32 motor, float scale)
+{
+ this->vibrationScale[motor] = scale;
+}
+
+float PadDualshock2::GetPressureModifier()
+{
+ return this->pressureModifier;
+}
+
+void PadDualshock2::SetPressureModifier(float mod)
+{
+ this->pressureModifier = mod;
+}
+
+void PadDualshock2::SetButtonDeadzone(float deadzone)
+{
+ this->buttonDeadzone = deadzone;
+}
+
+void PadDualshock2::SetAnalogInvertL(bool x, bool y)
+{
+ this->analogs.lxInvert = x;
+ this->analogs.lyInvert = y;
+}
+
+void PadDualshock2::SetAnalogInvertR(bool x, bool y)
+{
+ this->analogs.rxInvert = x;
+ this->analogs.ryInvert = y;
+}
+
+u8 PadDualshock2::GetRawInput(u32 index)
+{
+ return this->rawInputs[index];
+}
+
+std::tuple PadDualshock2::GetRawLeftAnalog()
+{
+ return {this->analogs.lx, this->analogs.ly};
+}
+
+std::tuple PadDualshock2::GetRawRightAnalog()
+{
+ return {this->analogs.rx, this->analogs.ry};
+}
+
+u32 PadDualshock2::GetButtons()
+{
+ return this->buttons;
+}
+
+u8 PadDualshock2::GetPressure(u32 index)
+{
+ switch (index)
+ {
+ case Dualshock2::Inputs::PAD_R_LEFT:
+ case Dualshock2::Inputs::PAD_R_RIGHT:
+ return this->analogs.rx;
+ case Dualshock2::Inputs::PAD_R_DOWN:
+ case Dualshock2::Inputs::PAD_R_UP:
+ return this->analogs.ry;
+ case Dualshock2::Inputs::PAD_L_LEFT:
+ case Dualshock2::Inputs::PAD_L_RIGHT:
+ return this->analogs.lx;
+ case Dualshock2::Inputs::PAD_L_DOWN:
+ case Dualshock2::Inputs::PAD_L_UP:
+ return this->analogs.ly;
+ default:
+ return this->rawInputs.at(index);
+ }
+}
+
+void PadDualshock2::Freeze(StateWrapper& sw)
+{
+ // Protected PadBase members
+ sw.Do(&rawInputs);
+ sw.Do(&unifiedSlot);
+ sw.Do(&isInConfig);
+ sw.Do(¤tMode);
+ sw.Do(¤tCommand);
+ sw.Do(&commandBytesReceived);
+
+ // Private PadDualshock2 members
+ sw.Do(&buttons);
+ sw.DoBytes(&analogs, sizeof(Dualshock2::Analogs));
+ sw.Do(&analogLight);
+ sw.Do(&analogLocked);
+ sw.Do(&analogPressed);
+ sw.Do(&responseBytes);
+ sw.Do(&pressures);
+ sw.Do(&vibrationMotors);
+ sw.Do(&axisScale);
+ sw.Do(&axisDeadzone);
+ sw.Do(&triggerScale);
+ sw.Do(&triggerDeadzone);
+ sw.Do(&vibrationScale);
+ sw.Do(&pressureModifier);
+ sw.Do(&buttonDeadzone);
+}
+
+u8 PadDualshock2::SendCommandByte(u8 commandByte)
+{
+ u8 ret = 0;
+
+ switch (this->commandBytesReceived)
+ {
+ case 0:
+ ret = 0x00;
+ break;
+ case 1:
+ this->currentCommand = static_cast(commandByte);
+
+ if (this->currentCommand != Pad::Command::POLL && this->currentCommand != Pad::Command::CONFIG && !this->isInConfig)
+ {
+ Console.Warning("%s(%02X) Config-only command was sent to a pad outside of config mode!", __FUNCTION__, commandByte);
+ }
+
+ ret = this->isInConfig ? static_cast(Pad::Mode::CONFIG) : static_cast(this->currentMode);
+ break;
+ case 2:
+ ret = 0x5a;
+ break;
+ default:
+ switch (this->currentCommand)
+ {
+ case Pad::Command::MYSTERY:
+ ret = Mystery(commandByte);
+ break;
+ case Pad::Command::BUTTON_QUERY:
+ ret = ButtonQuery(commandByte);
+ break;
+ case Pad::Command::POLL:
+ ret = Poll(commandByte);
+ break;
+ case Pad::Command::CONFIG:
+ ret = Config(commandByte);
+ break;
+ case Pad::Command::MODE_SWITCH:
+ ret = ModeSwitch(commandByte);
+ break;
+ case Pad::Command::STATUS_INFO:
+ ret = StatusInfo(commandByte);
+ break;
+ case Pad::Command::CONST_1:
+ ret = Constant1(commandByte);
+ break;
+ case Pad::Command::CONST_2:
+ ret = Constant2(commandByte);
+ break;
+ case Pad::Command::CONST_3:
+ ret = Constant3(commandByte);
+ break;
+ case Pad::Command::VIBRATION_MAP:
+ ret = VibrationMap(commandByte);
+ break;
+ case Pad::Command::RESPONSE_BYTES:
+ ret = ResponseBytes(commandByte);
+ break;
+ default:
+ ret = 0x00;
+ break;
+ }
+ }
+
+ this->commandBytesReceived++;
+ return ret;
+}
diff --git a/pcsx2/SIO/Pad/PadDualshock2.h b/pcsx2/SIO/Pad/PadDualshock2.h
new file mode 100644
index 0000000000..052b5876e7
--- /dev/null
+++ b/pcsx2/SIO/Pad/PadDualshock2.h
@@ -0,0 +1,101 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#pragma once
+
+#include "SIO/Pad/PadBase.h"
+#include "SIO/Pad/PadDualshock2Types.h"
+
+#include
+
+static inline bool IsButtonBitSet(u32 value, size_t bit)
+{
+ return !(value & (1 << bit));
+}
+
+static inline bool IsAnalogKey(int index)
+{
+ return ((index >= Dualshock2::Inputs::PAD_L_UP) && (index <= Dualshock2::Inputs::PAD_R_LEFT));
+}
+
+static inline bool IsTriggerKey(int index)
+{
+ return (index == Dualshock2::Inputs::PAD_L2 || index == Dualshock2::Inputs::PAD_R2);
+}
+
+class PadDualshock2 : public PadBase
+{
+private:
+ u32 buttons;
+ Dualshock2::Analogs analogs;
+ bool analogLight = false;
+ bool analogLocked = false;
+ // Analog button can be held without changing its state.
+ // We track here if it is currently held down, to avoid flipping in
+ // and out of analog mode every frame.
+ bool analogPressed = false;
+ u32 responseBytes;
+ std::array pressures;
+ std::array vibrationMotors;
+ float axisScale;
+ float axisDeadzone;
+ float triggerScale;
+ float triggerDeadzone;
+ std::array vibrationScale;
+ // When the pressure modifier binding is activated, this is multiplied against
+ // all values in pressures, to artificially reduce pressures and give players
+ // a way to simulate pressure sensitive controls.
+ float pressureModifier;
+ float buttonDeadzone;
+
+ u8 Mystery(u8 commandByte);
+ u8 ButtonQuery(u8 commandByte);
+ u8 Poll(u8 commandByte);
+ u8 Config(u8 commandByte);
+ u8 ModeSwitch(u8 commandByte);
+ u8 StatusInfo(u8 commandByte);
+ u8 Constant1(u8 commandByte);
+ u8 Constant2(u8 commandByte);
+ u8 Constant3(u8 commandByte);
+ u8 VibrationMap(u8 commandByte);
+ u8 ResponseBytes(u8 commandByte);
+
+public:
+ PadDualshock2(u8 unifiedSlot);
+ virtual ~PadDualshock2();
+
+ void Init() override;
+ Pad::ControllerType GetType() override;
+ void Set(u32 index, float value) override;
+ void SetRawAnalogs(const std::tuple left, const std::tuple right) override;
+ void SetAxisScale(float deadzone, float scale) override;
+ void SetTriggerScale(float deadzone, float scale) override;
+ float GetVibrationScale(u32 motor) override;
+ void SetVibrationScale(u32 motor, float scale) override;
+ float GetPressureModifier() override;
+ void SetPressureModifier(float mod) override;
+ void SetButtonDeadzone(float deadzone) override;
+ void SetAnalogInvertL(bool x, bool y) override;
+ void SetAnalogInvertR(bool x, bool y) override;
+ u8 GetRawInput(u32 index) override;
+ std::tuple GetRawLeftAnalog() override;
+ std::tuple GetRawRightAnalog() override;
+ u32 GetButtons() override;
+ u8 GetPressure(u32 index) override;
+
+ void Freeze(StateWrapper& sw) override;
+
+ u8 SendCommandByte(u8 commandByte) override;
+};
diff --git a/pcsx2/SIO/Pad/PadDualshock2Types.h b/pcsx2/SIO/Pad/PadDualshock2Types.h
new file mode 100644
index 0000000000..08c390a6c7
--- /dev/null
+++ b/pcsx2/SIO/Pad/PadDualshock2Types.h
@@ -0,0 +1,133 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#pragma once
+
+#include "Config.h"
+
+namespace Dualshock2
+{
+ enum Inputs
+ {
+ PAD_UP, // Directional pad up
+ PAD_RIGHT, // Directional pad right
+ PAD_DOWN, // Directional pad down
+ PAD_LEFT, // Directional pad left
+ 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_ANALOG, // Analog mode toggle
+ PAD_PRESSURE, // Pressure modifier
+ 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)
+ LENGTH,
+ };
+
+ static constexpr u32 PRESSURE_BUTTONS = 12;
+ static constexpr u8 VIBRATION_MOTORS = 2;
+
+ struct Analogs
+ {
+ u8 lx = 0x7f;
+ u8 ly = 0x7f;
+ u8 rx = 0x7f;
+ u8 ry = 0x7f;
+ u8 lxInvert = 0x7f;
+ u8 lyInvert = 0x7f;
+ u8 rxInvert = 0x7f;
+ u8 ryInvert = 0x7f;
+ };
+
+ static const InputBindingInfo defaultBindings[] = {
+ {"Up", "D-Pad Up", InputBindingInfo::Type::Button, Dualshock2::Inputs::PAD_UP, GenericInputBinding::DPadUp},
+ {"Right", "D-Pad Right", InputBindingInfo::Type::Button, Dualshock2::Inputs::PAD_RIGHT, GenericInputBinding::DPadRight},
+ {"Down", "D-Pad Down", InputBindingInfo::Type::Button, Dualshock2::Inputs::PAD_DOWN, GenericInputBinding::DPadDown},
+ {"Left", "D-Pad Left", InputBindingInfo::Type::Button, Dualshock2::Inputs::PAD_LEFT, GenericInputBinding::DPadLeft},
+ {"Triangle", "Triangle", InputBindingInfo::Type::Button, Dualshock2::Inputs::PAD_TRIANGLE, GenericInputBinding::Triangle},
+ {"Circle", "Circle", InputBindingInfo::Type::Button, Dualshock2::Inputs::PAD_CIRCLE, GenericInputBinding::Circle},
+ {"Cross", "Cross", InputBindingInfo::Type::Button, Dualshock2::Inputs::PAD_CROSS, GenericInputBinding::Cross},
+ {"Square", "Square", InputBindingInfo::Type::Button, Dualshock2::Inputs::PAD_SQUARE, GenericInputBinding::Square},
+ {"Select", "Select", InputBindingInfo::Type::Button, Dualshock2::Inputs::PAD_SELECT, GenericInputBinding::Select},
+ {"Start", "Start", InputBindingInfo::Type::Button, Dualshock2::Inputs::PAD_START, GenericInputBinding::Start},
+ {"L1", "L1 (Left Bumper)", InputBindingInfo::Type::Button, Dualshock2::Inputs::PAD_L1, GenericInputBinding::L1},
+ {"L2", "L2 (Left Trigger)", InputBindingInfo::Type::HalfAxis, Dualshock2::Inputs::PAD_L2, GenericInputBinding::L2},
+ {"R1", "R1 (Right Bumper)", InputBindingInfo::Type::Button, Dualshock2::Inputs::PAD_R1, GenericInputBinding::R1},
+ {"R2", "R2 (Right Trigger)", InputBindingInfo::Type::HalfAxis, Dualshock2::Inputs::PAD_R2, GenericInputBinding::R2},
+ {"L3", "L3 (Left Stick Button)", InputBindingInfo::Type::Button, Dualshock2::Inputs::PAD_L3, GenericInputBinding::L3},
+ {"R3", "R3 (Right Stick Button)", InputBindingInfo::Type::Button, Dualshock2::Inputs::PAD_R3, GenericInputBinding::R3},
+ {"Analog", "Analog Toggle", InputBindingInfo::Type::Button, Dualshock2::Inputs::PAD_ANALOG, GenericInputBinding::System},
+ {"Pressure", "Apply Pressure", InputBindingInfo::Type::Button, Dualshock2::Inputs::PAD_PRESSURE, GenericInputBinding::Unknown},
+ {"LUp", "Left Stick Up", InputBindingInfo::Type::HalfAxis, Dualshock2::Inputs::PAD_L_UP, GenericInputBinding::LeftStickUp},
+ {"LRight", "Left Stick Right", InputBindingInfo::Type::HalfAxis, Dualshock2::Inputs::PAD_L_RIGHT, GenericInputBinding::LeftStickRight},
+ {"LDown", "Left Stick Down", InputBindingInfo::Type::HalfAxis, Dualshock2::Inputs::PAD_L_DOWN, GenericInputBinding::LeftStickDown},
+ {"LLeft", "Left Stick Left", InputBindingInfo::Type::HalfAxis, Dualshock2::Inputs::PAD_L_LEFT, GenericInputBinding::LeftStickLeft},
+ {"RUp", "Right Stick Up", InputBindingInfo::Type::HalfAxis, Dualshock2::Inputs::PAD_R_UP, GenericInputBinding::RightStickUp},
+ {"RRight", "Right Stick Right", InputBindingInfo::Type::HalfAxis, Dualshock2::Inputs::PAD_R_RIGHT, GenericInputBinding::RightStickRight},
+ {"RDown", "Right Stick Down", InputBindingInfo::Type::HalfAxis, Dualshock2::Inputs::PAD_R_DOWN, GenericInputBinding::RightStickDown},
+ {"RLeft", "Right Stick Left", InputBindingInfo::Type::HalfAxis, Dualshock2::Inputs::PAD_R_LEFT, GenericInputBinding::RightStickLeft},
+ {"LargeMotor", "Large (Low Frequency) Motor", InputBindingInfo::Type::Motor, 0, GenericInputBinding::LargeMotor},
+ {"SmallMotor", "Small (High Frequency) Motor", InputBindingInfo::Type::Motor, 0, GenericInputBinding::SmallMotor},
+ };
+
+ static const char* invertOptions[] = {
+ "Not Inverted",
+ "Invert Left/Right",
+ "Invert Up/Down",
+ "Invert Left/Right + Up/Down",
+ nullptr};
+
+ static const SettingInfo defaultSettings[] = {
+ {SettingInfo::Type::IntegerList, "InvertL", "Invert Left Stick",
+ "Inverts the direction of the left analog stick.",
+ "0", "0", "3", nullptr, nullptr, invertOptions, nullptr, 0.0f},
+ {SettingInfo::Type::IntegerList, "InvertR", "Invert Right Stick",
+ "Inverts the direction of the right analog stick.",
+ "0", "0", "3", nullptr, nullptr, invertOptions, nullptr, 0.0f},
+ {SettingInfo::Type::Float, "Deadzone", "Analog Deadzone",
+ "Sets the analog stick deadzone, i.e. the fraction of the stick movement which will be ignored.",
+ "0.00", "0.00", "1.00", "0.01", "%.0f%%", nullptr, nullptr, 100.0f},
+ {SettingInfo::Type::Float, "AxisScale", "Analog Sensitivity",
+ "Sets the analog stick axis scaling factor. A value between 1.30 and 1.40 is recommended when using recent "
+ "controllers, e.g. DualShock 4, Xbox One Controller.",
+ "1.33", "0.01", "2.00", "0.01", "%.0f%%", nullptr, nullptr, 100.0f},
+ {SettingInfo::Type::Float, "LargeMotorScale", "Large Motor Vibration Scale",
+ "Increases or decreases the intensity of low frequency vibration sent by the game.",
+ "1.00", "0.00", "2.00", "0.01", "%.0f%%", nullptr, nullptr, 100.0f},
+ {SettingInfo::Type::Float, "SmallMotorScale", "Small Motor Vibration Scale",
+ "Increases or decreases the intensity of high frequency vibration sent by the game.",
+ "1.00", "0.00", "2.00", "0.01", "%.0f%%", nullptr, nullptr, 100.0f},
+ {SettingInfo::Type::Float, "ButtonDeadzone", "Button/Trigger Deadzone",
+ "Sets the deadzone for activating buttons/triggers, i.e. the fraction of the trigger which will be ignored.",
+ "0.00", "0.00", "1.00", "0.01", "%.0f%%", nullptr, nullptr, 100.0f},
+ {SettingInfo::Type::Float, "PressureModifier", "Modifier Pressure",
+ "Sets the pressure when the modifier button is held.",
+ "0.50", "0.01", "1.00", "0.01", "%.0f%%", nullptr, nullptr, 100.0f},
+ };
+} // namespace Dualshock2
diff --git a/pcsx2/SIO/Pad/PadGuitar.cpp b/pcsx2/SIO/Pad/PadGuitar.cpp
new file mode 100644
index 0000000000..c849726d05
--- /dev/null
+++ b/pcsx2/SIO/Pad/PadGuitar.cpp
@@ -0,0 +1,465 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#include "PrecompiledHeader.h"
+
+#include "SIO/Pad/PadGuitar.h"
+
+#include "SIO/Pad/PadManager.h"
+#include "SIO/Pad/PadGuitarTypes.h"
+#include "SIO/Sio.h"
+
+#include "Common.h"
+
+u8 PadGuitar::Mystery(u8 commandByte)
+{
+ switch (this->commandBytesReceived)
+ {
+ case 5:
+ return 0x02;
+ case 8:
+ return 0x5a;
+ default:
+ return 0x00;
+ }
+}
+
+u8 PadGuitar::ButtonQuery(u8 commandByte)
+{
+ switch (this->commandBytesReceived)
+ {
+ case 3:
+ case 4:
+ return 0xff;
+ case 5:
+ return 0x03;
+ case 8:
+ return 0x5a;
+ default:
+ return 0x00;
+ }
+}
+
+u8 PadGuitar::Poll(u8 commandByte)
+{
+ PadBase* pad = g_PadManager.GetPad(this->unifiedSlot);
+ const u32 buttons = pad->GetButtons();
+
+ switch (this->commandBytesReceived)
+ {
+ case 3:
+ return (buttons >> 8) & 0x7f;
+ case 4:
+ return buttons & 0xff;
+ case 5:
+ return 0x7f;
+ case 6:
+ return 0x7f;
+ case 7:
+ return 0x7f;
+ case 8:
+ return pad->GetPressure(Guitar::Inputs::WHAMMY);
+ }
+
+ Console.Warning("%s(%02X) Did not reach a valid return path! Returning zero as a failsafe!", __FUNCTION__, commandByte);
+ return 0x00;
+}
+
+u8 PadGuitar::Config(u8 commandByte)
+{
+ if (this->commandBytesReceived == 3)
+ {
+ if (commandByte)
+ {
+ if (!this->isInConfig)
+ {
+ this->isInConfig = true;
+ }
+ else
+ {
+ Console.Warning("%s(%02X) Unexpected enter while already in config mode", __FUNCTION__, commandByte);
+ }
+ }
+ else
+ {
+ if (this->isInConfig)
+ {
+ this->isInConfig = false;
+ const auto [port, slot] = sioConvertPadToPortAndSlot(unifiedSlot);
+ Console.WriteLn(StringUtil::StdStringFromFormat("[Pad] Game finished pad setup for port %d / slot %d - Analogs: %s - Analog Button: %s - Pressure: Not available on guitars",
+ port + 1,
+ slot + 1,
+ (this->analogLight ? "On" : "Off"),
+ (this->analogLocked ? "Locked" : "Usable")));
+ }
+ else
+ {
+ Console.Warning("%s(%02X) Unexpected exit while not in config mode", __FUNCTION__, commandByte);
+ }
+ }
+ }
+
+ return 0x00;
+}
+
+// Changes the mode of the controller between digital and analog, and adjusts the analog LED accordingly.
+u8 PadGuitar::ModeSwitch(u8 commandByte)
+{
+ switch (this->commandBytesReceived)
+ {
+ case 3:
+ this->analogLight = commandByte;
+
+ if (this->analogLight)
+ {
+ this->currentMode = Pad::Mode::ANALOG;
+ }
+ else
+ {
+ this->currentMode = Pad::Mode::DIGITAL;
+ }
+
+ break;
+ case 4:
+ this->analogLocked = (commandByte == 0x03);
+ break;
+ default:
+ break;
+ }
+
+ return 0x00;
+}
+
+u8 PadGuitar::StatusInfo(u8 commandByte)
+{
+ switch (this->commandBytesReceived)
+ {
+ case 3:
+ return static_cast(Pad::PhysicalType::GUITAR);
+ case 4:
+ return 0x02;
+ case 5:
+ return this->analogLight;
+ case 6:
+ return 0x02;
+ case 7:
+ return 0x01;
+ default:
+ return 0x00;
+ }
+}
+
+u8 PadGuitar::Constant1(u8 commandByte)
+{
+ static bool stage;
+
+ switch (this->commandBytesReceived)
+ {
+ case 3:
+ stage = commandByte;
+ return 0x00;
+ case 5:
+ return 0x01;
+ case 6:
+ return (!stage ? 0x02 : 0x01);
+ case 7:
+ return (!stage ? 0x00 : 0x01);
+ case 8:
+ return (stage ? 0x0a : 0x14);
+ default:
+ return 0x00;
+ }
+}
+
+u8 PadGuitar::Constant2(u8 commandByte)
+{
+ switch (this->commandBytesReceived)
+ {
+ case 5:
+ return 0x02;
+ case 7:
+ return 0x01;
+ default:
+ return 0x00;
+ }
+}
+
+u8 PadGuitar::Constant3(u8 commandByte)
+{
+ static bool stage;
+
+ switch (this->commandBytesReceived)
+ {
+ case 3:
+ stage = commandByte;
+ return 0x00;
+ case 6:
+ return (!stage ? 0x04 : 0x07);
+ default:
+ return 0x00;
+ }
+}
+
+u8 PadGuitar::VibrationMap(u8 commandByte)
+{
+ switch (this->commandBytesReceived)
+ {
+ default:
+ return 0xff;
+ }
+}
+
+PadGuitar::PadGuitar(u8 unifiedSlot)
+ : PadBase(unifiedSlot)
+{
+ this->currentMode = Pad::Mode::DIGITAL;
+ Init();
+}
+
+PadGuitar::~PadGuitar() = default;
+
+void PadGuitar::Init()
+{
+ this->buttons = 0xffffffff;
+ this->whammy = Pad::ANALOG_NEUTRAL_POSITION;
+ this->analogLight = false;
+ this->analogLocked = false;
+ this->whammyAxisScale = 1.0f;
+ this->whammyDeadzone = 0.0f;
+}
+
+Pad::ControllerType PadGuitar::GetType()
+{
+ return Pad::ControllerType::Guitar;
+}
+
+void PadGuitar::Set(u32 index, float value)
+{
+ if (index > Guitar::Inputs::LENGTH)
+ {
+ return;
+ }
+
+ // The whammy bar is a special kind of weird in that rather than resting at 0 and going to 255,
+ // they chose to rest it at 127 like a normal analog, but then also make its full press 0, as if
+ // it were the negative Y component of a normal analog. Fun!
+ if (index == Guitar::Inputs::WHAMMY)
+ {
+ this->whammy = static_cast(std::clamp(127 - (value * this->whammyAxisScale) * 255.0f, 0.0f, 127.0f));
+
+ if (this->whammyDeadzone > 0.0f)
+ {
+ // Whammy has a range of 0x7f to 0x00, since it is only half of an axis with no ability to go the
+ // other direction. So whatever we get in, we basically need to cut half of that off in order to
+ // figure out where our deadzone truly lives. I think.
+ const float whammyF = (static_cast(this->whammy - 127.0f) / 127.0f);
+
+ if (whammyF != 0.0f && whammyF <= this->whammyDeadzone)
+ {
+ this->whammy = 0x7f;
+ }
+ }
+ }
+ else
+ {
+ // Don't affect L2/R2, since they are analog on most pads.
+ const float dzValue = (value < this->buttonDeadzone) ? 0.0f : value;
+ this->rawInputs[index] = static_cast(std::clamp(dzValue * 255.0f, 0.0f, 255.0f));
+
+ // Since we reordered the buttons for better UI, we need to remap them here.
+ static constexpr std::array bitmaskMapping = {{
+ 12, // STRUM_UP
+ 14, // STRUM_DOWN
+ 8, // SELECT
+ 11, // START
+ 1, // GREEN
+ 5, // RED
+ 4, // YELLOW
+ 6, // BLUE
+ 7, // ORANGE
+ 0 // TILT
+ }};
+
+ if (dzValue > 0.0f)
+ {
+ this->buttons &= ~(1u << bitmaskMapping[index]);
+ }
+ else
+ {
+ this->buttons |= (1u << bitmaskMapping[index]);
+ }
+ }
+}
+
+void PadGuitar::SetRawAnalogs(const std::tuple left, const std::tuple right)
+{
+}
+
+void PadGuitar::SetAxisScale(float deadzone, float scale)
+{
+ this->whammyDeadzone = deadzone;
+ this->whammyAxisScale = scale;
+}
+
+void PadGuitar::SetTriggerScale(float deadzone, float scale)
+{
+
+}
+
+float PadGuitar::GetVibrationScale(u32 motor)
+{
+ return 0;
+}
+
+void PadGuitar::SetVibrationScale(u32 motor, float scale)
+{
+}
+
+float PadGuitar::GetPressureModifier()
+{
+ return 0;
+}
+
+void PadGuitar::SetPressureModifier(float mod)
+{
+}
+
+void PadGuitar::SetButtonDeadzone(float deadzone)
+{
+ this->buttonDeadzone = deadzone;
+}
+
+void PadGuitar::SetAnalogInvertL(bool x, bool y)
+{
+}
+
+void PadGuitar::SetAnalogInvertR(bool x, bool y)
+{
+}
+
+u8 PadGuitar::GetRawInput(u32 index)
+{
+ return this->rawInputs[index];
+}
+
+std::tuple PadGuitar::GetRawLeftAnalog()
+{
+ return std::tuple{0x7f, 0x7f};
+}
+
+std::tuple PadGuitar::GetRawRightAnalog()
+{
+ return std::tuple{0x7f, 0x7f};
+}
+
+u32 PadGuitar::GetButtons()
+{
+ return this->buttons;
+}
+
+u8 PadGuitar::GetPressure(u32 index)
+{
+ if (index == Guitar::Inputs::WHAMMY)
+ {
+ return this->whammy;
+ }
+
+ return 0;
+}
+
+void PadGuitar::Freeze(StateWrapper& sw)
+{
+ // Protected PadBase members
+ sw.Do(&rawInputs);
+ sw.Do(&unifiedSlot);
+ sw.Do(&isInConfig);
+ sw.Do(¤tMode);
+ sw.Do(¤tCommand);
+ sw.Do(&commandBytesReceived);
+
+ // Private PadGuitar members
+ sw.Do(&buttons);
+ sw.Do(&whammy);
+ sw.Do(&analogLight);
+ sw.Do(&analogLocked);
+ sw.Do(&whammyAxisScale);
+ sw.Do(&whammyDeadzone);
+ sw.Do(&buttonDeadzone);
+}
+
+u8 PadGuitar::SendCommandByte(u8 commandByte)
+{
+ u8 ret = 0;
+
+ switch (this->commandBytesReceived)
+ {
+ case 0:
+ ret = 0x00;
+ break;
+ case 1:
+ this->currentCommand = static_cast(commandByte);
+
+ if (this->currentCommand != Pad::Command::POLL && this->currentCommand != Pad::Command::CONFIG && !this->isInConfig)
+ {
+ Console.Warning("%s(%02X) Config-only command was sent to a pad outside of config mode!", __FUNCTION__, commandByte);
+ }
+
+ ret = this->isInConfig ? static_cast(Pad::Mode::CONFIG) : static_cast(this->currentMode);
+ break;
+ case 2:
+ ret = 0x5a;
+ break;
+ default:
+ switch (this->currentCommand)
+ {
+ case Pad::Command::MYSTERY:
+ ret = Mystery(commandByte);
+ break;
+ case Pad::Command::BUTTON_QUERY:
+ ret = ButtonQuery(commandByte);
+ break;
+ case Pad::Command::POLL:
+ ret = Poll(commandByte);
+ break;
+ case Pad::Command::CONFIG:
+ ret = Config(commandByte);
+ break;
+ case Pad::Command::MODE_SWITCH:
+ ret = ModeSwitch(commandByte);
+ break;
+ case Pad::Command::STATUS_INFO:
+ ret = StatusInfo(commandByte);
+ break;
+ case Pad::Command::CONST_1:
+ ret = Constant1(commandByte);
+ break;
+ case Pad::Command::CONST_2:
+ ret = Constant2(commandByte);
+ break;
+ case Pad::Command::CONST_3:
+ ret = Constant3(commandByte);
+ break;
+ case Pad::Command::VIBRATION_MAP:
+ ret = VibrationMap(commandByte);
+ break;
+ default:
+ ret = 0x00;
+ break;
+ }
+ }
+
+ this->commandBytesReceived++;
+ return ret;
+}
diff --git a/pcsx2/SIO/Pad/PadGuitar.h b/pcsx2/SIO/Pad/PadGuitar.h
new file mode 100644
index 0000000000..756f38f59a
--- /dev/null
+++ b/pcsx2/SIO/Pad/PadGuitar.h
@@ -0,0 +1,71 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#pragma once
+
+#include "SIO/Pad/PadBase.h"
+
+class PadGuitar : public PadBase
+{
+private:
+ u32 buttons;
+ u8 whammy;
+ // Technically guitars do not have an analog light, but they still use the same ModeSwitch command
+ // as a DS2, and are told to "turn on their light".
+ bool analogLight = false;
+ // Guitars are also instructed to "lock" their "analog light", despite not having one.
+ bool analogLocked = false;
+ float whammyAxisScale; // Guitars only have 1 axis on the whammy bar.
+ float whammyDeadzone;
+ float buttonDeadzone; // Button deadzone is still a good idea, in case a host analog stick is bound to a guitar button
+
+ u8 Mystery(u8 commandByte);
+ u8 ButtonQuery(u8 commandByte);
+ u8 Poll(u8 commandByte);
+ u8 Config(u8 commandByte);
+ u8 ModeSwitch(u8 commandByte);
+ u8 StatusInfo(u8 commandByte);
+ u8 Constant1(u8 commandByte);
+ u8 Constant2(u8 commandByte);
+ u8 Constant3(u8 commandByte);
+ u8 VibrationMap(u8 commandByte);
+
+public:
+ PadGuitar(u8 unifiedSlot);
+ virtual ~PadGuitar();
+
+ void Init() override;
+ Pad::ControllerType GetType() override;
+ void Set(u32 index, float value) override;
+ void SetRawAnalogs(const std::tuple left, const std::tuple right) override;
+ void SetAxisScale(float deadzone, float scale) override;
+ void SetTriggerScale(float deadzone, float scale) override;
+ float GetVibrationScale(u32 motor) override;
+ void SetVibrationScale(u32 motor, float scale) override;
+ float GetPressureModifier() override;
+ void SetPressureModifier(float mod) override;
+ void SetButtonDeadzone(float deadzone) override;
+ void SetAnalogInvertL(bool x, bool y) override;
+ void SetAnalogInvertR(bool x, bool y) override;
+ u8 GetRawInput(u32 index) override;
+ std::tuple GetRawLeftAnalog() override;
+ std::tuple GetRawRightAnalog() override;
+ u32 GetButtons() override;
+ u8 GetPressure(u32 index) override;
+
+ void Freeze(StateWrapper& sw) override;
+
+ u8 SendCommandByte(u8 commandByte) override;
+};
diff --git a/pcsx2/SIO/Pad/PadGuitarTypes.h b/pcsx2/SIO/Pad/PadGuitarTypes.h
new file mode 100644
index 0000000000..a326fc6617
--- /dev/null
+++ b/pcsx2/SIO/Pad/PadGuitarTypes.h
@@ -0,0 +1,62 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#pragma once
+
+#include "Config.h"
+
+namespace Guitar
+{
+ enum Inputs
+ {
+ STRUM_UP, // Strum bar
+ STRUM_DOWN, // Strum bar down
+ SELECT, // Select button
+ START, // Start button
+ GREEN, // Green fret
+ RED, // Red fret
+ YELLOW, // Yellow fret
+ BLUE, // Blue fret
+ ORANGE, // Orange fret
+ WHAMMY, // Whammy bar axis
+ TILT, // Tilt sensor
+ LENGTH,
+ };
+
+ // The generic input bindings on this might seem bizarre, but they are intended to match what DS2 buttons
+ // would do what actions, if you played Guitar Hero on a PS2 with a DS2 instead of a controller.
+ static const InputBindingInfo defaultBindings[] = {
+ {"Up", "Strum Up", InputBindingInfo::Type::Button, Guitar::Inputs::STRUM_UP, GenericInputBinding::DPadUp},
+ {"Down", "Strum Down", InputBindingInfo::Type::Button, Guitar::Inputs::STRUM_DOWN, GenericInputBinding::DPadDown},
+ {"Select", "Select", InputBindingInfo::Type::Button, Guitar::Inputs::SELECT, GenericInputBinding::Select},
+ {"Start", "Start", InputBindingInfo::Type::Button, Guitar::Inputs::START, GenericInputBinding::Start},
+ {"Green", "Green Fret", InputBindingInfo::Type::Button, Guitar::Inputs::GREEN, GenericInputBinding::R2},
+ {"Red", "Red Fret", InputBindingInfo::Type::Button, Guitar::Inputs::RED, GenericInputBinding::Circle},
+ {"Yellow", "Yellow Fret", InputBindingInfo::Type::Button, Guitar::Inputs::YELLOW, GenericInputBinding::Triangle},
+ {"Blue", "Blue Fret", InputBindingInfo::Type::Button, Guitar::Inputs::BLUE, GenericInputBinding::Cross},
+ {"Orange", "Orange Fret", InputBindingInfo::Type::Button, Guitar::Inputs::ORANGE, GenericInputBinding::Square},
+ {"Whammy", "Whammy Bar", InputBindingInfo::Type::HalfAxis, Guitar::Inputs::WHAMMY, GenericInputBinding::LeftStickUp},
+ {"Tilt", "Tilt Up", InputBindingInfo::Type::Button, Guitar::Inputs::TILT, GenericInputBinding::L2},
+ };
+
+ static const SettingInfo defaultSettings[] = {
+ {SettingInfo::Type::Float, "Deadzone", "Whammy Bar Deadzone",
+ "Sets the whammy bar deadzone. Inputs below this value will not be sent to the PS2.",
+ "0.00", "0.00", "1.00", "0.01", "%.0f%%", nullptr, nullptr, 100.0f},
+ {SettingInfo::Type::Float, "AxisScale", "Whammy Bar Sensitivity",
+ "Sets the whammy bar axis scaling factor.",
+ "1.0", "0.01", "2.00", "0.01", "%.0f%%", nullptr, nullptr, 100.0f},
+ };
+} // namespace Guitar
diff --git a/pcsx2/SIO/Pad/PadMacros.cpp b/pcsx2/SIO/Pad/PadMacros.cpp
new file mode 100644
index 0000000000..c43ee092bf
--- /dev/null
+++ b/pcsx2/SIO/Pad/PadMacros.cpp
@@ -0,0 +1,90 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#include "PrecompiledHeader.h"
+
+#include "SIO/Pad/PadMacros.h"
+
+#include "SIO/Pad/PadManager.h"
+#include "SIO/Pad/PadBase.h"
+
+PadMacros g_PadMacros;
+
+PadMacros::PadMacros() = default;
+PadMacros::~PadMacros() = default;
+
+void PadMacros::ClearMacros()
+{
+ this->s_macro_buttons = {};
+}
+
+PadMacros::MacroButton& PadMacros::GetMacroButton(u32 pad, u32 index)
+{
+ return this->s_macro_buttons.at(pad).at(index);
+}
+
+void PadMacros::SetMacroButtonState(u32 pad, u32 index, bool state)
+{
+ if (pad >= Pad::NUM_CONTROLLER_PORTS || index >= NUM_MACRO_BUTTONS_PER_CONTROLLER)
+ return;
+
+ PadMacros::MacroButton& mb = s_macro_buttons[pad][index];
+ if (mb.buttons.empty() || mb.trigger_state == state)
+ return;
+
+ mb.toggle_counter = mb.toggle_frequency;
+ mb.trigger_state = state;
+ if (mb.toggle_state != state)
+ {
+ mb.toggle_state = state;
+ ApplyMacroButton(pad, mb);
+ }
+}
+
+void PadMacros::ApplyMacroButton(u32 controller, const PadMacros::MacroButton& mb)
+{
+ const float value = mb.toggle_state ? 1.0f : 0.0f;
+ PadBase* pad = g_PadManager.GetPad(controller);
+
+ for (const u32 btn : mb.buttons)
+ pad->Set(btn, value);
+}
+
+void PadMacros::UpdateMacroButtons()
+{
+ for (u32 pad = 0; pad < Pad::NUM_CONTROLLER_PORTS; pad++)
+ {
+ for (u32 index = 0; index < NUM_MACRO_BUTTONS_PER_CONTROLLER; index++)
+ {
+ PadMacros::MacroButton& mb = this->s_macro_buttons[pad][index];
+
+ if (!mb.trigger_state || mb.toggle_frequency == 0)
+ {
+ continue;
+ }
+
+ mb.toggle_counter--;
+
+ if (mb.toggle_counter > 0)
+ {
+ continue;
+ }
+
+ mb.toggle_counter = mb.toggle_frequency;
+ mb.toggle_state = !mb.toggle_state;
+ ApplyMacroButton(pad, mb);
+ }
+ }
+}
\ No newline at end of file
diff --git a/pcsx2/SIO/Pad/PadMacros.h b/pcsx2/SIO/Pad/PadMacros.h
new file mode 100644
index 0000000000..393979f476
--- /dev/null
+++ b/pcsx2/SIO/Pad/PadMacros.h
@@ -0,0 +1,53 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#pragma once
+
+#include "SIO/Pad/PadTypes.h"
+
+#include
+#include
+
+class PadMacros
+{
+public: // Constants
+ struct MacroButton
+ {
+ std::vector buttons; ///< Buttons to activate.
+ u32 toggle_frequency; ///< Interval at which the buttons will be toggled, if not 0.
+ u32 toggle_counter; ///< When this counter reaches zero, buttons will be toggled.
+ bool toggle_state; ///< Current state for turbo.
+ bool trigger_state; ///< Whether the macro button is active.
+ };
+
+ // Number of macro buttons per controller.
+ static constexpr u32 NUM_MACRO_BUTTONS_PER_CONTROLLER = 16;
+
+private: // Private members
+ std::array, Pad::NUM_CONTROLLER_PORTS> s_macro_buttons;
+
+public: // Public members
+ PadMacros();
+ ~PadMacros();
+
+ // Sets the state of the specified macro button.
+ void ClearMacros();
+ PadMacros::MacroButton& GetMacroButton(u32 pad, u32 index);
+ void SetMacroButtonState(u32 pad, u32 index, bool state);
+ void ApplyMacroButton(u32 controller, const PadMacros::MacroButton& mb);
+ void UpdateMacroButtons();
+};
+
+extern PadMacros g_PadMacros;
diff --git a/pcsx2/SIO/Pad/PadManager.cpp b/pcsx2/SIO/Pad/PadManager.cpp
new file mode 100644
index 0000000000..7359fd3807
--- /dev/null
+++ b/pcsx2/SIO/Pad/PadManager.cpp
@@ -0,0 +1,139 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#include "PrecompiledHeader.h"
+
+#include "SIO/Pad/PadManager.h"
+
+#include "SIO/Pad/PadNotConnected.h"
+#include "SIO/Pad/PadDualshock2.h"
+#include "SIO/Pad/PadGuitar.h"
+
+PadManager g_PadManager;
+
+// Convert the PS2's port/slot addressing to a single value.
+// Physical ports 0 and 1 still correspond to unified slots 0 and 1.
+// The remaining unified slots are for multitapped slots.
+// Port 0's three multitap slots then occupy unified slots 2, 3 and 4.
+// Port 1's three multitap slots then occupy unified slots 5, 6 and 7.
+u8 PadManager::GetUnifiedSlot(u8 port, u8 slot)
+{
+ if (slot == 0)
+ {
+ return port;
+ }
+ else if (port == 0) // slot=[0,1]
+ {
+ return slot + 1;
+ }
+ else
+ {
+ return slot + 4;
+ }
+}
+
+PadManager::PadManager() = default;
+PadManager::~PadManager() = default;
+
+bool PadManager::Initialize()
+{
+ return true;
+}
+
+bool PadManager::Shutdown()
+{
+ for (u8 i = 0; i < 8; i++)
+ {
+ this->ps2Controllers.at(i) = nullptr;
+ }
+
+ return true;
+}
+
+PadBase* PadManager::ChangePadType(u8 unifiedSlot, Pad::ControllerType controllerType)
+{
+ switch (controllerType)
+ {
+ case Pad::ControllerType::DualShock2:
+ this->ps2Controllers.at(unifiedSlot) = std::make_unique(unifiedSlot);
+ break;
+ case Pad::ControllerType::Guitar:
+ this->ps2Controllers.at(unifiedSlot) = std::make_unique(unifiedSlot);
+ break;
+ default:
+ this->ps2Controllers.at(unifiedSlot) = std::make_unique(unifiedSlot);
+ break;
+ }
+
+ return this->ps2Controllers.at(unifiedSlot).get();
+}
+
+PadBase* PadManager::GetPad(u8 port, u8 slot)
+{
+ const u8 unifiedSlot = this->GetUnifiedSlot(port, slot);
+ return this->ps2Controllers.at(unifiedSlot).get();
+}
+
+PadBase* PadManager::GetPad(const u8 unifiedSlot)
+{
+ return this->ps2Controllers.at(unifiedSlot).get();
+}
+
+void PadManager::SetControllerState(u32 controller, u32 bind, float value)
+{
+ if (controller >= Pad::NUM_CONTROLLER_PORTS)
+ return;
+
+ PadBase* pad = g_PadManager.GetPad(controller);
+ pad->Set(bind, value);
+}
+
+bool PadManager::PadFreeze(StateWrapper& sw)
+{
+ if (sw.IsReading())
+ {
+ if (!sw.DoMarker("PAD"))
+ {
+ Console.Error("PAD state is invalid! Leaving the current state in place.");
+ return false;
+ }
+
+ for (u32 unifiedSlot = 0; unifiedSlot < Pad::NUM_CONTROLLER_PORTS; unifiedSlot++)
+ {
+ Pad::ControllerType type;
+ sw.Do(&type);
+
+ PadBase* pad = this->ChangePadType(unifiedSlot, type);
+ pad->Freeze(sw);
+ }
+ }
+ else
+ {
+ if (!sw.DoMarker("PAD"))
+ {
+ return false;
+ }
+
+ for (u32 unifiedSlot = 0; unifiedSlot < Pad::NUM_CONTROLLER_PORTS; unifiedSlot++)
+ {
+ PadBase* pad = this->GetPad(unifiedSlot);
+ Pad::ControllerType type = pad->GetType();
+ sw.Do(&type);
+ pad->Freeze(sw);
+ }
+ }
+
+ return true;
+}
diff --git a/pcsx2/SIO/Pad/PadManager.h b/pcsx2/SIO/Pad/PadManager.h
new file mode 100644
index 0000000000..00a027db85
--- /dev/null
+++ b/pcsx2/SIO/Pad/PadManager.h
@@ -0,0 +1,46 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#pragma once
+
+#include "SIO/Pad/PadBase.h"
+
+#include
+
+class PadManager
+{
+private:
+ std::array, 8> ps2Controllers;
+
+ u8 GetUnifiedSlot(u8 port, u8 slot);
+
+public:
+ PadManager();
+ ~PadManager();
+
+ bool Initialize();
+ bool Shutdown();
+
+ PadBase* ChangePadType(u8 unifiedSlot, Pad::ControllerType controllerType);
+ PadBase* GetPad(u8 port, u8 slot);
+ PadBase* GetPad(const u8 unifiedSlot);
+
+ // Sets the specified bind on a controller to the specified pressure (normalized to 0..1).
+ void SetControllerState(u32 controller, u32 bind, float value);
+
+ bool PadFreeze(StateWrapper& sw);
+};
+
+extern PadManager g_PadManager;
diff --git a/pcsx2/SIO/Pad/PadNotConnected.cpp b/pcsx2/SIO/Pad/PadNotConnected.cpp
new file mode 100644
index 0000000000..f585634cab
--- /dev/null
+++ b/pcsx2/SIO/Pad/PadNotConnected.cpp
@@ -0,0 +1,132 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#include "PrecompiledHeader.h"
+
+#include "SIO/Pad/PadNotConnected.h"
+
+PadNotConnected::PadNotConnected(u8 unifiedSlot)
+ : PadBase(unifiedSlot)
+{
+
+}
+
+PadNotConnected::~PadNotConnected() = default;
+
+void PadNotConnected::Init()
+{
+
+}
+
+Pad::ControllerType PadNotConnected::GetType()
+{
+ return Pad::ControllerType::NotConnected;
+}
+
+void PadNotConnected::Set(u32 index, float value)
+{
+
+}
+
+void PadNotConnected::SetRawAnalogs(const std::tuple left, const std::tuple right)
+{
+
+}
+
+void PadNotConnected::SetAxisScale(float deadzone, float scale)
+{
+
+}
+
+void PadNotConnected::SetTriggerScale(float deadzone, float scale)
+{
+
+}
+
+float PadNotConnected::GetVibrationScale(u32 motor)
+{
+ return 0;
+}
+
+void PadNotConnected::SetVibrationScale(u32 motor, float scale)
+{
+
+}
+
+float PadNotConnected::GetPressureModifier()
+{
+ return 0;
+}
+
+void PadNotConnected::SetPressureModifier(float mod)
+{
+
+}
+
+void PadNotConnected::SetButtonDeadzone(float deadzone)
+{
+
+}
+
+void PadNotConnected::SetAnalogInvertL(bool x, bool y)
+{
+
+}
+
+void PadNotConnected::SetAnalogInvertR(bool x, bool y)
+{
+
+}
+
+u8 PadNotConnected::GetRawInput(u32 index)
+{
+ return 0;
+}
+
+std::tuple PadNotConnected::GetRawLeftAnalog()
+{
+ return std::tuple{0, 0};
+}
+
+std::tuple PadNotConnected::GetRawRightAnalog()
+{
+ return std::tuple{0, 0};
+}
+
+u32 PadNotConnected::GetButtons()
+{
+ return 0;
+}
+
+u8 PadNotConnected::GetPressure(u32 index)
+{
+ return 0;
+}
+
+void PadNotConnected::Freeze(StateWrapper& sw)
+{
+ // Protected PadBase members
+ sw.Do(&rawInputs);
+ sw.Do(&unifiedSlot);
+ sw.Do(&isInConfig);
+ sw.Do(¤tMode);
+ sw.Do(¤tCommand);
+ sw.Do(&commandBytesReceived);
+}
+
+u8 PadNotConnected::SendCommandByte(u8 commandByte)
+{
+ return 0xff;
+}
diff --git a/pcsx2/SIO/Pad/PadNotConnected.h b/pcsx2/SIO/Pad/PadNotConnected.h
new file mode 100644
index 0000000000..56e204096c
--- /dev/null
+++ b/pcsx2/SIO/Pad/PadNotConnected.h
@@ -0,0 +1,48 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#pragma once
+
+#include "SIO/Pad/PadBase.h"
+
+class PadNotConnected : public PadBase
+{
+public:
+ PadNotConnected(u8 unifiedSlot);
+ virtual ~PadNotConnected();
+
+ void Init();
+ Pad::ControllerType GetType();
+ void Set(u32 index, float value);
+ void SetRawAnalogs(const std::tuple left, const std::tuple right);
+ void SetAxisScale(float deadzone, float scale);
+ void SetTriggerScale(float deadzone, float scale) override;
+ float GetVibrationScale(u32 motor);
+ void SetVibrationScale(u32 motor, float scale);
+ float GetPressureModifier();
+ void SetPressureModifier(float mod);
+ void SetButtonDeadzone(float deadzone);
+ void SetAnalogInvertL(bool x, bool y);
+ void SetAnalogInvertR(bool x, bool y);
+ u8 GetRawInput(u32 index);
+ std::tuple GetRawLeftAnalog();
+ std::tuple GetRawRightAnalog();
+ u32 GetButtons();
+ u8 GetPressure(u32 index);
+
+ void Freeze(StateWrapper& sw) override;
+
+ u8 SendCommandByte(u8 commandByte) override;
+};
diff --git a/pcsx2/SIO/Pad/PadTypes.h b/pcsx2/SIO/Pad/PadTypes.h
new file mode 100644
index 0000000000..20732b9c8f
--- /dev/null
+++ b/pcsx2/SIO/Pad/PadTypes.h
@@ -0,0 +1,96 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#pragma once
+
+namespace Pad
+{
+ enum class Command : u8
+ {
+ NOT_SET = 0x00,
+ MYSTERY = 0x40,
+ BUTTON_QUERY = 0x41,
+ POLL = 0x42,
+ CONFIG = 0x43,
+ MODE_SWITCH = 0x44,
+ STATUS_INFO = 0x45,
+ CONST_1 = 0x46,
+ CONST_2 = 0x47,
+ CONST_3 = 0x4c,
+ VIBRATION_MAP = 0x4d,
+ RESPONSE_BYTES = 0x4f
+ };
+
+ enum class Mode : u8
+ {
+ NOT_SET = 0x00,
+ PS1_MOUSE = 0x12,
+ NEGCON = 0x23,
+ PS1_KONAMI_LIGHTGUN = 0x31,
+ DIGITAL = 0x41,
+ PS1_FLIGHT_STICK = 0x53,
+ PS1_NAMCO_LIGHTGUN = 0x63,
+ ANALOG = 0x73,
+ DUALSHOCK2 = 0x79,
+ PS1_MULTITAP = 0x80,
+ PS1_JOGCON = 0xe3,
+ CONFIG = 0xf3,
+ DISCONNECTED = 0xff
+ };
+
+ enum class PhysicalType : u8
+ {
+ NOT_SET = 0x00,
+ GUITAR = 0x01,
+ STANDARD = 0x03
+ };
+
+ enum class ResponseBytes : u32
+ {
+ DIGITAL = 0x00000000,
+ ANALOG = 0x0000003f,
+ DUALSHOCK2 = 0x0003ffff
+ };
+
+ static constexpr u8 ANALOG_NEUTRAL_POSITION = 0x7f;
+
+ enum class ControllerType : u8
+ {
+ NotConnected,
+ DualShock2,
+ Guitar,
+ Count
+ };
+
+ enum class VibrationCapabilities : u8
+ {
+ NoVibration,
+ LargeSmallMotors,
+ SingleMotor,
+ Count
+ };
+
+ // Total number of pad ports, across both multitaps.
+ static constexpr u32 NUM_CONTROLLER_PORTS = 8;
+
+ // Default stick deadzone/sensitivity.
+ static constexpr float DEFAULT_STICK_DEADZONE = 0.0f;
+ static constexpr float DEFAULT_STICK_SCALE = 1.33f;
+ static constexpr float DEFAULT_TRIGGER_DEADZONE = 0.0f;
+ static constexpr float DEFAULT_TRIGGER_SCALE = 1.0f;
+ static constexpr float DEFAULT_MOTOR_SCALE = 1.0f;
+ static constexpr float DEFAULT_PRESSURE_MODIFIER = 0.5f;
+ static constexpr float DEFAULT_BUTTON_DEADZONE = 0.0f;
+} // namespace Pad
diff --git a/pcsx2/SIO/Sio.cpp b/pcsx2/SIO/Sio.cpp
new file mode 100644
index 0000000000..d138c0a649
--- /dev/null
+++ b/pcsx2/SIO/Sio.cpp
@@ -0,0 +1,136 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#include "PrecompiledHeader.h"
+
+#include "SIO/Sio.h"
+
+#include "SIO/SioTypes.h"
+#include "SIO/Memcard/MemoryCardProtocol.h"
+
+#include "Host.h"
+#include "IconsFontAwesome5.h"
+
+_mcd mcds[2][4];
+_mcd *mcd;
+
+void sioNextFrame() {
+ for ( uint port = 0; port < 2; ++port ) {
+ for ( uint slot = 0; slot < 4; ++slot ) {
+ mcds[port][slot].NextFrame();
+ }
+ }
+}
+
+void sioSetGameSerial( const std::string& serial ) {
+ for ( uint port = 0; port < 2; ++port ) {
+ for ( uint slot = 0; slot < 4; ++slot ) {
+ if ( mcds[port][slot].ReIndex( serial ) ) {
+ AutoEject::Set( port, slot );
+ }
+ }
+ }
+}
+
+std::tuple 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);
+}
+
+void AutoEject::CountDownTicks()
+{
+ bool reinserted = false;
+ for (size_t port = 0; port < SIO::PORTS; port++)
+ {
+ for (size_t slot = 0; slot < SIO::SLOTS; slot++)
+ {
+ if (mcds[port][slot].autoEjectTicks > 0)
+ {
+ if (--mcds[port][slot].autoEjectTicks == 0)
+ reinserted |= EmuConfig.Mcd[sioConvertPortAndSlotToPad(port, slot)].Enabled;
+ }
+ }
+ }
+
+ if (reinserted)
+ {
+ Host::AddIconOSDMessage("AutoEjectAllSet", ICON_FA_SD_CARD,
+ TRANSLATE_SV("MemoryCard", "Memory Cards reinserted."), Host::OSD_INFO_DURATION);
+ }
+}
+
+void AutoEject::Set(size_t port, size_t slot)
+{
+ if (EmuConfig.McdEnableEjection && mcds[port][slot].autoEjectTicks == 0)
+ {
+ mcds[port][slot].autoEjectTicks = 1; // 1 second is enough.
+ mcds[port][slot].term = 0x55; // Reset terminator to default (0x55), forces the PS2 to recheck the memcard.
+ }
+}
+
+void AutoEject::Clear(size_t port, size_t slot)
+{
+ mcds[port][slot].autoEjectTicks = 0;
+}
+
+void AutoEject::SetAll()
+{
+ Host::AddIconOSDMessage("AutoEjectAllSet", ICON_FA_SD_CARD,
+ TRANSLATE_SV("MemoryCard", "Force ejecting all Memory Cards. Reinserting in 1 second."), Host::OSD_INFO_DURATION);
+
+ for (size_t port = 0; port < SIO::PORTS; port++)
+ {
+ for (size_t slot = 0; slot < SIO::SLOTS; slot++)
+ {
+ AutoEject::Set(port, slot);
+ }
+ }
+}
+
+void AutoEject::ClearAll()
+{
+ for (size_t port = 0; port < SIO::PORTS; port++)
+ {
+ for (size_t slot = 0; slot < SIO::SLOTS; slot++)
+ {
+ AutoEject::Clear(port, slot);
+ }
+ }
+}
diff --git a/pcsx2/Sio.h b/pcsx2/SIO/Sio.h
similarity index 50%
rename from pcsx2/Sio.h
rename to pcsx2/SIO/Sio.h
index 8525abf97a..00e008adb3 100644
--- a/pcsx2/Sio.h
+++ b/pcsx2/SIO/Sio.h
@@ -1,5 +1,5 @@
/* PCSX2 - PS2 Emulator for PCs
- * Copyright (C) 2002-2022 PCSX2 Dev Team
+ * Copyright (C) 2002-2023 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-
@@ -18,10 +18,7 @@
#pragma once
-#include "SioTypes.h"
-#include "MemoryCardFile.h"
-#include
-#include
+#include "SIO/Memcard/MemoryCardFile.h"
struct _mcd
{
@@ -99,132 +96,11 @@ struct _mcd
FileMcd_NextFrame( port, slot );
}
- int ReIndex(const std::string& filter) {
+ bool ReIndex(const std::string& filter) {
return FileMcd_ReIndex(port, slot, filter);
}
};
-class Sio0
-{
-private:
- u32 txData; // 0x1f801040
- u32 rxData; // 0x1f801040
- u32 stat; // 0x1f801044
- u16 mode; // 0x1f801048
- u16 ctrl; // 0x1f80104a
- u16 baud; // 0x1f80104e
-
- void ClearStatAcknowledge();
-
-public:
- u8 flag = 0;
-
- SioStage sioStage = SioStage::IDLE;
- u8 sioMode = SioMode::NOT_SET;
- u8 sioCommand = 0;
- bool padStarted = false;
- bool rxDataSet = false;
-
- u8 port = 0;
- u8 slot = 0;
-
- Sio0();
- ~Sio0();
-
- void SoftReset();
- void FullReset();
-
- void Acknowledge();
- void Interrupt(Sio0Interrupt sio0Interrupt);
-
- u8 GetTxData();
- u8 GetRxData();
- u32 GetStat();
- u16 GetMode();
- u16 GetCtrl();
- u16 GetBaud();
-
- void SetTxData(u8 value);
- void SetRxData(u8 value);
- void SetStat(u32 value);
- void SetMode(u16 value);
- void SetCtrl(u16 value);
- void SetBaud(u16 value);
-
- bool IsPadCommand(u8 command);
- bool IsMemcardCommand(u8 command);
- bool IsPocketstationCommand(u8 command);
-
- u8 Pad(u8 value);
- u8 Memcard(u8 value);
-};
-
-class Sio2
-{
-private:
- void UpdateInputRecording(u8& dataIn, u8& dataOut);
-
-public:
- std::array send3; // 0x1f808200 - 0x1f80823f
- // SEND1 and SEND2 are an unusual bunch. It's not entirely clear just from
- // documentation but these registers almost seem like they are the same thing;
- // when bit 2 is set, SEND2 is being read/written. When bit 2 isn't set, it is
- // SEND1. Their use is not really known, either.
- std::array send1; // 0x1f808240 - 0x1f80825f
- std::array send2; // 0x1f808240 - 0x1f80825f
- u32 dataIn; // 0x1f808260
- u32 dataOut; // 0x1f808264
- u32 ctrl; // 0x1f808268
- u32 recv1; // 0x1f80826c
- u32 recv2; // 0x1f808270
- u32 recv3; // 0x1f808274
- u32 unknown1; // 0x1f808278
- u32 unknown2; // 0x1f80827c
- u32 iStat; // 0x1f808280
-
- u8 port = 0;
- u8 slot = 0;
-
- // The current working index of SEND3. The SEND3 register is a 16 position
- // array of command descriptors. Each descriptor describes the port the command
- // is targeting, as well as the length of the command in bytes.
- bool send3Read = false;
- size_t send3Position = 0;
- size_t commandLength = 0;
- size_t processedLength = 0;
- // Tracks the size of a single block of DMA11/DMA12 data. psxDma11 will set this prior
- // to doing writes, and Sio2::SetSend3 will clear this to ensure a non-DMA write into SIO2
- // does not accidentally use dmaBlockSize.
- size_t dmaBlockSize = 0;
- bool send3Complete = false;
-
- Sio2();
- ~Sio2();
-
- void SoftReset();
- void FullReset();
-
- void Interrupt();
-
- void SetCtrl(u32 value);
- void SetSend3(size_t position, u32 value);
- void SetRecv1(u32 value);
-
- void Pad();
- void Multitap();
- void Infrared();
- void Memcard();
-
- void Write(u8 data);
- u8 Read();
-};
-
-extern std::deque fifoIn;
-extern std::deque fifoOut;
-
-extern Sio0 sio0;
-extern Sio2 sio2;
-
extern _mcd mcds[2][4];
extern _mcd *mcd;
@@ -248,4 +124,4 @@ namespace AutoEject
extern void Clear(size_t port, size_t slot);
extern void SetAll();
extern void ClearAll();
-}
\ No newline at end of file
+} // namespace AutoEject
diff --git a/pcsx2/SIO/Sio0.cpp b/pcsx2/SIO/Sio0.cpp
new file mode 100644
index 0000000000..873fd1348c
--- /dev/null
+++ b/pcsx2/SIO/Sio0.cpp
@@ -0,0 +1,323 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#include "PrecompiledHeader.h"
+
+#include "SIO/Sio0.h"
+
+#include "SIO/Sio.h"
+#include "SIO/Pad/PadManager.h"
+#include "SIO/Memcard/MemoryCardProtocol.h"
+
+#include "Common.h"
+#include "IopHw.h"
+#include "IopDma.h"
+#include "R3000A.h"
+
+#define SIO0LOG_ENABLE 0
+#define Sio0Log if (SIO0LOG_ENABLE) DevCon
+
+Sio0 g_Sio0;
+
+void Sio0::ClearStatAcknowledge()
+{
+ stat &= ~(SIO0_STAT::ACK);
+}
+
+Sio0::Sio0() = default;
+Sio0::~Sio0() = default;
+
+bool Sio0::Initialize()
+{
+ SoftReset();
+
+ port = 0;
+ slot = 0;
+
+ for (int i = 0; i < 2; i++)
+ {
+ for (int j = 0; j < 4; j++)
+ {
+ mcds[i][j].term = 0x55;
+ mcds[i][j].port = i;
+ mcds[i][j].slot = j;
+ mcds[i][j].FLAG = 0x08;
+ mcds[i][j].autoEjectTicks = 0;
+ }
+ }
+
+ mcd = &mcds[0][0];
+ return true;
+}
+
+bool Sio0::Shutdown()
+{
+ return true;
+}
+
+void Sio0::SoftReset()
+{
+ padStarted = false;
+ sioMode = SioMode::NOT_SET;
+ sioCommand = 0;
+ sioStage = SioStage::IDLE;
+ g_MemoryCardProtocol.ResetPS1State();
+}
+
+// Simulates the ACK line on the bus. Peripherals are expected to send an ACK signal
+// over this line to tell the PS1 "keep sending me things I'm not done yet". The PS1
+// then uses this after it receives the peripheral's response to decide what to do.
+void Sio0::SetAcknowledge(bool ack)
+{
+ if (ack)
+ {
+ stat |= SIO0_STAT::ACK;
+ }
+ else
+ {
+ stat &= ~(SIO0_STAT::ACK);
+ }
+}
+
+void Sio0::Interrupt(Sio0Interrupt sio0Interrupt)
+{
+ switch (sio0Interrupt)
+ {
+ case Sio0Interrupt::TEST_EVENT:
+ iopIntcIrq(7);
+ break;
+ case Sio0Interrupt::STAT_READ:
+ ClearStatAcknowledge();
+ break;
+ case Sio0Interrupt::TX_DATA_WRITE:
+ break;
+ default:
+ Console.Error("%s(%d) Invalid parameter", __FUNCTION__, sio0Interrupt);
+ assert(false);
+ break;
+ }
+
+ if (!(psxRegs.interrupt & (1 << IopEvt_SIO)))
+ {
+ PSX_INT(IopEvt_SIO, PSXCLK / 250000); // PSXCLK/250000);
+ }
+}
+
+u8 Sio0::GetTxData()
+{
+ Sio0Log.WriteLn("%s()\tSIO0 TX_DATA Read\t(%02X)", __FUNCTION__, txData);
+ return txData;
+}
+
+u8 Sio0::GetRxData()
+{
+ Sio0Log.WriteLn("%s()\tSIO0 RX_DATA Read\t(%02X)", __FUNCTION__, rxData);
+ stat |= (SIO0_STAT::TX_READY | SIO0_STAT::TX_EMPTY);
+ stat &= ~(SIO0_STAT::RX_FIFO_NOT_EMPTY);
+ return rxData;
+}
+
+u32 Sio0::GetStat()
+{
+ Sio0Log.WriteLn("%s()\tSIO0 STAT Read\t(%08X)", __FUNCTION__, stat);
+ const u32 ret = stat;
+ Interrupt(Sio0Interrupt::STAT_READ);
+ return ret;
+}
+
+u16 Sio0::GetMode()
+{
+ Sio0Log.WriteLn("%s()\tSIO0 MODE Read\t(%08X)", __FUNCTION__, mode);
+ return mode;
+}
+
+u16 Sio0::GetCtrl()
+{
+ Sio0Log.WriteLn("%s()\tSIO0 CTRL Read\t(%08X)", __FUNCTION__, ctrl);
+ return ctrl;
+}
+
+u16 Sio0::GetBaud()
+{
+ Sio0Log.WriteLn("%s()\tSIO0 BAUD Read\t(%08X)", __FUNCTION__, baud);
+ return baud;
+}
+
+void Sio0::SetTxData(u8 cmd)
+{
+ Sio0Log.WriteLn("%s()\tSIO0 TX_DATA Write\t(%02X)", __FUNCTION__, cmd);
+
+ stat |= SIO0_STAT::TX_READY | SIO0_STAT::TX_EMPTY;
+ stat |= (SIO0_STAT::RX_FIFO_NOT_EMPTY);
+
+ if (!ctrl & SIO0_CTRL::TX_ENABLE)
+ {
+ Console.Warning("%s(%02X) CTRL in illegal state, exiting instantly", __FUNCTION__, cmd);
+ return;
+ }
+
+ txData = cmd;
+ u8 data = 0;
+ PadBase* currentPad = nullptr;
+
+ switch (sioMode)
+ {
+ case SioMode::NOT_SET:
+ sioMode = cmd;
+ currentPad = g_PadManager.GetPad(port, slot);
+ currentPad->SoftReset();
+ mcd = &mcds[port][slot];
+ SetAcknowledge(true);
+ break;
+ case SioMode::PAD:
+ currentPad = g_PadManager.GetPad(port, slot);
+ pxAssertMsg(currentPad != nullptr, "Got nullptr when looking up pad");
+ // Set ACK in advance of sending the command to the pad.
+ // The pad will, if the command is done, set ACK to false.
+ SetAcknowledge(true);
+ data = currentPad->SendCommandByte(cmd);
+ SetRxData(data);
+ break;
+ case SioMode::MEMCARD:
+ if (this->sioCommand == MemcardCommand::NOT_SET)
+ {
+ if (IsMemcardCommand(cmd) && mcd->IsPresent() && mcd->IsPSX())
+ {
+ this->sioCommand = cmd;
+ SetAcknowledge(true);
+ SetRxData(this->flag);
+ }
+ else
+ {
+ SetAcknowledge(false);
+ SetRxData(0x00);
+ }
+ }
+ else
+ {
+ SetRxData(Memcard(cmd));
+ }
+ break;
+ default:
+ SetRxData(0xff);
+ SetAcknowledge(false);
+ break;
+ }
+
+ // If the peripheral did not ACK, the command is done. Time for a soft reset.
+ if (!(this->stat & SIO0_STAT::ACK))
+ {
+ this->SoftReset();
+ }
+
+ Interrupt(Sio0Interrupt::TX_DATA_WRITE);
+}
+
+void Sio0::SetRxData(u8 value)
+{
+ Sio0Log.WriteLn("%s()\tSIO0 RX_DATA Write\t(%02X)", __FUNCTION__, value);
+ rxData = value;
+}
+
+void Sio0::SetStat(u32 value)
+{
+ Sio0Log.Error("%s()\tSIO0 STAT Write\t(%08X)", __FUNCTION__, value);
+}
+
+void Sio0::SetMode(u16 value)
+{
+ Sio0Log.WriteLn("%s()\tSIO0 MODE Write\t(%04X)", __FUNCTION__, value);
+ mode = value;
+}
+
+void Sio0::SetCtrl(u16 value)
+{
+ Sio0Log.WriteLn("%s()\tSIO0 CTRL Write\t(%04X)", __FUNCTION__, value);
+ ctrl = value;
+ port = (ctrl & SIO0_CTRL::PORT) > 0;
+
+ // CTRL appears to be set to 0 between every "transaction".
+ // Not documented anywhere, but we'll use this to "reset"
+ // the SIO0 state, particularly during the annoying probes
+ // to memcards that occur when a game boots.
+ if (ctrl == 0)
+ {
+ g_MemoryCardProtocol.ResetPS1State();
+ SoftReset();
+ }
+
+ // If CTRL acknowledge, reset STAT bits 3 and 9
+ if (ctrl & SIO0_CTRL::ACK)
+ {
+ stat &= ~(SIO0_STAT::IRQ | SIO0_STAT::RX_PARITY_ERROR);
+ }
+
+ if (ctrl & SIO0_CTRL::RESET)
+ {
+ stat = 0;
+ ctrl = 0;
+ mode = 0;
+ SoftReset();
+ }
+}
+
+void Sio0::SetBaud(u16 value)
+{
+ Sio0Log.WriteLn("%s()\tSIO0 BAUD Write\t(%04X)", __FUNCTION__, value);
+ baud = value;
+}
+
+bool Sio0::IsPadCommand(u8 command)
+{
+ return command >= static_cast(Pad::Command::MYSTERY) && command <= static_cast(Pad::Command::RESPONSE_BYTES);
+}
+
+bool Sio0::IsMemcardCommand(u8 command)
+{
+ return command == MemcardCommand::PS1_READ || command == MemcardCommand::PS1_STATE || command == MemcardCommand::PS1_WRITE;
+}
+
+bool Sio0::IsPocketstationCommand(u8 command)
+{
+ return command == MemcardCommand::PS1_POCKETSTATION;
+}
+
+u8 Sio0::Memcard(u8 value)
+{
+ switch (sioCommand)
+ {
+ case MemcardCommand::PS1_READ:
+ return g_MemoryCardProtocol.PS1Read(value);
+ case MemcardCommand::PS1_STATE:
+ return g_MemoryCardProtocol.PS1State(value);
+ case MemcardCommand::PS1_WRITE:
+ return g_MemoryCardProtocol.PS1Write(value);
+ case MemcardCommand::PS1_POCKETSTATION:
+ return g_MemoryCardProtocol.PS1Pocketstation(value);
+ default:
+ Console.Error("%s(%02X) Unhandled memcard command (%02X)", __FUNCTION__, value, sioCommand);
+ SoftReset();
+ break;
+ }
+
+ return 0xff;
+}
+
+bool SaveStateBase::Sio0Freeze()
+{
+ FreezeTag("sio0");
+ Freeze(g_Sio0);
+ return true;
+}
diff --git a/pcsx2/SIO/Sio0.h b/pcsx2/SIO/Sio0.h
new file mode 100644
index 0000000000..143a747a7a
--- /dev/null
+++ b/pcsx2/SIO/Sio0.h
@@ -0,0 +1,76 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#pragma once
+
+#include "SIO/SioTypes.h"
+
+class Sio0
+{
+private:
+ u32 txData; // 0x1f801040
+ u32 rxData; // 0x1f801040
+ u32 stat; // 0x1f801044
+ u16 mode; // 0x1f801048
+ u16 ctrl; // 0x1f80104a
+ u16 baud; // 0x1f80104e
+
+ void ClearStatAcknowledge();
+
+public:
+ u8 flag = 0;
+
+ SioStage sioStage = SioStage::IDLE;
+ u8 sioMode = SioMode::NOT_SET;
+ u8 sioCommand = 0;
+ bool padStarted = false;
+ bool rxDataSet = false;
+
+ u8 port = 0;
+ u8 slot = 0;
+
+ Sio0();
+ ~Sio0();
+
+ bool Initialize();
+ bool Shutdown();
+
+ void SoftReset();
+
+ void SetAcknowledge(bool ack);
+ void Interrupt(Sio0Interrupt sio0Interrupt);
+
+ u8 GetTxData();
+ u8 GetRxData();
+ u32 GetStat();
+ u16 GetMode();
+ u16 GetCtrl();
+ u16 GetBaud();
+
+ void SetTxData(u8 value);
+ void SetRxData(u8 value);
+ void SetStat(u32 value);
+ void SetMode(u16 value);
+ void SetCtrl(u16 value);
+ void SetBaud(u16 value);
+
+ bool IsPadCommand(u8 command);
+ bool IsMemcardCommand(u8 command);
+ bool IsPocketstationCommand(u8 command);
+
+ u8 Memcard(u8 value);
+};
+
+extern Sio0 g_Sio0;
diff --git a/pcsx2/SIO/Sio2.cpp b/pcsx2/SIO/Sio2.cpp
new file mode 100644
index 0000000000..7a9ebe7160
--- /dev/null
+++ b/pcsx2/SIO/Sio2.cpp
@@ -0,0 +1,561 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#include "PrecompiledHeader.h"
+
+#include "SIO/Sio2.h"
+
+#include "SIO/Sio.h"
+#include "SIO/SioTypes.h"
+#include "SIO/Pad/PadManager.h"
+#include "SIO/Memcard/MemoryCardProtocol.h"
+#include "SIO/Multitap/MultitapProtocol.h"
+
+#include "Common.h"
+#include "IopDma.h"
+#include "Recording/InputRecording.h"
+#include "Host.h"
+
+#define SIO2LOG_ENABLE 0
+#define Sio2Log if (SIO2LOG_ENABLE) DevCon
+
+std::deque g_Sio2FifoIn;
+std::deque g_Sio2FifoOut;
+
+Sio2 g_Sio2;
+
+Sio2::Sio2() = default;
+Sio2::~Sio2() = default;
+
+bool Sio2::Initialize()
+{
+ this->SoftReset();
+
+ for (size_t i = 0; i < send3.size(); i++)
+ {
+ send3.at(i) = 0;
+ }
+
+ for (size_t i = 0; i < send1.size(); i++)
+ {
+ send1.at(i) = 0;
+ send2.at(i) = 0;
+ }
+
+ dataIn = 0;
+ dataOut = 0;
+ SetCtrl(Sio2Ctrl::SIO2MAN_RESET);
+ SetRecv1(Recv1::DISCONNECTED);
+ recv2 = Recv2::DEFAULT;
+ recv3 = Recv3::DEFAULT;
+ unknown1 = 0;
+ unknown2 = 0;
+ iStat = 0;
+
+ port = 0;
+ slot = 0;
+
+ while (!g_Sio2FifoOut.empty())
+ {
+ g_Sio2FifoOut.pop_front();
+ }
+
+ for (int i = 0; i < 2; i++)
+ {
+ for (int j = 0; j < 4; j++)
+ {
+ mcds[i][j].term = 0x55;
+ mcds[i][j].port = i;
+ mcds[i][j].slot = j;
+ mcds[i][j].FLAG = 0x08;
+ mcds[i][j].autoEjectTicks = 0;
+ }
+ }
+
+ mcd = &mcds[0][0];
+ return true;
+}
+
+bool Sio2::Shutdown()
+{
+ return true;
+}
+
+void Sio2::SoftReset()
+{
+ send3Read = false;
+ send3Position = 0;
+ commandLength = 0;
+ processedLength = 0;
+ // Clear dmaBlockSize, in case the next SIO2 command is not sent over DMA11.
+ dmaBlockSize = 0;
+ send3Complete = false;
+
+ // Anything in g_Sio2FifoIn which was not necessary to consume should be cleared out prior to the next SIO2 cycle.
+ while (!g_Sio2FifoIn.empty())
+ {
+ g_Sio2FifoIn.pop_front();
+ }
+}
+
+void Sio2::Interrupt()
+{
+ iopIntcIrq(17);
+}
+
+void Sio2::SetCtrl(u32 value)
+{
+ this->ctrl = value;
+
+ if (this->ctrl & Sio2Ctrl::START_TRANSFER)
+ {
+ Interrupt();
+ }
+}
+
+void Sio2::SetSend3(size_t position, u32 value)
+{
+ this->send3.at(position) = value;
+
+ if (position == 0)
+ {
+ SoftReset();
+ }
+}
+
+void Sio2::SetRecv1(u32 value)
+{
+ this->recv1 = value;
+}
+
+void Sio2::Pad()
+{
+ // Send PAD our current port, and get back whatever it says the first response byte should be.
+ std::optional padPtr = g_PadManager.GetPad(port, slot);
+
+ // RECV1 is set once per DMA; if any device is present at all, it should be set to connected.
+ // For now, we will always report connected for pads.
+ SetRecv1(Recv1::CONNECTED);
+ g_Sio2FifoOut.push_back(0xff);
+
+ // Then for every byte in g_Sio2FifoIn, pass to PAD and see what it kicks back to us.
+ if (padPtr.has_value())
+ {
+ padPtr.value()->SoftReset();
+
+ while (!g_Sio2FifoIn.empty())
+ {
+ const u8 commandByte = g_Sio2FifoIn.front();
+ g_Sio2FifoIn.pop_front();
+ const u8 responseByte = padPtr.value()->SendCommandByte(commandByte);
+ g_Sio2FifoOut.push_back(responseByte);
+ }
+ }
+}
+
+void Sio2::Multitap()
+{
+ g_Sio2FifoOut.push_back(0x00);
+
+ const bool multitapEnabled = (port == 0 && EmuConfig.MultitapPort0_Enabled) || (port == 1 && EmuConfig.MultitapPort1_Enabled);
+ SetRecv1(multitapEnabled ? Recv1::CONNECTED : Recv1::DISCONNECTED);
+
+ if (multitapEnabled)
+ {
+ g_MultitapProtocol.SendToMultitap();
+ }
+ else
+ {
+ while (g_Sio2FifoOut.size() < commandLength)
+ {
+ g_Sio2FifoOut.push_back(0x00);
+ }
+ }
+}
+
+void Sio2::Infrared()
+{
+ SetRecv1(Recv1::DISCONNECTED);
+
+ g_Sio2FifoIn.pop_front();
+ const u8 responseByte = 0xff;
+
+ while (g_Sio2FifoOut.size() < commandLength)
+ {
+ g_Sio2FifoOut.push_back(responseByte);
+ }
+}
+
+void Sio2::Memcard()
+{
+ mcd = &mcds[port][slot];
+
+ // Check if auto ejection is active. If so, set RECV1 to DISCONNECTED,
+ // and zero out the fifo to simulate dead air over the wire.
+ if (mcd->autoEjectTicks)
+ {
+ SetRecv1(Recv1::DISCONNECTED);
+ g_Sio2FifoOut.push_back(0x00); // Because Sio2::Write pops the first g_Sio2FifoIn member
+
+ while (!g_Sio2FifoIn.empty())
+ {
+ g_Sio2FifoIn.pop_front();
+ g_Sio2FifoOut.push_back(0x00);
+ }
+
+ mcd->autoEjectTicks--;
+
+ if (mcd->autoEjectTicks == 0)
+ {
+ Host::AddKeyedOSDMessage(fmt::format("AutoEjectSlotClear{}{}", port, slot),
+ fmt::format(TRANSLATE_FS("MemoryCard", "Memory card in port {} / slot {} reinserted"),
+ port + 1,
+ slot + 1),
+ Host::OSD_INFO_DURATION);
+ }
+
+ return;
+ }
+
+ SetRecv1(mcd->IsPresent() ? Recv1::CONNECTED : Recv1::DISCONNECTED);
+
+ const u8 commandByte = g_Sio2FifoIn.front();
+ g_Sio2FifoIn.pop_front();
+ const u8 responseByte = mcd->IsPresent() ? 0x00 : 0xff;
+ g_Sio2FifoOut.push_back(responseByte);
+ // Technically, the FLAG byte is only for PS1 memcards. However,
+ // since this response byte is still a dud on PS2 memcards, we can
+ // basically just cheat and always make this our second response byte for memcards.
+ g_Sio2FifoOut.push_back(mcd->FLAG);
+ u8 ps1Input = 0;
+ u8 ps1Output = 0;
+
+ switch (commandByte)
+ {
+ case MemcardCommand::PROBE:
+ g_MemoryCardProtocol.Probe();
+ break;
+ case MemcardCommand::UNKNOWN_WRITE_DELETE_END:
+ g_MemoryCardProtocol.UnknownWriteDeleteEnd();
+ break;
+ case MemcardCommand::SET_ERASE_SECTOR:
+ case MemcardCommand::SET_WRITE_SECTOR:
+ case MemcardCommand::SET_READ_SECTOR:
+ g_MemoryCardProtocol.SetSector();
+ break;
+ case MemcardCommand::GET_SPECS:
+ g_MemoryCardProtocol.GetSpecs();
+ break;
+ case MemcardCommand::SET_TERMINATOR:
+ g_MemoryCardProtocol.SetTerminator();
+ break;
+ case MemcardCommand::GET_TERMINATOR:
+ g_MemoryCardProtocol.GetTerminator();
+ break;
+ case MemcardCommand::WRITE_DATA:
+ g_MemoryCardProtocol.WriteData();
+ break;
+ case MemcardCommand::READ_DATA:
+ g_MemoryCardProtocol.ReadData();
+ break;
+ case MemcardCommand::PS1_READ:
+ g_MemoryCardProtocol.ResetPS1State();
+
+ while (!g_Sio2FifoIn.empty())
+ {
+ ps1Input = g_Sio2FifoIn.front();
+ ps1Output = g_MemoryCardProtocol.PS1Read(ps1Input);
+ g_Sio2FifoIn.pop_front();
+ g_Sio2FifoOut.push_back(ps1Output);
+ }
+
+ break;
+ case MemcardCommand::PS1_STATE:
+ g_MemoryCardProtocol.ResetPS1State();
+
+ while (!g_Sio2FifoIn.empty())
+ {
+ ps1Input = g_Sio2FifoIn.front();
+ ps1Output = g_MemoryCardProtocol.PS1State(ps1Input);
+ g_Sio2FifoIn.pop_front();
+ g_Sio2FifoOut.push_back(ps1Output);
+ }
+
+ break;
+ case MemcardCommand::PS1_WRITE:
+ g_MemoryCardProtocol.ResetPS1State();
+
+ while (!g_Sio2FifoIn.empty())
+ {
+ ps1Input = g_Sio2FifoIn.front();
+ ps1Output = g_MemoryCardProtocol.PS1Write(ps1Input);
+ g_Sio2FifoIn.pop_front();
+ g_Sio2FifoOut.push_back(ps1Output);
+ }
+
+ break;
+ case MemcardCommand::PS1_POCKETSTATION:
+ g_MemoryCardProtocol.ResetPS1State();
+
+ while (!g_Sio2FifoIn.empty())
+ {
+ ps1Input = g_Sio2FifoIn.front();
+ ps1Output = g_MemoryCardProtocol.PS1Pocketstation(ps1Input);
+ g_Sio2FifoIn.pop_front();
+ g_Sio2FifoOut.push_back(ps1Output);
+ }
+
+ break;
+ case MemcardCommand::READ_WRITE_END:
+ g_MemoryCardProtocol.ReadWriteEnd();
+ break;
+ case MemcardCommand::ERASE_BLOCK:
+ g_MemoryCardProtocol.EraseBlock();
+ break;
+ case MemcardCommand::UNKNOWN_BOOT:
+ g_MemoryCardProtocol.UnknownBoot();
+ break;
+ case MemcardCommand::AUTH_XOR:
+ g_MemoryCardProtocol.AuthXor();
+ break;
+ case MemcardCommand::AUTH_F3:
+ g_MemoryCardProtocol.AuthF3();
+ break;
+ case MemcardCommand::AUTH_F7:
+ g_MemoryCardProtocol.AuthF7();
+ break;
+ default:
+ Console.Warning("%s() Unhandled memcard command %02X, things are about to break!", __FUNCTION__, commandByte);
+ break;
+ }
+}
+
+void Sio2::Write(u8 data)
+{
+ Sio2Log.WriteLn("%s(%02X) SIO2 DATA Write", __FUNCTION__, data);
+
+ if (!send3Read)
+ {
+ // No more SEND3 positions to access, but the game is still sending us SIO2 writes. Lets ignore them.
+ if (send3Position > send3.size())
+ {
+ Console.Warning("%s(%02X) Received data after exhausting all SEND3 values!", __FUNCTION__, data);
+ return;
+ }
+
+ const u32 currentSend3 = send3.at(send3Position);
+ port = currentSend3 & Send3::PORT;
+ commandLength = (currentSend3 >> 8) & Send3::COMMAND_LENGTH_MASK;
+ send3Read = true;
+
+ // The freshly read SEND3 position had a length of 0, so we are done handling SIO2 commands until
+ // the next SEND3 writes.
+ if (commandLength == 0)
+ {
+ send3Complete = true;
+ }
+
+ // If the prior command did not need to fully pop g_Sio2FifoIn, do so now,
+ // so that the next command isn't trying to read the last command's leftovers.
+ while (!g_Sio2FifoIn.empty())
+ {
+ g_Sio2FifoIn.pop_front();
+ }
+ }
+
+ if (send3Complete)
+ {
+ return;
+ }
+
+ g_Sio2FifoIn.push_back(data);
+
+ // We have received as many command bytes as we expect, and...
+ //
+ // ... These were from direct writes into IOP memory (DMA block size is zero when direct writes occur)
+ // ... These were from SIO2 DMA (DMA block size is non-zero when SIO2 DMA occurs)
+ if ((g_Sio2FifoIn.size() == g_Sio2.commandLength && g_Sio2.dmaBlockSize == 0) || g_Sio2FifoIn.size() == g_Sio2.dmaBlockSize)
+ {
+ // Go ahead and prep so the next write triggers a load of the new SEND3 value.
+ g_Sio2.send3Read = false;
+ g_Sio2.send3Position++;
+
+ // Check the SIO mode
+ const u8 sioMode = g_Sio2FifoIn.front();
+ g_Sio2FifoIn.pop_front();
+
+ switch (sioMode)
+ {
+ case SioMode::PAD:
+ this->Pad();
+ break;
+ case SioMode::MULTITAP:
+ this->Multitap();
+ break;
+ case SioMode::INFRARED:
+ this->Infrared();
+ break;
+ case SioMode::MEMCARD:
+ this->Memcard();
+ break;
+ default:
+ Console.Error("%s(%02X) Unhandled SIO mode %02X", __FUNCTION__, data, sioMode);
+ g_Sio2FifoOut.push_back(0x00);
+ SetRecv1(Recv1::DISCONNECTED);
+ break;
+ }
+
+ // If command was sent over SIO2 DMA, align g_Sio2FifoOut to the block size
+ if (g_Sio2.dmaBlockSize > 0)
+ {
+ const size_t dmaDiff = g_Sio2FifoOut.size() % g_Sio2.dmaBlockSize;
+
+ if (dmaDiff > 0)
+ {
+ const size_t padding = g_Sio2.dmaBlockSize - dmaDiff;
+
+ for (size_t i = 0; i < padding; i++)
+ {
+ g_Sio2FifoOut.push_back(0x00);
+ }
+ }
+ }
+ }
+}
+
+u8 Sio2::Read()
+{
+ u8 ret = 0x00;
+
+ if (!g_Sio2FifoOut.empty())
+ {
+ ret = g_Sio2FifoOut.front();
+ g_Sio2FifoOut.pop_front();
+ }
+ else
+ {
+ Console.Warning("%s() g_Sio2FifoOut underflow! Returning 0x00.", __FUNCTION__);
+ }
+
+ Sio2Log.WriteLn("%s() SIO2 DATA Read (%02X)", __FUNCTION__, ret);
+ return ret;
+}
+
+bool SaveStateBase::Sio2Freeze()
+{
+ FreezeTag("sio2");
+
+ if (IsSaving())
+ {
+ std::deque::iterator iter;
+ size_t backupSize;
+
+ // Copy g_Sio2FifoIn
+ if (g_Sio2FifoIn.size())
+ {
+ g_Sio2.fifoInBackup = std::make_unique(g_Sio2FifoIn.size());
+ iter = g_Sio2FifoIn.begin();
+ backupSize = 0;
+
+ while (iter != g_Sio2FifoIn.end())
+ {
+ const u8 val = *iter++;
+ g_Sio2.fifoInBackup.get()[backupSize++] = val;
+ }
+
+ g_Sio2.fifoInBackupSize = backupSize;
+ }
+ else
+ {
+ g_Sio2.fifoInBackupSize = 0;
+ }
+
+ // Copy g_Sio2FifoOut
+ if (g_Sio2FifoOut.size())
+ {
+ g_Sio2.fifoOutBackup = std::make_unique(g_Sio2FifoOut.size());
+ iter = g_Sio2FifoOut.begin();
+ backupSize = 0;
+
+ while (iter != g_Sio2FifoOut.end())
+ {
+ const u8 val = *iter++;
+ g_Sio2.fifoOutBackup.get()[backupSize++] = val;
+ }
+
+ g_Sio2.fifoOutBackupSize = backupSize;
+ }
+ else
+ {
+ g_Sio2.fifoOutBackupSize = 0;
+ }
+ }
+
+ Freeze(g_Sio2);
+
+ // CRCs for memory cards.
+ // If the memory card hasn't changed when loading state, we can safely skip ejecting it.
+ u64 mcdCrcs[SIO::PORTS][SIO::SLOTS];
+ if (IsSaving())
+ {
+ for (u32 port = 0; port < SIO::PORTS; port++)
+ {
+ for (u32 slot = 0; slot < SIO::SLOTS; slot++)
+ mcdCrcs[port][slot] = mcds[port][slot].GetChecksum();
+ }
+ }
+ Freeze(mcdCrcs);
+
+ if (IsLoading())
+ {
+ bool ejected = false;
+ for (u32 port = 0; port < SIO::PORTS && !ejected; port++)
+ {
+ for (u32 slot = 0; slot < SIO::SLOTS; slot++)
+ {
+ if (mcdCrcs[port][slot] != mcds[port][slot].GetChecksum())
+ {
+ AutoEject::SetAll();
+ ejected = true;
+ break;
+ }
+ }
+ }
+
+ // Restore g_Sio2FifoIn
+ g_Sio2FifoIn.clear();
+
+ if (g_Sio2.fifoInBackupSize)
+ {
+ for (size_t i = 0; i < g_Sio2.fifoInBackupSize; i++)
+ {
+ g_Sio2FifoIn.push_back(g_Sio2.fifoInBackup.get()[i]);
+ }
+ }
+
+ // Restore g_Sio2FifoOut
+ g_Sio2FifoOut.clear();
+
+ if (g_Sio2.fifoOutBackupSize)
+ {
+ for (size_t j = 0; j < g_Sio2.fifoOutBackupSize; j++)
+ {
+ g_Sio2FifoOut.push_back(g_Sio2.fifoOutBackup.get()[j]);
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/pcsx2/SIO/Sio2.h b/pcsx2/SIO/Sio2.h
new file mode 100644
index 0000000000..6fb42c39bf
--- /dev/null
+++ b/pcsx2/SIO/Sio2.h
@@ -0,0 +1,86 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2023 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 .
+ */
+
+#pragma once
+
+#include
+
+class Sio2
+{
+public:
+ std::array send3; // 0x1f808200 - 0x1f80823f
+ // SEND1 and SEND2 are an unusual bunch. It's not entirely clear just from
+ // documentation but these registers almost seem like they are the same thing;
+ // when bit 2 is set, SEND2 is being read/written. When bit 2 isn't set, it is
+ // SEND1. Their use is not really known, either.
+ std::array send1; // 0x1f808240 - 0x1f80825f
+ std::array send2; // 0x1f808240 - 0x1f80825f
+ u32 dataIn; // 0x1f808260
+ u32 dataOut; // 0x1f808264
+ u32 ctrl; // 0x1f808268
+ u32 recv1; // 0x1f80826c
+ u32 recv2; // 0x1f808270
+ u32 recv3; // 0x1f808274
+ u32 unknown1; // 0x1f808278
+ u32 unknown2; // 0x1f80827c
+ u32 iStat; // 0x1f808280
+
+ u8 port = 0;
+ u8 slot = 0;
+
+ // The current working index of SEND3. The SEND3 register is a 16 position
+ // array of command descriptors. Each descriptor describes the port the command
+ // is targeting, as well as the length of the command in bytes.
+ bool send3Read = false;
+ size_t send3Position = 0;
+ size_t commandLength = 0;
+ size_t processedLength = 0;
+ // Tracks the size of a single block of DMA11/DMA12 data. psxDma11 will set this prior
+ // to doing writes, and Sio2::SetSend3 will clear this to ensure a non-DMA write into SIO2
+ // does not accidentally use dmaBlockSize.
+ size_t dmaBlockSize = 0;
+ bool send3Complete = false;
+
+ std::unique_ptr fifoInBackup;
+ size_t fifoInBackupSize;
+ std::unique_ptr fifoOutBackup;
+ size_t fifoOutBackupSize;
+
+ Sio2();
+ ~Sio2();
+
+ bool Initialize();
+ bool Shutdown();
+
+ void SoftReset();
+
+ void Interrupt();
+
+ void SetCtrl(u32 value);
+ void SetSend3(size_t position, u32 value);
+ void SetRecv1(u32 value);
+
+ void Pad();
+ void Multitap();
+ void Infrared();
+ void Memcard();
+
+ void Write(u8 data);
+ u8 Read();
+};
+
+extern std::deque g_Sio2FifoIn;
+extern std::deque g_Sio2FifoOut;
+extern Sio2 g_Sio2;
diff --git a/pcsx2/SioTypes.h b/pcsx2/SIO/SioTypes.h
similarity index 98%
rename from pcsx2/SioTypes.h
rename to pcsx2/SIO/SioTypes.h
index f6a0a68c9b..ad190224ff 100644
--- a/pcsx2/SioTypes.h
+++ b/pcsx2/SIO/SioTypes.h
@@ -1,5 +1,5 @@
/* PCSX2 - PS2 Emulator for PCs
- * Copyright (C) 2002-2022 PCSX2 Dev Team
+ * Copyright (C) 2002-2023 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-
@@ -31,6 +31,7 @@ namespace SioMode
static constexpr u8 MEMCARD = 0x81;
} // namespace SioMode
+/*
namespace PadCommand
{
static constexpr u8 UNK_0 = 0x40;
@@ -50,6 +51,7 @@ namespace PadCommand
static constexpr u8 UNK_E = 0x4e;
static constexpr u8 ANALOG = 0x4f;
} // namespace PadCommand
+*/
namespace MemcardCommand
{
diff --git a/pcsx2/SaveState.cpp b/pcsx2/SaveState.cpp
index 3225e5aabe..24df4356e7 100644
--- a/pcsx2/SaveState.cpp
+++ b/pcsx2/SaveState.cpp
@@ -28,7 +28,7 @@
#include "Host.h"
#include "MTGS.h"
#include "MTVU.h"
-#include "PAD/Host/PAD.h"
+#include "SIO/Pad/PadManager.h"
#include "Patch.h"
#include "R3000A.h"
#include "SPU2/spu2.h"
@@ -237,8 +237,8 @@ bool SaveStateBase::FreezeInternals()
FreezeMem(iopMem->Sif, sizeof(iopMem->Sif)); // iop's sif memory (not really needed, but oh well)
okay = okay && psxRcntFreeze();
- okay = okay && sioFreeze();
- okay = okay && sio2Freeze();
+ okay = okay && Sio0Freeze();
+ okay = okay && Sio2Freeze();
okay = okay && cdrFreeze();
okay = okay && cdvdFreeze();
@@ -314,8 +314,12 @@ static int SysState_MTGSFreeze(FreezeAction mode, freezeData* fP)
return sstate.retval;
}
+static bool SysState_PadFreeze(StateWrapper& sw)
+{
+ return g_PadManager.PadFreeze(sw);
+}
+
static constexpr SysState_Component SPU2_{ "SPU2", SPU2freeze };
-static constexpr SysState_Component PAD_{ "PAD", PADfreeze };
static constexpr SysState_Component GS{ "GS", SysState_MTGSFreeze };
static bool SysState_ComponentFreezeIn(zip_file_t* zf, SysState_Component comp)
@@ -600,8 +604,8 @@ public:
~SavestateEntry_PAD() override = default;
const char* GetFilename() const override { return "PAD.bin"; }
- bool FreezeIn(zip_file_t* zf) const override { return SysState_ComponentFreezeIn(zf, PAD_); }
- bool FreezeOut(SaveStateBase& writer) const override { return SysState_ComponentFreezeOut(writer, PAD_); }
+ bool FreezeIn(zip_file_t* zf) const override { return SysState_ComponentFreezeInNew(zf, "PAD", &SysState_PadFreeze); }
+ bool FreezeOut(SaveStateBase& writer) const override { return SysState_ComponentFreezeOutNew(writer, "PAD", 16 * 1024, &SysState_PadFreeze); }
bool IsRequired() const override { return true; }
};
diff --git a/pcsx2/SaveState.h b/pcsx2/SaveState.h
index 3536e9a917..483b2e0ed3 100644
--- a/pcsx2/SaveState.h
+++ b/pcsx2/SaveState.h
@@ -37,7 +37,7 @@ enum class FreezeAction
// [SAVEVERSION+]
// This informs the auto updater that the users savestates will be invalidated.
-static const u32 g_SaveVersion = (0x9A37 << 16) | 0x0000;
+static const u32 g_SaveVersion = (0x9A3A << 16) | 0x0000;
// the freezing data between submodules and core
@@ -221,9 +221,9 @@ protected:
bool cdrFreeze();
bool cdvdFreeze();
bool psxRcntFreeze();
- bool sio2Freeze();
-
bool deci2Freeze();
+ bool Sio0Freeze();
+ bool Sio2Freeze();
// Save or load PCSX2's global frame counter (g_FrameCount) along with each savestate
//
diff --git a/pcsx2/Sio.cpp b/pcsx2/Sio.cpp
deleted file mode 100644
index 69e5f2326b..0000000000
--- a/pcsx2/Sio.cpp
+++ /dev/null
@@ -1,970 +0,0 @@
-/* PCSX2 - PS2 Emulator for PCs
- * Copyright (C) 2002-2022 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 .
- */
-
-#include "PrecompiledHeader.h"
-#include "R3000A.h"
-#include "IopHw.h"
-#include "IopDma.h"
-
-#include "Common.h"
-#include "Sio.h"
-#include "MemoryCardProtocol.h"
-#include "MultitapProtocol.h"
-#include "Config.h"
-#include "Host.h"
-#include "PAD/Host/PAD.h"
-
-#include "common/Timer.h"
-#include "Recording/InputRecording.h"
-#include "IconsFontAwesome5.h"
-
-#define SIO0LOG_ENABLE 0
-#define SIO2LOG_ENABLE 0
-
-#define Sio0Log if (SIO0LOG_ENABLE) DevCon
-#define Sio2Log if (SIO2LOG_ENABLE) DevCon
-
-std::deque fifoIn;
-std::deque fifoOut;
-
-Sio0 sio0;
-Sio2 sio2;
-
-_mcd mcds[2][4];
-_mcd *mcd;
-
-// ============================================================================
-// SIO0
-// ============================================================================
-
-void Sio0::ClearStatAcknowledge()
-{
- stat &= ~(SIO0_STAT::ACK);
-}
-
-Sio0::Sio0()
-{
- this->FullReset();
-}
-
-Sio0::~Sio0() = default;
-
-void Sio0::SoftReset()
-{
- padStarted = false;
- sioMode = SioMode::NOT_SET;
- sioCommand = 0;
- sioStage = SioStage::IDLE;
-}
-
-void Sio0::FullReset()
-{
- SoftReset();
-
- port = 0;
- slot = 0;
-
- for (int i = 0; i < 2; i++)
- {
- for (int j = 0; j < 4; j++)
- {
- mcds[i][j].term = 0x55;
- mcds[i][j].port = i;
- mcds[i][j].slot = j;
- mcds[i][j].FLAG = 0x08;
- mcds[i][j].autoEjectTicks = 0;
- }
- }
-
- mcd = &mcds[0][0];
-}
-
-// Simulates the ACK line on the bus. Peripherals are expected to send an ACK signal
-// over this line to tell the PS1 "keep sending me things I'm not done yet". The PS1
-// then uses this after it receives the peripheral's response to decide what to do.
-void Sio0::Acknowledge()
-{
- stat |= SIO0_STAT::ACK;
-}
-
-void Sio0::Interrupt(Sio0Interrupt sio0Interrupt)
-{
- switch (sio0Interrupt)
- {
- case Sio0Interrupt::TEST_EVENT:
- iopIntcIrq(7);
- break;
- case Sio0Interrupt::STAT_READ:
- ClearStatAcknowledge();
- break;
- case Sio0Interrupt::TX_DATA_WRITE:
- break;
- default:
- Console.Error("%s(%d) Invalid parameter", __FUNCTION__, sio0Interrupt);
- assert(false);
- break;
- }
-
- if (!(psxRegs.interrupt & (1 << IopEvt_SIO)))
- {
- PSX_INT(IopEvt_SIO, PSXCLK / 250000); // PSXCLK/250000);
- }
-}
-
-u8 Sio0::GetTxData()
-{
- Sio0Log.WriteLn("%s() SIO0 TX_DATA Read (%02X)", __FUNCTION__, txData);
- return txData;
-}
-
-u8 Sio0::GetRxData()
-{
- Sio0Log.WriteLn("%s() SIO0 RX_DATA Read (%02X)", __FUNCTION__, rxData);
- stat |= (SIO0_STAT::TX_READY | SIO0_STAT::TX_EMPTY);
- stat &= ~(SIO0_STAT::RX_FIFO_NOT_EMPTY);
- return rxData;
-}
-
-u32 Sio0::GetStat()
-{
- Sio0Log.WriteLn("%s() SIO0 STAT Read (%08X)", __FUNCTION__, stat);
- const u32 ret = stat;
- Interrupt(Sio0Interrupt::STAT_READ);
- return ret;
-}
-
-u16 Sio0::GetMode()
-{
- Sio0Log.WriteLn("%s() SIO0 MODE Read (%08X)", __FUNCTION__, mode);
- return mode;
-}
-
-u16 Sio0::GetCtrl()
-{
- Sio0Log.WriteLn("%s() SIO0 CTRL Read (%08X)", __FUNCTION__, ctrl);
- return ctrl;
-}
-
-u16 Sio0::GetBaud()
-{
- Sio0Log.WriteLn("%s() SIO0 BAUD Read (%08X)", __FUNCTION__, baud);
- return baud;
-}
-
-void Sio0::SetTxData(u8 value)
-{
- Sio0Log.WriteLn("%s(%02X) SIO0 TX_DATA Write", __FUNCTION__, value);
-
- stat |= SIO0_STAT::TX_READY | SIO0_STAT::TX_EMPTY;
- stat |= (SIO0_STAT::RX_FIFO_NOT_EMPTY);
-
- if (!(ctrl & SIO0_CTRL::TX_ENABLE))
- {
- Console.Warning("%s(%02X) CTRL in illegal state, exiting instantly", __FUNCTION__, value);
- return;
- }
-
- txData = value;
- u8 res = 0;
-
- switch (sioStage)
- {
- case SioStage::IDLE:
- sioMode = value;
- stat |= SIO0_STAT::TX_READY;
-
- switch (sioMode)
- {
- case SioMode::PAD:
- res = PADstartPoll(port, slot);
-
- if (res)
- {
- Acknowledge();
- }
-
- break;
- case SioMode::MEMCARD:
- mcd = &mcds[port][slot];
-
- // Check if auto ejection is active. If so, set RECV1 to DISCONNECTED,
- // and zero out the fifo to simulate dead air over the wire.
- if (mcd->autoEjectTicks)
- {
- SetRxData(0x00);
-
- return;
- }
-
- // If memcard is missing, not PS1, or auto ejected, do not let SIO0 stage advance,
- // reply with dead air and no ACK.
- if (!mcd->IsPresent() || !mcd->IsPSX())
- {
- SetRxData(0x00);
- return;
- }
-
- Acknowledge();
- break;
- }
-
- SetRxData(res);
- sioStage = SioStage::WAITING_COMMAND;
- break;
- case SioStage::WAITING_COMMAND:
- stat &= ~(SIO0_STAT::TX_READY);
-
- if (IsPadCommand(value))
- {
- res = PADpoll(value);
- SetRxData(res);
-
- if (!PADcomplete())
- {
- Acknowledge();
- }
-
- sioStage = SioStage::WORKING;
- }
- else if (IsMemcardCommand(value))
- {
- SetRxData(flag);
- Acknowledge();
- sioCommand = value;
- sioStage = SioStage::WORKING;
- }
- else if (IsPocketstationCommand(value))
- {
- // Set the line low, no acknowledge.
- SetRxData(0x00);
- sioStage = SioStage::IDLE;
- }
- else
- {
- Console.Error("%s(%02X) Bad SIO command", __FUNCTION__, value);
- SetRxData(0xff);
- SoftReset();
- }
-
- break;
- case SioStage::WORKING:
- switch (sioMode)
- {
- case SioMode::PAD:
- res = PADpoll(value);
- SetRxData(res);
-
- if (!PADcomplete())
- {
- Acknowledge();
- }
-
- break;
- case SioMode::MEMCARD:
- SetRxData(Memcard(value));
- break;
- default:
- Console.Error("%s(%02X) Unhandled SioMode: %02X", __FUNCTION__, value, sioMode);
- SetRxData(0xff);
- SoftReset();
- break;
- }
-
- break;
- default:
- Console.Error("%s(%02X) Unhandled SioStage: %02X", __FUNCTION__, value, static_cast(sioStage));
- SetRxData(0xff);
- SoftReset();
- break;
- }
-
- Interrupt(Sio0Interrupt::TX_DATA_WRITE);
-}
-
-void Sio0::SetRxData(u8 value)
-{
- Sio0Log.WriteLn("%s(%02X) SIO0 RX_DATA Write", __FUNCTION__, value);
- rxData = value;
-}
-
-void Sio0::SetStat(u32 value)
-{
- Sio0Log.Error("%s(%08X) SIO0 STAT Write", __FUNCTION__, value);
-}
-
-void Sio0::SetMode(u16 value)
-{
- Sio0Log.WriteLn("%s(%04X) SIO0 MODE Write", __FUNCTION__, value);
- mode = value;
-}
-
-void Sio0::SetCtrl(u16 value)
-{
- Sio0Log.WriteLn("%s(%04X) SIO0 CTRL Write", __FUNCTION__, value);
- ctrl = value;
- port = (ctrl & SIO0_CTRL::PORT) > 0;
-
- // CTRL appears to be set to 0 between every "transaction".
- // Not documented anywhere, but we'll use this to "reset"
- // the SIO0 state, particularly during the annoying probes
- // to memcards that occur when a game boots.
- if (ctrl == 0)
- {
- g_MemoryCardProtocol.ResetPS1State();
- SoftReset();
- }
-
- // If CTRL acknowledge, reset STAT bits 3 and 9
- if (ctrl & SIO0_CTRL::ACK)
- {
- stat &= ~(SIO0_STAT::IRQ | SIO0_STAT::RX_PARITY_ERROR);
- }
-
- if (ctrl & SIO0_CTRL::RESET)
- {
- stat = 0;
- ctrl = 0;
- mode = 0;
- SoftReset();
- }
-}
-
-void Sio0::SetBaud(u16 value)
-{
- Sio0Log.WriteLn("%s(%04X) SIO0 BAUD Write", __FUNCTION__, value);
- baud = value;
-}
-
-bool Sio0::IsPadCommand(u8 command)
-{
- return command >= PadCommand::UNK_0 && command <= PadCommand::ANALOG;
-}
-
-bool Sio0::IsMemcardCommand(u8 command)
-{
- return command == MemcardCommand::PS1_READ || command == MemcardCommand::PS1_STATE || command == MemcardCommand::PS1_WRITE;
-}
-
-bool Sio0::IsPocketstationCommand(u8 command)
-{
- return command == MemcardCommand::PS1_POCKETSTATION;
-}
-
-u8 Sio0::Pad(u8 value)
-{
- if (PADcomplete())
- {
- padStarted = false;
- }
- else if (!padStarted)
- {
- padStarted = true;
- PADstartPoll(port, slot);
- Acknowledge();
- }
-
- return PADpoll(value);
-}
-
-u8 Sio0::Memcard(u8 value)
-{
- switch (sioCommand)
- {
- case MemcardCommand::PS1_READ:
- return g_MemoryCardProtocol.PS1Read(value);
- case MemcardCommand::PS1_STATE:
- return g_MemoryCardProtocol.PS1State(value);
- case MemcardCommand::PS1_WRITE:
- return g_MemoryCardProtocol.PS1Write(value);
- case MemcardCommand::PS1_POCKETSTATION:
- return g_MemoryCardProtocol.PS1Pocketstation(value);
- default:
- Console.Error("%s(%02X) Unhandled memcard command (%02X)", __FUNCTION__, value, sioCommand);
- SoftReset();
- break;
- }
-
- return 0xff;
-}
-
-// ============================================================================
-// SIO2
-// ============================================================================
-
-
-Sio2::Sio2()
-{
- this->FullReset();
-}
-
-Sio2::~Sio2() = default;
-
-void Sio2::SoftReset()
-{
- send3Read = false;
- send3Position = 0;
- commandLength = 0;
- processedLength = 0;
- // Clear dmaBlockSize, in case the next SIO2 command is not sent over DMA11.
- dmaBlockSize = 0;
- send3Complete = false;
-
- // Anything in fifoIn which was not necessary to consume should be cleared out prior to the next SIO2 cycle.
- while (!fifoIn.empty())
- {
- fifoIn.pop_front();
- }
-}
-
-void Sio2::FullReset()
-{
- this->SoftReset();
-
- for (size_t i = 0; i < send3.size(); i++)
- {
- send3.at(i) = 0;
- }
-
- for (size_t i = 0; i < send1.size(); i++)
- {
- send1.at(i) = 0;
- send2.at(i) = 0;
- }
-
- dataIn = 0;
- dataOut = 0;
- SetCtrl(Sio2Ctrl::SIO2MAN_RESET);
- SetRecv1(Recv1::DISCONNECTED);
- recv2 = Recv2::DEFAULT;
- recv3 = Recv3::DEFAULT;
- unknown1 = 0;
- unknown2 = 0;
- iStat = 0;
-
- port = 0;
- slot = 0;
-
- while (!fifoOut.empty())
- {
- fifoOut.pop_front();
- }
-
- for (int i = 0; i < 2; i++)
- {
- for (int j = 0; j < 4; j++)
- {
- mcds[i][j].term = 0x55;
- mcds[i][j].port = i;
- mcds[i][j].slot = j;
- mcds[i][j].FLAG = 0x08;
- mcds[i][j].autoEjectTicks = 0;
- }
- }
-
- mcd = &mcds[0][0];
-}
-
-
-void Sio2::Interrupt()
-{
- iopIntcIrq(17);
-}
-
-void Sio2::SetCtrl(u32 value)
-{
- this->ctrl = value;
-
- if (this->ctrl & Sio2Ctrl::START_TRANSFER)
- {
- Interrupt();
- }
-}
-
-void Sio2::SetSend3(size_t position, u32 value)
-{
- this->send3.at(position) = value;
-
- if (position == 0)
- {
- SoftReset();
- }
-}
-
-void Sio2::SetRecv1(u32 value)
-{
- this->recv1 = value;
-}
-
-void Sio2::Pad()
-{
- // Send PAD our current port, and get back whatever it says the first response byte should be.
- const u8 firstResponseByte = PADstartPoll(port, slot);
- fifoOut.push_back(firstResponseByte);
- // Some games will refuse to read ALL pads, if RECV1 is not set to the CONNECTED value when ANY pad is polled,
- // REGARDLESS of whether that pad is truly connected or not.
- SetRecv1(Recv1::CONNECTED);
-
- // Then for every byte in fifoIn, pass to PAD and see what it kicks back to us.
- while (!fifoIn.empty())
- {
- const u8 commandByte = fifoIn.front();
- fifoIn.pop_front();
- const u8 responseByte = PADpoll(commandByte);
- fifoOut.push_back(responseByte);
- }
-}
-
-void Sio2::Multitap()
-{
- fifoOut.push_back(0x00);
-
- const bool multitapEnabled = (port == 0 && EmuConfig.MultitapPort0_Enabled) || (port == 1 && EmuConfig.MultitapPort1_Enabled);
- SetRecv1(multitapEnabled ? Recv1::CONNECTED : Recv1::DISCONNECTED);
-
- if (multitapEnabled)
- {
- g_MultitapProtocol.SendToMultitap();
- }
- else
- {
- while (fifoOut.size() < commandLength)
- {
- fifoOut.push_back(0x00);
- }
- }
-}
-
-void Sio2::Infrared()
-{
- SetRecv1(Recv1::DISCONNECTED);
-
- fifoIn.pop_front();
- const u8 responseByte = 0xff;
-
- while (fifoOut.size() < commandLength)
- {
- fifoOut.push_back(responseByte);
- }
-}
-
-void Sio2::Memcard()
-{
- mcd = &mcds[port][slot];
-
- // Check if auto ejection is active. If so, set RECV1 to DISCONNECTED,
- // and zero out the fifo to simulate dead air over the wire.
- if (mcd->autoEjectTicks)
- {
- SetRecv1(Recv1::DISCONNECTED);
- fifoOut.push_back(0x00); // Because Sio2::Write pops the first fifoIn member
-
- while (!fifoIn.empty())
- {
- fifoIn.pop_front();
- fifoOut.push_back(0x00);
- }
-
- return;
- }
-
- SetRecv1(mcd->IsPresent() ? Recv1::CONNECTED : Recv1::DISCONNECTED);
-
- const u8 commandByte = fifoIn.front();
- fifoIn.pop_front();
- const u8 responseByte = mcd->IsPresent() ? 0x00 : 0xff;
- fifoOut.push_back(responseByte);
- // Technically, the FLAG byte is only for PS1 memcards. However,
- // since this response byte is still a dud on PS2 memcards, we can
- // basically just cheat and always make this our second response byte for memcards.
- fifoOut.push_back(mcd->FLAG);
- u8 ps1Input = 0;
- u8 ps1Output = 0;
-
- switch (commandByte)
- {
- case MemcardCommand::PROBE:
- g_MemoryCardProtocol.Probe();
- break;
- case MemcardCommand::UNKNOWN_WRITE_DELETE_END:
- g_MemoryCardProtocol.UnknownWriteDeleteEnd();
- break;
- case MemcardCommand::SET_ERASE_SECTOR:
- case MemcardCommand::SET_WRITE_SECTOR:
- case MemcardCommand::SET_READ_SECTOR:
- g_MemoryCardProtocol.SetSector();
- break;
- case MemcardCommand::GET_SPECS:
- g_MemoryCardProtocol.GetSpecs();
- break;
- case MemcardCommand::SET_TERMINATOR:
- g_MemoryCardProtocol.SetTerminator();
- break;
- case MemcardCommand::GET_TERMINATOR:
- g_MemoryCardProtocol.GetTerminator();
- break;
- case MemcardCommand::WRITE_DATA:
- g_MemoryCardProtocol.WriteData();
- break;
- case MemcardCommand::READ_DATA:
- g_MemoryCardProtocol.ReadData();
- break;
- case MemcardCommand::PS1_READ:
- g_MemoryCardProtocol.ResetPS1State();
-
- while (!fifoIn.empty())
- {
- ps1Input = fifoIn.front();
- ps1Output = g_MemoryCardProtocol.PS1Read(ps1Input);
- fifoIn.pop_front();
- fifoOut.push_back(ps1Output);
- }
-
- break;
- case MemcardCommand::PS1_STATE:
- g_MemoryCardProtocol.ResetPS1State();
-
- while (!fifoIn.empty())
- {
- ps1Input = fifoIn.front();
- ps1Output = g_MemoryCardProtocol.PS1State(ps1Input);
- fifoIn.pop_front();
- fifoOut.push_back(ps1Output);
- }
-
- break;
- case MemcardCommand::PS1_WRITE:
- g_MemoryCardProtocol.ResetPS1State();
-
- while (!fifoIn.empty())
- {
- ps1Input = fifoIn.front();
- ps1Output = g_MemoryCardProtocol.PS1Write(ps1Input);
- fifoIn.pop_front();
- fifoOut.push_back(ps1Output);
- }
-
- break;
- case MemcardCommand::PS1_POCKETSTATION:
- g_MemoryCardProtocol.ResetPS1State();
-
- while (!fifoIn.empty())
- {
- ps1Input = fifoIn.front();
- ps1Output = g_MemoryCardProtocol.PS1Pocketstation(ps1Input);
- fifoIn.pop_front();
- fifoOut.push_back(ps1Output);
- }
-
- break;
- case MemcardCommand::READ_WRITE_END:
- g_MemoryCardProtocol.ReadWriteEnd();
- break;
- case MemcardCommand::ERASE_BLOCK:
- g_MemoryCardProtocol.EraseBlock();
- break;
- case MemcardCommand::UNKNOWN_BOOT:
- g_MemoryCardProtocol.UnknownBoot();
- break;
- case MemcardCommand::AUTH_XOR:
- g_MemoryCardProtocol.AuthXor();
- break;
- case MemcardCommand::AUTH_F3:
- g_MemoryCardProtocol.AuthF3();
- break;
- case MemcardCommand::AUTH_F7:
- g_MemoryCardProtocol.AuthF7();
- break;
- default:
- Console.Warning("%s() Unhandled memcard command %02X, things are about to break!", __FUNCTION__, commandByte);
- break;
- }
-}
-
-void Sio2::Write(u8 data)
-{
- Sio2Log.WriteLn("%s(%02X) SIO2 DATA Write", __FUNCTION__, data);
-
- if (!send3Read)
- {
- // No more SEND3 positions to access, but the game is still sending us SIO2 writes. Lets ignore them.
- if (send3Position > send3.size())
- {
- Console.Warning("%s(%02X) Received data after exhausting all SEND3 values!", __FUNCTION__, data);
- return;
- }
-
- const u32 currentSend3 = send3.at(send3Position);
- port = currentSend3 & Send3::PORT;
- commandLength = (currentSend3 >> 8) & Send3::COMMAND_LENGTH_MASK;
- send3Read = true;
-
- // The freshly read SEND3 position had a length of 0, so we are done handling SIO2 commands until
- // the next SEND3 writes.
- if (commandLength == 0)
- {
- send3Complete = true;
- }
-
- // If the prior command did not need to fully pop fifoIn, do so now,
- // so that the next command isn't trying to read the last command's leftovers.
- while (!fifoIn.empty())
- {
- fifoIn.pop_front();
- }
- }
-
- if (send3Complete)
- {
- return;
- }
-
- fifoIn.push_back(data);
-
- // We have received as many command bytes as we expect, and...
- //
- // ... These were from direct writes into IOP memory (DMA block size is zero when direct writes occur)
- // ... These were from SIO2 DMA (DMA block size is non-zero when SIO2 DMA occurs)
- if ((fifoIn.size() == sio2.commandLength && sio2.dmaBlockSize == 0) || fifoIn.size() == sio2.dmaBlockSize)
- {
- // Go ahead and prep so the next write triggers a load of the new SEND3 value.
- sio2.send3Read = false;
- sio2.send3Position++;
-
- // Check the SIO mode
- const u8 sioMode = fifoIn.front();
- fifoIn.pop_front();
-
- switch (sioMode)
- {
- case SioMode::PAD:
- this->Pad();
- break;
- case SioMode::MULTITAP:
- this->Multitap();
- break;
- case SioMode::INFRARED:
- this->Infrared();
- break;
- case SioMode::MEMCARD:
- this->Memcard();
- break;
- default:
- Console.Error("%s(%02X) Unhandled SIO mode %02X", __FUNCTION__, data, sioMode);
- fifoOut.push_back(0x00);
- SetRecv1(Recv1::DISCONNECTED);
- break;
- }
-
- // If command was sent over SIO2 DMA, align fifoOut to the block size
- if (sio2.dmaBlockSize > 0)
- {
- const size_t dmaDiff = fifoOut.size() % sio2.dmaBlockSize;
-
- if (dmaDiff > 0)
- {
- const size_t padding = sio2.dmaBlockSize - dmaDiff;
-
- for (size_t i = 0; i < padding; i++)
- {
- fifoOut.push_back(0x00);
- }
- }
- }
- }
-}
-
-u8 Sio2::Read()
-{
- u8 ret = 0x00;
-
- if (!fifoOut.empty())
- {
- ret = fifoOut.front();
- fifoOut.pop_front();
- }
- else
- {
- Console.Warning("%s() fifoOut underflow! Returning 0x00.", __FUNCTION__);
- }
-
- Sio2Log.WriteLn("%s() SIO2 DATA Read (%02X)", __FUNCTION__, ret);
- return ret;
-}
-
-void sioNextFrame() {
- for ( uint port = 0; port < 2; ++port ) {
- for ( uint slot = 0; slot < 4; ++slot ) {
- mcds[port][slot].NextFrame();
- }
- }
-}
-
-void sioSetGameSerial( const std::string& serial ) {
- for ( uint port = 0; port < 2; ++port ) {
- for ( uint slot = 0; slot < 4; ++slot ) {
- if ( int index = mcds[port][slot].ReIndex( serial ) >= 0 ) {
- Console.WriteLn("Ejecting Memory Card %u (port %u slot %u) due to source change. Reinsert in 1 second.", index, port, slot);
- AutoEject::Set( port, slot );
- }
- }
- }
-}
-
-bool SaveStateBase::sio2Freeze()
-{
- if (!FreezeTag("sio2"))
- return false;
-
- Freeze(sio2);
- FreezeDeque(fifoIn);
- FreezeDeque(fifoOut);
-
- if (!IsOkay())
- return false;
-
- // CRCs for memory cards.
- // If the memory card hasn't changed when loading state, we can safely skip ejecting it.
- u64 mcdCrcs[SIO::PORTS][SIO::SLOTS];
- if (IsSaving())
- {
- for (u32 port = 0; port < SIO::PORTS; port++)
- {
- for (u32 slot = 0; slot < SIO::SLOTS; slot++)
- mcdCrcs[port][slot] = mcds[port][slot].GetChecksum();
- }
- }
- Freeze(mcdCrcs);
- if (!IsOkay())
- return false;
-
- if (IsLoading())
- {
- bool ejected = false;
- for (u32 port = 0; port < SIO::PORTS && !ejected; port++)
- {
- for (u32 slot = 0; slot < SIO::SLOTS; slot++)
- {
- if (mcdCrcs[port][slot] != mcds[port][slot].GetChecksum())
- {
- AutoEject::SetAll();
- ejected = true;
- break;
- }
- }
- }
- }
-
- return true;
-}
-
-bool SaveStateBase::sioFreeze()
-{
- if (!FreezeTag("sio0"))
- return false;
-
- Freeze(sio0);
- return IsOkay();
-}
-
-std::tuple 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);
-}
-
-void AutoEject::CountDownTicks()
-{
- bool reinserted = false;
- for (size_t port = 0; port < SIO::PORTS; port++)
- {
- for (size_t slot = 0; slot < SIO::SLOTS; slot++)
- {
- if (mcds[port][slot].autoEjectTicks > 0)
- {
- if (--mcds[port][slot].autoEjectTicks == 0)
- reinserted |= EmuConfig.Mcd[sioConvertPortAndSlotToPad(port, slot)].Enabled;
- }
- }
- }
-
- if (reinserted)
- {
- Host::AddIconOSDMessage("AutoEjectAllSet", ICON_FA_SD_CARD,
- TRANSLATE_SV("MemoryCard", "Memory Cards reinserted."), Host::OSD_INFO_DURATION);
- }
-}
-
-void AutoEject::Set(size_t port, size_t slot)
-{
- if (EmuConfig.McdEnableEjection && mcds[port][slot].autoEjectTicks == 0)
- {
- mcds[port][slot].autoEjectTicks = 1; // 1 second is enough.
- mcds[port][slot].term = 0x55; // Reset terminator to default (0x55), forces the PS2 to recheck the memcard.
- }
-}
-
-void AutoEject::Clear(size_t port, size_t slot)
-{
- mcds[port][slot].autoEjectTicks = 0;
-}
-
-void AutoEject::SetAll()
-{
- Host::AddIconOSDMessage("AutoEjectAllSet", ICON_FA_SD_CARD,
- TRANSLATE_SV("MemoryCard", "Force ejecting all Memory Cards. Reinserting in 1 second."), Host::OSD_INFO_DURATION);
-
- for (size_t port = 0; port < SIO::PORTS; port++)
- {
- for (size_t slot = 0; slot < SIO::SLOTS; slot++)
- {
- AutoEject::Set(port, slot);
- }
- }
-}
-
-void AutoEject::ClearAll()
-{
- for (size_t port = 0; port < SIO::PORTS; port++)
- {
- for (size_t slot = 0; slot < SIO::SLOTS; slot++)
- {
- AutoEject::Clear(port, slot);
- }
- }
-}
\ No newline at end of file
diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp
index 17fe3f9e23..a5589d5a3a 100644
--- a/pcsx2/VMManager.cpp
+++ b/pcsx2/VMManager.cpp
@@ -37,8 +37,6 @@
#include "LogSink.h"
#include "MTGS.h"
#include "MTVU.h"
-#include "MemoryCardFile.h"
-#include "PAD/Host/PAD.h"
#include "PCSX2Base.h"
#include "PINE.h"
#include "Patch.h"
@@ -47,7 +45,6 @@
#include "Recording/InputRecording.h"
#include "Recording/InputRecordingControls.h"
#include "SPU2/spu2.h"
-#include "Sio.h"
#include "USB/USB.h"
#include "VMManager.h"
#include "ps2/BiosTools.h"
@@ -61,6 +58,13 @@
#include "common/Threading.h"
#include "common/Timer.h"
#include "common/emitter/tools.h"
+#include "SIO/Sio.h"
+#include "SIO/Sio0.h"
+#include "SIO/Sio2.h"
+#include "SIO/Pad/PadManager.h"
+#include "SIO/Pad/PadConfig.h"
+#include "SIO/Memcard/MemoryCardFile.h"
+
#include "IconsFontAwesome5.h"
#include "fmt/core.h"
@@ -447,12 +451,10 @@ void VMManager::SetDefaultSettings(
LogSink::SetDefaultLoggingSettings(si);
}
if (controllers)
- {
- PAD::SetDefaultControllerConfig(si);
+ g_PadConfig.SetDefaultControllerConfig(si);
USB::SetDefaultConfiguration(&si);
- }
if (hotkeys)
- PAD::SetDefaultHotkeyConfig(si);
+ g_PadConfig.SetDefaultHotkeyConfig(si);
if (ui)
Host::SetDefaultUISettings(si);
}
@@ -462,7 +464,7 @@ void VMManager::LoadSettings()
std::unique_lock lock = Host::GetSettingsLock();
SettingsInterface* si = Host::GetSettingsInterface();
LoadCoreSettings(si);
- PAD::LoadConfig(*si);
+ g_PadConfig.LoadConfig(*si);
Host::LoadSettings(*si, lock);
InputManager::ReloadSources(*si, lock);
InputManager::ReloadBindings(*si, *Host::GetSettingsInterfaceForBindings());
@@ -1205,15 +1207,35 @@ bool VMManager::Initialize(VMBootParameters boot_params)
}
ScopedGuard close_spu2(&SPU2::Close);
- Console.WriteLn("Opening PAD...");
- if (PADinit() != 0 || PADopen() != 0)
+
+ Console.WriteLn("Initializing PAD...");
+ if (!g_PadManager.Initialize())
{
- Host::ReportErrorAsync("Startup Error", "Failed to initialize PAD.");
+ Host::ReportErrorAsync("Startup Error", "Failed to initialize PAD");
return false;
}
- ScopedGuard close_pad = []() {
- PADclose();
- PADshutdown();
+ ScopedGuard close_pad = [](){
+ g_PadManager.Shutdown();
+ };
+
+ Console.WriteLn("Initializing SIO2...");
+ if (!g_Sio2.Initialize())
+ {
+ Host::ReportErrorAsync("Startup Error", "Failed to initialize SIO2");
+ return false;
+ }
+ ScopedGuard close_sio2 = []() {
+ g_Sio2.Shutdown();
+ };
+
+ Console.WriteLn("Initializing SIO0...");
+ if (!g_Sio0.Initialize())
+ {
+ Host::ReportErrorAsync("Startup Error", "Failed to initialize SIO0");
+ return false;
+ }
+ ScopedGuard close_sio0 = []() {
+ g_Sio0.Shutdown();
};
Console.WriteLn("Opening DEV9...");
@@ -1248,6 +1270,8 @@ bool VMManager::Initialize(VMBootParameters boot_params)
close_usb.Cancel();
close_dev9.Cancel();
close_pad.Cancel();
+ close_sio2.Cancel();
+ close_sio0.Cancel();
close_spu2.Cancel();
close_gs.Cancel();
close_memcards.Cancel();
@@ -1344,7 +1368,9 @@ void VMManager::Shutdown(bool save_resume_state)
vtlb_Shutdown();
USBclose();
SPU2::Close();
- PADclose();
+ g_PadManager.Shutdown();
+ g_Sio2.Shutdown();
+ g_Sio0.Shutdown();
DEV9close();
DoCDVDclose();
FWclose();
@@ -1362,7 +1388,6 @@ void VMManager::Shutdown(bool save_resume_state)
MTGS::WaitForClose();
}
- PADshutdown();
DEV9shutdown();
if (GSDumpReplayer::IsReplayingDump())
diff --git a/pcsx2/pcsx2.vcxproj b/pcsx2/pcsx2.vcxproj
index 989728c95f..df8ec161bc 100644
--- a/pcsx2/pcsx2.vcxproj
+++ b/pcsx2/pcsx2.vcxproj
@@ -230,21 +230,28 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -432,7 +439,6 @@
-
@@ -580,16 +586,8 @@
-
-
-
-
-
-
-
-
@@ -597,7 +595,24 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -761,7 +776,6 @@
-
diff --git a/pcsx2/pcsx2.vcxproj.filters b/pcsx2/pcsx2.vcxproj.filters
index 99aaa5b53a..4edf71e27b 100644
--- a/pcsx2/pcsx2.vcxproj.filters
+++ b/pcsx2/pcsx2.vcxproj.filters
@@ -172,9 +172,6 @@
{df9de75c-2272-4f73-b2a0-4f9f492ba1e9}
-
- {e1cbcaf6-9f65-4f14-9e89-27dd0f10e047}
-
{38f3b97b-eac6-4085-b6b0-567ec5081242}
@@ -268,6 +265,18 @@
{8a23ff29-0a43-4d5b-8c6c-a7722871058a}
+
+ {2fa4b100-51e1-4315-85b2-402a13136f8a}
+
+
+ {9cd399c0-6c75-4872-a785-83bcebd4bd0b}
+
+
+ {d37c74a9-5267-4eed-8356-86720df5beed}
+
+
+ {2ef3ebf7-d2a3-4b33-8059-a09b82c085ed}
+
@@ -614,9 +623,6 @@
System\Ps2\Iop
-
- System\Ps2\Iop
-
System\Ps2\Iop\Dynarec
@@ -1130,12 +1136,6 @@
System\Ps2\GS\Renderers\Direct3D11
-
- System\Ps2\Iop
-
-
- System\Ps2\Iop
-
System\Ps2\SPU2
@@ -1148,15 +1148,6 @@
System
-
- System\Ps2\PAD
-
-
- System\Ps2\PAD
-
-
- System\Ps2\PAD
-
System\Ps2\GS\Renderers\Hardware
@@ -1187,12 +1178,6 @@
Tools\Input Recording\Utilities
-
- System\Ps2\Iop
-
-
- System\Ps2\Iop
-
System\Ps2\USB
@@ -1412,6 +1397,48 @@
System\ISO
+
+ System\Ps2\Iop\SIO\Multitap
+
+
+ System\Ps2\Iop\SIO\Memcard
+
+
+ System\Ps2\Iop\SIO\Memcard
+
+
+ System\Ps2\Iop\SIO\Memcard
+
+
+ System\Ps2\Iop\SIO\PAD
+
+
+ System\Ps2\Iop\SIO\PAD
+
+
+ System\Ps2\Iop\SIO\PAD
+
+
+ System\Ps2\Iop\SIO\PAD
+
+
+ System\Ps2\Iop\SIO\PAD
+
+
+ System\Ps2\Iop\SIO
+
+
+ System\Ps2\Iop\SIO
+
+
+ System\Ps2\Iop\SIO
+
+
+ System\Ps2\Iop\SIO\PAD
+
+
+ System\Ps2\Iop\SIO\PAD
+
@@ -1582,9 +1609,6 @@
System\Ps2\Iop
-
- System\Ps2\Iop
-
System\Ps2\Iop\Dynarec
@@ -2053,12 +2077,6 @@
System\Ps2\GS\Renderers\Direct3D11
-
- System\Ps2\Iop
-
-
- System\Ps2\Iop
-
System\Include
@@ -2072,18 +2090,6 @@
System
-
- System\Ps2\PAD
-
-
- System\Ps2\PAD
-
-
- System\Ps2\PAD
-
-
- System\Ps2\PAD
-
System\Ps2\GS\Renderers\Hardware
@@ -2114,15 +2120,6 @@
System\Include
-
- System\Ps2\Iop
-
-
- System\Ps2\Iop
-
-
- System\Ps2\Iop
-
System\Ps2\USB
@@ -2336,6 +2333,60 @@
System\ISO
+
+ System\Ps2\Iop\SIO\Multitap
+
+
+ System\Ps2\Iop\SIO\Memcard
+
+
+ System\Ps2\Iop\SIO\Memcard
+
+
+ System\Ps2\Iop\SIO\Memcard
+
+
+ System\Ps2\Iop\SIO\PAD
+
+
+ System\Ps2\Iop\SIO\PAD
+
+
+ System\Ps2\Iop\SIO\PAD
+
+
+ System\Ps2\Iop\SIO\PAD
+
+
+ System\Ps2\Iop\SIO\PAD
+
+
+ System\Ps2\Iop\SIO\PAD
+
+
+ System\Ps2\Iop\SIO\PAD
+
+
+ System\Ps2\Iop\SIO\PAD
+
+
+ System\Ps2\Iop\SIO
+
+
+ System\Ps2\Iop\SIO
+
+
+ System\Ps2\Iop\SIO
+
+
+ System\Ps2\Iop\SIO
+
+
+ System\Ps2\Iop\SIO\PAD
+
+
+ System\Ps2\Iop\SIO\PAD
+
diff --git a/pcsx2/ps2/Iop/IopHwRead.cpp b/pcsx2/ps2/Iop/IopHwRead.cpp
index 483f2f36d7..3a1d0efb1a 100644
--- a/pcsx2/ps2/Iop/IopHwRead.cpp
+++ b/pcsx2/ps2/Iop/IopHwRead.cpp
@@ -17,7 +17,8 @@
#include "PrecompiledHeader.h"
#include "IopHw_Internal.h"
#include "Sif.h"
-#include "Sio.h"
+#include "SIO/Sio2.h"
+#include "SIO/Sio0.h"
#include "CDVD/Ps1CD.h"
#include "FW.h"
#include "SPU2/spu2.h"
@@ -52,7 +53,7 @@ mem8_t iopHwRead8_Page1( u32 addr )
switch( masked_addr )
{
case (HW_SIO_DATA & 0x0fff):
- ret = sio0.GetRxData();
+ ret = g_Sio0.GetRxData();
break;
case (HW_SIO_STAT & 0x0fff):
Sio0Log.Error("%s(%08X) Unexpected SIO0 STAT 8 bit read", __FUNCTION__, addr);
@@ -134,7 +135,7 @@ mem8_t iopHwRead8_Page8( u32 addr )
if (addr == HW_SIO2_FIFO)
{
- ret = sio2.Read();
+ ret = g_Sio2.Read();
}
else
{
@@ -275,19 +276,19 @@ static __fi T _HwRead_16or32_Page1( u32 addr )
// ------------------------------------------------------------------------
case (HW_SIO_DATA & 0x0fff):
Console.Warning("%s(%08X) Unexpected 16 or 32 bit access to SIO0 data register!", __FUNCTION__, addr);
- ret = sio0.GetRxData();
- ret |= sio0.GetRxData() << 8;
+ ret = g_Sio0.GetRxData();
+ ret |= g_Sio0.GetRxData() << 8;
if (sizeof(T) == 4)
{
- ret |= sio0.GetRxData() << 16;
- ret |= sio0.GetRxData() << 24;
+ ret |= g_Sio0.GetRxData() << 16;
+ ret |= g_Sio0.GetRxData() << 24;
}
break;
case (HW_SIO_STAT & 0x0fff):
- ret = sio0.GetStat();
+ ret = g_Sio0.GetStat();
break;
case (HW_SIO_MODE & 0x0fff):
- ret = sio0.GetMode();
+ ret = g_Sio0.GetMode();
if (sizeof(T) == 4)
{
@@ -296,10 +297,10 @@ static __fi T _HwRead_16or32_Page1( u32 addr )
break;
case (HW_SIO_CTRL & 0x0fff):
- ret = sio0.GetCtrl();
+ ret = g_Sio0.GetCtrl();
break;
case (HW_SIO_BAUD & 0x0fff):
- ret = sio0.GetBaud();
+ ret = g_Sio0.GetBaud();
break;
// ------------------------------------------------------------------------
@@ -435,7 +436,7 @@ mem32_t iopHwRead32_Page8( u32 addr )
if( masked_addr < 0x240 )
{
const int parm = (masked_addr-0x200) / 4;
- ret = sio2.send3.at(parm);
+ ret = g_Sio2.send3.at(parm);
Sio2Log.WriteLn("%s(%08X) SIO2 SEND3 Read (%08X)", __FUNCTION__, addr, ret);
}
else if( masked_addr < 0x260 )
@@ -443,7 +444,7 @@ mem32_t iopHwRead32_Page8( u32 addr )
// SIO2 Send commands alternate registers. First reg maps to Send1, second
// to Send2, third to Send1, etc. And the following clever code does this:
const int parm = (masked_addr-0x240) / 8;
- ret = (masked_addr & 4) ? sio2.send2.at(parm) : sio2.send1.at(parm);
+ ret = (masked_addr & 4) ? g_Sio2.send2.at(parm) : g_Sio2.send1.at(parm);
Sio2Log.WriteLn("%s(%08X) SIO2 SEND1/2 Read (%08X)", __FUNCTION__, addr, ret);
}
else if( masked_addr <= 0x280 )
@@ -459,31 +460,31 @@ mem32_t iopHwRead32_Page8( u32 addr )
Sio2Log.Warning("%s(%08X) Unexpected 32 bit read of HW_SIO2_FIFO (%08X)", __FUNCTION__, addr, ret);
break;
case (HW_SIO2_CTRL & 0x0fff):
- ret = sio2.ctrl;
+ ret = g_Sio2.ctrl;
Sio2Log.WriteLn("%s(%08X) SIO2 CTRL Read (%08X)", __FUNCTION__, addr, ret);
break;
case (HW_SIO2_RECV1 & 0xfff):
- ret = sio2.recv1;
+ ret = g_Sio2.recv1;
Sio2Log.WriteLn("%s(%08X) SIO2 RECV1 Read (%08X)", __FUNCTION__, addr, ret);
break;
case (HW_SIO2_RECV2 & 0x0fff):
- ret = sio2.recv2;
+ ret = g_Sio2.recv2;
Sio2Log.WriteLn("%s(%08X) SIO2 RECV2 Read (%08X)", __FUNCTION__, addr, ret);
break;
case (HW_SIO2_RECV3 & 0x0fff):
- ret = sio2.recv3;
+ ret = g_Sio2.recv3;
Sio2Log.WriteLn("%s(%08X) SIO2 RECV3 Read (%08X)", __FUNCTION__, addr, ret);
break;
case (0x1f808278 & 0x0fff):
- ret = sio2.unknown1;
+ ret = g_Sio2.unknown1;
Sio2Log.WriteLn("%s(%08X) SIO2 UNK1 Read (%08X)", __FUNCTION__, addr, ret);
break;
case (0x1f80827C & 0x0fff):
- ret = sio2.unknown2;
+ ret = g_Sio2.unknown2;
Sio2Log.WriteLn("%s(%08X) SIO2 UNK2 Read (%08X)", __FUNCTION__, addr, ret);
break;
case (HW_SIO2_INTR & 0x0fff):
- ret = sio2.iStat;
+ ret = g_Sio2.iStat;
Sio2Log.WriteLn("%s(%08X) SIO2 ISTAT Read (%08X)", __FUNCTION__, addr, ret);
break;
default:
diff --git a/pcsx2/ps2/Iop/IopHwWrite.cpp b/pcsx2/ps2/Iop/IopHwWrite.cpp
index 86ed6f03ef..92b2961f51 100644
--- a/pcsx2/ps2/Iop/IopHwWrite.cpp
+++ b/pcsx2/ps2/Iop/IopHwWrite.cpp
@@ -16,7 +16,8 @@
#include "PrecompiledHeader.h"
#include "IopHw_Internal.h"
#include "Sif.h"
-#include "Sio.h"
+#include "SIO/Sio2.h"
+#include "SIO/Sio0.h"
#include "FW.h"
#include "CDVD/Ps1CD.h"
#include "SPU2/spu2.h"
@@ -86,7 +87,7 @@ void iopHwWrite8_Page1( u32 addr, mem8_t val )
switch( masked_addr )
{
case (HW_SIO_DATA & 0x0fff):
- sio0.SetTxData(val);
+ g_Sio0.SetTxData(val);
break;
case (HW_SIO_STAT & 0x0fff):
Sio0Log.Error("%s(%08X, %08X) Unexpected SIO0 STAT 8 bit write", __FUNCTION__, addr, val);
@@ -176,7 +177,7 @@ void iopHwWrite8_Page8( u32 addr, mem8_t val )
if (addr == HW_SIO2_DATAIN)
{
- sio2.Write(val);
+ g_Sio2.Write(val);
}
else
{
@@ -301,22 +302,12 @@ static __fi void _HwWrite_16or32_Page1( u32 addr, T val )
// ------------------------------------------------------------------------
case (HW_SIO_DATA & 0x0fff):
Console.Error("%s(%08X, %08X) Unexpected 16 or 32 bit write to SIO0 DATA!", __FUNCTION__, addr, val);
-/*
- sio0.SetTxData(val & 0xFF);
- sio0.SetTxData((val >> 8) & 0xFF);
-
- if (sizeof(T) == 4)
- {
- sio0.SetTxData((static_cast(val) >> 16) & 0xFF);
- sio0.SetTxData((static_cast(val) >> 24) & 0xFF);
- }
-*/
break;
case (HW_SIO_STAT & 0x0fff):
Console.Error("%s(%08X, %08X) Write issued to read-only SIO0 STAT!", __FUNCTION__, addr, val);
break;
case (HW_SIO_MODE & 0x0fff):
- sio0.SetMode(static_cast(val));
+ g_Sio0.SetMode(static_cast(val));
if (sizeof(T) == 4)
{
@@ -326,11 +317,11 @@ static __fi void _HwWrite_16or32_Page1( u32 addr, T val )
break;
case (HW_SIO_CTRL & 0x0fff):
- sio0.SetCtrl(static_cast(val));
+ g_Sio0.SetCtrl(static_cast(val));
break;
case (HW_SIO_BAUD & 0x0fff):
- sio0.SetBaud(static_cast(val));
+ g_Sio0.SetBaud(static_cast(val));
break;
// ------------------------------------------------------------------------
@@ -612,7 +603,7 @@ void iopHwWrite32_Page8( u32 addr, mem32_t val )
{
Sio2Log.WriteLn("%s(%08X, %08X) SIO2 SEND3 Write (len = %d / %d) (port = %d)", __FUNCTION__, addr, val, (val >> 8) & Send3::COMMAND_LENGTH_MASK, (val >> 18) & Send3::COMMAND_LENGTH_MASK, val & 0x01);
const int parm = (masked_addr - 0x200) / 4;
- sio2.SetSend3(parm, val);
+ g_Sio2.SetSend3(parm, val);
}
else if( masked_addr < 0x260 )
{
@@ -624,12 +615,12 @@ void iopHwWrite32_Page8( u32 addr, mem32_t val )
if (masked_addr & 4)
{
Sio2Log.WriteLn("%s(%08X, %08X) SIO2 SEND2 Write", __FUNCTION__, addr, val);
- sio2.send2.at(parm) = val;
+ g_Sio2.send2.at(parm) = val;
}
else
{
Sio2Log.WriteLn("%s(%08X, %08X) SIO2 SEND1 Write", __FUNCTION__, addr, val);
- sio2.send1.at(parm) = val;
+ g_Sio2.send1.at(parm) = val;
}
}
else if( masked_addr <= 0x280 )
@@ -644,31 +635,31 @@ void iopHwWrite32_Page8( u32 addr, mem32_t val )
break;
case (HW_SIO2_CTRL & 0x0fff):
Sio2Log.WriteLn("%s(%08X, %08X) SIO2 CTRL Write", __FUNCTION__, addr, val);
- sio2.SetCtrl(val);
+ g_Sio2.SetCtrl(val);
break;
case (HW_SIO2_RECV1 & 0x0fff):
Sio2Log.WriteLn("%s(%08X, %08X) SIO2 RECV1 Write", __FUNCTION__, addr, val);
- sio2.recv1 = val;
+ g_Sio2.recv1 = val;
break;
case (HW_SIO2_RECV2 & 0x0fff):
Sio2Log.WriteLn("%s(%08X, %08X) SIO2 RECV2 Write", __FUNCTION__, addr, val);
- sio2.recv2 = val;
+ g_Sio2.recv2 = val;
break;
case (HW_SIO2_RECV3 & 0x0fff):
Sio2Log.WriteLn("%s(%08X, %08X) SIO2 RECV3 Write", __FUNCTION__, addr, val);
- sio2.recv3 = val;
+ g_Sio2.recv3 = val;
break;
case (HW_SIO2_8278 & 0x0fff):
Sio2Log.WriteLn("%s(%08X, %08X) SIO2 UNK1 Write", __FUNCTION__, addr, val);
- sio2.unknown1 = val;
+ g_Sio2.unknown1 = val;
break;
case (HW_SIO2_827C & 0x0fff):
Sio2Log.WriteLn("%s(%08X, %08X) SIO2 UNK2 Write", __FUNCTION__, addr, val);
- sio2.unknown2 = val;
+ g_Sio2.unknown2 = val;
break;
case (HW_SIO2_INTR & 0x0fff):
Sio2Log.WriteLn("%s(%08X, %08X) SIO2 ISTAT Write", __FUNCTION__, addr, val);
- sio2.iStat = val;
+ g_Sio2.iStat = val;
break;
// Other SIO2 registers are read-only, no-ops on write.
default: