Pad: Fixes and cleanup (#9623)

* Pad: Get rid of redundant object lookup

* Pad: Serialize 'stage' state

[SAVEVERSION+]

* Pad: Reduce Freeze duplication and add markers

Catch any possible unhandled device type change.

[SAVEVERSION+]

* Pad: Localize controller type names

* Pad: Handle mismatch between state and current config

* Misc: Drop .at() from vector/array access

We don't use exceptions.

* Pad: Remove redundant GetUnifiedSlot()

Sio has routines for converting these.

* Sio2: Remove redundant optional in Pad()

* Pad: Constify and finalize controller classes

* Pad: Move PadManager to Pad namespace

No point having a class when there's only a single instance.

* Pad: Move PadConfig to Pad namespace

* Pad: Move PadMacros to Pad namespace

* Pad: Re-localize controller bindings/settings

* Pad: Make controller info local

Don't want to create duplicates.

* Pad: Use span for ControllerInfo settings/bindings

* Pad: Fix auto-toggled macro buttons

* Pad: Fix pressure for macros

* Pad: Merge PadConfig/PadManager/PadMacros

Faster compile time.

* Pad: Fix incorrect condition in Sio0::SetTxData()

* Pad: Add deadzone for macro triggers
This commit is contained in:
Connor McLaughlin 2023-07-26 01:10:02 +10:00 committed by GitHub
parent d4cb35469d
commit ab5c03b1d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 1010 additions and 1152 deletions

View File

@ -26,7 +26,7 @@
#include "common/StringUtil.h"
#include "pcsx2/Host.h"
#include "pcsx2/SIO/Pad/PadConfig.h"
#include "pcsx2/SIO/Pad/Pad.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", g_PadConfig.GetDefaultPadType(port));
m_dialog->getProfileSettingsInterface(), m_ui.controllerType, m_config_section, "Type", Pad::GetDefaultPadType(port));
connect(m_ui.controllerType, QOverload<int>::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] : g_PadConfig.GetControllerTypeNames())
m_ui.controllerType->addItem(qApp->translate("Pad", display_name), QString::fromStdString(name));
for (const auto& [name, display_name] : Pad::GetControllerTypeNames())
m_ui.controllerType->addItem(QString::fromUtf8(display_name), QString::fromUtf8(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", g_PadConfig.GetDefaultPadType(m_port_number));
m_controller_type = m_dialog->getStringValue(m_config_section.c_str(), "Type", Pad::GetDefaultPadType(m_port_number));
if (m_bindings_widget)
{
@ -99,9 +99,9 @@ void ControllerBindingWidget::onTypeChanged()
m_macros_widget = nullptr;
}
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);
const Pad::ControllerInfo* cinfo = Pad::GetControllerInfo(m_controller_type);
const bool has_settings = (cinfo && !cinfo->settings.empty());
const bool has_macros = (cinfo && !cinfo->bindings.empty());
m_ui.settings->setEnabled(has_settings);
m_ui.macros->setEnabled(has_macros);
@ -123,9 +123,8 @@ void ControllerBindingWidget::onTypeChanged()
if (has_settings)
{
const std::span<const SettingInfo> settings(cinfo->settings, cinfo->num_settings);
m_settings_widget = new ControllerCustomSettingsWidget(
settings, m_config_section, std::string(), "Pad", getDialog(), m_ui.stackedWidget);
cinfo->settings, m_config_section, std::string(), "Pad", getDialog(), m_ui.stackedWidget);
m_ui.stackedWidget->addWidget(m_settings_widget);
}
@ -218,13 +217,13 @@ void ControllerBindingWidget::onClearBindingsClicked()
{
{
auto lock = Host::GetSettingsLock();
g_PadConfig.ClearPortBindings(*Host::Internal::GetBaseSettingsLayer(), m_port_number);
Pad::ClearPortBindings(*Host::Internal::GetBaseSettingsLayer(), m_port_number);
}
Host::CommitBaseSettingChanges();
}
else
{
g_PadConfig.ClearPortBindings(*m_dialog->getProfileSettingsInterface(), m_port_number);
Pad::ClearPortBindings(*m_dialog->getProfileSettingsInterface(), m_port_number);
m_dialog->getProfileSettingsInterface()->Save();
}
@ -248,14 +247,14 @@ void ControllerBindingWidget::doDeviceAutomaticBinding(const QString& device)
{
{
auto lock = Host::GetSettingsLock();
result = g_PadConfig.MapController(*Host::Internal::GetBaseSettingsLayer(), m_port_number, mapping);
result = Pad::MapController(*Host::Internal::GetBaseSettingsLayer(), m_port_number, mapping);
}
if (result)
Host::CommitBaseSettingChanges();
}
else
{
result = g_PadConfig.MapController(*m_dialog->getProfileSettingsInterface(), m_port_number, mapping);
result = Pad::MapController(*m_dialog->getProfileSettingsInterface(), m_port_number, mapping);
if (result)
{
m_dialog->getProfileSettingsInterface()->Save();
@ -320,7 +319,7 @@ ControllerMacroEditWidget::ControllerMacroEditWidget(ControllerMacroWidget* pare
ControllerSettingsDialog* dialog = m_bwidget->getDialog();
const std::string& section = m_bwidget->getConfigSection();
const PadConfig::ControllerInfo* cinfo = g_PadConfig.GetControllerInfo(m_bwidget->getControllerType());
const Pad::ControllerInfo* cinfo = Pad::GetControllerInfo(m_bwidget->getControllerType());
if (!cinfo)
{
// Shouldn't ever happen.
@ -333,20 +332,19 @@ ControllerMacroEditWidget::ControllerMacroEditWidget(ControllerMacroWidget* pare
for (const std::string_view& button : buttons_split)
{
for (u32 i = 0; i < cinfo->num_bindings; i++)
for (const InputBindingInfo& bi : cinfo->bindings)
{
if (button == cinfo->bindings[i].name)
if (button == bi.name)
{
m_binds.push_back(&cinfo->bindings[i]);
m_binds.push_back(&bi);
break;
}
}
}
// populate list view
for (u32 i = 0; i < cinfo->num_bindings; i++)
for (const InputBindingInfo& bi : cinfo->bindings)
{
const InputBindingInfo& bi = cinfo->bindings[i];
if (bi.bind_type == InputBindingInfo::Type::Motor)
continue;
@ -358,8 +356,12 @@ ControllerMacroEditWidget::ControllerMacroEditWidget(ControllerMacroWidget* pare
ControllerSettingWidgetBinder::BindWidgetToInputProfileNormalized(
dialog->getProfileSettingsInterface(), m_ui.pressure, section, fmt::format("Macro{}Pressure", index + 1u), 100.0f, 1.0f);
ControllerSettingWidgetBinder::BindWidgetToInputProfileNormalized(
dialog->getProfileSettingsInterface(), m_ui.deadzone, section, fmt::format("Macro{}Deadzone", index + 1u), 100.0f, 0.0f);
connect(m_ui.pressure, &QSlider::valueChanged, this, &ControllerMacroEditWidget::onPressureChanged);
connect(m_ui.deadzone, &QSlider::valueChanged, this, &ControllerMacroEditWidget::onDeadzoneChanged);
onPressureChanged();
onDeadzoneChanged();
m_frequency = dialog->getIntValue(section.c_str(), fmt::format("Macro{}Frequency", index + 1u).c_str(), 0);
updateFrequencyText();
@ -392,6 +394,11 @@ void ControllerMacroEditWidget::onPressureChanged()
m_ui.pressureValue->setText(tr("%1%").arg(m_ui.pressure->value()));
}
void ControllerMacroEditWidget::onDeadzoneChanged()
{
m_ui.deadzoneValue->setText(tr("%1%").arg(m_ui.deadzone->value()));
}
void ControllerMacroEditWidget::onSetFrequencyClicked()
{
bool okay;
@ -431,12 +438,12 @@ void ControllerMacroEditWidget::updateFrequencyText()
void ControllerMacroEditWidget::updateBinds()
{
ControllerSettingsDialog* dialog = m_bwidget->getDialog();
const PadConfig::ControllerInfo* cinfo = g_PadConfig.GetControllerInfo(m_bwidget->getControllerType());
const Pad::ControllerInfo* cinfo = Pad::GetControllerInfo(m_bwidget->getControllerType());
if (!cinfo)
return;
std::vector<const InputBindingInfo*> new_binds;
for (u32 i = 0, bind_index = 0; i < cinfo->num_bindings; i++)
for (u32 i = 0, bind_index = 0; i < static_cast<u32>(cinfo->bindings.size()); i++)
{
const InputBindingInfo& bi = cinfo->bindings[i];
if (bi.bind_type == InputBindingInfo::Type::Motor)
@ -801,16 +808,15 @@ QIcon ControllerBindingWidget_Base::getIcon() const
void ControllerBindingWidget_Base::initBindingWidgets()
{
const PadConfig::ControllerInfo* cinfo = g_PadConfig.GetControllerInfo(getControllerType());
const Pad::ControllerInfo* cinfo = Pad::GetControllerInfo(getControllerType());
if (!cinfo)
return;
const std::string& config_section = getConfigSection();
SettingsInterface* sif = getDialog()->getProfileSettingsInterface();
for (u32 i = 0; i < cinfo->num_bindings; i++)
for (const InputBindingInfo& bi : cinfo->bindings)
{
const InputBindingInfo& bi = cinfo->bindings[i];
if (bi.bind_type == InputBindingInfo::Type::Axis || bi.bind_type == InputBindingInfo::Type::HalfAxis ||
bi.bind_type == InputBindingInfo::Type::Button || bi.bind_type == InputBindingInfo::Type::Pointer ||
bi.bind_type == InputBindingInfo::Type::Device)

View File

@ -15,7 +15,7 @@
#pragma once
#include "pcsx2/SIO/Pad/PadMacros.h"
#include "pcsx2/SIO/Pad/PadTypes.h"
#include <QtWidgets/QWidget>
@ -92,7 +92,7 @@ public:
void updateListItem(u32 index);
private:
static constexpr u32 NUM_MACROS = PadMacros::NUM_MACRO_BUTTONS_PER_CONTROLLER;
static constexpr u32 NUM_MACROS = Pad::NUM_MACRO_BUTTONS_PER_CONTROLLER;
void createWidgets(ControllerBindingWidget* parent);
@ -115,6 +115,7 @@ public:
private Q_SLOTS:
void onPressureChanged();
void onDeadzoneChanged();
void onSetFrequencyClicked();
void updateBinds();

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>691</width>
<height>547</height>
<height>433</height>
</rect>
</property>
<property name="windowTitle">
@ -83,6 +83,9 @@
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>10</number>
</property>
</widget>
</item>
<item>
@ -123,6 +126,46 @@
</property>
</widget>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="deadzoneLayout" stretch="0,1,0">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Deadzone:</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="deadzone">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>10</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="deadzoneValue">
<property name="text">
<string>100%</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>

View File

@ -22,7 +22,7 @@
#include "Settings/HotkeySettingsWidget.h"
#include "pcsx2/INISettingsInterface.h"
#include "pcsx2/SIO/Pad/PadConfig.h"
#include "pcsx2/SIO/Pad/Pad.h"
#include "pcsx2/SIO/Sio.h"
#include "pcsx2/VMManager.h"
@ -130,7 +130,7 @@ void ControllerSettingsDialog::onNewProfileClicked()
{
// from global
auto lock = Host::GetSettingsLock();
g_PadConfig.CopyConfiguration(&temp_si, *Host::Internal::GetBaseSettingsLayer(), true, true, false);
Pad::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);
g_PadConfig.CopyConfiguration(&temp_si, *m_profile_interface, true, true, copy_hotkey_bindings);
Pad::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();
g_PadConfig.CopyConfiguration(Host::Internal::GetBaseSettingsLayer(), *m_profile_interface, true, true, false);
Pad::CopyConfiguration(Host::Internal::GetBaseSettingsLayer(), *m_profile_interface, true, true, false);
USB::CopyConfiguration(Host::Internal::GetBaseSettingsLayer(), *m_profile_interface, true, true);
}
Host::CommitBaseSettingChanges();
@ -403,8 +403,8 @@ 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 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"));
const Pad::ControllerInfo* ci = Pad::GetControllerInfo(m_port_bindings[global_slot]->getControllerType());
const QString display_name(QString::fromUtf8(ci ? ci->GetLocalizedName() : "Unknown"));
QListWidgetItem* item = new QListWidgetItem();
//: Controller Port is an official term from Sony. Find the official translation for your language inside the console's manual.
@ -459,8 +459,8 @@ 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 PadConfig::ControllerInfo* ci = g_PadConfig.GetControllerInfo(widget->getControllerType());
const QString display_name(ci ? qApp->translate("Pad", ci->display_name) : QStringLiteral("Unknown"));
const Pad::ControllerInfo* ci = Pad::GetControllerInfo(widget->getControllerType());
const QString display_name = QString::fromUtf8(ci ? ci->GetLocalizedName() : "Unknown");
//: Controller Port is an official term from Sony. Find the official translation for your language inside the console's manual.
item->setText(mtap_enabled ? (tr("Controller Port %1%2\n%3").arg(port + 1).arg(s_mtap_slot_names[slot]).arg(display_name)) :
@ -492,7 +492,7 @@ void ControllerSettingsDialog::updateListDescription(u32 port, USBDeviceWidget*
void ControllerSettingsDialog::refreshProfileList()
{
const std::vector<std::string> names(g_PadConfig.GetInputProfileNames());
const std::vector<std::string> names = Pad::GetInputProfileNames();
QSignalBlocker sb(m_ui.currentProfile);
m_ui.currentProfile->clear();

View File

@ -15,7 +15,7 @@
#include "PrecompiledHeader.h"
#include "pcsx2/SIO/Pad/PadConfig.h"
#include "pcsx2/SIO/Pad/Pad.h"
#include "GameSummaryWidget.h"
#include "SettingsDialog.h"
#include "MainWindow.h"
@ -69,7 +69,7 @@ GameSummaryWidget::~GameSummaryWidget() = default;
void GameSummaryWidget::populateInputProfiles()
{
for (const std::string& name : g_PadConfig.GetInputProfileNames())
for (const std::string& name : Pad::GetInputProfileNames())
m_ui.inputProfile->addItem(QString::fromStdString(name));
}

View File

@ -15,7 +15,7 @@
#include "PrecompiledHeader.h"
#include "pcsx2/SIO/Pad/PadConfig.h"
#include "pcsx2/SIO/Pad/Pad.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] : g_PadConfig.GetControllerTypeNames())
w.type_combo->addItem(qApp->translate("Pad", display_name), QString::fromStdString(name));
for (const auto& [name, display_name] : Pad::GetControllerTypeNames())
w.type_combo->addItem(QString::fromUtf8(display_name), QString::fromUtf8(name));
ControllerSettingWidgetBinder::BindWidgetToInputProfileString(
nullptr, w.type_combo, section, "Type", g_PadConfig.GetDefaultPadType(port));
nullptr, w.type_combo, section, "Type", Pad::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 = g_PadConfig.MapController(*Host::Internal::GetBaseSettingsLayer(), port, mapping);
result = Pad::MapController(*Host::Internal::GetBaseSettingsLayer(), port, mapping);
}
if (!result)
return;

View File

@ -482,23 +482,17 @@ endif()
# Host PAD
set(pcsx2PADSources
SIO/Pad/Pad.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
SIO/Pad/Pad.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
)

View File

@ -547,20 +547,6 @@ static __fi void DoFMVSwitch()
RendererSwitched = false;
}
// Convenience function to update UI thread and set patches.
static __fi void VSyncUpdateCore()
{
DoFMVSwitch();
VMManager::Internal::VSyncOnCPUThread();
}
static __fi void VSyncCheckExit()
{
if (VMManager::Internal::IsExecutionInterrupted())
Cpu->ExitExecution();
}
// Framelimiter - Measures the delta time between calls and stalls until a
// certain amount of time passes if such time hasn't passed yet.
static __fi void frameLimit()
@ -605,12 +591,12 @@ static __fi void frameLimit()
static __fi void VSyncStart(u32 sCycle)
{
// Update vibration at the end of a frame.
VSyncUpdateCore();
// End-of-frame tasks.
DoFMVSwitch();
VMManager::Internal::VSyncOnCPUThread();
frameLimit(); // limit FPS
gsPostVsyncStart(); // MUST be after framelimit; doing so before causes funk with frame times!
VSyncCheckExit();
if(EmuConfig.Trace.Enabled && EmuConfig.Trace.EE.m_EnableAll)
SysTrace.EE.Counters.Write( " ================ EE COUNTER VSYNC START (frame: %d) ================", g_FrameCount );
@ -642,6 +628,10 @@ static __fi void VSyncStart(u32 sCycle)
// Therefore, there needs to be some delay in order for it to see the interrupt flag before the interrupt is acknowledged, likely helped on real hardware by the pipelines.
// Without the patch and fixing this, the games have other issues, so I'm not going to rush to fix it.
// Refraction
// Bail out before the next frame starts if we're paused, or the CPU has changed
if (VMManager::Internal::IsExecutionInterrupted())
Cpu->ExitExecution();
}
static __fi void GSVSync()

View File

@ -5823,8 +5823,8 @@ bool GSTextureCache::SurfaceOffsetKeyEqual::operator()(const GSTextureCache::Sur
{
for (size_t i = 0; i < lhs.elems.size(); ++i)
{
const SurfaceOffsetKeyElem& lhs_elem = lhs.elems.at(i);
const SurfaceOffsetKeyElem& rhs_elem = rhs.elems.at(i);
const SurfaceOffsetKeyElem& lhs_elem = lhs.elems[i];
const SurfaceOffsetKeyElem& rhs_elem = rhs.elems[i];
if (lhs_elem.bp != rhs_elem.bp
|| lhs_elem.bw != rhs_elem.bw
|| lhs_elem.psm != rhs_elem.psm

View File

@ -48,8 +48,7 @@
#include "common/Timer.h"
#include "SIO/Memcard/MemoryCardFile.h"
#include "SIO/Pad/PadConfig.h"
#include "SIO/Pad/PadMacros.h"
#include "SIO/Pad/Pad.h"
#include "SIO/Sio.h"
#include "imgui.h"
@ -2273,7 +2272,7 @@ void FullscreenUI::StartAutomaticBinding(u32 port)
Host::RunOnCPUThread([port, name = std::move(names[index])]() {
auto lock = Host::GetSettingsLock();
SettingsInterface* bsi = GetEditingSettingsInterface();
const bool result = g_PadConfig.MapController(*bsi, port, InputManager::GetGenericBindingMapping(name));
const bool result = Pad::MapController(*bsi, port, InputManager::GetGenericBindingMapping(name));
SetSettingsChanged(bsi);
@ -3650,7 +3649,7 @@ void FullscreenUI::CopyGlobalControllerSettingsToGame()
SettingsInterface* dsi = GetEditingSettingsInterface(true);
SettingsInterface* ssi = GetEditingSettingsInterface(false);
g_PadConfig.CopyConfiguration(dsi, *ssi, true, true, false);
Pad::CopyConfiguration(dsi, *ssi, true, true, false);
USB::CopyConfiguration(dsi, *ssi, true, true);
SetSettingsChanged(dsi);
@ -3661,15 +3660,15 @@ void FullscreenUI::ResetControllerSettings()
{
SettingsInterface* dsi = GetEditingSettingsInterface();
g_PadConfig.SetDefaultControllerConfig(*dsi);
g_PadConfig.SetDefaultHotkeyConfig(*dsi);
Pad::SetDefaultControllerConfig(*dsi);
Pad::SetDefaultHotkeyConfig(*dsi);
USB::SetDefaultConfiguration(dsi);
ShowToast(std::string(), "Controller settings reset to default.");
}
void FullscreenUI::DoLoadInputProfile()
{
std::vector<std::string> profiles(g_PadConfig.GetInputProfileNames());
std::vector<std::string> profiles = Pad::GetInputProfileNames();
if (profiles.empty())
{
ShowToast(std::string(), "No input profiles available.");
@ -3695,7 +3694,7 @@ void FullscreenUI::DoLoadInputProfile()
auto lock = Host::GetSettingsLock();
SettingsInterface* dsi = GetEditingSettingsInterface();
g_PadConfig.CopyConfiguration(dsi, ssi, true, true, IsEditingGameSettings(dsi));
Pad::CopyConfiguration(dsi, ssi, true, true, IsEditingGameSettings(dsi));
USB::CopyConfiguration(dsi, ssi, true, true);
SetSettingsChanged(dsi);
ShowToast(std::string(), fmt::format("Input profile '{}' loaded.", title));
@ -3709,7 +3708,7 @@ void FullscreenUI::DoSaveInputProfile(const std::string& name)
auto lock = Host::GetSettingsLock();
SettingsInterface* ssi = GetEditingSettingsInterface();
g_PadConfig.CopyConfiguration(&dsi, *ssi, true, true, IsEditingGameSettings(ssi));
Pad::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));
@ -3719,7 +3718,7 @@ void FullscreenUI::DoSaveInputProfile(const std::string& name)
void FullscreenUI::DoSaveInputProfile()
{
std::vector<std::string> profiles(g_PadConfig.GetInputProfileNames());
std::vector<std::string> profiles = Pad::GetInputProfileNames();
ImGuiFullscreen::ChoiceDialogOptions coptions;
coptions.reserve(profiles.size() + 1);
@ -3853,11 +3852,11 @@ void FullscreenUI::DrawControllerSettingsPage()
.c_str());
const char* section = sections[global_slot];
const std::string type(bsi->GetStringValue(section, "Type", g_PadConfig.GetDefaultPadType(global_slot)));
const PadConfig::ControllerInfo* ci = g_PadConfig.GetControllerInfo(type);
const std::string type(bsi->GetStringValue(section, "Type", Pad::GetDefaultPadType(global_slot)));
const Pad::ControllerInfo* ci = Pad::GetControllerInfo(type);
if (MenuButton(ICON_FA_GAMEPAD " Controller Type", ci ? ci->display_name : "Unknown"))
{
const std::vector<std::pair<const char*, const char*>> raw_options = g_PadConfig.GetControllerTypeNames();
const std::vector<std::pair<const char*, const char*>> raw_options = Pad::GetControllerTypeNames();
ImGuiFullscreen::ChoiceDialogOptions options;
options.reserve(raw_options.size());
for (auto& it : raw_options)
@ -3878,7 +3877,7 @@ void FullscreenUI::DrawControllerSettingsPage()
});
}
if (!ci || ci->num_bindings == 0)
if (!ci || ci->bindings.empty())
{
ImGui::PopID();
continue;
@ -3887,20 +3886,17 @@ void FullscreenUI::DrawControllerSettingsPage()
if (MenuButton(ICON_FA_MAGIC " Automatic Mapping", "Attempts to map the selected port to a chosen controller."))
StartAutomaticBinding(global_slot);
for (u32 i = 0; i < ci->num_bindings; i++)
{
const InputBindingInfo& bi = ci->bindings[i];
DrawInputBindingButton(bsi, bi.bind_type, section, bi.name, bi.display_name, true);
}
for (const InputBindingInfo& bi : ci->bindings)
DrawInputBindingButton(bsi, bi.bind_type, section, bi.name, Host::TranslateToCString("Pad", bi.display_name), true);
MenuHeading((mtap_enabled[mtap_port] ?
fmt::format(ICON_FA_MICROCHIP " Controller Port {}{} Macros", mtap_port + 1, mtap_slot_names[mtap_slot]) :
fmt::format(ICON_FA_MICROCHIP " Controller Port {} Macros", mtap_port + 1))
.c_str());
static bool macro_button_expanded[Pad::NUM_CONTROLLER_PORTS][PadMacros::NUM_MACRO_BUTTONS_PER_CONTROLLER] = {};
static bool macro_button_expanded[Pad::NUM_CONTROLLER_PORTS][Pad::NUM_MACRO_BUTTONS_PER_CONTROLLER] = {};
for (u32 macro_index = 0; macro_index < PadMacros::NUM_MACRO_BUTTONS_PER_CONTROLLER; macro_index++)
for (u32 macro_index = 0; macro_index < Pad::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(),
@ -3916,15 +3912,14 @@ void FullscreenUI::DrawControllerSettingsPage()
{
std::vector<std::string_view> buttons_split(StringUtil::SplitString(binds_string, '&', true));
ImGuiFullscreen::ChoiceDialogOptions options;
for (u32 i = 0; i < ci->num_bindings; i++)
for (const InputBindingInfo& bi : ci->bindings)
{
const InputBindingInfo& bi = ci->bindings[i];
if (bi.bind_type != InputBindingInfo::Type::Button && bi.bind_type != InputBindingInfo::Type::Axis &&
bi.bind_type != InputBindingInfo::Type::HalfAxis)
{
continue;
}
options.emplace_back(bi.display_name, std::any_of(buttons_split.begin(), buttons_split.end(),
options.emplace_back(Host::TranslateToCString("Pad", bi.display_name), std::any_of(buttons_split.begin(), buttons_split.end(),
[bi](const std::string_view& it) { return (it == bi.name); }));
}
@ -3932,9 +3927,8 @@ void FullscreenUI::DrawControllerSettingsPage()
[section, macro_index, ci](s32 index, const std::string& title, bool checked) {
// convert display name back to bind name
std::string_view to_modify;
for (u32 j = 0; j < ci->num_bindings; j++)
for (const InputBindingInfo& bi : ci->bindings)
{
const InputBindingInfo& bi = ci->bindings[j];
if (bi.display_name == title)
{
to_modify = bi.name;
@ -3984,6 +3978,10 @@ void FullscreenUI::DrawControllerSettingsPage()
DrawFloatSpinBoxSetting(bsi, ICON_FA_ARROW_DOWN " Pressure", "Determines how much pressure is simulated when macro is active.",
section, pressure_key.c_str(), 1.0f, 0.01f, 1.0f, 0.01f, 100.0f, "%.0f%%");
const std::string deadzone_key(fmt::format("Macro{}Deadzone", macro_index + 1));
DrawFloatSpinBoxSetting(bsi, ICON_FA_ARROW_DOWN " Pressure", "Determines the pressure required to activate the macro.",
section, deadzone_key.c_str(), 0.0f, 0.00f, 1.0f, 0.01f, 100.0f, "%.0f%%");
ImGui::SetNextWindowSize(LayoutScale(500.0f, 180.0f));
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
@ -4018,19 +4016,16 @@ void FullscreenUI::DrawControllerSettingsPage()
ImGui::PopFont();
}
if (ci->num_settings > 0)
if (!ci->settings.empty())
{
MenuHeading((mtap_enabled[mtap_port] ?
fmt::format(ICON_FA_SLIDERS_H " Controller Port {}{} Settings", mtap_port + 1, mtap_slot_names[mtap_slot]) :
fmt::format(ICON_FA_SLIDERS_H " Controller Port {} Settings", mtap_port + 1))
.c_str());
for (u32 i = 0; i < ci->num_settings; i++)
{
const SettingInfo& si = ci->settings[i];
for (const SettingInfo& si : ci->settings)
DrawSettingInfoSetting(bsi, section, si.name, si);
}
}
ImGui::PopID();
}
@ -4109,7 +4104,10 @@ void FullscreenUI::DrawControllerSettingsPage()
const std::string section(USB::GetConfigSection(port));
for (const InputBindingInfo& bi : bindings)
DrawInputBindingButton(bsi, bi.bind_type, section.c_str(), USB::GetConfigSubKey(type, bi.name).c_str(), bi.display_name);
{
DrawInputBindingButton(bsi, bi.bind_type, section.c_str(), USB::GetConfigSubKey(type, bi.name).c_str(),
Host::TranslateToCString("USB", bi.display_name));
}
}
const std::span<const SettingInfo> settings(USB::GetDeviceSettings(type, subtype));

View File

@ -29,11 +29,11 @@
#include "ImGui/ImGuiOverlays.h"
#include "Input/InputManager.h"
#include "PerformanceMetrics.h"
#include "Recording/InputRecording.h"
#include "SIO/Pad/Pad.h"
#include "SIO/Pad/PadBase.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"
@ -485,19 +485,9 @@ void ImGuiManager::DrawInputsOverlay()
for (u32 slot = 0; slot < Pad::NUM_CONTROLLER_PORTS; slot++)
{
const std::optional<PadBase*> padOpt = g_PadManager.GetPad(slot);
if (padOpt.has_value())
{
PadBase* pad = padOpt.value();
const Pad::ControllerType ctype = pad->GetType();
if (ctype != Pad::ControllerType::NotConnected)
{
if (Pad::HasConnectedPad(slot))
num_ports++;
}
}
}
for (u32 port = 0; port < USB::NUM_PORTS; port++)
{
@ -515,27 +505,18 @@ void ImGuiManager::DrawInputsOverlay()
for (u32 slot = 0; slot < Pad::NUM_CONTROLLER_PORTS; slot++)
{
const std::optional<PadBase*> padOpt = g_PadManager.GetPad(slot);
if (padOpt.has_value())
{
PadBase* pad = padOpt.value();
const PadBase* const pad = Pad::GetPad(slot);
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++)
const Pad::ControllerInfo& cinfo = pad->GetInfo();
for (u32 bind = 0; bind < static_cast<u32>(cinfo.bindings.size()); bind++)
{
const InputBindingInfo& bi = cinfo->bindings[bind];
const InputBindingInfo& bi = cinfo.bindings[bind];
switch (bi.bind_type)
{
case InputBindingInfo::Type::Axis:
@ -574,7 +555,6 @@ void ImGuiManager::DrawInputsOverlay()
current_y += font->FontSize + spacing;
}
}
for (u32 port = 0; port < USB::NUM_PORTS; port++)
{

View File

@ -18,9 +18,7 @@
#include "ImGui/ImGuiManager.h"
#include "Input/InputManager.h"
#include "Input/InputSource.h"
#include "SIO/Pad/PadConfig.h"
#include "SIO/Pad/PadMacros.h"
#include "SIO/Pad/PadManager.h"
#include "SIO/Pad/Pad.h"
#include "USB/USB.h"
#include "VMManager.h"
@ -634,14 +632,12 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const ch
if (type.empty() || type == "None")
return;
const PadConfig::ControllerInfo* cinfo = g_PadConfig.GetControllerInfo(type);
const Pad::ControllerInfo* cinfo = Pad::GetControllerInfo(type);
if (!cinfo)
return;
for (u32 i = 0; i < cinfo->num_bindings; i++)
for (const InputBindingInfo& bi : cinfo->bindings)
{
const InputBindingInfo& bi = cinfo->bindings[i];
switch (bi.bind_type)
{
case InputBindingInfo::Type::Button:
@ -656,7 +652,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) {
g_PadManager.SetControllerState(pad_index, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value));
Pad::SetControllerState(pad_index, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value));
}});
}
}
@ -669,13 +665,15 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const ch
}
}
for (u32 macro_button_index = 0; macro_button_index < PadMacros::NUM_MACRO_BUTTONS_PER_CONTROLLER; macro_button_index++)
for (u32 macro_button_index = 0; macro_button_index < Pad::NUM_MACRO_BUTTONS_PER_CONTROLLER; macro_button_index++)
{
const std::vector<std::string> 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) {
g_PadMacros.SetMacroButtonState(pad_index, macro_button_index, state);
const float deadzone = si.GetFloatValue(section.c_str(), fmt::format("Macro{}Deadzone", macro_button_index + 1).c_str(), 0.0f);
AddBindings(bindings, InputAxisEventHandler{[pad_index, macro_button_index, deadzone](float value) {
const bool state = (value > deadzone);
Pad::SetMacroButtonState(pad_index, macro_button_index, state);
}});
}
}
@ -1293,7 +1291,7 @@ 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, g_PadConfig.GetDefaultPadType(pad));
AddPadBindings(binding_si, pad, Pad::GetDefaultPadType(pad));
constexpr float ui_ctrl_range = 100.0f;
constexpr float pointer_sensitivity = 0.05f;

View File

@ -17,8 +17,8 @@
#include "DebugTools/Debug.h"
#include "Recording/PadData.h"
#include "SIO/Pad/PadManager.h"
#include "SIO/Pad/PadDualshock2Types.h"
#include "SIO/Pad/Pad.h"
#include "SIO/Pad/PadDualshock2.h"
#include "SIO/Sio.h"
#include <fmt/core.h>
@ -28,7 +28,7 @@ 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);
PadBase* pad = Pad::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 = pad->GetButtons();
@ -56,23 +56,23 @@ PadData::PadData(const int port, const int slot)
m_rightAnalog = pad->GetRawRightAnalog();
m_leftAnalog = pad->GetRawLeftAnalog();
// Get pressure bytes (12 of them)
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_left = {(0b10000000 & m_compactPressFlagsGroupOne) == 0, pad->GetRawInput(PadDualshock2::Inputs::PAD_LEFT)};
m_down = {(0b01000000 & m_compactPressFlagsGroupOne) == 0, pad->GetRawInput(PadDualshock2::Inputs::PAD_DOWN)};
m_right = {(0b00100000 & m_compactPressFlagsGroupOne) == 0, pad->GetRawInput(PadDualshock2::Inputs::PAD_RIGHT)};
m_up = {(0b00010000 & m_compactPressFlagsGroupOne) == 0, pad->GetRawInput(PadDualshock2::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, 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)};
m_square = {(0b10000000 & m_compactPressFlagsGroupTwo) == 0, pad->GetRawInput(PadDualshock2::Inputs::PAD_SQUARE)};
m_cross = {(0b01000000 & m_compactPressFlagsGroupTwo) == 0, pad->GetRawInput(PadDualshock2::Inputs::PAD_CROSS)};
m_circle = {(0b00100000 & m_compactPressFlagsGroupTwo) == 0, pad->GetRawInput(PadDualshock2::Inputs::PAD_CIRCLE)};
m_triangle = {(0b00010000 & m_compactPressFlagsGroupTwo) == 0, pad->GetRawInput(PadDualshock2::Inputs::PAD_TRIANGLE)};
m_r1 = {(0b00001000 & m_compactPressFlagsGroupTwo) == 0, pad->GetRawInput(PadDualshock2::Inputs::PAD_R1)};
m_l1 = {(0b00000100 & m_compactPressFlagsGroupTwo) == 0, pad->GetRawInput(PadDualshock2::Inputs::PAD_L1)};
m_r2 = {(0b00000010 & m_compactPressFlagsGroupTwo) == 0, pad->GetRawInput(PadDualshock2::Inputs::PAD_R2)};
m_l2 = {(0b00000001 & m_compactPressFlagsGroupTwo) == 0, pad->GetRawInput(PadDualshock2::Inputs::PAD_L2)};
}
PadData::PadData(const int port, const int slot, const std::array<u8, 18> data)
@ -81,54 +81,54 @@ PadData::PadData(const int port, const int slot, const std::array<u8, 18> data)
m_slot = slot;
m_ext_port = sioConvertPortAndSlotToPad(m_port, m_slot);
m_compactPressFlagsGroupOne = data.at(0);
m_compactPressFlagsGroupTwo = data.at(1);
m_compactPressFlagsGroupOne = data[0];
m_compactPressFlagsGroupTwo = data[1];
m_rightAnalog = {data.at(2), data.at(3)};
m_leftAnalog = {data.at(4), data.at(5)};
m_rightAnalog = {data[2], data[3]};
m_leftAnalog = {data[4], data[5]};
m_left = {(0b10000000 & m_compactPressFlagsGroupOne) == 0, data.at(7)};
m_down = {(0b01000000 & m_compactPressFlagsGroupOne) == 0, data.at(9)};
m_right = {(0b00100000 & m_compactPressFlagsGroupOne) == 0, data.at(6)};
m_up = {(0b00010000 & m_compactPressFlagsGroupOne) == 0, data.at(8)};
m_left = {(0b10000000 & m_compactPressFlagsGroupOne) == 0, data[7]};
m_down = {(0b01000000 & m_compactPressFlagsGroupOne) == 0, data[9]};
m_right = {(0b00100000 & m_compactPressFlagsGroupOne) == 0, data[6]};
m_up = {(0b00010000 & m_compactPressFlagsGroupOne) == 0, data[8]};
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, data.at(13)};
m_cross = {(0b01000000 & m_compactPressFlagsGroupTwo) == 0, data.at(12)};
m_circle = {(0b00100000 & m_compactPressFlagsGroupTwo) == 0, data.at(11)};
m_triangle = {(0b00010000 & m_compactPressFlagsGroupTwo) == 0, data.at(10)};
m_r1 = {(0b00001000 & m_compactPressFlagsGroupTwo) == 0, data.at(15)};
m_l1 = {(0b00000100 & m_compactPressFlagsGroupTwo) == 0, data.at(14)};
m_r2 = {(0b00000010 & m_compactPressFlagsGroupTwo) == 0, data.at(17)};
m_l2 = {(0b00000001 & m_compactPressFlagsGroupTwo) == 0, data.at(16)};
m_square = {(0b10000000 & m_compactPressFlagsGroupTwo) == 0, data[13]};
m_cross = {(0b01000000 & m_compactPressFlagsGroupTwo) == 0, data[12]};
m_circle = {(0b00100000 & m_compactPressFlagsGroupTwo) == 0, data[11]};
m_triangle = {(0b00010000 & m_compactPressFlagsGroupTwo) == 0, data[10]};
m_r1 = {(0b00001000 & m_compactPressFlagsGroupTwo) == 0, data[15]};
m_l1 = {(0b00000100 & m_compactPressFlagsGroupTwo) == 0, data[14]};
m_r2 = {(0b00000010 & m_compactPressFlagsGroupTwo) == 0, data[17]};
m_l2 = {(0b00000001 & m_compactPressFlagsGroupTwo) == 0, data[16]};
}
void PadData::OverrideActualController() const
{
PadBase* pad = g_PadManager.GetPad(m_ext_port);
PadBase* pad = Pad::GetPad(m_ext_port);
pad->SetRawAnalogs(m_leftAnalog, m_rightAnalog);
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);
pad->Set(PadDualshock2::Inputs::PAD_RIGHT, std::get<1>(m_right));
pad->Set(PadDualshock2::Inputs::PAD_LEFT, std::get<1>(m_left));
pad->Set(PadDualshock2::Inputs::PAD_UP, std::get<1>(m_up));
pad->Set(PadDualshock2::Inputs::PAD_DOWN, std::get<1>(m_down));
pad->Set(PadDualshock2::Inputs::PAD_START, m_start);
pad->Set(PadDualshock2::Inputs::PAD_SELECT, m_select);
pad->Set(PadDualshock2::Inputs::PAD_R3, m_r3);
pad->Set(PadDualshock2::Inputs::PAD_L3, m_l3);
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));
pad->Set(PadDualshock2::Inputs::PAD_SQUARE, std::get<1>(m_square));
pad->Set(PadDualshock2::Inputs::PAD_CROSS, std::get<1>(m_cross));
pad->Set(PadDualshock2::Inputs::PAD_CIRCLE, std::get<1>(m_circle));
pad->Set(PadDualshock2::Inputs::PAD_TRIANGLE, std::get<1>(m_triangle));
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));
pad->Set(PadDualshock2::Inputs::PAD_R1, std::get<1>(m_r1));
pad->Set(PadDualshock2::Inputs::PAD_L1, std::get<1>(m_l1));
pad->Set(PadDualshock2::Inputs::PAD_R2, std::get<1>(m_r2));
pad->Set(PadDualshock2::Inputs::PAD_L2, std::get<1>(m_l2));
}
void addButtonInfoToString(std::string label, std::string& str, std::tuple<bool, u8> buttonInfo)

View File

@ -297,7 +297,7 @@ u8 MemoryCardProtocol::PS1Read(u8 data)
mcd->Read(ps1McState.buf.data(), ps1McState.buf.size());
[[fallthrough]];
default:
ret = ps1McState.buf.at(ps1McState.currentByte - 10);
ret = ps1McState.buf[ps1McState.currentByte - 10];
ps1McState.checksum ^= ret;
break;
}
@ -372,7 +372,7 @@ u8 MemoryCardProtocol::PS1Write(u8 data)
ps1McState.checksum = ps1McState.sectorAddrMSB ^ ps1McState.sectorAddrLSB;
[[fallthrough]];
default:
ps1McState.buf.at(ps1McState.currentByte - 6) = data;
ps1McState.buf[ps1McState.currentByte - 6] = data;
ps1McState.checksum ^= data;
ret = 0x00;
break;

View File

@ -15,28 +15,67 @@
#include "PrecompiledHeader.h"
#include "SIO/Pad/PadConfig.h"
#include "Host.h"
#include "Input/InputManager.h"
#include "SIO/Pad/Pad.h"
#include "SIO/Pad/PadDualshock2.h"
#include "SIO/Pad/PadGuitar.h"
#include "SIO/Pad/PadNotConnected.h"
#include "SIO/Sio.h"
#include "SIO/Pad/PadManager.h"
#include "SIO/Pad/PadMacros.h"
#include "SIO/Pad/PadDualshock2Types.h"
#include "SIO/Pad/PadGuitarTypes.h"
#include "IconsFontAwesome5.h"
#include "common/FileSystem.h"
#include "common/Path.h"
#include "common/StringUtil.h"
#include "common/SettingsInterface.h"
#include "common/StringUtil.h"
#include "Input/InputManager.h"
#include "fmt/format.h"
PadConfig g_PadConfig;
#include <vector>
PadConfig::PadConfig() = default;
PadConfig::~PadConfig() = default;
void PadConfig::LoadConfig(const SettingsInterface& si)
namespace Pad
{
g_PadMacros.ClearMacros();
struct MacroButton
{
std::vector<u32> 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::unique_ptr<PadBase> CreatePad(u8 unifiedSlot, Pad::ControllerType controllerType);
static PadBase* ChangePadType(u8 unifiedSlot, Pad::ControllerType controllerType);
void LoadMacroButtonConfig(
const SettingsInterface& si, u32 pad, const std::string_view& type, const std::string& section);
static void ApplyMacroButton(u32 controller, const MacroButton& mb);
static std::array<std::array<MacroButton, NUM_MACRO_BUTTONS_PER_CONTROLLER>, NUM_CONTROLLER_PORTS> s_macro_buttons;
static std::array<std::unique_ptr<PadBase>, NUM_CONTROLLER_PORTS> s_controllers;
}
bool Pad::Initialize()
{
return true;
}
void Pad::Shutdown()
{
for (auto& port : s_controllers)
port.reset();
}
const char* Pad::ControllerInfo::GetLocalizedName() const
{
return Host::TranslateToCString("Pad", display_name);
}
void Pad::LoadConfig(const SettingsInterface& si)
{
s_macro_buttons = {};
EmuConfig.MultitapPort0_Enabled = si.GetBoolValue("Pad", "MultitapPort1", false);
EmuConfig.MultitapPort1_Enabled = si.GetBoolValue("Pad", "MultitapPort2", false);
@ -47,18 +86,18 @@ void PadConfig::LoadConfig(const SettingsInterface& si)
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);
PadBase* pad = Pad::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);
pad = Pad::ChangePadType(i, Pad::ControllerType::NotConnected);
}
if (!ci)
{
pad = g_PadManager.ChangePadType(i, Pad::ControllerType::NotConnected);
pad = Pad::ChangePadType(i, Pad::ControllerType::NotConnected);
continue;
}
@ -67,7 +106,7 @@ void PadConfig::LoadConfig(const SettingsInterface& si)
if (ci->type != oldType)
{
pad = g_PadManager.ChangePadType(i, ci->type);
pad = Pad::ChangePadType(i, ci->type);
}
const float axis_deadzone = si.GetFloatValue(section.c_str(), "Deadzone", Pad::DEFAULT_STICK_DEADZONE);
@ -94,17 +133,16 @@ void PadConfig::LoadConfig(const SettingsInterface& si)
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)
const char* Pad::GetDefaultPadType(u32 pad)
{
return (pad == 0) ? "DualShock2" : "None";
}
void PadConfig::SetDefaultControllerConfig(SettingsInterface& si)
void Pad::SetDefaultControllerConfig(SettingsInterface& si)
{
si.ClearSection("InputSources");
si.ClearSection("Hotkeys");
@ -136,9 +174,8 @@ void PadConfig::SetDefaultControllerConfig(SettingsInterface& si)
const ControllerInfo* ci = GetControllerInfo(type);
if (ci)
{
for (u32 i = 0; i < ci->num_settings; i++)
for (const SettingInfo& csi : ci->settings)
{
const SettingInfo& csi = ci->settings[i];
switch (csi.type)
{
case SettingInfo::Type::Boolean:
@ -168,7 +205,7 @@ void PadConfig::SetDefaultControllerConfig(SettingsInterface& si)
MapController(si, 0, InputManager::GetGenericBindingMapping("Keyboard"));
}
void PadConfig::SetDefaultHotkeyConfig(SettingsInterface& si)
void Pad::SetDefaultHotkeyConfig(SettingsInterface& si)
{
// PCSX2 Controller Settings - Hotkeys
@ -215,74 +252,70 @@ void PadConfig::SetDefaultHotkeyConfig(SettingsInterface& si)
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},
static const Pad::ControllerInfo* s_controller_info[] = {
&PadNotConnected::ControllerInfo,
&PadDualshock2::ControllerInfo,
&PadGuitar::ControllerInfo,
};
const PadConfig::ControllerInfo* PadConfig::GetControllerInfo(Pad::ControllerType type)
const Pad::ControllerInfo* Pad::GetControllerInfo(Pad::ControllerType type)
{
for (const ControllerInfo& info : s_controller_info)
for (const ControllerInfo* info : s_controller_info)
{
if (type == info.type)
return &info;
if (type == info->type)
return info;
}
return nullptr;
}
const PadConfig::ControllerInfo* PadConfig::GetControllerInfo(const std::string_view& name)
const Pad::ControllerInfo* Pad::GetControllerInfo(const std::string_view& name)
{
for (const ControllerInfo& info : s_controller_info)
for (const ControllerInfo* info : s_controller_info)
{
if (name == info.name)
return &info;
if (name == info->name)
return info;
}
return nullptr;
}
const std::vector<std::pair<const char*, const char*>> PadConfig::GetControllerTypeNames()
const char* Pad::GetControllerTypeName(Pad::ControllerType type)
{
// Not localized, because it should never happen.
const ControllerInfo* ci = GetControllerInfo(type);
return ci ? ci->GetLocalizedName() : "UNKNOWN";
}
const std::vector<std::pair<const char*, const char*>> Pad::GetControllerTypeNames()
{
std::vector<std::pair<const char*, const char*>> ret;
for (const ControllerInfo& info : s_controller_info)
ret.emplace_back(info.name, info.display_name);
for (const ControllerInfo* info : s_controller_info)
ret.emplace_back(info->name, info->GetLocalizedName());
return ret;
}
std::vector<std::string> PadConfig::GetControllerBinds(const std::string_view& type)
std::vector<std::string> Pad::GetControllerBinds(const std::string_view& type)
{
std::vector<std::string> ret;
const ControllerInfo* info = GetControllerInfo(type);
if (info)
{
for (u32 i = 0; i < info->num_bindings; i++)
for (const InputBindingInfo& bi : info->bindings)
{
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);
ret.emplace_back(bi.name);
}
}
return ret;
}
void PadConfig::ClearPortBindings(SettingsInterface& si, u32 port)
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)));
@ -291,11 +324,11 @@ void PadConfig::ClearPortBindings(SettingsInterface& si, u32 port)
if (!info)
return;
for (u32 i = 0; i < info->num_bindings; i++)
si.DeleteValue(section.c_str(), info->bindings[i].name);
for (const InputBindingInfo& bi : info->bindings)
si.DeleteValue(section.c_str(), bi.name);
}
void PadConfig::CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface& src_si,
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)
@ -329,15 +362,14 @@ void PadConfig::CopyConfiguration(SettingsInterface* dest_si, const SettingsInte
if (copy_pad_bindings)
{
for (u32 i = 0; i < info->num_bindings; i++)
{
const InputBindingInfo& bi = info->bindings[i];
for (const InputBindingInfo& bi : info->bindings)
dest_si->CopyStringListValue(src_si, section.c_str(), bi.name);
}
for (u32 i = 0; i < PadMacros::NUM_MACRO_BUTTONS_PER_CONTROLLER; i++)
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->CopyFloatValue(src_si, section.c_str(), fmt::format("Macro{}Pressure", i + 1).c_str());
dest_si->CopyFloatValue(src_si, section.c_str(), fmt::format("Macro{}Deadzone", 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());
}
@ -353,9 +385,8 @@ void PadConfig::CopyConfiguration(SettingsInterface* dest_si, const SettingsInte
dest_si->CopyFloatValue(src_si, section.c_str(), "SmallMotorScale");
}
for (u32 i = 0; i < info->num_settings; i++)
for (const SettingInfo& csi : info->settings)
{
const SettingInfo& csi = info->settings[i];
switch (csi.type)
{
case SettingInfo::Type::Boolean:
@ -417,7 +448,7 @@ static u32 TryMapGenericMapping(SettingsInterface& si, const std::string& sectio
}
bool PadConfig::MapController(SettingsInterface& si, u32 controller,
bool Pad::MapController(SettingsInterface& si, u32 controller,
const std::vector<std::pair<GenericInputBinding, std::string>>& mapping)
{
const std::string section(StringUtil::StdStringFromFormat("Pad%u", controller + 1));
@ -427,9 +458,8 @@ bool PadConfig::MapController(SettingsInterface& si, u32 controller,
return false;
u32 num_mappings = 0;
for (u32 i = 0; i < info->num_bindings; i++)
for (const InputBindingInfo& bi : info->bindings)
{
const InputBindingInfo& bi = info->bindings[i];
if (bi.generic_mapping == GenericInputBinding::Unknown)
continue;
@ -451,7 +481,7 @@ bool PadConfig::MapController(SettingsInterface& si, u32 controller,
return (num_mappings > 0);
}
std::vector<std::string> PadConfig::GetInputProfileNames()
std::vector<std::string> Pad::GetInputProfileNames()
{
FileSystem::FindResultsArray results;
FileSystem::FindFiles(EmuFolders::InputProfiles.c_str(), "*.ini",
@ -465,17 +495,123 @@ std::vector<std::string> PadConfig::GetInputProfileNames()
return ret;
}
std::string PadConfig::GetConfigSection(u32 pad_index)
std::string Pad::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)
std::unique_ptr<PadBase> Pad::CreatePad(u8 unifiedSlot, ControllerType controllerType)
{
switch (controllerType)
{
case ControllerType::DualShock2:
return std::make_unique<PadDualshock2>(unifiedSlot);
case ControllerType::Guitar:
return std::make_unique<PadGuitar>(unifiedSlot);
default:
return std::make_unique<PadNotConnected>(unifiedSlot);
}
}
PadBase* Pad::ChangePadType(u8 unifiedSlot, ControllerType controllerType)
{
s_controllers[unifiedSlot] = CreatePad(unifiedSlot, controllerType);
return s_controllers[unifiedSlot].get();
}
bool Pad::HasConnectedPad(u8 unifiedSlot)
{
return (
unifiedSlot < NUM_CONTROLLER_PORTS && s_controllers[unifiedSlot]->GetType() != ControllerType::NotConnected);
}
PadBase* Pad::GetPad(u8 port, u8 slot)
{
const u8 unifiedSlot = sioConvertPortAndSlotToPad(port, slot);
return s_controllers[unifiedSlot].get();
}
PadBase* Pad::GetPad(const u8 unifiedSlot)
{
return s_controllers[unifiedSlot].get();
}
void Pad::SetControllerState(u32 controller, u32 bind, float value)
{
if (controller >= NUM_CONTROLLER_PORTS)
return;
s_controllers[controller]->Set(bind, value);
}
bool Pad::Freeze(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 < NUM_CONTROLLER_PORTS; unifiedSlot++)
{
ControllerType type;
sw.Do(&type);
if (sw.HasError())
return false;
std::unique_ptr<PadBase> tempPad;
PadBase* pad = GetPad(unifiedSlot);
if (!pad || pad->GetType() != type)
{
const auto& [port, slot] = sioConvertPadToPortAndSlot(unifiedSlot);
Host::AddIconOSDMessage(fmt::format("UnfreezePad{}Changed", unifiedSlot), ICON_FA_GAMEPAD,
fmt::format(TRANSLATE_FS("Pad",
"Controller port {}, slot {} has a {} connected, but the save state has a "
"{}.\nLeaving the original controller type connected, but this may cause issues."),
port, slot,
Pad::GetControllerTypeName(pad ? pad->GetType() : Pad::ControllerType::NotConnected),
Pad::GetControllerTypeName(type)));
// Reset the transfer etc state of the pad, at least it has a better chance of surviving.
if (pad)
pad->SoftReset();
// But we still need to pull the data from the state..
tempPad = CreatePad(unifiedSlot, type);
pad = tempPad.get();
}
if (!pad->Freeze(sw))
return false;
}
}
else
{
if (!sw.DoMarker("PAD"))
return false;
for (u32 unifiedSlot = 0; unifiedSlot < NUM_CONTROLLER_PORTS; unifiedSlot++)
{
PadBase* pad = GetPad(unifiedSlot);
ControllerType type = pad->GetType();
sw.Do(&type);
if (sw.HasError() || !pad->Freeze(sw))
return false;
}
}
return !sw.HasError();
}
void Pad::LoadMacroButtonConfig(const SettingsInterface& si, u32 pad, const std::string_view& type, const std::string& section)
{
// lazily initialized
std::vector<std::string> binds;
for (u32 i = 0; i < PadMacros::NUM_MACRO_BUTTONS_PER_CONTROLLER; i++)
for (u32 i = 0; i < 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))
@ -485,6 +621,8 @@ void PadConfig::LoadMacroButtonConfig(const SettingsInterface& si, u32 pad, cons
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<u32> bind_indices;
std::vector<std::string_view> buttons_split(StringUtil::SplitString(binds_string, '&', true));
@ -504,8 +642,63 @@ void PadConfig::LoadMacroButtonConfig(const SettingsInterface& si, u32 pad, cons
if (bind_indices.empty())
continue;
PadMacros::MacroButton& macro = g_PadMacros.GetMacroButton(pad, i);
MacroButton& macro = s_macro_buttons[pad][i];
macro.buttons = std::move(bind_indices);
macro.toggle_frequency = frequency;
macro.pressure = pressure;
}
}
void Pad::SetMacroButtonState(u32 pad, u32 index, bool state)
{
if (pad >= 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);
}
}
void Pad::ApplyMacroButton(u32 controller, const Pad::MacroButton& mb)
{
const float value = mb.toggle_state ? mb.pressure : 0.0f;
PadBase* const pad = Pad::GetPad(controller);
for (const u32 btn : mb.buttons)
pad->Set(btn, value);
}
void Pad::UpdateMacroButtons()
{
for (u32 pad = 0; pad < Pad::NUM_CONTROLLER_PORTS; pad++)
{
for (u32 index = 0; index < NUM_MACRO_BUTTONS_PER_CONTROLLER; index++)
{
Pad::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);
}
}
}

View File

@ -15,58 +15,69 @@
#pragma once
#include "Config.h"
#include "SIO/Pad/PadTypes.h"
#include "Config.h"
#include <memory>
class PadBase;
class SettingsInterface;
enum class GenericInputBinding : u8;
class SettingsInterface;
class StateWrapper;
class PadConfig
namespace Pad
{
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();
bool Initialize();
void Shutdown();
// 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);
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<std::pair<const char*, const char*>> GetControllerTypeNames();
// Returns the list of binds for the specified controller type.
std::vector<std::string> 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);
const char* GetControllerTypeName(Pad::ControllerType type);
// Performs automatic controller mapping with the provided list of generic mappings.
bool MapController(SettingsInterface& si, u32 controller,
const std::vector<std::pair<GenericInputBinding, std::string>>& mapping);
bool MapController(
SettingsInterface& si, u32 controller, const std::vector<std::pair<GenericInputBinding, std::string>>& mapping);
// Returns a list of input profiles available.
std::vector<std::string> 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;
bool HasConnectedPad(u8 unifiedSlot);
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 Freeze(StateWrapper& sw);
// Sets the state of the specified macro button.
void SetMacroButtonState(u32 pad, u32 index, bool state);
void UpdateMacroButtons();
}; // namespace Pad

View File

@ -34,3 +34,18 @@ void PadBase::FullReset()
this->isInConfig = false;
this->currentMode = Pad::Mode::DIGITAL;
}
bool PadBase::Freeze(StateWrapper& sw)
{
if (!sw.DoMarker("PadBase"))
return false;
// Protected PadBase members
sw.Do(&rawInputs);
sw.Do(&unifiedSlot);
sw.Do(&isInConfig);
sw.Do(&currentMode);
sw.Do(&currentCommand);
sw.Do(&commandBytesReceived);
return !sw.HasError();
}

View File

@ -39,25 +39,27 @@ public: // Public members
void FullReset();
virtual void Init() = 0;
virtual Pad::ControllerType GetType() = 0;
virtual Pad::ControllerType GetType() const = 0;
virtual const Pad::ControllerInfo& GetInfo() const = 0;
virtual void Set(u32 index, float value) = 0;
virtual void SetRawAnalogs(const std::tuple<u8, u8> left, const std::tuple<u8, u8> 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 float GetVibrationScale(u32 motor) const = 0;
virtual void SetVibrationScale(u32 motor, float scale) = 0;
virtual float GetPressureModifier() = 0;
virtual float GetPressureModifier() const = 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<u8, u8> GetRawLeftAnalog() = 0;
virtual std::tuple<u8, u8> GetRawRightAnalog() = 0;
virtual u32 GetButtons() = 0;
virtual u8 GetPressure(u32 index) = 0;
virtual u8 GetRawInput(u32 index) const = 0;
virtual std::tuple<u8, u8> GetRawLeftAnalog() const = 0;
virtual std::tuple<u8, u8> GetRawRightAnalog() const = 0;
virtual u32 GetButtons() const = 0;
virtual u8 GetPressure(u32 index) const = 0;
virtual void Freeze(StateWrapper& sw) = 0;
virtual bool Freeze(StateWrapper& sw);
virtual u8 SendCommandByte(u8 commandByte) = 0;
};

View File

@ -16,8 +16,7 @@
#include "PrecompiledHeader.h"
#include "SIO/Pad/PadDualshock2.h"
#include "SIO/Pad/PadManager.h"
#include "SIO/Pad/Pad.h"
#include "SIO/Sio.h"
#include "SIO/Sio0.h"
@ -25,6 +24,77 @@
#include "Input/InputManager.h"
#include "Host.h"
static const InputBindingInfo s_bindings[] = {
// clang-format off
{"Up", TRANSLATE_NOOP("Pad", "D-Pad Up"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_UP, GenericInputBinding::DPadUp},
{"Right", TRANSLATE_NOOP("Pad", "D-Pad Right"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_RIGHT, GenericInputBinding::DPadRight},
{"Down", TRANSLATE_NOOP("Pad", "D-Pad Down"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_DOWN, GenericInputBinding::DPadDown},
{"Left", TRANSLATE_NOOP("Pad", "D-Pad Left"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_LEFT, GenericInputBinding::DPadLeft},
{"Triangle", TRANSLATE_NOOP("Pad", "Triangle"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_TRIANGLE, GenericInputBinding::Triangle},
{"Circle", TRANSLATE_NOOP("Pad", "Circle"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_CIRCLE, GenericInputBinding::Circle},
{"Cross", TRANSLATE_NOOP("Pad", "Cross"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_CROSS, GenericInputBinding::Cross},
{"Square", TRANSLATE_NOOP("Pad", "Square"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_SQUARE, GenericInputBinding::Square},
{"Select", TRANSLATE_NOOP("Pad", "Select"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_SELECT, GenericInputBinding::Select},
{"Start", TRANSLATE_NOOP("Pad", "Start"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_START, GenericInputBinding::Start},
{"L1", TRANSLATE_NOOP("Pad", "L1 (Left Bumper)"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_L1, GenericInputBinding::L1},
{"L2", TRANSLATE_NOOP("Pad", "L2 (Left Trigger)"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_L2, GenericInputBinding::L2},
{"R1", TRANSLATE_NOOP("Pad", "R1 (Right Bumper)"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_R1, GenericInputBinding::R1},
{"R2", TRANSLATE_NOOP("Pad", "R2 (Right Trigger)"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_R2, GenericInputBinding::R2},
{"L3", TRANSLATE_NOOP("Pad", "L3 (Left Stick Button)"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_L3, GenericInputBinding::L3},
{"R3", TRANSLATE_NOOP("Pad", "R3 (Right Stick Button)"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_R3, GenericInputBinding::R3},
{"Analog", TRANSLATE_NOOP("Pad", "Analog Toggle"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_ANALOG, GenericInputBinding::System},
{"Pressure", TRANSLATE_NOOP("Pad", "Apply Pressure"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_PRESSURE, GenericInputBinding::Unknown},
{"LUp", TRANSLATE_NOOP("Pad", "Left Stick Up"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_L_UP, GenericInputBinding::LeftStickUp},
{"LRight", TRANSLATE_NOOP("Pad", "Left Stick Right"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_L_RIGHT, GenericInputBinding::LeftStickRight},
{"LDown", TRANSLATE_NOOP("Pad", "Left Stick Down"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_L_DOWN, GenericInputBinding::LeftStickDown},
{"LLeft", TRANSLATE_NOOP("Pad", "Left Stick Left"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_L_LEFT, GenericInputBinding::LeftStickLeft},
{"RUp", TRANSLATE_NOOP("Pad", "Right Stick Up"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_R_UP, GenericInputBinding::RightStickUp},
{"RRight", TRANSLATE_NOOP("Pad", "Right Stick Right"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_R_RIGHT, GenericInputBinding::RightStickRight},
{"RDown", TRANSLATE_NOOP("Pad", "Right Stick Down"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_R_DOWN, GenericInputBinding::RightStickDown},
{"RLeft", TRANSLATE_NOOP("Pad", "Right Stick Left"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::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},
// clang-format on
};
static const char* s_invert_options[] = {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_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_invert_options, 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_invert_options, 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 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 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", 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/Trigger Deadzone"),
TRANSLATE_NOOP("Pad", "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", 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},
};
const Pad::ControllerInfo PadDualshock2::ControllerInfo = {Pad::ControllerType::DualShock2, "DualShock2",
TRANSLATE_NOOP("Pad", "DualShock 2"), s_bindings, s_settings, Pad::VibrationCapabilities::LargeSmallMotors};
u8 PadDualshock2::Mystery(u8 commandByte)
{
switch (commandBytesReceived)
@ -57,19 +127,18 @@ u8 PadDualshock2::ButtonQuery(u8 commandByte)
u8 PadDualshock2::Poll(u8 commandByte)
{
PadBase* pad = g_PadManager.GetPad(this->unifiedSlot);
const u32 buttons = pad->GetButtons();
const u32 buttons = GetButtons();
switch (commandBytesReceived)
{
case 3:
this->vibrationMotors.at(0) = commandByte;
this->vibrationMotors[0] = commandByte;
return (buttons >> 8) & 0xff;
case 4:
this->vibrationMotors.at(1) = commandByte;
this->vibrationMotors[1] = commandByte;
InputManager::SetPadVibrationIntensity(this->unifiedSlot,
std::min(static_cast<float>(this->vibrationMotors.at(0)) * GetVibrationScale(0) * (1.0f / 255.0f), 1.0f),
std::min(static_cast<float>(this->vibrationMotors.at(1)) * GetVibrationScale(1) * (1.0f / 255.0f), 1.0f)
std::min(static_cast<float>(this->vibrationMotors[0]) * GetVibrationScale(0) * (1.0f / 255.0f), 1.0f),
std::min(static_cast<float>(this->vibrationMotors[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.
@ -80,40 +149,40 @@ u8 PadDualshock2::Poll(u8 commandByte)
return buttons & 0xff;
case 5:
return pad->GetPressure(Dualshock2::Inputs::PAD_R_RIGHT);
return GetPressure(Inputs::PAD_R_RIGHT);
case 6:
return pad->GetPressure(Dualshock2::Inputs::PAD_R_UP);
return GetPressure(Inputs::PAD_R_UP);
case 7:
return pad->GetPressure(Dualshock2::Inputs::PAD_L_RIGHT);
return GetPressure(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);
return GetPressure(Inputs::PAD_L_UP);
case 9:
return IsButtonBitSet(buttons, 13) ? pad->GetPressure(Dualshock2::Inputs::PAD_RIGHT) : 0;
return IsButtonBitSet(buttons, 13) ? GetPressure(Inputs::PAD_RIGHT) : 0;
case 10:
return IsButtonBitSet(buttons, 15) ? pad->GetPressure(Dualshock2::Inputs::PAD_LEFT) : 0;
return IsButtonBitSet(buttons, 15) ? GetPressure(Inputs::PAD_LEFT) : 0;
case 11:
return IsButtonBitSet(buttons, 12) ? pad->GetPressure(Dualshock2::Inputs::PAD_UP) : 0;
return IsButtonBitSet(buttons, 12) ? GetPressure(Inputs::PAD_UP) : 0;
case 12:
return IsButtonBitSet(buttons, 14) ? pad->GetPressure(Dualshock2::Inputs::PAD_DOWN) : 0;
return IsButtonBitSet(buttons, 14) ? GetPressure(Inputs::PAD_DOWN) : 0;
case 13:
return IsButtonBitSet(buttons, 4) ? pad->GetPressure(Dualshock2::Inputs::PAD_TRIANGLE) : 0;
return IsButtonBitSet(buttons, 4) ? GetPressure(Inputs::PAD_TRIANGLE) : 0;
case 14:
return IsButtonBitSet(buttons, 5) ? pad->GetPressure(Dualshock2::Inputs::PAD_CIRCLE) : 0;
return IsButtonBitSet(buttons, 5) ? GetPressure(Inputs::PAD_CIRCLE) : 0;
case 15:
return IsButtonBitSet(buttons, 6) ? pad->GetPressure(Dualshock2::Inputs::PAD_CROSS) : 0;
return IsButtonBitSet(buttons, 6) ? GetPressure(Inputs::PAD_CROSS) : 0;
case 16:
return IsButtonBitSet(buttons, 7) ? pad->GetPressure(Dualshock2::Inputs::PAD_SQUARE) : 0;
return IsButtonBitSet(buttons, 7) ? GetPressure(Inputs::PAD_SQUARE) : 0;
case 17:
return IsButtonBitSet(buttons, 2) ? pad->GetPressure(Dualshock2::Inputs::PAD_L1) : 0;
return IsButtonBitSet(buttons, 2) ? GetPressure(Inputs::PAD_L1) : 0;
case 18:
return IsButtonBitSet(buttons, 3) ? pad->GetPressure(Dualshock2::Inputs::PAD_R1) : 0;
return IsButtonBitSet(buttons, 3) ? GetPressure(Inputs::PAD_R1) : 0;
case 19:
return IsButtonBitSet(buttons, 0) ? pad->GetPressure(Dualshock2::Inputs::PAD_L2) : 0;
return IsButtonBitSet(buttons, 0) ? GetPressure(Inputs::PAD_L2) : 0;
case 20:
return IsButtonBitSet(buttons, 1) ? pad->GetPressure(Dualshock2::Inputs::PAD_R2) : 0;
return IsButtonBitSet(buttons, 1) ? GetPressure(Inputs::PAD_R2) : 0;
}
Console.Warning("%s(%02X) Did not reach a valid return path! Returning zero as a failsafe!", __FUNCTION__, commandByte);
@ -224,17 +293,15 @@ u8 PadDualshock2::StatusInfo(u8 commandByte)
u8 PadDualshock2::Constant1(u8 commandByte)
{
static bool stage;
switch (commandBytesReceived)
{
case 3:
stage = commandByte;
commandStage = commandByte != 0;
return 0x00;
case 5:
return 0x01;
case 6:
if (stage)
if (commandStage)
{
return 0x01;
}
@ -243,7 +310,7 @@ u8 PadDualshock2::Constant1(u8 commandByte)
return 0x02;
}
case 7:
if (stage)
if (commandStage)
{
return 0x01;
}
@ -253,7 +320,7 @@ u8 PadDualshock2::Constant1(u8 commandByte)
}
case 8:
g_Sio0.SetAcknowledge(false);
return (stage ? 0x14 : 0x0a);
return (commandStage ? 0x14 : 0x0a);
default:
return 0x00;
}
@ -275,15 +342,13 @@ u8 PadDualshock2::Constant2(u8 commandByte)
u8 PadDualshock2::Constant3(u8 commandByte)
{
static bool stage;
switch (commandBytesReceived)
{
case 3:
stage = commandByte;
commandStage = (commandByte != 0);
return 0x00;
case 6:
if (stage)
if (commandStage)
{
return 0x07;
}
@ -379,38 +444,43 @@ void PadDualshock2::Init()
for (u8 i = 0; i < this->rawInputs.size(); i++)
{
this->rawInputs.at(i) = 0;
this->rawInputs[i] = 0;
}
for (u8 i = 0; i < this->pressures.size(); i++)
{
this->pressures.at(i) = 0;
this->pressures[i] = 0;
}
this->axisScale = 1.0f;
this->axisDeadzone = 0.0f;
this->vibrationScale.at(0) = 0.0f;
this->vibrationScale.at(1) = 1.0f;
this->vibrationScale[0] = 0.0f;
this->vibrationScale[1] = 1.0f;
this->pressureModifier = 0.5f;
this->buttonDeadzone = 0.0f;
}
Pad::ControllerType PadDualshock2::GetType()
Pad::ControllerType PadDualshock2::GetType() const
{
return Pad::ControllerType::DualShock2;
}
const Pad::ControllerInfo& PadDualshock2::GetInfo() const
{
return ControllerInfo;
}
void PadDualshock2::Set(u32 index, float value)
{
if (index > Dualshock2::Inputs::LENGTH)
if (index > Inputs::LENGTH)
{
return;
}
// Since we reordered the buttons for better UI, we need to remap them here.
static constexpr std::array<u8, Dualshock2::Inputs::LENGTH> bitmaskMapping = {{
static constexpr std::array<u8, Inputs::LENGTH> bitmaskMapping = {{
12, // PAD_UP
13, // PAD_RIGHT
14, // PAD_DOWN
@ -445,17 +515,17 @@ void PadDualshock2::Set(u32 index, float value)
// 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)
if (index <= 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);
this->analogs.lx = this->analogs.lxInvert ? MERGE(Inputs::PAD_L_LEFT, Inputs::PAD_L_RIGHT) : MERGE(Inputs::PAD_L_RIGHT, Inputs::PAD_L_LEFT);
this->analogs.ly = this->analogs.lyInvert ? MERGE(Inputs::PAD_L_UP, Inputs::PAD_L_DOWN) : MERGE(Inputs::PAD_L_DOWN, 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);
this->analogs.rx = this->analogs.rxInvert ? MERGE(Inputs::PAD_R_LEFT, Inputs::PAD_R_RIGHT) : MERGE(Inputs::PAD_R_RIGHT, Inputs::PAD_R_LEFT);
this->analogs.ry = this->analogs.ryInvert ? MERGE(Inputs::PAD_R_UP, Inputs::PAD_R_DOWN) : MERGE(Inputs::PAD_R_DOWN, Inputs::PAD_R_UP);
}
#undef MERGE
@ -466,15 +536,15 @@ void PadDualshock2::Set(u32 index, float value)
{
#define MERGE_F(pos, neg) ((this->rawInputs[pos] != 0) ? (static_cast<float>(this->rawInputs[pos]) / 255.0f) : (static_cast<float>(this->rawInputs[neg]) / -255.0f))
float posX, posY;
if (index <= Dualshock2::Inputs::PAD_L_LEFT)
if (index <= 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);
posX = this->analogs.lxInvert ? MERGE_F(Inputs::PAD_L_LEFT, Inputs::PAD_L_RIGHT) : MERGE_F(Inputs::PAD_L_RIGHT, Inputs::PAD_L_LEFT);
posY = this->analogs.lyInvert ? MERGE_F(Inputs::PAD_L_UP, Inputs::PAD_L_DOWN) : MERGE_F(Inputs::PAD_L_DOWN, 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);
posX = this->analogs.rxInvert ? MERGE_F(Inputs::PAD_R_LEFT, Inputs::PAD_R_RIGHT) : MERGE_F(Inputs::PAD_R_RIGHT, Inputs::PAD_R_LEFT);
posY = this->analogs.ryInvert ? MERGE_F(Inputs::PAD_R_UP, Inputs::PAD_R_DOWN) : MERGE_F(Inputs::PAD_R_DOWN, Inputs::PAD_R_UP);
}
// No point checking if we're at dead center (usually keyboard with no buttons pressed).
@ -494,7 +564,7 @@ void PadDualshock2::Set(u32 index, float value)
if (inX && inY)
{
// In deadzone. Set to 127 (center).
if (index <= Dualshock2::Inputs::PAD_L_LEFT)
if (index <= Inputs::PAD_L_LEFT)
{
this->analogs.lx = this->analogs.ly = 127;
}
@ -520,7 +590,7 @@ void PadDualshock2::Set(u32 index, float value)
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 pMod = ((this->buttons & (1u << Inputs::PAD_PRESSURE)) == 0 && !IsTriggerKey(index)) ? this->pressureModifier : 1.0f;
const float dzValue = (value < this->buttonDeadzone) ? 0.0f : value;
this->rawInputs[index] = static_cast<u8>(std::clamp(dzValue * pMod * 255.0f, 0.0f, 255.0f));
@ -534,11 +604,11 @@ void PadDualshock2::Set(u32 index, float value)
}
// Adjust pressure of all other face buttons which are active when pressure modifier is pressed..
if (index == Dualshock2::Inputs::PAD_PRESSURE)
if (index == Inputs::PAD_PRESSURE)
{
const float adjustPMod = ((this->buttons & (1u << Dualshock2::Inputs::PAD_PRESSURE)) == 0) ? this->pressureModifier : (1.0f / this->pressureModifier);
const float adjustPMod = ((this->buttons & (1u << Inputs::PAD_PRESSURE)) == 0) ? this->pressureModifier : (1.0f / this->pressureModifier);
for (u32 i = 0; i < Dualshock2::Inputs::LENGTH; i++)
for (u32 i = 0; i < Inputs::LENGTH; i++)
{
if (i == index || IsAnalogKey(i) || IsTriggerKey(i))
{
@ -551,7 +621,7 @@ void PadDualshock2::Set(u32 index, float value)
}
}
if (index == Dualshock2::Inputs::PAD_ANALOG && !this->analogPressed && value > 0)
if (index == Inputs::PAD_ANALOG && !this->analogPressed && value > 0)
{
this->analogPressed = true;
@ -605,7 +675,7 @@ void PadDualshock2::SetTriggerScale(float deadzone, float scale)
this->triggerScale = scale;
}
float PadDualshock2::GetVibrationScale(u32 motor)
float PadDualshock2::GetVibrationScale(u32 motor) const
{
return this->vibrationScale[motor];
}
@ -615,7 +685,7 @@ void PadDualshock2::SetVibrationScale(u32 motor, float scale)
this->vibrationScale[motor] = scale;
}
float PadDualshock2::GetPressureModifier()
float PadDualshock2::GetPressureModifier() const
{
return this->pressureModifier;
}
@ -642,63 +712,59 @@ void PadDualshock2::SetAnalogInvertR(bool x, bool y)
this->analogs.ryInvert = y;
}
u8 PadDualshock2::GetRawInput(u32 index)
u8 PadDualshock2::GetRawInput(u32 index) const
{
return this->rawInputs[index];
return rawInputs[index];
}
std::tuple<u8, u8> PadDualshock2::GetRawLeftAnalog()
std::tuple<u8, u8> PadDualshock2::GetRawLeftAnalog() const
{
return {this->analogs.lx, this->analogs.ly};
return {analogs.lx, analogs.ly};
}
std::tuple<u8, u8> PadDualshock2::GetRawRightAnalog()
std::tuple<u8, u8> PadDualshock2::GetRawRightAnalog() const
{
return {this->analogs.rx, this->analogs.ry};
return {analogs.rx, analogs.ry};
}
u32 PadDualshock2::GetButtons()
u32 PadDualshock2::GetButtons() const
{
return this->buttons;
return buttons;
}
u8 PadDualshock2::GetPressure(u32 index)
u8 PadDualshock2::GetPressure(u32 index) const
{
switch (index)
{
case Dualshock2::Inputs::PAD_R_LEFT:
case Dualshock2::Inputs::PAD_R_RIGHT:
case Inputs::PAD_R_LEFT:
case Inputs::PAD_R_RIGHT:
return this->analogs.rx;
case Dualshock2::Inputs::PAD_R_DOWN:
case Dualshock2::Inputs::PAD_R_UP:
case Inputs::PAD_R_DOWN:
case Inputs::PAD_R_UP:
return this->analogs.ry;
case Dualshock2::Inputs::PAD_L_LEFT:
case Dualshock2::Inputs::PAD_L_RIGHT:
case Inputs::PAD_L_LEFT:
case Inputs::PAD_L_RIGHT:
return this->analogs.lx;
case Dualshock2::Inputs::PAD_L_DOWN:
case Dualshock2::Inputs::PAD_L_UP:
case Inputs::PAD_L_DOWN:
case Inputs::PAD_L_UP:
return this->analogs.ly;
default:
return this->rawInputs.at(index);
return this->rawInputs[index];
}
}
void PadDualshock2::Freeze(StateWrapper& sw)
bool PadDualshock2::Freeze(StateWrapper& sw)
{
// Protected PadBase members
sw.Do(&rawInputs);
sw.Do(&unifiedSlot);
sw.Do(&isInConfig);
sw.Do(&currentMode);
sw.Do(&currentCommand);
sw.Do(&commandBytesReceived);
if (!PadBase::Freeze(sw) || !sw.DoMarker("PadDualshock2"))
return false;
// Private PadDualshock2 members
sw.Do(&buttons);
sw.DoBytes(&analogs, sizeof(Dualshock2::Analogs));
sw.DoBytes(&analogs, sizeof(Analogs));
sw.Do(&analogLight);
sw.Do(&analogLocked);
sw.Do(&analogPressed);
sw.Do(&commandStage);
sw.Do(&responseBytes);
sw.Do(&pressures);
sw.Do(&vibrationMotors);
@ -709,6 +775,7 @@ void PadDualshock2::Freeze(StateWrapper& sw)
sw.Do(&vibrationScale);
sw.Do(&pressureModifier);
sw.Do(&buttonDeadzone);
return !sw.HasError();
}
u8 PadDualshock2::SendCommandByte(u8 commandByte)

View File

@ -16,7 +16,6 @@
#pragma once
#include "SIO/Pad/PadBase.h"
#include "SIO/Pad/PadDualshock2Types.h"
#include <array>
@ -25,30 +24,68 @@ static inline bool IsButtonBitSet(u32 value, size_t bit)
return !(value & (1 << bit));
}
static inline bool IsAnalogKey(int index)
class PadDualshock2 final : public PadBase
{
return ((index >= Dualshock2::Inputs::PAD_L_UP) && (index <= Dualshock2::Inputs::PAD_R_LEFT));
}
public:
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 inline bool IsTriggerKey(int index)
{
return (index == Dualshock2::Inputs::PAD_L2 || index == Dualshock2::Inputs::PAD_R2);
}
static constexpr u32 PRESSURE_BUTTONS = 12;
static constexpr u8 VIBRATION_MOTORS = 2;
class PadDualshock2 : public PadBase
{
private:
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;
};
u32 buttons;
Dualshock2::Analogs analogs;
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;
bool commandStage = false;
u32 responseBytes;
std::array<u8, Dualshock2::PRESSURE_BUTTONS> pressures;
std::array<u8, Dualshock2::VIBRATION_MOTORS> vibrationMotors;
std::array<u8, PRESSURE_BUTTONS> pressures;
std::array<u8, VIBRATION_MOTORS> vibrationMotors;
float axisScale;
float axisDeadzone;
float triggerScale;
@ -74,28 +111,41 @@ private:
public:
PadDualshock2(u8 unifiedSlot);
virtual ~PadDualshock2();
~PadDualshock2() override;
static inline bool IsAnalogKey(int index)
{
return ((index >= Inputs::PAD_L_UP) && (index <= Inputs::PAD_R_LEFT));
}
static inline bool IsTriggerKey(int index)
{
return (index == Inputs::PAD_L2 || index == Inputs::PAD_R2);
}
void Init() override;
Pad::ControllerType GetType() override;
Pad::ControllerType GetType() const override;
const Pad::ControllerInfo& GetInfo() const override;
void Set(u32 index, float value) override;
void SetRawAnalogs(const std::tuple<u8, u8> left, const std::tuple<u8, u8> right) override;
void SetAxisScale(float deadzone, float scale) override;
void SetTriggerScale(float deadzone, float scale) override;
float GetVibrationScale(u32 motor) override;
float GetVibrationScale(u32 motor) const override;
void SetVibrationScale(u32 motor, float scale) override;
float GetPressureModifier() override;
float GetPressureModifier() const 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<u8, u8> GetRawLeftAnalog() override;
std::tuple<u8, u8> GetRawRightAnalog() override;
u32 GetButtons() override;
u8 GetPressure(u32 index) override;
u8 GetRawInput(u32 index) const override;
std::tuple<u8, u8> GetRawLeftAnalog() const override;
std::tuple<u8, u8> GetRawRightAnalog() const override;
u32 GetButtons() const override;
u8 GetPressure(u32 index) const override;
void Freeze(StateWrapper& sw) override;
bool Freeze(StateWrapper& sw) override;
u8 SendCommandByte(u8 commandByte) override;
static const Pad::ControllerInfo ControllerInfo;
};

View File

@ -1,133 +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 <http://www.gnu.org/licenses/>.
*/
#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

View File

@ -16,12 +16,40 @@
#include "PrecompiledHeader.h"
#include "SIO/Pad/PadGuitar.h"
#include "SIO/Pad/PadManager.h"
#include "SIO/Pad/PadGuitarTypes.h"
#include "SIO/Pad/Pad.h"
#include "SIO/Sio.h"
#include "Common.h"
#include "Host.h"
// 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 s_bindings[] = {
// clang-format off
{"Up", TRANSLATE_NOOP("Pad", "Strum Up"), InputBindingInfo::Type::Button, PadGuitar::Inputs::STRUM_UP, GenericInputBinding::DPadUp},
{"Down", TRANSLATE_NOOP("Pad", "Strum Down"), InputBindingInfo::Type::Button, PadGuitar::Inputs::STRUM_DOWN, GenericInputBinding::DPadDown},
{"Select", TRANSLATE_NOOP("Pad", "Select"), InputBindingInfo::Type::Button, PadGuitar::Inputs::SELECT, GenericInputBinding::Select},
{"Start", TRANSLATE_NOOP("Pad", "Start"), InputBindingInfo::Type::Button, PadGuitar::Inputs::START, GenericInputBinding::Start},
{"Green", TRANSLATE_NOOP("Pad", "Green Fret"), InputBindingInfo::Type::Button, PadGuitar::Inputs::GREEN, GenericInputBinding::R2},
{"Red", TRANSLATE_NOOP("Pad", "Red Fret"), InputBindingInfo::Type::Button, PadGuitar::Inputs::RED, GenericInputBinding::Circle},
{"Yellow", TRANSLATE_NOOP("Pad", "Yellow Fret"), InputBindingInfo::Type::Button, PadGuitar::Inputs::YELLOW, GenericInputBinding::Triangle},
{"Blue", TRANSLATE_NOOP("Pad", "Blue Fret"), InputBindingInfo::Type::Button, PadGuitar::Inputs::BLUE, GenericInputBinding::Cross},
{"Orange", TRANSLATE_NOOP("Pad", "Orange Fret"), InputBindingInfo::Type::Button, PadGuitar::Inputs::ORANGE, GenericInputBinding::Square},
{"Whammy", TRANSLATE_NOOP("Pad", "Whammy Bar"), InputBindingInfo::Type::HalfAxis, PadGuitar::Inputs::WHAMMY, GenericInputBinding::LeftStickUp},
{"Tilt", TRANSLATE_NOOP("Pad", "Tilt Up"), InputBindingInfo::Type::Button, PadGuitar::Inputs::TILT, GenericInputBinding::L2},
// clang-format on
};
static const SettingInfo s_settings[] = {
{SettingInfo::Type::Float, "Deadzone", TRANSLATE_NOOP("Pad", "Whammy Bar Deadzone"),
TRANSLATE_NOOP("Pad", "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", TRANSLATE_NOOP("Pad", "Whammy Bar Sensitivity"),
TRANSLATE_NOOP("Pad", "Sets the whammy bar axis scaling factor."), "1.0", "0.01", "2.00", "0.01", "%.0f%%",
nullptr, nullptr, 100.0f},
};
const Pad::ControllerInfo PadGuitar::ControllerInfo = {Pad::ControllerType::Guitar, "Guitar",
TRANSLATE_NOOP("Pad", "Guitar"), s_bindings, s_settings, Pad::VibrationCapabilities::NoVibration};
u8 PadGuitar::Mystery(u8 commandByte)
{
@ -54,8 +82,7 @@ u8 PadGuitar::ButtonQuery(u8 commandByte)
u8 PadGuitar::Poll(u8 commandByte)
{
PadBase* pad = g_PadManager.GetPad(this->unifiedSlot);
const u32 buttons = pad->GetButtons();
const u32 buttons = GetButtons();
switch (this->commandBytesReceived)
{
@ -70,7 +97,7 @@ u8 PadGuitar::Poll(u8 commandByte)
case 7:
return 0x7f;
case 8:
return pad->GetPressure(Guitar::Inputs::WHAMMY);
return GetPressure(Inputs::WHAMMY);
}
Console.Warning("%s(%02X) Did not reach a valid return path! Returning zero as a failsafe!", __FUNCTION__, commandByte);
@ -98,11 +125,11 @@ u8 PadGuitar::Config(u8 commandByte)
{
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",
Console.WriteLn("[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")));
(this->analogLocked ? "Locked" : "Usable"));
}
else
{
@ -163,21 +190,19 @@ u8 PadGuitar::StatusInfo(u8 commandByte)
u8 PadGuitar::Constant1(u8 commandByte)
{
static bool stage;
switch (this->commandBytesReceived)
{
case 3:
stage = commandByte;
commandStage = (commandByte != 0);
return 0x00;
case 5:
return 0x01;
case 6:
return (!stage ? 0x02 : 0x01);
return (!commandStage ? 0x02 : 0x01);
case 7:
return (!stage ? 0x00 : 0x01);
return (!commandStage ? 0x00 : 0x01);
case 8:
return (stage ? 0x0a : 0x14);
return (commandStage ? 0x0a : 0x14);
default:
return 0x00;
}
@ -198,15 +223,13 @@ u8 PadGuitar::Constant2(u8 commandByte)
u8 PadGuitar::Constant3(u8 commandByte)
{
static bool stage;
switch (this->commandBytesReceived)
{
case 3:
stage = commandByte;
commandStage = (commandByte != 0);
return 0x00;
case 6:
return (!stage ? 0x04 : 0x07);
return (!commandStage ? 0x04 : 0x07);
default:
return 0x00;
}
@ -214,11 +237,7 @@ u8 PadGuitar::Constant3(u8 commandByte)
u8 PadGuitar::VibrationMap(u8 commandByte)
{
switch (this->commandBytesReceived)
{
default:
return 0xff;
}
}
PadGuitar::PadGuitar(u8 unifiedSlot)
@ -240,14 +259,19 @@ void PadGuitar::Init()
this->whammyDeadzone = 0.0f;
}
Pad::ControllerType PadGuitar::GetType()
Pad::ControllerType PadGuitar::GetType() const
{
return Pad::ControllerType::Guitar;
}
const Pad::ControllerInfo& PadGuitar::GetInfo() const
{
return ControllerInfo;
}
void PadGuitar::Set(u32 index, float value)
{
if (index > Guitar::Inputs::LENGTH)
if (index > Inputs::LENGTH)
{
return;
}
@ -255,7 +279,7 @@ void PadGuitar::Set(u32 index, float value)
// 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)
if (index == Inputs::WHAMMY)
{
this->whammy = static_cast<u8>(std::clamp(127 - (value * this->whammyAxisScale) * 255.0f, 0.0f, 127.0f));
@ -279,7 +303,7 @@ void PadGuitar::Set(u32 index, float value)
this->rawInputs[index] = static_cast<u8>(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<u8, Guitar::Inputs::LENGTH> bitmaskMapping = {{
static constexpr std::array<u8, Inputs::LENGTH> bitmaskMapping = {{
12, // STRUM_UP
14, // STRUM_DOWN
8, // SELECT
@ -318,7 +342,7 @@ void PadGuitar::SetTriggerScale(float deadzone, float scale)
}
float PadGuitar::GetVibrationScale(u32 motor)
float PadGuitar::GetVibrationScale(u32 motor) const
{
return 0;
}
@ -327,7 +351,7 @@ void PadGuitar::SetVibrationScale(u32 motor, float scale)
{
}
float PadGuitar::GetPressureModifier()
float PadGuitar::GetPressureModifier() const
{
return 0;
}
@ -349,54 +373,49 @@ void PadGuitar::SetAnalogInvertR(bool x, bool y)
{
}
u8 PadGuitar::GetRawInput(u32 index)
u8 PadGuitar::GetRawInput(u32 index) const
{
return this->rawInputs[index];
return rawInputs[index];
}
std::tuple<u8, u8> PadGuitar::GetRawLeftAnalog()
std::tuple<u8, u8> PadGuitar::GetRawLeftAnalog() const
{
return std::tuple<u8, u8>{0x7f, 0x7f};
}
std::tuple<u8, u8> PadGuitar::GetRawRightAnalog()
std::tuple<u8, u8> PadGuitar::GetRawRightAnalog() const
{
return std::tuple<u8, u8>{0x7f, 0x7f};
}
u32 PadGuitar::GetButtons()
u32 PadGuitar::GetButtons() const
{
return this->buttons;
return buttons;
}
u8 PadGuitar::GetPressure(u32 index)
u8 PadGuitar::GetPressure(u32 index) const
{
if (index == Guitar::Inputs::WHAMMY)
{
return this->whammy;
}
if (index == Inputs::WHAMMY)
return whammy;
return 0;
}
void PadGuitar::Freeze(StateWrapper& sw)
bool PadGuitar::Freeze(StateWrapper& sw)
{
// Protected PadBase members
sw.Do(&rawInputs);
sw.Do(&unifiedSlot);
sw.Do(&isInConfig);
sw.Do(&currentMode);
sw.Do(&currentCommand);
sw.Do(&commandBytesReceived);
if (!PadBase::Freeze(sw) || !sw.DoMarker("PadGuitar"))
return false;
// Private PadGuitar members
sw.Do(&buttons);
sw.Do(&whammy);
sw.Do(&analogLight);
sw.Do(&analogLocked);
sw.Do(&commandStage);
sw.Do(&whammyAxisScale);
sw.Do(&whammyDeadzone);
sw.Do(&buttonDeadzone);
return !sw.HasError();
}
u8 PadGuitar::SendCommandByte(u8 commandByte)

View File

@ -17,8 +17,25 @@
#include "SIO/Pad/PadBase.h"
class PadGuitar : public PadBase
class PadGuitar final : public PadBase
{
public:
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,
};
private:
u32 buttons;
u8 whammy;
@ -27,6 +44,7 @@ private:
bool analogLight = false;
// Guitars are also instructed to "lock" their "analog light", despite not having one.
bool analogLocked = false;
bool commandStage = 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
@ -44,28 +62,31 @@ private:
public:
PadGuitar(u8 unifiedSlot);
virtual ~PadGuitar();
~PadGuitar() override;
void Init() override;
Pad::ControllerType GetType() override;
Pad::ControllerType GetType() const override;
const Pad::ControllerInfo& GetInfo() const override;
void Set(u32 index, float value) override;
void SetRawAnalogs(const std::tuple<u8, u8> left, const std::tuple<u8, u8> right) override;
void SetAxisScale(float deadzone, float scale) override;
void SetTriggerScale(float deadzone, float scale) override;
float GetVibrationScale(u32 motor) override;
float GetVibrationScale(u32 motor) const override;
void SetVibrationScale(u32 motor, float scale) override;
float GetPressureModifier() override;
float GetPressureModifier() const 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<u8, u8> GetRawLeftAnalog() override;
std::tuple<u8, u8> GetRawRightAnalog() override;
u32 GetButtons() override;
u8 GetPressure(u32 index) override;
u8 GetRawInput(u32 index) const override;
std::tuple<u8, u8> GetRawLeftAnalog() const override;
std::tuple<u8, u8> GetRawRightAnalog() const override;
u32 GetButtons() const override;
u8 GetPressure(u32 index) const override;
void Freeze(StateWrapper& sw) override;
bool Freeze(StateWrapper& sw) override;
u8 SendCommandByte(u8 commandByte) override;
static const Pad::ControllerInfo ControllerInfo;
};

View File

@ -1,62 +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 <http://www.gnu.org/licenses/>.
*/
#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

View File

@ -1,90 +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 <http://www.gnu.org/licenses/>.
*/
#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);
}
}
}

View File

@ -1,53 +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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "SIO/Pad/PadTypes.h"
#include <vector>
#include <array>
class PadMacros
{
public: // Constants
struct MacroButton
{
std::vector<u32> 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<std::array<PadMacros::MacroButton, PadMacros::NUM_MACRO_BUTTONS_PER_CONTROLLER>, 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;

View File

@ -1,139 +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 <http://www.gnu.org/licenses/>.
*/
#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<PadDualshock2>(unifiedSlot);
break;
case Pad::ControllerType::Guitar:
this->ps2Controllers.at(unifiedSlot) = std::make_unique<PadGuitar>(unifiedSlot);
break;
default:
this->ps2Controllers.at(unifiedSlot) = std::make_unique<PadNotConnected>(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;
}

View File

@ -1,46 +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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "SIO/Pad/PadBase.h"
#include <array>
class PadManager
{
private:
std::array<std::unique_ptr<PadBase>, 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;

View File

@ -17,6 +17,11 @@
#include "SIO/Pad/PadNotConnected.h"
#include "Host.h"
const Pad::ControllerInfo PadNotConnected::ControllerInfo = {Pad::ControllerType::NotConnected, "None",
TRANSLATE_NOOP("Pad", "Not Connected"), {}, {}, Pad::VibrationCapabilities::NoVibration };
PadNotConnected::PadNotConnected(u8 unifiedSlot)
: PadBase(unifiedSlot)
{
@ -30,11 +35,16 @@ void PadNotConnected::Init()
}
Pad::ControllerType PadNotConnected::GetType()
Pad::ControllerType PadNotConnected::GetType() const
{
return Pad::ControllerType::NotConnected;
}
const Pad::ControllerInfo& PadNotConnected::GetInfo() const
{
return ControllerInfo;
}
void PadNotConnected::Set(u32 index, float value)
{
@ -55,7 +65,7 @@ void PadNotConnected::SetTriggerScale(float deadzone, float scale)
}
float PadNotConnected::GetVibrationScale(u32 motor)
float PadNotConnected::GetVibrationScale(u32 motor) const
{
return 0;
}
@ -65,7 +75,7 @@ void PadNotConnected::SetVibrationScale(u32 motor, float scale)
}
float PadNotConnected::GetPressureModifier()
float PadNotConnected::GetPressureModifier() const
{
return 0;
}
@ -90,42 +100,31 @@ void PadNotConnected::SetAnalogInvertR(bool x, bool y)
}
u8 PadNotConnected::GetRawInput(u32 index)
u8 PadNotConnected::GetRawInput(u32 index) const
{
return 0;
}
std::tuple<u8, u8> PadNotConnected::GetRawLeftAnalog()
std::tuple<u8, u8> PadNotConnected::GetRawLeftAnalog() const
{
return std::tuple<u8, u8>{0, 0};
}
std::tuple<u8, u8> PadNotConnected::GetRawRightAnalog()
std::tuple<u8, u8> PadNotConnected::GetRawRightAnalog() const
{
return std::tuple<u8, u8>{0, 0};
}
u32 PadNotConnected::GetButtons()
u32 PadNotConnected::GetButtons() const
{
return 0;
}
u8 PadNotConnected::GetPressure(u32 index)
u8 PadNotConnected::GetPressure(u32 index) const
{
return 0;
}
void PadNotConnected::Freeze(StateWrapper& sw)
{
// Protected PadBase members
sw.Do(&rawInputs);
sw.Do(&unifiedSlot);
sw.Do(&isInConfig);
sw.Do(&currentMode);
sw.Do(&currentCommand);
sw.Do(&commandBytesReceived);
}
u8 PadNotConnected::SendCommandByte(u8 commandByte)
{
return 0xff;

View File

@ -17,32 +17,33 @@
#include "SIO/Pad/PadBase.h"
class PadNotConnected : public PadBase
class PadNotConnected final : public PadBase
{
public:
PadNotConnected(u8 unifiedSlot);
virtual ~PadNotConnected();
~PadNotConnected() override;
void Init();
Pad::ControllerType GetType();
void Set(u32 index, float value);
void SetRawAnalogs(const std::tuple<u8, u8> left, const std::tuple<u8, u8> right);
void SetAxisScale(float deadzone, float scale);
void Init() override;
Pad::ControllerType GetType() const override;
const Pad::ControllerInfo& GetInfo() const override;
void Set(u32 index, float value) override;
void SetRawAnalogs(const std::tuple<u8, u8> left, const std::tuple<u8, u8> right) override;
void SetAxisScale(float deadzone, float scale) override;
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<u8, u8> GetRawLeftAnalog();
std::tuple<u8, u8> GetRawRightAnalog();
u32 GetButtons();
u8 GetPressure(u32 index);
void Freeze(StateWrapper& sw) override;
float GetVibrationScale(u32 motor) const override;
void SetVibrationScale(u32 motor, float scale) override;
float GetPressureModifier() const 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) const override;
std::tuple<u8, u8> GetRawLeftAnalog() const override;
std::tuple<u8, u8> GetRawRightAnalog() const override;
u32 GetButtons() const override;
u8 GetPressure(u32 index) const override;
u8 SendCommandByte(u8 commandByte) override;
static const Pad::ControllerInfo ControllerInfo;
};

View File

@ -15,6 +15,10 @@
#pragma once
#include "Config.h"
#include <span>
namespace Pad
{
enum class Command : u8
@ -82,6 +86,19 @@ namespace Pad
Count
};
struct ControllerInfo
{
ControllerType type;
const char* name;
const char* display_name;
std::span<const InputBindingInfo> bindings;
std::span<const SettingInfo> settings;
VibrationCapabilities vibration_caps;
// Returns localized controller type name.
const char* GetLocalizedName() const;
};
// Total number of pad ports, across both multitaps.
static constexpr u32 NUM_CONTROLLER_PORTS = 8;
@ -93,4 +110,7 @@ namespace Pad
static constexpr float DEFAULT_MOTOR_SCALE = 1.0f;
static constexpr float DEFAULT_PRESSURE_MODIFIER = 0.5f;
static constexpr float DEFAULT_BUTTON_DEADZONE = 0.0f;
// Number of macro buttons per controller.
static constexpr u32 NUM_MACRO_BUTTONS_PER_CONTROLLER = 16;
} // namespace Pad

View File

@ -109,7 +109,11 @@ extern void sioNextFrame();
/// Converts a global pad index to a multitap port and slot.
extern std::tuple<u32, u32> sioConvertPadToPortAndSlot(u32 index);
/// Converts a multitap port and slot to a global pad index.
/// 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.
extern u32 sioConvertPortAndSlotToPad(u32 port, u32 slot);
/// Returns true if the given pad index is a multitap slot.

View File

@ -18,7 +18,8 @@
#include "SIO/Sio0.h"
#include "SIO/Sio.h"
#include "SIO/Pad/PadManager.h"
#include "SIO/Pad/Pad.h"
#include "SIO/Pad/PadBase.h"
#include "SIO/Memcard/MemoryCardProtocol.h"
#include "Common.h"
@ -162,7 +163,7 @@ void Sio0::SetTxData(u8 cmd)
stat |= SIO0_STAT::TX_READY | SIO0_STAT::TX_EMPTY;
stat |= (SIO0_STAT::RX_FIFO_NOT_EMPTY);
if (!ctrl & SIO0_CTRL::TX_ENABLE)
if (!(ctrl & SIO0_CTRL::TX_ENABLE))
{
Console.Warning("%s(%02X) CTRL in illegal state, exiting instantly", __FUNCTION__, cmd);
return;
@ -176,13 +177,13 @@ void Sio0::SetTxData(u8 cmd)
{
case SioMode::NOT_SET:
sioMode = cmd;
currentPad = g_PadManager.GetPad(port, slot);
currentPad = Pad::GetPad(port, slot);
currentPad->SoftReset();
mcd = &mcds[port][slot];
SetAcknowledge(true);
break;
case SioMode::PAD:
currentPad = g_PadManager.GetPad(port, slot);
currentPad = Pad::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.

View File

@ -19,7 +19,8 @@
#include "SIO/Sio.h"
#include "SIO/SioTypes.h"
#include "SIO/Pad/PadManager.h"
#include "SIO/Pad/Pad.h"
#include "SIO/Pad/PadBase.h"
#include "SIO/Memcard/MemoryCardProtocol.h"
#include "SIO/Multitap/MultitapProtocol.h"
@ -45,13 +46,13 @@ bool Sio2::Initialize()
for (size_t i = 0; i < send3.size(); i++)
{
send3.at(i) = 0;
send3[i] = 0;
}
for (size_t i = 0; i < send1.size(); i++)
{
send1.at(i) = 0;
send2.at(i) = 0;
send1[i] = 0;
send2[i] = 0;
}
dataIn = 0;
@ -127,7 +128,7 @@ void Sio2::SetCtrl(u32 value)
void Sio2::SetSend3(size_t position, u32 value)
{
this->send3.at(position) = value;
this->send3[position] = value;
if (position == 0)
{
@ -143,7 +144,7 @@ void Sio2::SetRecv1(u32 value)
void Sio2::Pad()
{
// Send PAD our current port, and get back whatever it says the first response byte should be.
std::optional<PadBase*> padPtr = g_PadManager.GetPad(port, slot);
PadBase* pad = Pad::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.
@ -151,18 +152,15 @@ void Sio2::Pad()
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();
pad->SoftReset();
while (!g_Sio2FifoIn.empty())
{
const u8 commandByte = g_Sio2FifoIn.front();
g_Sio2FifoIn.pop_front();
const u8 responseByte = padPtr.value()->SendCommandByte(commandByte);
const u8 responseByte = pad->SendCommandByte(commandByte);
g_Sio2FifoOut.push_back(responseByte);
}
}
}
void Sio2::Multitap()
@ -355,7 +353,7 @@ void Sio2::Write(u8 data)
return;
}
const u32 currentSend3 = send3.at(send3Position);
const u32 currentSend3 = send3[send3Position];
port = currentSend3 & Send3::PORT;
commandLength = (currentSend3 >> 8) & Send3::COMMAND_LENGTH_MASK;
send3Read = true;

View File

@ -28,7 +28,7 @@
#include "Host.h"
#include "MTGS.h"
#include "MTVU.h"
#include "SIO/Pad/PadManager.h"
#include "SIO/Pad/Pad.h"
#include "Patch.h"
#include "R3000A.h"
#include "SPU2/spu2.h"
@ -314,11 +314,6 @@ 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 GS{ "GS", SysState_MTGSFreeze };
@ -604,8 +599,8 @@ public:
~SavestateEntry_PAD() override = default;
const char* GetFilename() const override { return "PAD.bin"; }
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 FreezeIn(zip_file_t* zf) const override { return SysState_ComponentFreezeInNew(zf, "PAD", &Pad::Freeze); }
bool FreezeOut(SaveStateBase& writer) const override { return SysState_ComponentFreezeOutNew(writer, "PAD", 16 * 1024, &Pad::Freeze); }
bool IsRequired() const override { return true; }
};

View File

@ -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-
@ -44,6 +44,11 @@
#include "R5900.h"
#include "Recording/InputRecording.h"
#include "Recording/InputRecordingControls.h"
#include "SIO/Memcard/MemoryCardFile.h"
#include "SIO/Pad/Pad.h"
#include "SIO/Sio.h"
#include "SIO/Sio0.h"
#include "SIO/Sio2.h"
#include "SPU2/spu2.h"
#include "USB/USB.h"
#include "VMManager.h"
@ -58,13 +63,6 @@
#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"
@ -451,10 +449,12 @@ void VMManager::SetDefaultSettings(
LogSink::SetDefaultLoggingSettings(si);
}
if (controllers)
g_PadConfig.SetDefaultControllerConfig(si);
{
Pad::SetDefaultControllerConfig(si);
USB::SetDefaultConfiguration(&si);
}
if (hotkeys)
g_PadConfig.SetDefaultHotkeyConfig(si);
Pad::SetDefaultHotkeyConfig(si);
if (ui)
Host::SetDefaultUISettings(si);
}
@ -464,7 +464,7 @@ void VMManager::LoadSettings()
std::unique_lock<std::mutex> lock = Host::GetSettingsLock();
SettingsInterface* si = Host::GetSettingsInterface();
LoadCoreSettings(si);
g_PadConfig.LoadConfig(*si);
Pad::LoadConfig(*si);
Host::LoadSettings(*si, lock);
InputManager::ReloadSources(*si, lock);
InputManager::ReloadBindings(*si, *Host::GetSettingsInterfaceForBindings());
@ -1208,15 +1208,13 @@ bool VMManager::Initialize(VMBootParameters boot_params)
ScopedGuard close_spu2(&SPU2::Close);
Console.WriteLn("Initializing PAD...");
if (!g_PadManager.Initialize())
Console.WriteLn("Initializing Pad...");
if (!Pad::Initialize())
{
Host::ReportErrorAsync("Startup Error", "Failed to initialize PAD");
return false;
}
ScopedGuard close_pad = [](){
g_PadManager.Shutdown();
};
ScopedGuard close_pad = &Pad::Shutdown;
Console.WriteLn("Initializing SIO2...");
if (!g_Sio2.Initialize())
@ -1368,7 +1366,7 @@ void VMManager::Shutdown(bool save_resume_state)
vtlb_Shutdown();
USBclose();
SPU2::Close();
g_PadManager.Shutdown();
Pad::Shutdown();
g_Sio2.Shutdown();
g_Sio0.Shutdown();
DEV9close();
@ -1984,7 +1982,8 @@ void VMManager::Internal::EntryPointCompilingOnCPUThread()
void VMManager::Internal::VSyncOnCPUThread()
{
// TODO: Move frame limiting here to reduce CPU usage after sleeping...
Pad::UpdateMacroButtons();
Patch::ApplyLoadedPatches(Patch::PPT_CONTINUOUSLY);
Patch::ApplyLoadedPatches(Patch::PPT_COMBINED_0_1);

View File

@ -243,11 +243,9 @@
<ClCompile Include="SIO\Memcard\MemoryCardProtocol.cpp" />
<ClCompile Include="SIO\Multitap\MultitapProtocol.cpp" />
<ClCompile Include="SIO\Pad\PadBase.cpp" />
<ClCompile Include="SIO\Pad\PadConfig.cpp" />
<ClCompile Include="SIO\Pad\Pad.cpp" />
<ClCompile Include="SIO\Pad\PadDualshock2.cpp" />
<ClCompile Include="SIO\Pad\PadGuitar.cpp" />
<ClCompile Include="SIO\Pad\PadMacros.cpp" />
<ClCompile Include="SIO\Pad\PadManager.cpp" />
<ClCompile Include="SIO\Pad\PadNotConnected.cpp" />
<ClCompile Include="SIO\Sio.cpp" />
<ClCompile Include="SIO\Sio0.cpp" />
@ -599,14 +597,10 @@
<ClInclude Include="SIO\Memcard\MemoryCardFolder.h" />
<ClInclude Include="SIO\Memcard\MemoryCardProtocol.h" />
<ClInclude Include="SIO\Multitap\MultitapProtocol.h" />
<ClInclude Include="SIO\Pad\PadDualshock2Types.h" />
<ClInclude Include="SIO\Pad\PadGuitarTypes.h" />
<ClInclude Include="SIO\Pad\PadBase.h" />
<ClInclude Include="SIO\Pad\PadConfig.h" />
<ClInclude Include="SIO\Pad\Pad.h" />
<ClInclude Include="SIO\Pad\PadDualshock2.h" />
<ClInclude Include="SIO\Pad\PadGuitar.h" />
<ClInclude Include="SIO\Pad\PadMacros.h" />
<ClInclude Include="SIO\Pad\PadManager.h" />
<ClInclude Include="SIO\Pad\PadNotConnected.h" />
<ClInclude Include="SIO\Pad\PadTypes.h" />
<ClInclude Include="SIO\Sio.h" />

View File

@ -1418,9 +1418,6 @@
<ClCompile Include="SIO\Pad\PadGuitar.cpp">
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClCompile>
<ClCompile Include="SIO\Pad\PadManager.cpp">
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClCompile>
<ClCompile Include="SIO\Pad\PadNotConnected.cpp">
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClCompile>
@ -1433,10 +1430,7 @@
<ClCompile Include="SIO\Sio2.cpp">
<Filter>System\Ps2\Iop\SIO</Filter>
</ClCompile>
<ClCompile Include="SIO\Pad\PadMacros.cpp">
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClCompile>
<ClCompile Include="SIO\Pad\PadConfig.cpp">
<ClCompile Include="SIO\Pad\Pad.cpp">
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClCompile>
</ItemGroup>
@ -2345,12 +2339,6 @@
<ClInclude Include="SIO\Memcard\MemoryCardProtocol.h">
<Filter>System\Ps2\Iop\SIO\Memcard</Filter>
</ClInclude>
<ClInclude Include="SIO\Pad\PadDualshock2Types.h">
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClInclude>
<ClInclude Include="SIO\Pad\PadGuitarTypes.h">
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClInclude>
<ClInclude Include="SIO\Pad\PadBase.h">
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClInclude>
@ -2360,9 +2348,6 @@
<ClInclude Include="SIO\Pad\PadGuitar.h">
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClInclude>
<ClInclude Include="SIO\Pad\PadManager.h">
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClInclude>
<ClInclude Include="SIO\Pad\PadNotConnected.h">
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClInclude>
@ -2381,10 +2366,7 @@
<ClInclude Include="SIO\SioTypes.h">
<Filter>System\Ps2\Iop\SIO</Filter>
</ClInclude>
<ClInclude Include="SIO\Pad\PadMacros.h">
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClInclude>
<ClInclude Include="SIO\Pad\PadConfig.h">
<ClInclude Include="SIO\Pad\Pad.h">
<Filter>System\Ps2\Iop\SIO\PAD</Filter>
</ClInclude>
</ItemGroup>

View File

@ -436,7 +436,7 @@ mem32_t iopHwRead32_Page8( u32 addr )
if( masked_addr < 0x240 )
{
const int parm = (masked_addr-0x200) / 4;
ret = g_Sio2.send3.at(parm);
ret = g_Sio2.send3[parm];
Sio2Log.WriteLn("%s(%08X) SIO2 SEND3 Read (%08X)", __FUNCTION__, addr, ret);
}
else if( masked_addr < 0x260 )
@ -444,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) ? g_Sio2.send2.at(parm) : g_Sio2.send1.at(parm);
ret = (masked_addr & 4) ? g_Sio2.send2[parm] : g_Sio2.send1[parm];
Sio2Log.WriteLn("%s(%08X) SIO2 SEND1/2 Read (%08X)", __FUNCTION__, addr, ret);
}
else if( masked_addr <= 0x280 )

View File

@ -615,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);
g_Sio2.send2.at(parm) = val;
g_Sio2.send2[parm] = val;
}
else
{
Sio2Log.WriteLn("%s(%08X, %08X) SIO2 SEND1 Write", __FUNCTION__, addr, val);
g_Sio2.send1.at(parm) = val;
g_Sio2.send1[parm] = val;
}
}
else if( masked_addr <= 0x280 )