Compare commits

...
9 Commits
Author SHA1 Message Date
Stenzekandrefractionpcsx2 1edca6235c Sio: Migrate to StateWrapper
Also fixes the crash due to serializing pointers.

[SAVEVERSION+] and actually bump the save version.
2023-07-26 01:22:11 +01:00
Connor McLaughlinandGitHub ab5c03b1d9 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
2023-07-26 01:10:02 +10:00
TheLastRarandrefractionpcsx2 d4cb35469d DEV9: Perform a null check on ifa_addr when searching adapters 2023-07-25 13:23:01 +01:00
Stenzek fd1b1d0c24 GS/HW: Improve Basic blending
- Don't assume no overlap = free. Reduces a number of barriers.
 - Prefer SW blend for Ad blends, except when it's constant. It's
   impossible to emulate accurately in HW, because 128 is 1, not 255.
 - Prefer SW blend for AFIX > 128 on no-overlap sprites.
2023-07-25 13:04:45 +10:00
Stenzek bf96ceeacc GS/HW: Assume primitive does not overlap if it is a single quad
Enables one-barrier software blending for fullscreen quads.
We can also use tex-is-fb safely in these scenarios too.

Fixes Persona 4 menu background, Hard Hitter Tennis shadows at
Basic blending.
2023-07-25 13:04:45 +10:00
RedPanda4552andrefractionpcsx2 2c08b385e5 Pad: Revert back to old Constant 1 behavior
Fixes pad detection in NBA 2K7, possibly others.
2023-07-25 00:27:53 +01:00
JordanTheToasterandrefractionpcsx2 051d13ec7a GameDB: Various fixes
Adds missing fixes and game entry's for guitar hero and re fixes LOTR Third Age.
2023-07-24 23:18:30 +01:00
refractionpcsx2 a2a9643123 GameDB: Move Haunting Ground over to HPO Special + Round Sprite Half 2023-07-24 21:44:24 +01:00
refractionpcsx2 1286bfe75b UI/UX: Added popup when updating if savestate version changes 2023-07-24 19:42:36 +01:00
51 changed files with 1324 additions and 1345 deletions
+45 -11
View File
@@ -9583,9 +9583,9 @@ SLAJ-25049:
region: "NTSC-Unk"
SLAJ-25051:
name: "Lord of the Rings, The - The Third Age"
region: "NTSC-Unk"
region: "NTSC-Ch"
gsHWFixes:
halfPixelOffset: 1 # Fixes ghosting in cutscenes.
halfPixelOffset: 2 # Fixes ghosting.
SLAJ-25052:
name: "Urbz, The - Sims in the City"
region: "NTSC-Unk"
@@ -10245,6 +10245,11 @@ SLED-54401:
mergeSprite: 1 # Fixes misaligned bloom.
halfPixelOffset: 2 # Fixes misaligned bloom.
autoFlush: 2 # Fixes sun occlusion and brightness.
SLED-54509:
name: "Guitar Hero II [Demo]"
region: "PAL-M5"
roundModes:
vu1RoundMode: 0 # Massages the Z on the score meter for hardware mode, software doesn't really need this.
SLES-50003:
name: "Swap Magic DVD Disc v2.0"
region: "PAL-Unk"
@@ -16703,25 +16708,27 @@ SLES-52801:
name: "Lord of the Rings, The - The Third Age"
region: "PAL-M5"
gsHWFixes:
halfPixelOffset: 1 # Fixes ghosting in cutscenes.
halfPixelOffset: 2 # Fixes ghosting.
SLES-52802:
name: "Seigneur des anneaux, Le - Le Tiers Âge"
region: "PAL-F"
gsHWFixes:
halfPixelOffset: 1 # Fixes ghosting in cutscenes.
halfPixelOffset: 2 # Fixes ghosting.
SLES-52803:
name: "Herr der Ringe, Der - Das dritte Zeitalter"
region: "PAL-G"
gsHWFixes:
halfPixelOffset: 2 # Fixes ghosting.
SLES-52804:
name: "Signore degli Anelli, Il - La Terza Era"
region: "PAL-I"
gsHWFixes:
halfPixelOffset: 1 # Fixes ghosting in cutscenes.
halfPixelOffset: 2 # Fixes ghosting.
SLES-52805:
name: "Señor de Los Anillos, El - La Tercera Edad"
region: "PAL-S"
gsHWFixes:
halfPixelOffset: 1 # Fixes ghosting in cutscenes.
halfPixelOffset: 2 # Fixes ghosting.
SLES-52807:
name: "Lemony Snicket's A Series of Unfortunate Events"
region: "PAL-E"
@@ -16925,7 +16932,8 @@ SLES-52877:
region: "PAL-M5"
compat: 5
gsHWFixes:
halfPixelOffset: 1 # Fixes blurriness.
halfPixelOffset: 2 # Reduces blurriness. Normal Vertex works better, but causes some lights to disappear.
roundSprite: 1 # Further reduces blurriness.
beforeDraw: "OI_HauntingGround" # Fix bloom.
SLES-52882:
name: "Stolen"
@@ -22647,6 +22655,8 @@ SLES-54962:
region: "PAL-E"
roundModes:
vu1RoundMode: 0 # Crashes without.
gsHWFixes:
textureInsideRT: 1 # Crowd textures.
SLES-54963:
name: "Tony Hawk's Proving Ground"
region: "PAL-E"
@@ -22678,6 +22688,8 @@ SLES-54974:
region: "PAL-M4"
roundModes:
vu1RoundMode: 0 # Crashes without.
gsHWFixes:
textureInsideRT: 1 # Crowd textures.
SLES-54975:
name: "George Of The Jungle"
region: "PAL-E"
@@ -24174,6 +24186,7 @@ SLES-55605:
- SoftwareRendererFMVHack # Fixes vertical lines in FMVs.
gsHWFixes:
halfPixelOffset: 1 # Corrects post processing position.
cpuCLUTRender: 1 # Reduces the bloomy blur of characters.
clampModes:
vu1ClampMode: 3 # Fixes SPS on characters.
SLES-55609:
@@ -25445,7 +25458,7 @@ SLKA-25237:
name: "Lord of the Rings, The - The Third Age"
region: "NTSC-K"
gsHWFixes:
halfPixelOffset: 1 # Fixes ghosting in cutscenes.
halfPixelOffset: 2 # Fixes ghosting.
SLKA-25241:
name: "Need for Speed - Underground 2"
region: "NTSC-K"
@@ -25933,6 +25946,13 @@ SLKA-25410:
SLKA-25413:
name: "SD Gundam G - Generation Spirits"
region: "NTSC-K"
SLKA-25414:
name: "Guitar Hero III - Legends of Rock"
region: "NTSC-K"
roundModes:
vu1RoundMode: 0 # Crashes without.
gsHWFixes:
textureInsideRT: 1 # Crowd textures.
SLKA-25422:
name: "Silent Hill - Origins"
region: "NTSC-K"
@@ -32250,6 +32270,8 @@ SLPM-65845:
SLPM-65846:
name: "Lord of the Rings, The - Uchitsu Kuni Daisanki"
region: "NTSC-J"
gsHWFixes:
halfPixelOffset: 2 # Fixes ghosting.
SLPM-65847:
name: "Soriaro no Fuukin Remix [First Print - Limited Edition]"
region: "NTSC-J"
@@ -36909,6 +36931,7 @@ SLPM-68019:
- SoftwareRendererFMVHack # Fixes vertical lines in FMVs.
gsHWFixes:
halfPixelOffset: 1 # Corrects post processing position.
cpuCLUTRender: 1 # Reduces the bloomy blur of characters.
clampModes:
vu1ClampMode: 3 # Fixes SPS on characters.
SLPM-68503:
@@ -41914,6 +41937,7 @@ SLPS-25837:
- SoftwareRendererFMVHack # Fixes vertical lines in FMVs.
gsHWFixes:
halfPixelOffset: 1 # Corrects post processing position.
cpuCLUTRender: 1 # Reduces the bloomy blur of characters.
clampModes:
vu1ClampMode: 3 # Fixes SPS on characters.
SLPS-25838:
@@ -41927,6 +41951,8 @@ SLPS-25840:
region: "NTSC-J"
roundModes:
vu1RoundMode: 0 # Crashes without.
gsHWFixes:
textureInsideRT: 1 # Crowd textures.
SLPS-25841:
name: "Tales of Destiny [Director's Cut] [Premium Box]"
region: "NTSC-J"
@@ -42099,10 +42125,12 @@ SLPS-25889:
roundModes:
vu1RoundMode: 0 # Fixes VU size spam and potential graphical issues with GH3 engine.
SLPS-25890:
name: "Guitar Hero III - Legends of Rock [with Guitar]"
name: "Guitar Hero III - Legends of Rock"
region: "NTSC-J"
roundModes:
vu1RoundMode: 0 # Crashes without.
gsHWFixes:
textureInsideRT: 1 # Crowd textures.
SLPS-25892:
name: "Nogizaka Haruka no Himitsu - Cosplay Hajimemashita"
region: "NTSC-J"
@@ -47932,7 +47960,7 @@ SLUS-21027:
region: "NTSC-U"
compat: 5
gsHWFixes:
halfPixelOffset: 1 # Fixes ghosting in cutscenes.
halfPixelOffset: 2 # Fixes ghosting.
SLUS-21028:
name: "World Championship Poker"
region: "NTSC-U"
@@ -48190,7 +48218,8 @@ SLUS-21075:
region: "NTSC-U"
compat: 5
gsHWFixes:
halfPixelOffset: 1 # Fixes blurriness.
halfPixelOffset: 2 # Reduces blurriness. Normal Vertex works better, but causes some lights to disappear.
roundSprite: 1 # Further reduces blurriness.
beforeDraw: "OI_HauntingGround" # Fix bloom.
SLUS-21076:
name: "Atari Anthology"
@@ -53502,6 +53531,11 @@ SLUS-29174:
SLUS-29175:
name: "Namco Transmission Demo Disc Vol. 3.2"
region: "NTSC-U"
SLUS-29177:
name: "Guitar Hero [Demo]"
region: "NTSC-U"
roundModes:
vu1RoundMode: 1 # Fixes cut-off numbers and restores missing whitespace inside combo meter.
SLUS-29178:
name: "TOCA Race Driver 3 [Demo]"
region: "NTSC-U"
+19
View File
@@ -380,6 +380,8 @@ void AutoUpdaterDialog::getChangesComplete(QNetworkReply* reply)
changes_html.prepend(tr("<h2>Save State Warning</h2><p>Installing this update will make your save states "
"<b>incompatible</b>. Please ensure you have saved your games to a Memory Card "
"before installing this update or you will lose progress.</p>"));
m_update_will_break_save_states = true;
}
if (update_increases_settings_version)
@@ -407,6 +409,23 @@ void AutoUpdaterDialog::getChangesComplete(QNetworkReply* reply)
void AutoUpdaterDialog::downloadUpdateClicked()
{
if (m_update_will_break_save_states)
{
QMessageBox msgbox;
msgbox.setIcon(QMessageBox::Critical);
msgbox.setWindowTitle(tr("Savestate Warning"));
msgbox.setText(tr("<h1>WARNING</h1><p style='font-size:12pt;'>Installing this update will make your <b>save states incompatible</b>.</p><p>Do you wish to continue?</p>"));
msgbox.addButton(QMessageBox::Yes);
msgbox.addButton(QMessageBox::No);
msgbox.setDefaultButton(QMessageBox::No);
// This makes the box wider, for some reason sizing boxes in Qt is hard - Source: The internet.
QSpacerItem* horizontalSpacer = new QSpacerItem(500, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
QGridLayout* layout = (QGridLayout*)msgbox.layout();
layout->addItem(horizontalSpacer, layout->rowCount(), 0, 1, layout->columnCount());
if (msgbox.exec() != QMessageBox::Yes)
return;
}
m_display_messages = true;
QUrl url(m_download_url);
QNetworkRequest request(url);
+31 -25
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)
+3 -2
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();
+44 -1
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>
@@ -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();
+2 -2
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));
}
+5 -5
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;
+2 -8
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
)
+8 -18
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,13 +591,13 @@ 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()
+3 -1
View File
@@ -187,7 +187,9 @@ bool AdapterUtils::GetAdapter(const std::string& name, Adapter* adapter, Adapter
do
{
if (pAdapter->ifa_addr->sa_family == AF_INET && strcmp(pAdapter->ifa_name, name.c_str()) == 0)
if (pAdapter->ifa_addr != nullptr &&
pAdapter->ifa_addr->sa_family == AF_INET &&
strcmp(pAdapter->ifa_name, name.c_str()) == 0)
break;
pAdapter = pAdapter->ifa_next;
+52 -1
View File
@@ -2720,6 +2720,55 @@ void GSState::GrowVertexBuffer()
m_index.buff = index;
}
bool GSState::TrianglesAreQuads() const
{
// If this is a quad, there should only be two distinct values for both X and Y, which
// also happen to be the minimum/maximum bounds of the primitive.
const GSVertex* const v = m_vertex.buff;
for (u32 idx = 0; idx < m_index.tail; idx += 6)
{
const u16* const i = m_index.buff + idx;
// Degenerate triangles should've been culled already, so we can check indices.
u32 extra_verts = 0;
for (u32 j = 3; j < 6; j++)
{
const u16 idx = i[j];
if (idx != i[0] && idx != i[1] && idx != i[2])
extra_verts++;
}
if (extra_verts == 1)
return true;
// As a fallback, they might've used different vertices with a tri list, not strip.
// Note that this won't work unless the quad is axis-aligned.
u16 distinct_x_values[2] = {v[i[0]].XYZ.X};
u16 distinct_y_values[2] = {v[i[0]].XYZ.Y};
u32 num_distinct_x_values = 1, num_distinct_y_values = 1;
for (u32 j = 1; j < 6; j++)
{
const GSVertex& jv = v[i[j]];
if (jv.XYZ.X != distinct_x_values[0] && jv.XYZ.X != distinct_x_values[1])
{
if (num_distinct_x_values > 1)
return false;
distinct_x_values[num_distinct_x_values++] = jv.XYZ.X;
}
if (jv.XYZ.Y != distinct_y_values[0] && jv.XYZ.Y != distinct_y_values[1])
{
if (num_distinct_y_values > 1)
return false;
distinct_y_values[num_distinct_y_values++] = jv.XYZ.Y;
}
}
}
return true;
}
GSState::PRIM_OVERLAP GSState::PrimitiveOverlap()
{
// Either 1 triangle or 1 line or 3 POINTs
@@ -2727,7 +2776,9 @@ GSState::PRIM_OVERLAP GSState::PrimitiveOverlap()
if (m_vertex.next < 4)
return PRIM_OVERLAP_NO;
if (m_vt.m_primclass != GS_SPRITE_CLASS)
if (m_vt.m_primclass == GS_TRIANGLE_CLASS)
return (m_index.tail == 6 && TrianglesAreQuads()) ? PRIM_OVERLAP_NO : PRIM_OVERLAP_UNKNOW;
else if (m_vt.m_primclass != GS_SPRITE_CLASS)
return PRIM_OVERLAP_UNKNOW; // maybe, maybe not
// Check intersection of sprite primitive only
+1
View File
@@ -418,6 +418,7 @@ public:
void DumpVertices(const std::string& filename);
bool TrianglesAreQuads() const;
PRIM_OVERLAP PrimitiveOverlap();
GIFRegTEX0 GetTex0Layer(u32 lod);
};
+72 -74
View File
@@ -3345,7 +3345,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
return true;
}
void GSRendererHW::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool& blending_alpha_pass)
void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DATE_PRIMID, bool& DATE_BARRIER, bool& blending_alpha_pass)
{
// AA1: Don't enable blending on AA1, not yet implemented on hardware mode,
// it requires coverage sample so it's safer to turn it off instead.
@@ -3377,6 +3377,13 @@ void GSRendererHW::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool&
m_conf.ps.blend_c = ALPHA.C;
m_conf.ps.blend_d = ALPHA.D;
#ifdef ENABLE_OGL_DEBUG
static constexpr const char* col[3] = {"Cs", "Cd", "0"};
static constexpr const char* alpha[3] = {"As", "Ad", "Af"};
GL_INS("EmulateBlending(): (%s - %s) * %s + %s", col[ALPHA.A], col[ALPHA.B], alpha[ALPHA.C], col[ALPHA.D]);
GL_INS("Draw AlphaMinMax: %d-%d, RT AlphaMinMax: %d-%d", GetAlphaMinMax().min, GetAlphaMinMax().max, rt_alpha_min, rt_alpha_max);
#endif
// When AA1 is enabled and Alpha Blending is disabled, alpha blending done with coverage instead of alpha.
// We use a COV value of 128 (full coverage) in triangles (except the edge geometry, which we can't do easily).
if (IsCoverageAlpha())
@@ -3396,10 +3403,12 @@ void GSRendererHW::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool&
const bool alpha_c0_one = (m_conf.ps.blend_c == 0 && (GetAlphaMinMax().min == 128) && (GetAlphaMinMax().max == 128));
const bool alpha_c0_high_min_one = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().min > 128);
const bool alpha_c0_high_max_one = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().max > 128);
const bool alpha_c1_zero = (m_conf.ps.blend_c == 1 && rt_alpha_min == 0 && rt_alpha_max == 0);
const bool alpha_c1_one = (m_conf.ps.blend_c == 1 && rt_alpha_min == 128 && rt_alpha_max == 128);
const bool alpha_c2_zero = (m_conf.ps.blend_c == 2 && AFIX == 0u);
const bool alpha_c2_one = (m_conf.ps.blend_c == 2 && AFIX == 128u);
const bool alpha_c2_high_one = (m_conf.ps.blend_c == 2 && AFIX > 128u);
const bool alpha_one = alpha_c0_one || alpha_c2_one;
const bool alpha_one = alpha_c0_one || alpha_c1_one || alpha_c2_one;
// Optimize blending equations, must be done before index calculation
if ((m_conf.ps.blend_a == m_conf.ps.blend_b) || ((m_conf.ps.blend_b == m_conf.ps.blend_d) && alpha_one))
@@ -3418,7 +3427,7 @@ void GSRendererHW::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool&
m_conf.ps.blend_b = 0;
m_conf.ps.blend_c = 0;
}
else if (alpha_c0_zero || alpha_c2_zero)
else if (alpha_c0_zero || alpha_c1_zero || alpha_c2_zero)
{
// C == 0.0f
// (A - B) * C, result will be 0.0f so set A B to Cs
@@ -3444,7 +3453,8 @@ void GSRendererHW::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool&
// Ad cases, alpha write is masked, one barrier is enough, for d3d11 read the fb
// Replace Ad with As, blend flags will be used from As since we are chaging the blend_index value.
// Must be done before index calculation, after blending equation optimizations
bool blend_ad_alpha_masked = (m_conf.ps.blend_c == 1) && (m_cached_ctx.FRAME.FBMSK & 0xFF000000) == 0xFF000000;
const bool blend_ad = m_conf.ps.blend_c == 1;
bool blend_ad_alpha_masked = blend_ad && (m_cached_ctx.FRAME.FBMSK & 0xFF000000) == 0xFF000000;
if (((GSConfig.AccurateBlendingUnit >= AccBlendLevel::Basic) || (COLCLAMP.CLAMP == 0))
&& g_gs_device->Features().texture_barrier && blend_ad_alpha_masked)
m_conf.ps.blend_c = 0;
@@ -3506,13 +3516,15 @@ void GSRendererHW::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool&
const bool prefer_sw_blend = m_conf.require_full_barrier || (one_barrier && m_prim_overlap == PRIM_OVERLAP_NO);
// SW Blend is (nearly) free. Let's use it.
const bool no_prim_overlap = features.framebuffer_fetch ? (m_vt.m_primclass == GS_SPRITE_CLASS) : (m_prim_overlap == PRIM_OVERLAP_NO);
const bool no_prim_overlap = (m_prim_overlap == PRIM_OVERLAP_NO);
const bool impossible_or_free_blend = (blend_flag & BLEND_A_MAX) // Impossible blending
|| blend_non_recursive // Free sw blending, doesn't require barriers or reading fb
|| accumulation_blend // Mix of hw/sw blending
|| no_prim_overlap // Blend can be done in a single draw
|| (m_conf.require_full_barrier) // Another effect (for example fbmask) already requires a full barrier
|| (one_barrier && features.framebuffer_fetch); // On fbfetch, one barrier is like full barrier
|| (m_conf.require_full_barrier) // Another effect (for example fbmask) already requires a full barrier
// Blend can be done in a single draw, and we already need a barrier
// On fbfetch, one barrier is like full barrier
|| (one_barrier && (no_prim_overlap || features.framebuffer_fetch))
|| ((alpha_c2_high_one || alpha_c0_high_max_one) && no_prim_overlap);
switch (GSConfig.AccurateBlendingUnit)
{
@@ -3540,6 +3552,8 @@ void GSRendererHW::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool&
// If prims don't overlap prefer full sw blend on blend_ad_alpha_masked cases.
accumulation_blend &= !(prefer_sw_blend || (blend_ad_alpha_masked && m_prim_overlap == PRIM_OVERLAP_NO));
sw_blending |= impossible_or_free_blend;
// Ad blends are completely wrong without sw blend (Ad is 0.5 not 1 for 128). We can spare a barrier for it.
sw_blending |= blend_ad && no_prim_overlap;
// Try to do hw blend for clr2 case.
sw_blending &= !clr_blend1_2;
// Do not run BLEND MIX if sw blending is already present, it's less accurate
@@ -4412,7 +4426,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
}
bool GSRendererHW::CanUseTexIsFB(const GSTextureCache::Target* rt, const GSTextureCache::Source* tex,
const TextureMinMaxResult& tmm) const
const TextureMinMaxResult& tmm)
{
// Minimum blending or no barriers -> we can't use tex-is-fb.
if (GSConfig.AccurateBlendingUnit == AccBlendLevel::Minimum || !g_gs_device->Features().texture_barrier)
@@ -4460,21 +4474,8 @@ bool GSRendererHW::CanUseTexIsFB(const GSTextureCache::Target* rt, const GSTextu
// Texture is actually the frame buffer. Stencil emulation to compute shadow (Jak series/tri-ace game)
// Will hit the "m_ps_sel.tex_is_fb = 1" path in the draw
if (m_vt.m_primclass == GS_TRIANGLE_CLASS)
{
// This pattern is used by several games to emulate a stencil (shadow)
// Ratchet & Clank, Jak do alpha integer multiplication (tfx) which is mostly equivalent to +1/-1
// Tri-Ace (Star Ocean 3/RadiataStories/VP2) uses a palette to handle the +1/-1
if (m_cached_ctx.FRAME.FBMSK == 0x00FFFFFF)
{
GL_CACHE("Tex-is-fb hack for Jak");
return true;
}
GL_CACHE("Triangle draw, not using tex-is-fb");
return false;
}
else if (m_vt.m_primclass == GS_SPRITE_CLASS)
const bool is_quads = (m_vt.m_primclass == GS_SPRITE_CLASS || m_prim_overlap == PRIM_OVERLAP_NO);
if (is_quads)
{
// No bilinear for tex-is-fb.
if (m_vt.IsLinear())
@@ -4507,6 +4508,21 @@ bool GSRendererHW::CanUseTexIsFB(const GSTextureCache::Target* rt, const GSTextu
return false;
}
if (m_vt.m_primclass == GS_TRIANGLE_CLASS)
{
// This pattern is used by several games to emulate a stencil (shadow)
// Ratchet & Clank, Jak do alpha integer multiplication (tfx) which is mostly equivalent to +1/-1
// Tri-Ace (Star Ocean 3/RadiataStories/VP2) uses a palette to handle the +1/-1
if (m_cached_ctx.FRAME.FBMSK == 0x00FFFFFF)
{
GL_CACHE("Tex-is-fb hack for Jak");
return true;
}
GL_CACHE("Triangle draw, not using tex-is-fb");
return false;
}
return false;
}
@@ -4605,14 +4621,12 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
// Upscaling hack to avoid various line/grid issues
MergeSprite(tex);
const GSDevice::FeatureSupport features(g_gs_device->Features());
if (!features.framebuffer_fetch)
m_prim_overlap = PrimitiveOverlap();
else
m_prim_overlap = PRIM_OVERLAP_UNKNOW;
m_prim_overlap = PrimitiveOverlap();
EmulateTextureShuffleAndFbmask(rt);
const GSDevice::FeatureSupport features = g_gs_device->Features();
// DATE: selection of the algorithm. Must be done before blending because GL42 is not compatible with blending
if (DATE)
{
@@ -4709,8 +4723,20 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
}
// Blend
int blend_alpha_min = 0, blend_alpha_max = 255;
if (rt)
{
blend_alpha_min = rt->m_alpha_min;
blend_alpha_max = rt->m_alpha_max;
const bool is_24_bit = (GSLocalMemory::m_psm[rt->m_TEX0.PSM].trbpp == 24);
if (is_24_bit)
{
// C24/Z24 - alpha is 1.
blend_alpha_min = 128;
blend_alpha_max = 128;
}
if (!m_channel_shuffle && !m_texture_shuffle)
{
const int fba_value = m_prev_env.CTXT[m_prev_env.PRIM.CTXT].FBA.FBA * 128;
@@ -4732,7 +4758,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
rt->m_alpha_max |= std::max(GetAlphaMinMax().max | fba_value, rt->m_alpha_max);
rt->m_alpha_min = std::min(GetAlphaMinMax().min | fba_value, rt->m_alpha_min);
}
else
else if (!is_24_bit)
{
// If both are zero then we probably don't know what the alpha is.
if (rt->m_alpha_max == 0 && rt->m_alpha_min == 0)
@@ -4747,6 +4773,16 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
rt->m_alpha_max = 255;
rt->m_alpha_min = 0;
}
GL_INS("RT Alpha Range: %d-%d => %d-%d", blend_alpha_min, blend_alpha_max, rt->m_alpha_min, rt->m_alpha_max);
// If there's no overlap, the values in the RT before FB write will be the old values.
if (m_prim_overlap != PRIM_OVERLAP_NO)
{
// Otherwise, it may be a mix of the old/new values.
blend_alpha_min = std::min(blend_alpha_min, rt->m_alpha_min);
blend_alpha_max = std::max(blend_alpha_max, rt->m_alpha_max);
}
}
// Not gonna spend too much time with this, it's not likely to be used much, can't be less accurate than it was.
@@ -4754,12 +4790,13 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
{
ds->m_alpha_max = std::max(ds->m_alpha_max, static_cast<int>(m_vt.m_max.p.z) >> 24);
ds->m_alpha_min = std::min(ds->m_alpha_min, static_cast<int>(m_vt.m_min.p.z) >> 24);
GL_INS("New DS Alpha Range: %d-%d", ds->m_alpha_min, ds->m_alpha_max);
}
bool blending_alpha_pass = false;
if ((!IsOpaque() || m_context->ALPHA.IsBlack()) && rt && (m_conf.colormask.wrgba & 0x7))
{
EmulateBlending(DATE_PRIMID, DATE_BARRIER, blending_alpha_pass);
EmulateBlending(blend_alpha_min, blend_alpha_max, DATE_PRIMID, DATE_BARRIER, blending_alpha_pass);
}
else
{
@@ -5798,52 +5835,13 @@ bool GSRendererHW::PrimitiveCoversWithoutGaps()
if (m_vt.m_primclass == GS_POINT_CLASS)
{
m_primitive_covers_without_gaps = true;
return true;
m_primitive_covers_without_gaps = (m_vertex.next < 2);
return m_primitive_covers_without_gaps.value();
}
else if (m_vt.m_primclass == GS_TRIANGLE_CLASS)
{
if (m_index.tail != 6)
{
m_primitive_covers_without_gaps = false;
return false;
}
// If this is a quad, there should only be two distinct values for both X and Y, which
// also happen to be the minimum/maximum bounds of the primitive.
const GSVertex* const v = m_vertex.buff;
const u16* const i = m_index.buff;
u16 distinct_x_values[2] = {v[i[0]].XYZ.X};
u16 distinct_y_values[2] = {v[i[0]].XYZ.Y};
u32 num_distinct_x_values = 1, num_distinct_y_values = 1;
for (u32 j = 1; j < 6; j++)
{
const GSVertex& jv = v[i[j]];
if (jv.XYZ.X != distinct_x_values[0] && jv.XYZ.X != distinct_x_values[1])
{
if (num_distinct_x_values > 1)
{
m_primitive_covers_without_gaps = false;
return false;
}
distinct_x_values[num_distinct_x_values++] = jv.XYZ.X;
}
if (jv.XYZ.Y != distinct_y_values[0] && jv.XYZ.Y != distinct_y_values[1])
{
if (num_distinct_y_values > 1)
{
m_primitive_covers_without_gaps = false;
return false;
}
distinct_y_values[num_distinct_y_values++] = jv.XYZ.Y;
}
}
m_primitive_covers_without_gaps = true;
return true;
m_primitive_covers_without_gaps = (m_index.tail == 6 && TrianglesAreQuads());
return m_primitive_covers_without_gaps.value();
}
else if (m_vt.m_primclass != GS_SPRITE_CLASS)
{
+2 -2
View File
@@ -86,7 +86,7 @@ private:
void SetupIA(float target_scale, float sx, float sy);
void EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt);
bool EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only);
void EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool& blending_alpha_pass);
void EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DATE_PRIMID, bool& DATE_BARRIER, bool& blending_alpha_pass);
void EmulateTextureSampler(const GSTextureCache::Target* rt, const GSTextureCache::Target* ds,
GSTextureCache::Source* tex, const TextureMinMaxResult& tmm, GSTexture*& src_copy);
@@ -94,7 +94,7 @@ private:
const GSTextureCache::Source* tex, const TextureMinMaxResult& tmm, GSTextureCache::SourceRegion& source_region,
bool& target_region, GSVector2i& unscaled_size, float& scale, GSTexture*& src_copy);
bool CanUseTexIsFB(const GSTextureCache::Target* rt, const GSTextureCache::Source* tex,
const TextureMinMaxResult& tmm) const;
const TextureMinMaxResult& tmm);
void EmulateZbuffer(const GSTextureCache::Target* ds);
void EmulateATST(float& AREF, GSHWDrawConfig::PSSelector& ps, bool pass_2);
+2 -2
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
+31 -33
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,25 +3912,23 @@ 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(),
[bi](const std::string_view& it) { return (it == bi.name); }));
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); }));
}
OpenChoiceDialog(fmt::format("Select Macro {} Binds", macro_index + 1).c_str(), true, std::move(options),
[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,18 +4016,15 @@ 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));
+47 -67
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,18 +485,8 @@ 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)
{
num_ports++;
}
}
if (Pad::HasConnectedPad(slot))
num_ports++;
}
for (u32 port = 0; port < USB::NUM_PORTS; port++)
@@ -515,65 +505,55 @@ void ImGuiManager::DrawInputsOverlay()
for (u32 slot = 0; slot < Pad::NUM_CONTROLLER_PORTS; slot++)
{
const std::optional<PadBase*> padOpt = g_PadManager.GetPad(slot);
const PadBase* const pad = Pad::GetPad(slot);
const Pad::ControllerType ctype = pad->GetType();
if (ctype == Pad::ControllerType::NotConnected)
continue;
text.clear();
fmt::format_to(std::back_inserter(text), "P{} |", slot + 1u);
if (padOpt.has_value())
const Pad::ControllerInfo& cinfo = pad->GetInfo();
for (u32 bind = 0; bind < static_cast<u32>(cinfo.bindings.size()); bind++)
{
PadBase* pad = padOpt.value();
const Pad::ControllerType ctype = pad->GetType();
if (ctype == Pad::ControllerType::NotConnected)
continue;
const PadConfig::ControllerInfo* cinfo = g_PadConfig.GetControllerInfo(ctype);
if (!cinfo)
continue;
text.clear();
fmt::format_to(std::back_inserter(text), "P{} |", slot + 1u);
for (u32 bind = 0; bind < cinfo->num_bindings; bind++)
const InputBindingInfo& bi = cinfo.bindings[bind];
switch (bi.bind_type)
{
const InputBindingInfo& bi = cinfo->bindings[bind];
switch (bi.bind_type)
case InputBindingInfo::Type::Axis:
case InputBindingInfo::Type::HalfAxis:
{
case InputBindingInfo::Type::Axis:
case InputBindingInfo::Type::HalfAxis:
{
// axes are always shown
const float value = static_cast<float>(pad->GetRawInput(bind)) * (1.0f / 255.0f);
if (value >= (254.0f / 255.0f))
fmt::format_to(std::back_inserter(text), " {}", bi.name);
else if (value > (1.0f / 255.0f))
fmt::format_to(std::back_inserter(text), " {}: {:.2f}", bi.name, value);
}
break;
case InputBindingInfo::Type::Button:
{
// buttons only shown when active
const float value = static_cast<float>(pad->GetRawInput(bind)) * (1.0f / 255.0f);
if (value >= 0.5f)
fmt::format_to(std::back_inserter(text), " {}", bi.name);
}
break;
case InputBindingInfo::Type::Motor:
case InputBindingInfo::Type::Macro:
case InputBindingInfo::Type::Unknown:
default:
break;
// axes are always shown
const float value = static_cast<float>(pad->GetRawInput(bind)) * (1.0f / 255.0f);
if (value >= (254.0f / 255.0f))
fmt::format_to(std::back_inserter(text), " {}", bi.name);
else if (value > (1.0f / 255.0f))
fmt::format_to(std::back_inserter(text), " {}: {:.2f}", bi.name, value);
}
break;
case InputBindingInfo::Type::Button:
{
// buttons only shown when active
const float value = static_cast<float>(pad->GetRawInput(bind)) * (1.0f / 255.0f);
if (value >= 0.5f)
fmt::format_to(std::back_inserter(text), " {}", bi.name);
}
break;
case InputBindingInfo::Type::Motor:
case InputBindingInfo::Type::Macro:
case InputBindingInfo::Type::Unknown:
default:
break;
}
dl->AddText(font, font->FontSize, ImVec2(current_x + shadow_offset, current_y + shadow_offset), shadow_color, text.c_str(),
text.c_str() + text.length(), 0.0f, &clip_rect);
dl->AddText(
font, font->FontSize, ImVec2(current_x, current_y), text_color, text.c_str(), text.c_str() + text.length(), 0.0f, &clip_rect);
current_y += font->FontSize + spacing;
}
dl->AddText(font, font->FontSize, ImVec2(current_x + shadow_offset, current_y + shadow_offset), shadow_color, text.c_str(),
text.c_str() + text.length(), 0.0f, &clip_rect);
dl->AddText(
font, font->FontSize, ImVec2(current_x, current_y), text_color, text.c_str(), text.c_str() + text.length(), 0.0f, &clip_rect);
current_y += font->FontSize + spacing;
}
for (u32 port = 0; port < USB::NUM_PORTS; port++)
+10 -12
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;
+48 -48
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)
+2 -2
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;
@@ -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);
}
}
}
@@ -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
+15
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();
}
+11 -9
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;
};
+164 -86
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,25 +293,34 @@ 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 0x00;
return 0x01;
}
else
{
return 0x02;
}
case 7:
if (commandStage)
{
return 0x01;
}
else
{
return 0x00;
}
case 8:
g_Sio0.SetAcknowledge(false);
return (stage ? 0x14 : 0x0a);
return (commandStage ? 0x14 : 0x0a);
default:
return 0x00;
}
@@ -264,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;
}
@@ -368,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
@@ -434,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
@@ -455,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).
@@ -483,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;
}
@@ -509,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));
@@ -523,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))
{
@@ -540,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;
@@ -594,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];
}
@@ -604,7 +685,7 @@ void PadDualshock2::SetVibrationScale(u32 motor, float scale)
this->vibrationScale[motor] = scale;
}
float PadDualshock2::GetPressureModifier()
float PadDualshock2::GetPressureModifier() const
{
return this->pressureModifier;
}
@@ -631,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);
@@ -698,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)
+73 -23
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;
};
-133
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
+68 -49
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;
}
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)
+32 -11
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;
};
-62
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
-90
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);
}
}
}
-53
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;
-139
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;
}
-46
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;
+18 -19
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;
+22 -21
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;
};
+20
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
+5 -1
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.
+30 -14
View File
@@ -15,16 +15,16 @@
#include "PrecompiledHeader.h"
#include "SIO/Sio0.h"
#include "SIO/Sio.h"
#include "SIO/Pad/PadManager.h"
#include "SIO/Memcard/MemoryCardProtocol.h"
#include "Common.h"
#include "IopHw.h"
#include "IopDma.h"
#include "IopHw.h"
#include "R3000A.h"
#include "SIO/Memcard/MemoryCardProtocol.h"
#include "SIO/Pad/Pad.h"
#include "SIO/Pad/PadBase.h"
#include "SIO/Sio.h"
#include "SIO/Sio0.h"
#include "StateWrapper.h"
#define SIO0LOG_ENABLE 0
#define Sio0Log if (SIO0LOG_ENABLE) DevCon
@@ -162,7 +162,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 +176,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.
@@ -315,9 +315,25 @@ u8 Sio0::Memcard(u8 value)
return 0xff;
}
bool SaveStateBase::Sio0Freeze()
bool Sio0::DoState(StateWrapper& sw)
{
FreezeTag("sio0");
Freeze(g_Sio0);
return true;
if (!sw.DoMarker("Sio0"))
return false;
sw.Do(&txData);
sw.Do(&rxData);
sw.Do(&stat);
sw.Do(&mode);
sw.Do(&ctrl);
sw.Do(&baud);
sw.Do(&flag);
sw.Do(&sioStage);
sw.Do(&sioMode);
sw.Do(&sioCommand);
sw.Do(&padStarted);
sw.Do(&rxDataSet);
sw.Do(&port);
sw.Do(&slot);
return sw.IsGood();
}
+3
View File
@@ -17,6 +17,8 @@
#include "SIO/SioTypes.h"
class StateWrapper;
class Sio0
{
private:
@@ -48,6 +50,7 @@ public:
bool Shutdown();
void SoftReset();
bool DoState(StateWrapper& sw);
void SetAcknowledge(bool ack);
void Interrupt(Sio0Interrupt sio0Interrupt);
+51 -99
View File
@@ -15,18 +15,18 @@
#include "PrecompiledHeader.h"
#include "SIO/Sio2.h"
#include "SIO/Sio.h"
#include "SIO/SioTypes.h"
#include "SIO/Pad/PadManager.h"
#include "SIO/Memcard/MemoryCardProtocol.h"
#include "SIO/Multitap/MultitapProtocol.h"
#include "Common.h"
#include "Host.h"
#include "IopDma.h"
#include "Recording/InputRecording.h"
#include "Host.h"
#include "SIO/Memcard/MemoryCardProtocol.h"
#include "SIO/Multitap/MultitapProtocol.h"
#include "SIO/Pad/Pad.h"
#include "SIO/Pad/PadBase.h"
#include "SIO/Sio.h"
#include "SIO/Sio2.h"
#include "SIO/SioTypes.h"
#include "StateWrapper.h"
#define SIO2LOG_ENABLE 0
#define Sio2Log if (SIO2LOG_ENABLE) DevCon
@@ -45,13 +45,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 +127,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 +143,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,17 +151,14 @@ 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);
g_Sio2FifoOut.push_back(responseByte);
}
while (!g_Sio2FifoIn.empty())
{
const u8 commandByte = g_Sio2FifoIn.front();
g_Sio2FifoIn.pop_front();
const u8 responseByte = pad->SendCommandByte(commandByte);
g_Sio2FifoOut.push_back(responseByte);
}
}
@@ -355,7 +352,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;
@@ -453,62 +450,39 @@ u8 Sio2::Read()
return ret;
}
bool SaveStateBase::Sio2Freeze()
bool Sio2::DoState(StateWrapper& sw)
{
FreezeTag("sio2");
if (!sw.DoMarker("Sio2"))
return false;
if (IsSaving())
{
std::deque<u8>::iterator iter;
size_t backupSize;
sw.Do(&send3);
sw.Do(&send1);
sw.Do(&send2);
sw.Do(&dataIn);
sw.Do(&dataOut);
sw.Do(&ctrl);
sw.Do(&recv1);
sw.Do(&recv2);
sw.Do(&recv3);
sw.Do(&unknown1);
sw.Do(&unknown2);
sw.Do(&iStat);
sw.Do(&port);
sw.Do(&slot);
sw.Do(&send3Read);
sw.Do(&send3Position);
sw.Do(&commandLength);
sw.Do(&processedLength);
sw.Do(&dmaBlockSize);
sw.Do(&send3Complete);
// Copy g_Sio2FifoIn
if (g_Sio2FifoIn.size())
{
g_Sio2.fifoInBackup = std::make_unique<u8[]>(g_Sio2FifoIn.size());
iter = g_Sio2FifoIn.begin();
backupSize = 0;
while (iter != g_Sio2FifoIn.end())
{
const u8 val = *iter++;
g_Sio2.fifoInBackup.get()[backupSize++] = val;
}
g_Sio2.fifoInBackupSize = backupSize;
}
else
{
g_Sio2.fifoInBackupSize = 0;
}
// Copy g_Sio2FifoOut
if (g_Sio2FifoOut.size())
{
g_Sio2.fifoOutBackup = std::make_unique<u8[]>(g_Sio2FifoOut.size());
iter = g_Sio2FifoOut.begin();
backupSize = 0;
while (iter != g_Sio2FifoOut.end())
{
const u8 val = *iter++;
g_Sio2.fifoOutBackup.get()[backupSize++] = val;
}
g_Sio2.fifoOutBackupSize = backupSize;
}
else
{
g_Sio2.fifoOutBackupSize = 0;
}
}
Freeze(g_Sio2);
sw.Do(&g_Sio2FifoIn);
sw.Do(&g_Sio2FifoOut);
// CRCs for memory cards.
// If the memory card hasn't changed when loading state, we can safely skip ejecting it.
u64 mcdCrcs[SIO::PORTS][SIO::SLOTS];
if (IsSaving())
if (sw.IsWriting())
{
for (u32 port = 0; port < SIO::PORTS; port++)
{
@@ -516,9 +490,9 @@ bool SaveStateBase::Sio2Freeze()
mcdCrcs[port][slot] = mcds[port][slot].GetChecksum();
}
}
Freeze(mcdCrcs);
sw.DoBytes(mcdCrcs, sizeof(mcdCrcs));
if (IsLoading())
if (sw.IsReading())
{
bool ejected = false;
for (u32 port = 0; port < SIO::PORTS && !ejected; port++)
@@ -533,29 +507,7 @@ bool SaveStateBase::Sio2Freeze()
}
}
}
// Restore g_Sio2FifoIn
g_Sio2FifoIn.clear();
if (g_Sio2.fifoInBackupSize)
{
for (size_t i = 0; i < g_Sio2.fifoInBackupSize; i++)
{
g_Sio2FifoIn.push_back(g_Sio2.fifoInBackup.get()[i]);
}
}
// Restore g_Sio2FifoOut
g_Sio2FifoOut.clear();
if (g_Sio2.fifoOutBackupSize)
{
for (size_t j = 0; j < g_Sio2.fifoOutBackupSize; j++)
{
g_Sio2FifoOut.push_back(g_Sio2.fifoOutBackup.get()[j]);
}
}
}
return true;
return sw.IsGood();
}
+3 -5
View File
@@ -17,6 +17,8 @@
#include <array>
class StateWrapper;
class Sio2
{
public:
@@ -53,11 +55,6 @@ public:
size_t dmaBlockSize = 0;
bool send3Complete = false;
std::unique_ptr<u8[]> fifoInBackup;
size_t fifoInBackupSize;
std::unique_ptr<u8[]> fifoOutBackup;
size_t fifoOutBackupSize;
Sio2();
~Sio2();
@@ -65,6 +62,7 @@ public:
bool Shutdown();
void SoftReset();
bool DoState(StateWrapper& sw);
void Interrupt();
+41 -10
View File
@@ -28,9 +28,11 @@
#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 "SIO/Sio0.h"
#include "SIO/Sio2.h"
#include "SPU2/spu2.h"
#include "SaveState.h"
#include "StateWrapper.h"
@@ -237,8 +239,42 @@ bool SaveStateBase::FreezeInternals()
FreezeMem(iopMem->Sif, sizeof(iopMem->Sif)); // iop's sif memory (not really needed, but oh well)
okay = okay && psxRcntFreeze();
okay = okay && Sio0Freeze();
okay = okay && Sio2Freeze();
// TODO: move all the others over to StateWrapper too...
if (!okay)
return false;
{
// This is horrible. We need to move the rest over...
std::optional<StateWrapper::VectorMemoryStream> save_stream;
std::optional<StateWrapper::ReadOnlyMemoryStream> load_stream;
if (IsSaving())
save_stream.emplace();
else
load_stream.emplace(&m_memory[m_idx], static_cast<int>(m_memory.size()) - m_idx);
StateWrapper sw(IsSaving() ? static_cast<StateWrapper::IStream*>(&save_stream.value()) :
static_cast<StateWrapper::IStream*>(&load_stream.value()),
IsSaving() ? StateWrapper::Mode::Write : StateWrapper::Mode::Read, g_SaveVersion);
okay = okay && g_Sio0.DoState(sw);
okay = okay && g_Sio2.DoState(sw);
if (!okay || !sw.IsGood())
return false;
if (IsSaving())
{
FreezeMem(const_cast<u8*>(save_stream->GetBuffer().data()), save_stream->GetPosition());
}
else
{
const int new_idx = m_idx + static_cast<int>(load_stream->GetPosition());
if (static_cast<size_t>(new_idx) >= m_memory.size())
return false;
m_idx = new_idx;
}
}
okay = okay && cdrFreeze();
okay = okay && cdvdFreeze();
@@ -314,11 +350,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 +635,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; }
};
+1 -3
View File
@@ -37,7 +37,7 @@ enum class FreezeAction
// [SAVEVERSION+]
// This informs the auto updater that the users savestates will be invalidated.
static const u32 g_SaveVersion = (0x9A3A << 16) | 0x0000;
static const u32 g_SaveVersion = (0x9A3B << 16) | 0x0000;
// the freezing data between submodules and core
@@ -222,8 +222,6 @@ protected:
bool cdvdFreeze();
bool psxRcntFreeze();
bool deci2Freeze();
bool Sio0Freeze();
bool Sio2Freeze();
// Save or load PCSX2's global frame counter (g_FrameCount) along with each savestate
//
+3 -2
View File
@@ -107,6 +107,7 @@ public:
IStream* GetStream() const { return m_stream; }
bool HasError() const { return m_error; }
bool IsGood() const { return !m_error; }
bool IsReading() const { return (m_mode == Mode::Read); }
bool IsWriting() const { return (m_mode == Mode::Write); }
Mode GetMode() const { return m_mode; }
@@ -226,8 +227,8 @@ public:
}
else
{
for (u32 i = 0; i < length; i++)
Do(&data[i]);
for (T& ch : *data)
Do(&ch);
}
}
+17 -18
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);
+2 -8
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" />
+2 -20
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>
+2 -2
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 )
+2 -2
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 )