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