From 1f74f82796ce8304a17accafcb067fea7d8106b8 Mon Sep 17 00:00:00 2001 From: RedPanda4552 Date: Sun, 11 Jun 2023 02:06:40 -0400 Subject: [PATCH] [SAVEVERSION+] Reimplement PAD system Force pushed because git hates me [SAVEVERSION+] Bump savestate version CI: Update locations of pad/memcard sources Discard leftover old PAD code Fix additional merge oddities Add translations for OSD messages Copyright headers Version bump Fix a whole boatload of concurrency problems from file moves and other miscellaneous update problems Partial redo of PS1 pad support Fix incorrect mode due to analog behavior at startup Mostly reimplement SIO0 memory card logic Still needs pocketstation Use new runtime wrapped translate function Dead code Fix multiple port/slot/presence issues for PS1 Save State version bump Clean up some duplicate/unused headers More header consistency Remove old stray files Fix incorrect return Fix uninitialized array Add missing overrides Switch to init/close model used by other subsystems Remove old input recording references Rename SIO globals Rename SIO2 FIFO globals Remove commented SIO0 code for illegal write Add guitar icon --- .github/labeler.yml | 8 +- pcsx2-qt/CMakeLists.txt | 1 + pcsx2-qt/QtHost.cpp | 1 - .../ControllerBindingWidget_Guitar.ui | 317 ++++++ .../Settings/ControllerBindingWidgets.cpp | 59 +- pcsx2-qt/Settings/ControllerBindingWidgets.h | 21 +- .../Settings/ControllerSettingsDialog.cpp | 16 +- pcsx2-qt/Settings/GameSummaryWidget.cpp | 4 +- pcsx2-qt/Settings/MemoryCardConvertDialog.h | 2 +- pcsx2-qt/Settings/MemoryCardConvertWorker.h | 4 +- pcsx2-qt/Settings/MemoryCardCreateDialog.cpp | 2 +- .../Settings/MemoryCardSettingsWidget.cpp | 2 +- pcsx2-qt/SetupWizardDialog.cpp | 8 +- pcsx2-qt/pcsx2-qt.vcxproj | 3 + pcsx2-qt/pcsx2-qt.vcxproj.filters | 3 + .../resources/icons/black/svg/guitar-line.svg | 1 + .../resources/icons/white/svg/guitar-line.svg | 1 + pcsx2-qt/resources/images/guitar.png | Bin 0 -> 34988 bytes pcsx2-qt/resources/resources.qrc | 293 +++--- pcsx2/CDVD/CDVD.cpp | 2 +- pcsx2/CMakeLists.txt | 49 +- pcsx2/Counters.cpp | 4 +- pcsx2/Host.cpp | 2 - pcsx2/ImGui/FullscreenUI.cpp | 38 +- pcsx2/ImGui/ImGuiOverlays.cpp | 122 ++- pcsx2/Input/InputManager.cpp | 23 +- pcsx2/IopDma.cpp | 8 +- pcsx2/IopHw.cpp | 5 +- pcsx2/PAD/Host/Global.h | 59 -- pcsx2/PAD/Host/KeyStatus.cpp | 217 ---- pcsx2/PAD/Host/KeyStatus.h | 101 -- pcsx2/PAD/Host/PAD.cpp | 757 -------------- pcsx2/PAD/Host/PAD.h | 127 --- pcsx2/PAD/Host/StateManagement.cpp | 523 ---------- pcsx2/PAD/Host/StateManagement.h | 132 --- pcsx2/Pcsx2Config.cpp | 2 +- pcsx2/R3000A.cpp | 4 +- pcsx2/Recording/PadData.cpp | 74 +- pcsx2/{ => SIO/Memcard}/MemoryCardFile.cpp | 68 +- pcsx2/{ => SIO/Memcard}/MemoryCardFile.h | 2 +- pcsx2/{ => SIO/Memcard}/MemoryCardFolder.cpp | 12 +- pcsx2/{ => SIO/Memcard}/MemoryCardFolder.h | 2 +- .../{ => SIO/Memcard}/MemoryCardProtocol.cpp | 143 ++- pcsx2/{ => SIO/Memcard}/MemoryCardProtocol.h | 2 +- pcsx2/{ => SIO/Multitap}/MultitapProtocol.cpp | 40 +- pcsx2/{ => SIO/Multitap}/MultitapProtocol.h | 0 pcsx2/SIO/Pad/PadBase.cpp | 36 + pcsx2/SIO/Pad/PadBase.h | 63 ++ pcsx2/SIO/Pad/PadConfig.cpp | 511 +++++++++ pcsx2/SIO/Pad/PadConfig.h | 72 ++ pcsx2/SIO/Pad/PadDualshock2.cpp | 769 ++++++++++++++ pcsx2/SIO/Pad/PadDualshock2.h | 101 ++ pcsx2/SIO/Pad/PadDualshock2Types.h | 133 +++ pcsx2/SIO/Pad/PadGuitar.cpp | 465 +++++++++ pcsx2/SIO/Pad/PadGuitar.h | 71 ++ pcsx2/SIO/Pad/PadGuitarTypes.h | 62 ++ pcsx2/SIO/Pad/PadMacros.cpp | 90 ++ pcsx2/SIO/Pad/PadMacros.h | 53 + pcsx2/SIO/Pad/PadManager.cpp | 139 +++ pcsx2/SIO/Pad/PadManager.h | 46 + pcsx2/SIO/Pad/PadNotConnected.cpp | 132 +++ pcsx2/SIO/Pad/PadNotConnected.h | 48 + pcsx2/SIO/Pad/PadTypes.h | 96 ++ pcsx2/SIO/Sio.cpp | 136 +++ pcsx2/{ => SIO}/Sio.h | 132 +-- pcsx2/SIO/Sio0.cpp | 323 ++++++ pcsx2/SIO/Sio0.h | 76 ++ pcsx2/SIO/Sio2.cpp | 561 ++++++++++ pcsx2/SIO/Sio2.h | 86 ++ pcsx2/{ => SIO}/SioTypes.h | 4 +- pcsx2/SaveState.cpp | 16 +- pcsx2/SaveState.h | 6 +- pcsx2/Sio.cpp | 970 ------------------ pcsx2/VMManager.cpp | 57 +- pcsx2/pcsx2.vcxproj | 50 +- pcsx2/pcsx2.vcxproj.filters | 165 ++- pcsx2/ps2/Iop/IopHwRead.cpp | 41 +- pcsx2/ps2/Iop/IopHwWrite.cpp | 43 +- 78 files changed, 5186 insertions(+), 3631 deletions(-) create mode 100644 pcsx2-qt/Settings/ControllerBindingWidget_Guitar.ui create mode 100644 pcsx2-qt/resources/icons/black/svg/guitar-line.svg create mode 100644 pcsx2-qt/resources/icons/white/svg/guitar-line.svg create mode 100644 pcsx2-qt/resources/images/guitar.png delete mode 100644 pcsx2/PAD/Host/Global.h delete mode 100644 pcsx2/PAD/Host/KeyStatus.cpp delete mode 100644 pcsx2/PAD/Host/KeyStatus.h delete mode 100644 pcsx2/PAD/Host/PAD.cpp delete mode 100644 pcsx2/PAD/Host/PAD.h delete mode 100644 pcsx2/PAD/Host/StateManagement.cpp delete mode 100644 pcsx2/PAD/Host/StateManagement.h rename pcsx2/{ => SIO/Memcard}/MemoryCardFile.cpp (92%) rename pcsx2/{ => SIO/Memcard}/MemoryCardFile.h (97%) rename pcsx2/{ => SIO/Memcard}/MemoryCardFolder.cpp (99%) rename pcsx2/{ => SIO/Memcard}/MemoryCardFolder.h (99%) rename pcsx2/{ => SIO/Memcard}/MemoryCardProtocol.cpp (78%) rename pcsx2/{ => SIO/Memcard}/MemoryCardProtocol.h (98%) rename pcsx2/{ => SIO/Multitap}/MultitapProtocol.cpp (70%) rename pcsx2/{ => SIO/Multitap}/MultitapProtocol.h (100%) create mode 100644 pcsx2/SIO/Pad/PadBase.cpp create mode 100644 pcsx2/SIO/Pad/PadBase.h create mode 100644 pcsx2/SIO/Pad/PadConfig.cpp create mode 100644 pcsx2/SIO/Pad/PadConfig.h create mode 100644 pcsx2/SIO/Pad/PadDualshock2.cpp create mode 100644 pcsx2/SIO/Pad/PadDualshock2.h create mode 100644 pcsx2/SIO/Pad/PadDualshock2Types.h create mode 100644 pcsx2/SIO/Pad/PadGuitar.cpp create mode 100644 pcsx2/SIO/Pad/PadGuitar.h create mode 100644 pcsx2/SIO/Pad/PadGuitarTypes.h create mode 100644 pcsx2/SIO/Pad/PadMacros.cpp create mode 100644 pcsx2/SIO/Pad/PadMacros.h create mode 100644 pcsx2/SIO/Pad/PadManager.cpp create mode 100644 pcsx2/SIO/Pad/PadManager.h create mode 100644 pcsx2/SIO/Pad/PadNotConnected.cpp create mode 100644 pcsx2/SIO/Pad/PadNotConnected.h create mode 100644 pcsx2/SIO/Pad/PadTypes.h create mode 100644 pcsx2/SIO/Sio.cpp rename pcsx2/{ => SIO}/Sio.h (50%) create mode 100644 pcsx2/SIO/Sio0.cpp create mode 100644 pcsx2/SIO/Sio0.h create mode 100644 pcsx2/SIO/Sio2.cpp create mode 100644 pcsx2/SIO/Sio2.h rename pcsx2/{ => SIO}/SioTypes.h (98%) delete mode 100644 pcsx2/Sio.cpp 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 0000000000000000000000000000000000000000..708cd1c22b9e61e0c9fbe9c28353f49057320ce8 GIT binary patch literal 34988 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 r>FRm;R1 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 B2rK{q literal 0 HcmV?d00001 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: