Patch: Add new toggleable cheat and patch interface

This commit is contained in:
Stenzek 2023-05-26 02:24:01 +10:00 committed by refractionpcsx2
parent ec35330593
commit 81da9fb5a4
36 changed files with 2512 additions and 1250 deletions

View File

@ -371,7 +371,6 @@ SysMtgsThread& GetMTGS()
// Interface Stuff
//////////////////////////////////////////////////////////////////////////
const IConsoleWriter* PatchesCon = &Console;
BEGIN_HOTKEY_LIST(g_host_hotkeys)
END_HOTKEY_LIST()

View File

@ -76,12 +76,19 @@ target_sources(pcsx2-qt PRIVATE
Settings/FolderSettingsWidget.cpp
Settings/FolderSettingsWidget.h
Settings/FolderSettingsWidget.ui
Settings/GameCheatSettingsWidget.cpp
Settings/GameCheatSettingsWidget.h
Settings/GameCheatSettingsWidget.ui
Settings/GameFixSettingsWidget.cpp
Settings/GameFixSettingsWidget.h
Settings/GameFixSettingsWidget.ui
Settings/GameListSettingsWidget.cpp
Settings/GameListSettingsWidget.h
Settings/GameListSettingsWidget.ui
Settings/GamePatchDetailsWidget.ui
Settings/GamePatchSettingsWidget.cpp
Settings/GamePatchSettingsWidget.h
Settings/GamePatchSettingsWidget.ui
Settings/GameSummaryWidget.cpp
Settings/GameSummaryWidget.h
Settings/GameSummaryWidget.ui
@ -116,6 +123,7 @@ target_sources(pcsx2-qt PRIVATE
Settings/DEV9UiCommon.h
Settings/HddCreateQt.cpp
Settings/HddCreateQt.h
Settings/PatchDetailsWidget.ui
Settings/SettingsDialog.cpp
Settings/SettingsDialog.h
Settings/SettingsDialog.ui

View File

@ -84,7 +84,6 @@ namespace QtHost
//////////////////////////////////////////////////////////////////////////
// Local variable declarations
//////////////////////////////////////////////////////////////////////////
const IConsoleWriter* PatchesCon = &Console;
static std::unique_ptr<QTimer> s_settings_save_timer;
static std::unique_ptr<INISettingsInterface> s_base_settings_interface;
static bool s_batch_mode = false;
@ -671,7 +670,11 @@ void EmuThread::reloadPatches()
if (!VMManager::HasValidVM())
return;
VMManager::ReloadPatches(true, true);
Patch::ReloadPatches(true, false, true);
// Might change widescreen mode.
if (Patch::ReloadPatchAffectingOptions())
applySettings();
}
void EmuThread::reloadInputSources()

View File

@ -64,9 +64,29 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsDialog* dialog, QWidget
.arg(m_ui.eeCycleRate->itemText(
std::clamp(Host::GetBaseIntSettingValue("EmuCore/Speedhacks", "EECycleRate", DEFAULT_EE_CYCLE_RATE) - MINIMUM_EE_CYCLE_RATE,
0, MAXIMUM_EE_CYCLE_RATE - MINIMUM_EE_CYCLE_RATE))));
// Disable cheats, use the cheats panel instead (move fastcvd up in its spot).
const int count = m_ui.systemSettingsLayout->count();
for (int i = 0; i < count; i++)
{
QLayoutItem* item = m_ui.systemSettingsLayout->itemAt(i);
if (item && item->widget() == m_ui.cheats)
{
int row, col, rowSpan, colSpan;
m_ui.systemSettingsLayout->getItemPosition(i, &row, &col, &rowSpan, &colSpan);
delete m_ui.systemSettingsLayout->takeAt(i);
m_ui.systemSettingsLayout->removeWidget(m_ui.fastCDVD);
m_ui.systemSettingsLayout->addWidget(m_ui.fastCDVD, row, col);
delete m_ui.cheats;
m_ui.cheats = nullptr;
break;
}
}
}
else
{
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.cheats, "EmuCore", "EnableCheats", false);
// Allow for FastCDVD for per-game settings only
m_ui.systemSettingsLayout->removeWidget(m_ui.fastCDVD);
m_ui.fastCDVD->deleteLater();
@ -84,7 +104,6 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsDialog* dialog, QWidget
m_dialog->setIntSettingValue("EmuCore/Speedhacks", "EECycleRate", value);
});
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.cheats, "EmuCore", "EnableCheats", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hostFilesystem, "EmuCore", "HostFs", false);
dialog->registerWidgetHelp(m_ui.normalSpeed, tr("Normal Speed"), "100%",

View File

@ -0,0 +1,236 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 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 "MainWindow.h"
#include "QtHost.h"
#include "QtUtils.h"
#include "SettingWidgetBinder.h"
#include "Settings/GameCheatSettingsWidget.h"
#include "Settings/SettingsDialog.h"
#include "pcsx2/GameList.h"
#include "pcsx2/Patch.h"
#include "common/HeterogeneousContainers.h"
GameCheatSettingsWidget::GameCheatSettingsWidget(const GameList::Entry* entry, SettingsDialog* dialog, QWidget* parent)
: m_dialog(dialog)
, m_serial(entry->serial)
, m_crc(entry->crc)
{
m_ui.setupUi(this);
QtUtils::ResizeColumnsForTreeView(m_ui.cheatList, {300, 100, -1});
reloadList();
SettingsInterface* sif = m_dialog->getSettingsInterface();
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableCheats, "EmuCore", "EnableCheats", false);
updateListEnabled();
connect(m_ui.enableCheats, &QCheckBox::stateChanged, this, &GameCheatSettingsWidget::updateListEnabled);
connect(m_ui.cheatList, &QTreeWidget::itemDoubleClicked, this, &GameCheatSettingsWidget::onCheatListItemDoubleClicked);
connect(m_ui.cheatList, &QTreeWidget::itemChanged, this, &GameCheatSettingsWidget::onCheatListItemChanged);
connect(m_ui.reloadCheats, &QPushButton::clicked, this, &GameCheatSettingsWidget::reloadList);
connect(m_ui.enableAll, &QPushButton::clicked, this, [this]() { setStateForAll(true); });
connect(m_ui.disableAll, &QPushButton::clicked, this, [this]() { setStateForAll(false); });
}
GameCheatSettingsWidget::~GameCheatSettingsWidget() = default;
void GameCheatSettingsWidget::onCheatListItemDoubleClicked(QTreeWidgetItem* item, int column)
{
QVariant data = item->data(0, Qt::UserRole);
if (!data.isValid())
return;
std::string cheat_name = data.toString().toStdString();
const bool new_state = !(item->checkState(0) == Qt::Checked);
item->setCheckState(0, new_state ? Qt::Checked : Qt::Unchecked);
setCheatEnabled(std::move(cheat_name), new_state, true);
}
void GameCheatSettingsWidget::onCheatListItemChanged(QTreeWidgetItem* item, int column)
{
QVariant data = item->data(0, Qt::UserRole);
if (!data.isValid())
return;
std::string cheat_name = data.toString().toStdString();
const bool current_enabled =
(std::find(m_enabled_patches.begin(), m_enabled_patches.end(), cheat_name) != m_enabled_patches.end());
const bool current_checked = (item->checkState(0) == Qt::Checked);
if (current_enabled == current_checked)
return;
setCheatEnabled(std::move(cheat_name), current_checked, true);
}
void GameCheatSettingsWidget::onReloadClicked()
{
reloadList();
// reload it on the emu thread too, so it picks up any changes
g_emu_thread->reloadPatches();
}
void GameCheatSettingsWidget::updateListEnabled()
{
const bool cheats_enabled = m_dialog->getEffectiveBoolValue("EmuCore", "EnableCheats", false);
m_ui.cheatList->setEnabled(cheats_enabled);
m_ui.enableAll->setEnabled(cheats_enabled);
m_ui.disableAll->setEnabled(cheats_enabled);
m_ui.reloadCheats->setEnabled(cheats_enabled);
}
void GameCheatSettingsWidget::setCheatEnabled(std::string name, bool enabled, bool save_and_reload_settings)
{
SettingsInterface* si = m_dialog->getSettingsInterface();
auto it = std::find(m_enabled_patches.begin(), m_enabled_patches.end(), name);
if (enabled)
{
si->AddToStringList(Patch::CHEATS_CONFIG_SECTION, Patch::PATCH_ENABLE_CONFIG_KEY, name.c_str());
if (it == m_enabled_patches.end())
m_enabled_patches.push_back(std::move(name));
}
else
{
si->RemoveFromStringList(Patch::CHEATS_CONFIG_SECTION, Patch::PATCH_ENABLE_CONFIG_KEY, name.c_str());
if (it != m_enabled_patches.end())
m_enabled_patches.erase(it);
}
if (save_and_reload_settings)
{
si->Save();
g_emu_thread->reloadGameSettings();
}
}
void GameCheatSettingsWidget::setStateForAll(bool enabled)
{
QSignalBlocker sb(m_ui.cheatList);
setStateRecursively(nullptr, enabled);
m_dialog->getSettingsInterface()->Save();
g_emu_thread->reloadGameSettings();
}
void GameCheatSettingsWidget::setStateRecursively(QTreeWidgetItem* parent, bool enabled)
{
const int count = parent ? parent->childCount() : m_ui.cheatList->topLevelItemCount();
for (int i = 0; i < count; i++)
{
QTreeWidgetItem* item = parent ? parent->child(i) : m_ui.cheatList->topLevelItem(i);
QVariant data = item->data(0, Qt::UserRole);
if (data.isValid())
{
if ((item->checkState(0) == Qt::Checked) != enabled)
{
item->setCheckState(0, enabled ? Qt::Checked : Qt::Unchecked);
setCheatEnabled(data.toString().toStdString(), enabled, false);
}
}
else
{
setStateRecursively(item, enabled);
}
}
}
void GameCheatSettingsWidget::reloadList()
{
u32 num_unlabelled_codes = 0;
m_patches = Patch::GetPatchInfo(m_serial, m_crc, true, &num_unlabelled_codes);
m_enabled_patches =
m_dialog->getSettingsInterface()->GetStringList(Patch::CHEATS_CONFIG_SECTION, Patch::PATCH_ENABLE_CONFIG_KEY);
m_parent_map.clear();
while (m_ui.cheatList->topLevelItemCount() > 0)
delete m_ui.cheatList->takeTopLevelItem(0);
for (const Patch::PatchInfo& pi : m_patches)
{
const bool enabled =
(std::find(m_enabled_patches.begin(), m_enabled_patches.end(), pi.name) != m_enabled_patches.end());
const std::string_view name_part = pi.GetNamePart();
const std::string_view parent_part = pi.GetNameParentPart();
QTreeWidgetItem* parent = getTreeWidgetParent(parent_part);
QTreeWidgetItem* item = new QTreeWidgetItem();
populateTreeWidgetItem(item, pi, enabled);
if (parent)
parent->addChild(item);
else
m_ui.cheatList->addTopLevelItem(item);
}
// Hide root indicator when there's no groups, frees up some whitespace.
m_ui.cheatList->setRootIsDecorated(!m_parent_map.empty());
if (num_unlabelled_codes > 0)
{
QTreeWidgetItem* item = new QTreeWidgetItem();
item->setText(0, tr("%1 unlabelled patch codes will automatically activate.").arg(num_unlabelled_codes));
m_ui.cheatList->addTopLevelItem(item);
}
}
QTreeWidgetItem* GameCheatSettingsWidget::getTreeWidgetParent(const std::string_view& parent)
{
if (parent.empty())
return nullptr;
auto it = UnorderedStringMapFind(m_parent_map, parent);
if (it != m_parent_map.end())
return it->second;
std::string_view this_part = parent;
QTreeWidgetItem* parent_to_this = nullptr;
const std::string_view::size_type pos = parent.rfind('\\');
if (pos != std::string::npos && pos != (parent.size() - 1))
{
// go up the chain until we find the real parent, then back down
parent_to_this = getTreeWidgetParent(parent.substr(0, pos));
this_part = parent.substr(pos + 1);
}
QTreeWidgetItem* item = new QTreeWidgetItem();
item->setText(0, QString::fromUtf8(this_part.data(), this_part.length()));
if (parent_to_this)
parent_to_this->addChild(item);
else
m_ui.cheatList->addTopLevelItem(item);
// Must be called after adding.
item->setExpanded(true);
m_parent_map.emplace(parent, item);
return item;
}
void GameCheatSettingsWidget::populateTreeWidgetItem(QTreeWidgetItem* item, const Patch::PatchInfo& pi, bool enabled)
{
const std::string_view name_part = pi.GetNamePart();
item->setFlags(item->flags() | Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren);
item->setCheckState(0, enabled ? Qt::Checked : Qt::Unchecked);
item->setData(0, Qt::UserRole, QString::fromStdString(pi.name));
if (!name_part.empty())
item->setText(0, QString::fromUtf8(name_part.data(), name_part.length()));
item->setText(1, QString::fromStdString(pi.author));
item->setText(2, QString::fromStdString(pi.description));
}

View File

@ -0,0 +1,68 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 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 <QtWidgets/QWidget>
#include "ui_GameCheatSettingsWidget.h"
#include "pcsx2/Patch.h"
#include "common/HeterogeneousContainers.h"
#include <string>
#include <string_view>
#include <vector>
namespace GameList
{
struct Entry;
}
class SettingsDialog;
class GameCheatSettingsWidget : public QWidget
{
Q_OBJECT
public:
GameCheatSettingsWidget(const GameList::Entry* entry, SettingsDialog* dialog, QWidget* parent);
~GameCheatSettingsWidget();
private Q_SLOTS:
void onCheatListItemDoubleClicked(QTreeWidgetItem* item, int column);
void onCheatListItemChanged(QTreeWidgetItem* item, int column);
void onReloadClicked();
void updateListEnabled();
private:
QTreeWidgetItem* getTreeWidgetParent(const std::string_view& parent);
void populateTreeWidgetItem(QTreeWidgetItem* item, const Patch::PatchInfo& pi, bool enabled);
void setCheatEnabled(std::string name, bool enabled, bool save_and_reload_settings);
void setStateForAll(bool enabled);
void setStateRecursively(QTreeWidgetItem* parent, bool enabled);
void reloadList();
Ui::GameCheatSettingsWidget m_ui;
SettingsDialog* m_dialog;
std::string m_serial;
u32 m_crc;
UnorderedStringMap<QTreeWidgetItem*> m_parent_map;
std::vector<Patch::PatchInfo> m_patches;
std::vector<std::string> m_enabled_patches;
};

View File

@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GameCheatSettingsWidget</class>
<widget class="QWidget" name="GameCheatSettingsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>697</width>
<height>361</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Activating cheats can cause unpredictable behavior, crashing, soft-locks, or broken saved games. Use cheats at your own risk, the PCSX2 team will provide no support for users who have enabled cheats.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="enableCheats">
<property name="text">
<string>Enable Cheats</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="cheatList">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectItems</enum>
</property>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Author</string>
</property>
</column>
<column>
<property name="text">
<string>Description</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="enableAll">
<property name="text">
<string>Enable All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="disableAll">
<property name="text">
<string>Disable All</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="reloadCheats">
<property name="text">
<string>Reload Cheats</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GamePatchDetailsWidget</class>
<widget class="QWidget" name="GamePatchDetailsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>541</width>
<height>112</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="name">
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Patch Title</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="enabled">
<property name="text">
<string>Enabled</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="description">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Author: &lt;/span&gt;Patch Author&lt;/p&gt;&lt;p&gt;Description would go here&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,132 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 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 "MainWindow.h"
#include "QtHost.h"
#include "QtUtils.h"
#include "Settings/GamePatchSettingsWidget.h"
#include "Settings/SettingsDialog.h"
#include "pcsx2/GameList.h"
#include "pcsx2/Patch.h"
#include <algorithm>
GamePatchDetailsWidget::GamePatchDetailsWidget(std::string name, const std::string& author,
const std::string& description, bool enabled, SettingsDialog* dialog, QWidget* parent)
: QWidget(parent)
, m_dialog(dialog)
, m_name(name)
{
m_ui.setupUi(this);
m_ui.name->setText(QString::fromStdString(name));
m_ui.description->setText(
tr("<strong>Author: </strong>%1<br>%2")
.arg(author.empty() ? tr("Unknown") : QString::fromStdString(author))
.arg(description.empty() ? tr("No description provided.") : QString::fromStdString(description)));
pxAssert(dialog->getSettingsInterface());
m_ui.enabled->setChecked(enabled);
connect(m_ui.enabled, &QCheckBox::stateChanged, this, &GamePatchDetailsWidget::onEnabledStateChanged);
}
GamePatchDetailsWidget::~GamePatchDetailsWidget() = default;
void GamePatchDetailsWidget::onEnabledStateChanged(int state)
{
SettingsInterface* si = m_dialog->getSettingsInterface();
if (state == Qt::Checked)
si->AddToStringList("Patches", "Enable", m_name.c_str());
else
si->RemoveFromStringList("Patches", "Enable", m_name.c_str());
si->Save();
g_emu_thread->reloadGameSettings();
}
GamePatchSettingsWidget::GamePatchSettingsWidget(const GameList::Entry* entry, SettingsDialog* dialog, QWidget* parent)
: m_dialog(dialog)
, m_serial(entry->serial)
, m_crc(entry->crc)
{
m_ui.setupUi(this);
m_ui.scrollArea->setFrameShape(QFrame::WinPanel);
m_ui.scrollArea->setFrameShadow(QFrame::Sunken);
connect(m_ui.reload, &QPushButton::clicked, this, &GamePatchSettingsWidget::onReloadClicked);
reloadList();
}
GamePatchSettingsWidget::~GamePatchSettingsWidget() = default;
void GamePatchSettingsWidget::onReloadClicked()
{
reloadList();
// reload it on the emu thread too, so it picks up any changes
g_emu_thread->reloadPatches();
}
void GamePatchSettingsWidget::reloadList()
{
// Patches shouldn't have any unlabelled patch groups, because they're new.
std::vector<Patch::PatchInfo> patches = Patch::GetPatchInfo(m_serial, m_crc, false, nullptr);
std::vector<std::string> enabled_list =
m_dialog->getSettingsInterface()->GetStringList(Patch::PATCHES_CONFIG_SECTION, Patch::PATCH_ENABLE_CONFIG_KEY);
delete m_ui.scrollArea->takeWidget();
QWidget* container = new QWidget(m_ui.scrollArea);
QVBoxLayout* layout = new QVBoxLayout(container);
layout->setContentsMargins(0, 0, 0, 0);
if (!patches.empty())
{
bool first = true;
for (Patch::PatchInfo& pi : patches)
{
if (!first)
{
QFrame* frame = new QFrame(container);
frame->setFrameShape(QFrame::HLine);
frame->setFrameShadow(QFrame::Sunken);
layout->addWidget(frame);
}
else
{
first = false;
}
const bool enabled = (std::find(enabled_list.begin(), enabled_list.end(), pi.name) != enabled_list.end());
GamePatchDetailsWidget* it =
new GamePatchDetailsWidget(std::move(pi.name), pi.author, pi.description, enabled, m_dialog, container);
layout->addWidget(it);
}
}
else
{
QLabel* label = new QLabel(tr("There are no patches available for this game."), container);
layout->addWidget(label);
}
layout->addStretch(1);
m_ui.scrollArea->setWidget(container);
}

View File

@ -0,0 +1,69 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 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 <QtWidgets/QWidget>
#include "ui_GamePatchDetailsWidget.h"
#include "ui_GamePatchSettingsWidget.h"
#include "pcsx2/Patch.h"
namespace GameList
{
struct Entry;
}
class SettingsDialog;
class GamePatchDetailsWidget : public QWidget
{
Q_OBJECT
public:
GamePatchDetailsWidget(std::string name, const std::string& author, const std::string& description, bool enabled,
SettingsDialog* dialog, QWidget* parent);
~GamePatchDetailsWidget();
private Q_SLOTS:
void onEnabledStateChanged(int state);
private:
Ui::GamePatchDetailsWidget m_ui;
SettingsDialog* m_dialog;
std::string m_name;
};
class GamePatchSettingsWidget : public QWidget
{
Q_OBJECT
public:
GamePatchSettingsWidget(const GameList::Entry* entry, SettingsDialog* dialog, QWidget* parent);
~GamePatchSettingsWidget();
private Q_SLOTS:
void onReloadClicked();
private:
void reloadList();
Ui::GamePatchSettingsWidget m_ui;
SettingsDialog* m_dialog;
std::string m_serial;
u32 m_crc;
};

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GamePatchSettingsWidget</class>
<widget class="QWidget" name="GamePatchSettingsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>766</width>
<height>392</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Activating game patches can cause unpredictable behavior, crashing, soft-locks, or broken saved games. Use patches at your own risk, the PCSX2 team will provide no support for users who have enabled game patches.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="reload">
<property name="text">
<string>Reload Patches</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GamePatchDetailsWidget</class>
<widget class="QWidget" name="GamePatchDetailsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>541</width>
<height>112</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="name">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Patch Title</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="enabled">
<property name="text">
<string>Enabled</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="description">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Author: &lt;/span&gt;Patch Author&lt;/p&gt;&lt;p&gt;Description would go here&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -12,7 +12,7 @@
* 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 "MainWindow.h"
@ -26,8 +26,10 @@
#include "Settings/DebugSettingsWidget.h"
#include "Settings/EmulationSettingsWidget.h"
#include "Settings/FolderSettingsWidget.h"
#include "Settings/GameCheatSettingsWidget.h"
#include "Settings/GameFixSettingsWidget.h"
#include "Settings/GameListSettingsWidget.h"
#include "Settings/GamePatchSettingsWidget.h"
#include "Settings/GameSummaryWidget.h"
#include "Settings/GraphicsSettingsWidget.h"
#include "Settings/HotkeySettingsWidget.h"
@ -106,11 +108,24 @@ void SettingsDialog::setupUi(const GameList::Entry* game)
tr("<strong>Emulation Settings</strong><hr>These options determine the configuration of frame pacing and game "
"settings.<br><br>Mouse over an option for additional information."));
if (isPerGameSettings() && game && game->crc != 0)
{
addWidget(m_game_patch_settings_widget = new GamePatchSettingsWidget(game, this, m_ui.settingsContainer),
tr("Patches"), QStringLiteral("tools-line"),
tr("<strong>Patches</strong><hr>This section allows you to select optional patches to apply to the game, "
"which may provide performance, visual, or gameplay improvements."));
addWidget(m_game_cheat_settings_widget = new GameCheatSettingsWidget(game, this, m_ui.settingsContainer),
tr("Cheats"), QStringLiteral("flask-line"),
tr("<strong>Cheats</strong><hr>This section allows you to select which cheats you wish to enable. You "
"cannot enable/disable cheats without labels for old-format pnach files, those will automatically "
"activate if the main cheat enable option is checked."));
}
// Only show the game fixes for per-game settings, there's really no reason to be setting them globally.
if (show_advanced_settings && isPerGameSettings())
{
addWidget(m_game_fix_settings_widget = new GameFixSettingsWidget(this, m_ui.settingsContainer), tr("Game Fixes"),
QStringLiteral("close-line"),
QStringLiteral("hammer-line"),
tr("<strong>Game Fixes Settings</strong><hr>Game Fixes can work around incorrect emulation in some titles.<br>However, they can "
"also cause problems in games if used incorrectly.<br>It is best to leave them all disabled unless advised otherwise."));
}

View File

@ -33,7 +33,9 @@ class InterfaceSettingsWidget;
class GameListSettingsWidget;
class EmulationSettingsWidget;
class BIOSSettingsWidget;
class GameCheatSettingsWidget;
class GameFixSettingsWidget;
class GamePatchSettingsWidget;
class GraphicsSettingsWidget;
class AudioSettingsWidget;
class MemoryCardSettingsWidget;
@ -61,7 +63,9 @@ public:
__fi GameListSettingsWidget* getGameListSettingsWidget() const { return m_game_list_settings; }
__fi BIOSSettingsWidget* getBIOSSettingsWidget() const { return m_bios_settings; }
__fi EmulationSettingsWidget* getEmulationSettingsWidget() const { return m_emulation_settings; }
__fi GameCheatSettingsWidget* getGameCheatSettingsWidget() const { return m_game_cheat_settings_widget; }
__fi GameFixSettingsWidget* getGameFixSettingsWidget() const { return m_game_fix_settings_widget; }
__fi GamePatchSettingsWidget* getGamePatchSettingsWidget() const { return m_game_patch_settings_widget; }
__fi GraphicsSettingsWidget* getGraphicsSettingsWidget() const { return m_graphics_settings; }
__fi AudioSettingsWidget* getAudioSettingsWidget() const { return m_audio_settings; }
__fi MemoryCardSettingsWidget* getMemoryCardSettingsWidget() const { return m_memory_card_settings; }
@ -107,7 +111,7 @@ protected:
private:
enum : u32
{
MAX_SETTINGS_WIDGETS = 12
MAX_SETTINGS_WIDGETS = 13
};
void setupUi(const GameList::Entry* game);
@ -122,7 +126,9 @@ private:
GameListSettingsWidget* m_game_list_settings = nullptr;
BIOSSettingsWidget* m_bios_settings = nullptr;
EmulationSettingsWidget* m_emulation_settings = nullptr;
GameCheatSettingsWidget* m_game_cheat_settings_widget = nullptr;
GameFixSettingsWidget* m_game_fix_settings_widget = nullptr;
GamePatchSettingsWidget* m_game_patch_settings_widget = nullptr;
GraphicsSettingsWidget* m_graphics_settings = nullptr;
AudioSettingsWidget* m_audio_settings = nullptr;
MemoryCardSettingsWidget* m_memory_card_settings = nullptr;

View File

@ -48,7 +48,6 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
<PreprocessorDefinitions>LZMA_API_STATIC;BUILD_DX=1;ENABLE_RAINTEGRATION;ENABLE_ACHIEVEMENTS;ENABLE_DISCORD_PRESENCE;ENABLE_OPENGL;ENABLE_VULKAN;SDL_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<!-- Current Qt debug builds assert on RTTI. Remove this once we next build Qt. -->
<RuntimeTypeInfo Condition="$(Configuration.Contains(Clang)) And $(Configuration.Contains(Debug))">true</RuntimeTypeInfo>
</ClCompile>
@ -85,6 +84,8 @@
<ClCompile Include="QtProgressCallback.cpp" />
<ClCompile Include="Settings\DebugSettingsWidget.cpp" />
<ClCompile Include="Settings\FolderSettingsWidget.cpp" />
<ClCompile Include="Settings\GameCheatSettingsWidget.cpp" />
<ClCompile Include="Settings\GamePatchSettingsWidget.cpp" />
<ClCompile Include="Settings\MemoryCardConvertWorker.cpp" />
<ClCompile Include="Themes.cpp" />
<ClCompile Include="Tools\InputRecording\InputRecordingViewer.cpp" />
@ -162,6 +163,8 @@
<ClInclude Include="Settings\ControllerSettingWidgetBinder.h" />
<QtMoc Include="Settings\FolderSettingsWidget.h" />
<QtMoc Include="Settings\DebugSettingsWidget.h" />
<QtMoc Include="Settings\GameCheatSettingsWidget.h" />
<QtMoc Include="Settings\GamePatchSettingsWidget.h" />
<ClInclude Include="Settings\HddCreateQt.h" />
<QtMoc Include="Settings\GameSummaryWidget.h" />
<QtMoc Include="Settings\CreateMemoryCardDialog.h" />
@ -211,7 +214,9 @@
<ClCompile Include="$(IntDir)Settings\moc_InterfaceSettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_SettingsDialog.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_AdvancedSettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_GameCheatSettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_GameFixSettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_GamePatchSettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_HotkeySettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_InputBindingDialog.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_InputBindingWidget.cpp" />
@ -369,6 +374,12 @@
<QtUi Include="Settings\ControllerMouseSettingsDialog.ui">
<FileType>Document</FileType>
</QtUi>
<QtUi Include="Settings\GameCheatSettingsWidget.ui">
<FileType>Document</FileType>
</QtUi>
<QtUi Include="Settings\GamePatchSettingsWidget.ui">
<FileType>Document</FileType>
</QtUi>
<None Include="Settings\USBBindingWidget_DrivingForce.ui" />
<None Include="Settings\USBBindingWidget_GTForce.ui" />
<QtUi Include="Settings\USBDeviceWidget.ui">
@ -385,4 +396,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="$(SolutionDir)common\vsprops\QtCompile.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>

View File

@ -316,6 +316,18 @@
<Filter>moc</Filter>
</ClCompile>
<ClCompile Include="Themes.cpp" />
<ClCompile Include="Settings\GamePatchSettingsWidget.cpp">
<Filter>Settings</Filter>
</ClCompile>
<ClCompile Include="Settings\GameCheatSettingsWidget.cpp">
<Filter>Settings</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Settings\moc_GamePatchSettingsWidget.cpp">
<Filter>moc</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Settings\moc_GameCheatSettingsWidget.cpp">
<Filter>moc</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Manifest Include="..\pcsx2\windows\PCSX2.manifest">
@ -463,6 +475,8 @@
<Filter>Debugger\Models</Filter>
</QtMoc>
<QtMoc Include="ColorPickerButton.h" />
<QtMoc Include="Settings\GameCheatSettingsWidget.h" />
<QtMoc Include="Settings\GamePatchSettingsWidget.h" />
</ItemGroup>
<ItemGroup>
<QtResource Include="resources\resources.qrc">
@ -587,6 +601,12 @@
<QtUi Include="Settings\ControllerMouseSettingsDialog.ui">
<Filter>Settings</Filter>
</QtUi>
<QtUi Include="Settings\GameCheatSettingsWidget.ui">
<Filter>Settings</Filter>
</QtUi>
<QtUi Include="Settings\GamePatchSettingsWidget.ui">
<Filter>Settings</Filter>
</QtUi>
</ItemGroup>
<ItemGroup>
<None Include="Settings\FolderSettingsWidget.ui">

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 2C20.5523 2 21 2.44772 21 3V8C21 8.55228 20.5523 9 20 9H15V22C15 22.5523 14.5523 23 14 23H10C9.44772 23 9 22.5523 9 22V9H3.5C2.94772 9 2.5 8.55228 2.5 8V5.61803C2.5 5.23926 2.714 4.893 3.05279 4.72361L8.5 2H20ZM15 4H8.97214L4.5 6.23607V7H11V21H13V7H15V4ZM19 4H17V7H19V4Z" fill="#000000"></path></svg>

After

Width:  |  Height:  |  Size: 373 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5.32894 3.27158C6.56203 2.8332 7.99181 3.10749 8.97878 4.09446C10.0997 5.21537 10.3014 6.90741 9.58382 8.23385L20.2925 18.9437L18.8783 20.3579L8.16933 9.64875C6.84277 10.3669 5.1502 10.1654 4.02903 9.04421C3.04178 8.05696 2.76761 6.62665 3.20652 5.39332L5.44325 7.63C6.02903 8.21578 6.97878 8.21578 7.56457 7.63C8.15035 7.04421 8.15035 6.09446 7.56457 5.50868L5.32894 3.27158ZM15.6963 5.15512L18.8783 3.38736L20.2925 4.80157L18.5247 7.98355L16.757 8.3371L14.6356 10.4584L13.2214 9.04421L15.3427 6.92289L15.6963 5.15512ZM8.97878 13.2868L10.393 14.7011L5.08969 20.0044C4.69917 20.3949 4.066 20.3949 3.67548 20.0044C3.31285 19.6417 3.28695 19.0699 3.59777 18.6774L3.67548 18.5902L8.97878 13.2868Z" fill="#000000"></path></svg>

After

Width:  |  Height:  |  Size: 793 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 2C20.5523 2 21 2.44772 21 3V8C21 8.55228 20.5523 9 20 9H15V22C15 22.5523 14.5523 23 14 23H10C9.44772 23 9 22.5523 9 22V9H3.5C2.94772 9 2.5 8.55228 2.5 8V5.61803C2.5 5.23926 2.714 4.893 3.05279 4.72361L8.5 2H20ZM15 4H8.97214L4.5 6.23607V7H11V21H13V7H15V4ZM19 4H17V7H19V4Z" fill="#ffffff"></path></svg>

After

Width:  |  Height:  |  Size: 373 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5.32894 3.27158C6.56203 2.8332 7.99181 3.10749 8.97878 4.09446C10.0997 5.21537 10.3014 6.90741 9.58382 8.23385L20.2925 18.9437L18.8783 20.3579L8.16933 9.64875C6.84277 10.3669 5.1502 10.1654 4.02903 9.04421C3.04178 8.05696 2.76761 6.62665 3.20652 5.39332L5.44325 7.63C6.02903 8.21578 6.97878 8.21578 7.56457 7.63C8.15035 7.04421 8.15035 6.09446 7.56457 5.50868L5.32894 3.27158ZM15.6963 5.15512L18.8783 3.38736L20.2925 4.80157L18.5247 7.98355L16.757 8.3371L14.6356 10.4584L13.2214 9.04421L15.3427 6.92289L15.6963 5.15512ZM8.97878 13.2868L10.393 14.7011L5.08969 20.0044C4.69917 20.3949 4.066 20.3949 3.67548 20.0044C3.31285 19.6417 3.28695 19.0699 3.59777 18.6774L3.67548 18.5902L8.97878 13.2868Z" fill="#ffffff"></path></svg>

After

Width:  |  Height:  |  Size: 793 B

View File

@ -37,6 +37,7 @@
<file>icons/black/svg/function-line.svg</file>
<file>icons/black/svg/gamepad-line.svg</file>
<file>icons/black/svg/global-line.svg</file>
<file>icons/black/svg/hammer-line.svg</file>
<file>icons/black/svg/hard-drive-2-line.svg</file>
<file>icons/black/svg/image-fill.svg</file>
<file>icons/black/svg/keyboard-line.svg</file>
@ -55,6 +56,7 @@
<file>icons/black/svg/sd-card-line.svg</file>
<file>icons/black/svg/settings-3-line.svg</file>
<file>icons/black/svg/shut-down-line.svg</file>
<file>icons/black/svg/tools-line.svg</file>
<file>icons/black/svg/trophy-line.svg</file>
<file>icons/black/svg/tv-2-line.svg</file>
<file>icons/black/svg/usb-fill.svg</file>
@ -101,6 +103,7 @@
<file>icons/white/svg/function-line.svg</file>
<file>icons/white/svg/gamepad-line.svg</file>
<file>icons/white/svg/global-line.svg</file>
<file>icons/white/svg/hammer-line.svg</file>
<file>icons/white/svg/hard-drive-2-line.svg</file>
<file>icons/white/svg/image-fill.svg</file>
<file>icons/white/svg/keyboard-line.svg</file>
@ -119,6 +122,7 @@
<file>icons/white/svg/sd-card-line.svg</file>
<file>icons/white/svg/settings-3-line.svg</file>
<file>icons/white/svg/shut-down-line.svg</file>
<file>icons/white/svg/tools-line.svg</file>
<file>icons/white/svg/trophy-line.svg</file>
<file>icons/white/svg/tv-2-line.svg</file>
<file>icons/white/svg/usb-fill.svg</file>

View File

@ -124,7 +124,6 @@ set(pcsx2Sources
MultipartFileReader.cpp
MultitapProtocol.cpp
Patch.cpp
Patch_Memory.cpp
Pcsx2Config.cpp
PerformanceMetrics.cpp
PrecompiledHeader.cpp

View File

@ -623,6 +623,9 @@ struct Pcsx2Config
static constexpr float DEFAULT_FRAME_RATE_NTSC = 59.94f;
static constexpr float DEFAULT_FRAME_RATE_PAL = 50.00f;
static constexpr AspectRatioType DEFAULT_ASPECT_RATIO = AspectRatioType::RAuto4_3_3_2;
static constexpr GSInterlaceMode DEFAULT_INTERLACE_MODE = GSInterlaceMode::Automatic;
static constexpr int DEFAULT_VIDEO_CAPTURE_BITRATE = 6000;
static constexpr int DEFAULT_VIDEO_CAPTURE_WIDTH = 640;
static constexpr int DEFAULT_VIDEO_CAPTURE_HEIGHT = 480;
@ -719,9 +722,9 @@ struct Pcsx2Config
float FramerateNTSC = DEFAULT_FRAME_RATE_NTSC;
float FrameratePAL = DEFAULT_FRAME_RATE_PAL;
AspectRatioType AspectRatio = AspectRatioType::RAuto4_3_3_2;
AspectRatioType AspectRatio = DEFAULT_ASPECT_RATIO;
FMVAspectRatioSwitchType FMVAspectRatioSwitch = FMVAspectRatioSwitchType::Off;
GSInterlaceMode InterlaceMode = GSInterlaceMode::Automatic;
GSInterlaceMode InterlaceMode = DEFAULT_INTERLACE_MODE;
GSPostBilinearMode LinearPresent = GSPostBilinearMode::BilinearSmooth;
float StretchY = 100.0f;
@ -1306,6 +1309,9 @@ struct Pcsx2Config
void LoadSave(SettingsWrapper& wrap);
void LoadSaveMemcards(SettingsWrapper& wrap);
/// Reloads options affected by patches.
void ReloadPatchAffectingOptions();
std::string FullpathToBios() const;
std::string FullpathToMcd(uint slot) const;
@ -1335,8 +1341,7 @@ namespace EmuFolders
extern std::string Langs;
extern std::string Logs;
extern std::string Cheats;
extern std::string CheatsWS;
extern std::string CheatsNI;
extern std::string Patches;
extern std::string Resources;
extern std::string Cache;
extern std::string Covers;

View File

@ -312,13 +312,13 @@ void GameDatabase::parseAndInsert(const std::string_view& serial, const c4::yml:
{
for (const auto& n : node["dynaPatches"].children())
{
DynamicPatch patch;
Patch::DynamicPatch patch;
if (n.has_child("pattern") && n["pattern"].has_children())
{
for (const auto& db_pattern : n["pattern"].children())
{
DynamicPatchEntry entry;
Patch::DynamicPatchEntry entry;
db_pattern["offset"] >> entry.offset;
db_pattern["value"] >> entry.value;
@ -326,7 +326,7 @@ void GameDatabase::parseAndInsert(const std::string_view& serial, const c4::yml:
}
for (const auto& db_replacement : n["replacement"].children())
{
DynamicPatchEntry entry;
Patch::DynamicPatchEntry entry;
db_replacement["offset"] >> entry.offset;
db_replacement["value"] >> entry.value;
@ -431,12 +431,12 @@ u32 GameDatabaseSchema::GameEntry::applyGameFixes(Pcsx2Config& config, bool appl
{
if (applyAuto)
{
PatchesCon->WriteLn("(GameDB) Changing EE/FPU roundmode to %d [%s]", eeRM, EnumToString(eeRM));
Console.WriteLn("(GameDB) Changing EE/FPU roundmode to %d [%s]", eeRM, EnumToString(eeRM));
config.Cpu.sseMXCSR.SetRoundMode(eeRM);
num_applied_fixes++;
}
else
PatchesCon->Warning("[GameDB] Skipping changing EE/FPU roundmode to %d [%s]", eeRM, EnumToString(eeRM));
Console.Warning("[GameDB] Skipping changing EE/FPU roundmode to %d [%s]", eeRM, EnumToString(eeRM));
}
}
@ -447,12 +447,12 @@ u32 GameDatabaseSchema::GameEntry::applyGameFixes(Pcsx2Config& config, bool appl
{
if (applyAuto)
{
PatchesCon->WriteLn("(GameDB) Changing VU0 roundmode to %d [%s]", vuRM, EnumToString(vuRM));
Console.WriteLn("(GameDB) Changing VU0 roundmode to %d [%s]", vuRM, EnumToString(vuRM));
config.Cpu.sseVU0MXCSR.SetRoundMode(vuRM);
num_applied_fixes++;
}
else
PatchesCon->Warning("[GameDB] Skipping changing VU0 roundmode to %d [%s]", vuRM, EnumToString(vuRM));
Console.Warning("[GameDB] Skipping changing VU0 roundmode to %d [%s]", vuRM, EnumToString(vuRM));
}
}
@ -463,12 +463,12 @@ u32 GameDatabaseSchema::GameEntry::applyGameFixes(Pcsx2Config& config, bool appl
{
if (applyAuto)
{
PatchesCon->WriteLn("(GameDB) Changing VU1 roundmode to %d [%s]", vuRM, EnumToString(vuRM));
Console.WriteLn("(GameDB) Changing VU1 roundmode to %d [%s]", vuRM, EnumToString(vuRM));
config.Cpu.sseVU1MXCSR.SetRoundMode(vuRM);
num_applied_fixes++;
}
else
PatchesCon->Warning("[GameDB] Skipping changing VU1 roundmode to %d [%s]", vuRM, EnumToString(vuRM));
Console.Warning("[GameDB] Skipping changing VU1 roundmode to %d [%s]", vuRM, EnumToString(vuRM));
}
}
@ -477,14 +477,14 @@ u32 GameDatabaseSchema::GameEntry::applyGameFixes(Pcsx2Config& config, bool appl
const int clampMode = enum_cast(eeClampMode);
if (applyAuto)
{
PatchesCon->WriteLn("(GameDB) Changing EE/FPU clamp mode [mode=%d]", clampMode);
Console.WriteLn("(GameDB) Changing EE/FPU clamp mode [mode=%d]", clampMode);
config.Cpu.Recompiler.fpuOverflow = (clampMode >= 1);
config.Cpu.Recompiler.fpuExtraOverflow = (clampMode >= 2);
config.Cpu.Recompiler.fpuFullMode = (clampMode >= 3);
num_applied_fixes++;
}
else
PatchesCon->Warning("[GameDB] Skipping changing EE/FPU clamp mode [mode=%d]", clampMode);
Console.Warning("[GameDB] Skipping changing EE/FPU clamp mode [mode=%d]", clampMode);
}
if (vu0ClampMode != GameDatabaseSchema::ClampMode::Undefined)
@ -492,14 +492,14 @@ u32 GameDatabaseSchema::GameEntry::applyGameFixes(Pcsx2Config& config, bool appl
const int clampMode = enum_cast(vu0ClampMode);
if (applyAuto)
{
PatchesCon->WriteLn("(GameDB) Changing VU0 clamp mode [mode=%d]", clampMode);
Console.WriteLn("(GameDB) Changing VU0 clamp mode [mode=%d]", clampMode);
config.Cpu.Recompiler.vu0Overflow = (clampMode >= 1);
config.Cpu.Recompiler.vu0ExtraOverflow = (clampMode >= 2);
config.Cpu.Recompiler.vu0SignOverflow = (clampMode >= 3);
num_applied_fixes++;
}
else
PatchesCon->Warning("[GameDB] Skipping changing VU0 clamp mode [mode=%d]", clampMode);
Console.Warning("[GameDB] Skipping changing VU0 clamp mode [mode=%d]", clampMode);
}
if (vu1ClampMode != GameDatabaseSchema::ClampMode::Undefined)
@ -507,14 +507,14 @@ u32 GameDatabaseSchema::GameEntry::applyGameFixes(Pcsx2Config& config, bool appl
const int clampMode = enum_cast(vu1ClampMode);
if (applyAuto)
{
PatchesCon->WriteLn("(GameDB) Changing VU1 clamp mode [mode=%d]", clampMode);
Console.WriteLn("(GameDB) Changing VU1 clamp mode [mode=%d]", clampMode);
config.Cpu.Recompiler.vu1Overflow = (clampMode >= 1);
config.Cpu.Recompiler.vu1ExtraOverflow = (clampMode >= 2);
config.Cpu.Recompiler.vu1SignOverflow = (clampMode >= 3);
num_applied_fixes++;
}
else
PatchesCon->Warning("[GameDB] Skipping changing VU1 clamp mode [mode=%d]", clampMode);
Console.Warning("[GameDB] Skipping changing VU1 clamp mode [mode=%d]", clampMode);
}
// TODO - config - this could be simplified with maps instead of bitfields and enums
@ -523,13 +523,13 @@ u32 GameDatabaseSchema::GameEntry::applyGameFixes(Pcsx2Config& config, bool appl
const bool mode = it.second != 0;
if (!applyAuto)
{
PatchesCon->Warning("[GameDB] Skipping setting Speedhack '%s' to [mode=%d]", EnumToString(it.first), mode);
Console.Warning("[GameDB] Skipping setting Speedhack '%s' to [mode=%d]", EnumToString(it.first), mode);
continue;
}
// Legacy note - speedhacks are setup in the GameDB as integer values, but
// are effectively booleans like the gamefixes
config.Speedhacks.Set(it.first, mode);
PatchesCon->WriteLn("(GameDB) Setting Speedhack '%s' to [mode=%d]", EnumToString(it.first), mode);
Console.WriteLn("(GameDB) Setting Speedhack '%s' to [mode=%d]", EnumToString(it.first), mode);
num_applied_fixes++;
}
@ -538,12 +538,12 @@ u32 GameDatabaseSchema::GameEntry::applyGameFixes(Pcsx2Config& config, bool appl
{
if (!applyAuto)
{
PatchesCon->Warning("[GameDB] Skipping Gamefix: %s", EnumToString(id));
Console.Warning("[GameDB] Skipping Gamefix: %s", EnumToString(id));
continue;
}
// if the fix is present, it is said to be enabled
config.Gamefixes.Set(id, true);
PatchesCon->WriteLn("(GameDB) Enabled Gamefix: %s", EnumToString(id));
Console.WriteLn("(GameDB) Enabled Gamefix: %s", EnumToString(id));
num_applied_fixes++;
// The LUT is only used for 1 game so we allocate it only when the gamefix is enabled (save 4MB)
@ -685,7 +685,7 @@ u32 GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions&
if (configMatchesHWFix(config, id, value))
continue;
PatchesCon->Warning("[GameDB] Skipping GS Hardware Fix: %s to [mode=%d]", getHWFixName(id), value);
Console.Warning("[GameDB] Skipping GS Hardware Fix: %s to [mode=%d]", getHWFixName(id), value);
fmt::format_to(std::back_inserter(disabled_fixes), "{} {} = {}", disabled_fixes.empty() ? " " : "\n ", getHWFixName(id), value);
continue;
}
@ -900,7 +900,7 @@ u32 GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions&
break;
}
PatchesCon->WriteLn("[GameDB] Enabled GS Hardware Fix: %s to [mode=%d]", getHWFixName(id), value);
Console.WriteLn("[GameDB] Enabled GS Hardware Fix: %s to [mode=%d]", getHWFixName(id), value);
num_applied_fixes++;
}

View File

@ -117,7 +117,7 @@ namespace GameDatabaseSchema
std::vector<std::pair<GSHWFixId, s32>> gsHWFixes;
std::vector<std::string> memcardFilters;
std::unordered_map<u32, std::string> patches;
std::vector<DynamicPatch> dynaPatches;
std::vector<Patch::DynamicPatch> dynaPatches;
// Returns the list of memory card serials as a `/` delimited string
std::string memcardFiltersAsString() const;

View File

@ -4098,8 +4098,7 @@ void FullscreenUI::DrawFoldersSettingsPage()
DrawFolderSetting(bsi, ICON_FA_WRENCH " Game Settings Directory", "Folders", "GameSettings", EmuFolders::GameSettings);
DrawFolderSetting(bsi, ICON_FA_GAMEPAD " Input Profile Directory", "Folders", "InputProfiles", EmuFolders::InputProfiles);
DrawFolderSetting(bsi, ICON_FA_FROWN " Cheats Directory", "Folders", "Cheats", EmuFolders::Cheats);
DrawFolderSetting(bsi, ICON_FA_TV " Widescreen Cheats Directory", "Folders", "CheatsWS", EmuFolders::CheatsWS);
DrawFolderSetting(bsi, ICON_FA_MAGIC " No-Interlace Cheats Directory", "Folders", "CheatsNI", EmuFolders::CheatsNI);
DrawFolderSetting(bsi, ICON_FA_MAGIC " Patches Directory", "Folders", "Patches", EmuFolders::Patches);
DrawFolderSetting(bsi, ICON_FA_SLIDERS_H "Texture Replacements Directory", "Folders", "Textures", EmuFolders::Textures);
DrawFolderSetting(bsi, ICON_FA_SLIDERS_H "Video Dumping Directory", "Folders", "Videos", EmuFolders::Videos);

File diff suppressed because it is too large Load Diff

View File

@ -28,121 +28,91 @@
// - UI name: "Patches", controlled via system -> enable automatic game fixes
// - note that automatic game fixes also controls automatic config changes from GameIndex.dbf (UI name: "fixes")
//
// So, if the console title contains the following suffix: "... [2 Fixes] [1 Patches] [0 Cheats] [6 widescreen hacks]" then:
// - The 2 fixes are configuration (not patches) fixes applied from the Games DB (automatic game fixes).
// - The 1 Patch is one uncommented pnach-style patch line from the GamesDB (automatic game fixes).
// - The 0 cheats - cheats are enabled but nothing found/loaded from the "cheats" folder.
// - The 6 widescreen patches are 6 pnach-style patch lines loaded either from cheats_ws folder or from cheats_ws.zip
#include "common/Pcsx2Defs.h"
#include "SysForwardDefs.h"
#include "Config.h"
#include <string>
#include <string_view>
#include <vector>
struct IConsoleWriter;
enum patch_cpu_type {
NO_CPU,
CPU_EE,
CPU_IOP
};
enum patch_data_type {
NO_TYPE,
BYTE_T,
SHORT_T,
WORD_T,
DOUBLE_T,
EXTENDED_T,
SHORT_BE_T,
WORD_BE_T,
DOUBLE_BE_T
};
// "place" is the first number at a pnach line (patch=<place>,...), e.g.:
// - patch=1,EE,001110e0,word,00000000 <-- place is 1
// - patch=0,EE,0010BC88,word,48468800 <-- place is 0
// In PCSX2 it indicates how/when/where the patch line should be applied. If
// place is not one of the supported values then the patch line is never applied.
// PCSX2 currently supports the following values:
// 0 - apply the patch line once on game boot/startup
// 1 - apply the patch line continuously (technically - on every vsync)
// 2 - effect of 0 and 1 combined, see below
// Note:
// - while it may seem that a value of 1 does the same as 0, but also later
// continues to apply the patch on every vsync - it's not.
// The current (and past) behavior is that these patches are applied at different
// places at the code, and it's possible, depending on circumstances, that 0 patches
// will get applied before the first vsync and therefore earlier than 1 patches.
// - There's no "place" value which indicates to apply both once on startup
// and then also continuously, however such behavior can be achieved by
// duplicating the line where one has a 0 place and the other has a 1 place.
enum patch_place_type {
PPT_ONCE_ON_LOAD = 0,
PPT_CONTINUOUSLY = 1,
PPT_COMBINED_0_1 = 2,
_PPT_END_MARKER
};
typedef void PATCHTABLEFUNC(const std::string_view& text1, const std::string_view& text2);
struct IniPatch
namespace Patch
{
int enabled;
patch_data_type type;
patch_cpu_type cpu;
int placetopatch;
u32 addr;
u64 data;
};
// "place" is the first number at a pnach line (patch=<place>,...), e.g.:
// - patch=1,EE,001110e0,word,00000000 <-- place is 1
// - patch=0,EE,0010BC88,word,48468800 <-- place is 0
// In PCSX2 it indicates how/when/where the patch line should be applied. If
// place is not one of the supported values then the patch line is never applied.
// PCSX2 currently supports the following values:
// 0 - apply the patch line once on game boot/startup
// 1 - apply the patch line continuously (technically - on every vsync)
// 2 - effect of 0 and 1 combined, see below
// Note:
// - while it may seem that a value of 1 does the same as 0, but also later
// continues to apply the patch on every vsync - it's not.
// The current (and past) behavior is that these patches are applied at different
// places at the code, and it's possible, depending on circumstances, that 0 patches
// will get applied before the first vsync and therefore earlier than 1 patches.
// - There's no "place" value which indicates to apply both once on startup
// and then also continuously, however such behavior can be achieved by
// duplicating the line where one has a 0 place and the other has a 1 place.
enum patch_place_type
{
PPT_ONCE_ON_LOAD = 0,
PPT_CONTINUOUSLY = 1,
PPT_COMBINED_0_1 = 2,
struct DynamicPatchEntry
{
u32 offset;
u32 value;
};
PPT_END_MARKER
};
struct DynamicPatch
{
std::vector<DynamicPatchEntry> pattern;
std::vector<DynamicPatchEntry> replacement;
};
struct PatchInfo
{
std::string name;
std::string description;
std::string author;
namespace PatchFunc
{
PATCHTABLEFUNC author;
PATCHTABLEFUNC comment;
PATCHTABLEFUNC gametitle;
PATCHTABLEFUNC patch;
}
std::string_view GetNamePart() const;
std::string_view GetNameParentPart() const;
};
// The following LoadPatchesFrom* functions:
// - do not reset/unload previously loaded patches (use ForgetLoadedPatches() for that)
// - do not actually patch the emulation memory (that happens at ApplyLoadedPatches(...) )
extern int LoadPatchesFromString(const std::string& patches);
extern int LoadPatchesFromDir(const std::string& crc, const std::string& folder, const char* friendly_name, bool show_error_when_missing);
extern int LoadPatchesFromZip(const std::string& crc, const u8* zip_data, size_t zip_data_size);
using PatchInfoList = std::vector<PatchInfo>;
// Functions for Dynamic EE patching.
extern void LoadDynamicPatches(const std::vector<DynamicPatch>& patches);
extern void ApplyDynamicPatches(u32 pc);
struct DynamicPatchEntry
{
u32 offset;
u32 value;
};
// Patches the emulation memory by applying all the loaded patches with a specific place value.
// Note: unless you know better, there's no need to check whether or not different patch sources
// are enabled (e.g. ws patches, auto game fixes, etc) before calling ApplyLoadedPatches,
// because on boot or on any configuration change --> all the loaded patches are invalidated,
// and then it loads only the ones which are enabled according to the current config
// (this happens at AppCoreThread::ApplySettings(...) )
extern void ApplyLoadedPatches(patch_place_type place);
struct DynamicPatch
{
std::vector<DynamicPatchEntry> pattern;
std::vector<DynamicPatchEntry> replacement;
};
// Empties the patches store ("unload" the patches) but doesn't touch the emulation memory.
// Following ApplyLoadedPatches calls will do nothing until some LoadPatchesFrom* are invoked.
extern void ForgetLoadedPatches();
// Config sections/keys to use to enable patches.
extern const char* PATCHES_CONFIG_SECTION;
extern const char* CHEATS_CONFIG_SECTION;
extern const char* PATCH_ENABLE_CONFIG_KEY;
extern const IConsoleWriter *PatchesCon;
extern PatchInfoList GetPatchInfo(const std::string& serial, u32 crc, bool cheats, u32* num_unlabelled_patches);
// The following prototypes seem unused in PCSX2, but maybe part of the cheats browser?
// regardless, they don't seem to have an implementation anywhere.
// extern int AddPatch(int Mode, int Place, int Address, int Size, u64 data);
// extern void ResetPatch(void);
/// Reloads cheats/patches. If verbose is set, the number of patches loaded will be shown in the OSD.
extern void ReloadPatches(std::string serial, u32 crc, bool force_reload_files, bool reload_enabled_list, bool verbose);
extern void ReloadPatches(bool force_reload_files, bool reload_enabled_list, bool verbose);
extern void UpdateActivePatches(bool reload_enabled_list, bool verbose, bool verbose_if_changed);
extern void ApplyPatchSettingOverrides();
extern bool ReloadPatchAffectingOptions();
extern void UnloadPatches();
// Functions for Dynamic EE patching.
extern void LoadDynamicPatches(const std::vector<DynamicPatch>& patches);
extern void ApplyDynamicPatches(u32 pc);
// Patches the emulation memory by applying all the loaded patches with a specific place value.
// Note: unless you know better, there's no need to check whether or not different patch sources
// are enabled (e.g. ws patches, auto game fixes, etc) before calling ApplyLoadedPatches,
// because on boot or on any configuration change --> all the loaded patches are invalidated,
// and then it loads only the ones which are enabled according to the current config
// (this happens at AppCoreThread::ApplySettings(...) )
extern void ApplyLoadedPatches(patch_place_type place);
} // namespace Patch

View File

@ -1,609 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "common/ByteSwap.h"
#define _PC_ // disables MIPS opcode macros.
#include "Common.h"
#include "Patch.h"
#include "IopMem.h"
u32 SkipCount = 0, IterationCount = 0;
u32 IterationIncrement = 0, ValueIncrement = 0;
u32 PrevCheatType = 0, PrevCheatAddr = 0, LastType = 0;
void writeCheat()
{
switch (LastType)
{
case 0x0:
memWrite8(PrevCheatAddr, IterationIncrement & 0xFF);
break;
case 0x1:
memWrite16(PrevCheatAddr, IterationIncrement & 0xFFFF);
break;
case 0x2:
memWrite32(PrevCheatAddr, IterationIncrement);
break;
default:
break;
}
}
void handle_extended_t(IniPatch *p)
{
if (SkipCount > 0)
{
SkipCount--;
}
else switch (PrevCheatType)
{
case 0x3040: // vvvvvvvv 00000000 Inc
{
u32 mem = memRead32(PrevCheatAddr);
memWrite32(PrevCheatAddr, mem + (p->addr));
PrevCheatType = 0;
break;
}
case 0x3050: // vvvvvvvv 00000000 Dec
{
u32 mem = memRead32(PrevCheatAddr);
memWrite32(PrevCheatAddr, mem - (p->addr));
PrevCheatType = 0;
break;
}
case 0x4000: // vvvvvvvv iiiiiiii
for (u32 i = 0; i < IterationCount; i++)
{
memWrite32((u32)(PrevCheatAddr + (i * IterationIncrement)), (u32)(p->addr + ((u32)p->data * i)));
}
PrevCheatType = 0;
break;
case 0x5000: // bbbbbbbb 00000000
for (u32 i = 0; i < IterationCount; i++)
{
u8 mem = memRead8(PrevCheatAddr + i);
memWrite8((p->addr + i) & 0x0FFFFFFF, mem);
}
PrevCheatType = 0;
break;
case 0x6000: // 000Xnnnn iiiiiiii
{
// Get Number of pointers
if (((u32)p->addr & 0x0000FFFF) == 0)
IterationCount = 1;
else
IterationCount = (u32)p->addr & 0x0000FFFF;
// Read first pointer
LastType = ((u32)p->addr & 0x000F0000) >> 16;
u32 mem = memRead32(PrevCheatAddr);
PrevCheatAddr = mem + (u32)p->data;
IterationCount--;
// Check if needed to read another pointer
if (IterationCount == 0)
{
PrevCheatType = 0;
if (((mem & 0x0FFFFFFF) & 0x3FFFFFFC) != 0) writeCheat();
}
else
{
if (((mem & 0x0FFFFFFF) & 0x3FFFFFFC) == 0)
PrevCheatType = 0;
else
PrevCheatType = 0x6001;
}
}
break;
case 0x6001: // 000Xnnnn iiiiiiii
{
// Read first pointer
u32 mem = memRead32(PrevCheatAddr & 0x0FFFFFFF);
PrevCheatAddr = mem + (u32)p->addr;
IterationCount--;
// Check if needed to read another pointer
if (IterationCount == 0)
{
PrevCheatType = 0;
if (((mem & 0x0FFFFFFF) & 0x3FFFFFFC) != 0) writeCheat();
}
else
{
mem = memRead32(PrevCheatAddr);
PrevCheatAddr = mem + (u32)p->data;
IterationCount--;
if (IterationCount == 0)
{
PrevCheatType = 0;
if (((mem & 0x0FFFFFFF) & 0x3FFFFFFC) != 0) writeCheat();
}
}
}
break;
default:
if ((p->addr & 0xF0000000) == 0x00000000) // 0aaaaaaa 0000000vv
{
memWrite8(p->addr & 0x0FFFFFFF, (u8)p->data & 0x000000FF);
PrevCheatType = 0;
}
else if ((p->addr & 0xF0000000) == 0x10000000) // 1aaaaaaa 0000vvvv
{
memWrite16(p->addr & 0x0FFFFFFF, (u16)p->data & 0x0000FFFF);
PrevCheatType = 0;
}
else if ((p->addr & 0xF0000000) == 0x20000000) // 2aaaaaaa vvvvvvvv
{
memWrite32(p->addr & 0x0FFFFFFF, (u32)p->data);
PrevCheatType = 0;
}
else if ((p->addr & 0xFFFF0000) == 0x30000000) // 300000vv 0aaaaaaa Inc
{
u8 mem = memRead8((u32)p->data);
memWrite8((u32)p->data, mem + (p->addr & 0x000000FF));
PrevCheatType = 0;
}
else if ((p->addr & 0xFFFF0000) == 0x30100000) // 301000vv 0aaaaaaa Dec
{
u8 mem = memRead8((u32)p->data);
memWrite8((u32)p->data, mem - (p->addr & 0x000000FF));
PrevCheatType = 0;
}
else if ((p->addr & 0xFFFF0000) == 0x30200000) // 3020vvvv 0aaaaaaa Inc
{
u16 mem = memRead16((u32)p->data);
memWrite16((u32)p->data, mem + (p->addr & 0x0000FFFF));
PrevCheatType = 0;
}
else if ((p->addr & 0xFFFF0000) == 0x30300000) // 3030vvvv 0aaaaaaa Dec
{
u16 mem = memRead16((u32)p->data);
memWrite16((u32)p->data, mem - (p->addr & 0x0000FFFF));
PrevCheatType = 0;
}
else if ((p->addr & 0xFFFF0000) == 0x30400000) // 30400000 0aaaaaaa Inc + Another line
{
PrevCheatType = 0x3040;
PrevCheatAddr = (u32)p->data;
}
else if ((p->addr & 0xFFFF0000) == 0x30500000) // 30500000 0aaaaaaa Inc + Another line
{
PrevCheatType = 0x3050;
PrevCheatAddr = (u32)p->data;
}
else if ((p->addr & 0xF0000000) == 0x40000000) // 4aaaaaaa nnnnssss + Another line
{
IterationCount = ((u32)p->data & 0xFFFF0000) >> 16;
IterationIncrement = ((u32)p->data & 0x0000FFFF) * 4;
PrevCheatAddr = (u32)p->addr & 0x0FFFFFFF;
PrevCheatType = 0x4000;
}
else if ((p->addr & 0xF0000000) == 0x50000000) // 5sssssss nnnnnnnn + Another line
{
PrevCheatAddr = (u32)p->addr & 0x0FFFFFFF;
IterationCount = ((u32)p->data);
PrevCheatType = 0x5000;
}
else if ((p->addr & 0xF0000000) == 0x60000000) // 6aaaaaaa 000000vv + Another line/s
{
PrevCheatAddr = (u32)p->addr & 0x0FFFFFFF;
IterationIncrement = ((u32)p->data);
IterationCount = 0;
PrevCheatType = 0x6000;
}
else if ((p->addr & 0xF0000000) == 0x70000000)
{
if ((p->data & 0x00F00000) == 0x00000000) // 7aaaaaaa 000000vv
{
u8 mem = memRead8((u32)p->addr & 0x0FFFFFFF);
memWrite8((u32)p->addr & 0x0FFFFFFF, (u8)(mem | (p->data & 0x000000FF)));
}
else if ((p->data & 0x00F00000) == 0x00100000) // 7aaaaaaa 0010vvvv
{
u16 mem = memRead16((u32)p->addr & 0x0FFFFFFF);
memWrite16((u32)p->addr & 0x0FFFFFFF, (u16)(mem | (p->data & 0x0000FFFF)));
}
else if ((p->data & 0x00F00000) == 0x00200000) // 7aaaaaaa 002000vv
{
u8 mem = memRead8((u32)p->addr & 0x0FFFFFFF);
memWrite8((u32)p->addr & 0x0FFFFFFF, (u8)(mem & (p->data & 0x000000FF)));
}
else if ((p->data & 0x00F00000) == 0x00300000) // 7aaaaaaa 0030vvvv
{
u16 mem = memRead16((u32)p->addr & 0x0FFFFFFF);
memWrite16((u32)p->addr & 0x0FFFFFFF, (u16)(mem & (p->data & 0x0000FFFF)));
}
else if ((p->data & 0x00F00000) == 0x00400000) // 7aaaaaaa 004000vv
{
u8 mem = memRead8((u32)p->addr & 0x0FFFFFFF);
memWrite8((u32)p->addr & 0x0FFFFFFF, (u8)(mem ^ (p->data & 0x000000FF)));
}
else if ((p->data & 0x00F00000) == 0x00500000) // 7aaaaaaa 0050vvvv
{
u16 mem = memRead16((u32)p->addr & 0x0FFFFFFF);
memWrite16((u32)p->addr & 0x0FFFFFFF, (u16)(mem ^ (p->data & 0x0000FFFF)));
}
}
else if ((p->addr & 0xF0000000) == 0xD0000000 || (p->addr & 0xF0000000) == 0xE0000000)
{
u32 addr = (u32)p->addr;
u32 data = (u32)p->data;
// Since D-codes now have the additional functionality present in PS2rd which
// incorporates E-code-like functionality by making use of the unused bits in
// D-codes, the E-codes are now just converted to D-codes to reduce bloat.
if ((addr & 0xF0000000) == 0xE0000000)
{
// Ezyyvvvv taaaaaaa -> Daaaaaaa yytzvvvv
addr = 0xD0000000 | ((u32)p->data & 0x0FFFFFFF);
data = 0x00000000 | ((u32)p->addr & 0x0000FFFF);
data = data | ((u32)p->addr & 0x00FF0000) << 8;
data = data | ((u32)p->addr & 0x0F000000) >> 8;
data = data | ((u32)p->data & 0xF0000000) >> 8;
}
const u8 type = (data & 0x000F0000) >> 16;
const u8 cond = (data & 0x00F00000) >> 20;
if (cond == 0) // Daaaaaaa yy0zvvvv
{
if (type == 0) // Daaaaaaa yy00vvvv
{
u16 mem = memRead16(addr & 0x0FFFFFFF);
if (mem != (data & 0x0000FFFF))
{
SkipCount = (data & 0xFF000000) >> 24;
if (!SkipCount)
{
SkipCount = 1;
}
}
PrevCheatType = 0;
}
else if (type == 1) // Daaaaaaa yy0100vv
{
u8 mem = memRead8(addr & 0x0FFFFFFF);
if (mem != (data & 0x000000FF))
{
SkipCount = (data & 0xFF000000) >> 24;
if (!SkipCount)
{
SkipCount = 1;
}
}
PrevCheatType = 0;
}
}
else if (cond == 1) // Daaaaaaa yy1zvvvv
{
if (type == 0) // Daaaaaaa yy10vvvv
{
u16 mem = memRead16(addr & 0x0FFFFFFF);
if (mem == (data & 0x0000FFFF))
{
SkipCount = (data & 0xFF000000) >> 24;
if (!SkipCount)
{
SkipCount = 1;
}
}
PrevCheatType = 0;
}
else if (type == 1) // Daaaaaaa yy1100vv
{
u8 mem = memRead8(addr & 0x0FFFFFFF);
if (mem == (data & 0x000000FF))
{
SkipCount = (data & 0xFF000000) >> 24;
if (!SkipCount)
{
SkipCount = 1;
}
}
PrevCheatType = 0;
}
}
else if (cond == 2) // Daaaaaaa yy2zvvvv
{
if (type == 0) // Daaaaaaa yy20vvvv
{
u16 mem = memRead16(addr & 0x0FFFFFFF);
if (mem >= (data & 0x0000FFFF))
{
SkipCount = (data & 0xFF000000) >> 24;
if (!SkipCount)
{
SkipCount = 1;
}
}
PrevCheatType = 0;
}
else if (type == 1) // Daaaaaaa yy2100vv
{
u8 mem = memRead8(addr & 0x0FFFFFFF);
if (mem >= (data & 0x000000FF))
{
SkipCount = (data & 0xFF000000) >> 24;
if (!SkipCount)
{
SkipCount = 1;
}
}
PrevCheatType = 0;
}
}
else if (cond == 3) // Daaaaaaa yy3zvvvv
{
if (type == 0) // Daaaaaaa yy30vvvv
{
u16 mem = memRead16(addr & 0x0FFFFFFF);
if (mem <= (data & 0x0000FFFF))
{
SkipCount = (data & 0xFF000000) >> 24;
if (!SkipCount)
{
SkipCount = 1;
}
}
PrevCheatType = 0;
}
else if (type == 1) // Daaaaaaa yy3100vv
{
u8 mem = memRead8(addr & 0x0FFFFFFF);
if (mem <= (data & 0x000000FF))
{
SkipCount = (data & 0xFF000000) >> 24;
if (!SkipCount)
{
SkipCount = 1;
}
}
PrevCheatType = 0;
}
}
else if (cond == 4) // Daaaaaaa yy4zvvvv
{
if (type == 0) // Daaaaaaa yy40vvvv
{
u16 mem = memRead16(addr & 0x0FFFFFFF);
if (mem & (data & 0x0000FFFF))
{
SkipCount = (data & 0xFF000000) >> 24;
if (!SkipCount)
{
SkipCount = 1;
}
}
PrevCheatType = 0;
}
else if (type == 1) // Daaaaaaa yy4100vv
{
u8 mem = memRead8(addr & 0x0FFFFFFF);
if (mem & (data & 0x000000FF))
{
SkipCount = (data & 0xFF000000) >> 24;
if (!SkipCount)
{
SkipCount = 1;
}
}
PrevCheatType = 0;
}
}
else if (cond == 5) // Daaaaaaa yy5zvvvv
{
if (type == 0) // Daaaaaaa yy50vvvv
{
u16 mem = memRead16(addr & 0x0FFFFFFF);
if (!(mem & (data & 0x0000FFFF)))
{
SkipCount = (data & 0xFF000000) >> 24;
if (!SkipCount)
{
SkipCount = 1;
}
}
PrevCheatType = 0;
}
else if (type == 1) // Daaaaaaa yy5100vv
{
u8 mem = memRead8(addr & 0x0FFFFFFF);
if (!(mem & (data & 0x000000FF)))
{
SkipCount = (data & 0xFF000000) >> 24;
if (!SkipCount)
{
SkipCount = 1;
}
}
PrevCheatType = 0;
}
}
else if (cond == 6) // Daaaaaaa yy6zvvvv
{
if (type == 0) // Daaaaaaa yy60vvvv
{
u16 mem = memRead16(addr & 0x0FFFFFFF);
if (mem | (data & 0x0000FFFF))
{
SkipCount = (data & 0xFF000000) >> 24;
if (!SkipCount)
{
SkipCount = 1;
}
}
PrevCheatType = 0;
}
else if (type == 1) // Daaaaaaa yy6100vv
{
u8 mem = memRead8(addr & 0x0FFFFFFF);
if (mem | (data & 0x000000FF))
{
SkipCount = (data & 0xFF000000) >> 24;
if (!SkipCount)
{
SkipCount = 1;
}
}
PrevCheatType = 0;
}
}
else if (cond == 7) // Daaaaaaa yy7zvvvv
{
if (type == 0) // Daaaaaaa yy70vvvv
{
u16 mem = memRead16(addr & 0x0FFFFFFF);
if (!(mem | (data & 0x0000FFFF)))
{
SkipCount = (data & 0xFF000000) >> 24;
if (!SkipCount)
{
SkipCount = 1;
}
}
PrevCheatType = 0;
}
else if (type == 1) // Daaaaaaa yy7100vv
{
u8 mem = memRead8(addr & 0x0FFFFFFF);
if (!(mem | (data & 0x000000FF)))
{
SkipCount = (data & 0xFF000000) >> 24;
if (!SkipCount)
{
SkipCount = 1;
}
}
PrevCheatType = 0;
}
}
}
}
}
// Only used from Patch.cpp and we don't export this in any h file.
// Patch.cpp itself declares this prototype, so make sure to keep in sync.
void _ApplyPatch(IniPatch *p)
{
u64 ledata = 0;
if (p->enabled == 0) return;
switch (p->cpu)
{
case CPU_EE:
switch (p->type)
{
case BYTE_T:
if (memRead8(p->addr) != (u8)p->data)
memWrite8(p->addr, (u8)p->data);
break;
case SHORT_T:
if (memRead16(p->addr) != (u16)p->data)
memWrite16(p->addr, (u16)p->data);
break;
case WORD_T:
if (memRead32(p->addr) != (u32)p->data)
memWrite32(p->addr, (u32)p->data);
break;
case DOUBLE_T:
if (memRead64(p->addr) != (u64)p->data)
memWrite64(p->addr, (u64)p->data);
break;
case EXTENDED_T:
handle_extended_t(p);
break;
case SHORT_BE_T:
ledata = ByteSwap(static_cast<u16>(p->data));
if (memRead16(p->addr) != (u16)ledata)
memWrite16(p->addr, (u16)ledata);
break;
case WORD_BE_T:
ledata = ByteSwap(static_cast<u32>(p->data));
if (memRead32(p->addr) != (u32)ledata)
memWrite32(p->addr, (u32)ledata);
break;
case DOUBLE_BE_T:
ledata = ByteSwap(p->data);
if (memRead64(p->addr) != (u64)ledata)
memWrite64(p->addr, (u64)ledata);
break;
default:
break;
}
break;
case CPU_IOP:
switch (p->type)
{
case BYTE_T:
if (iopMemRead8(p->addr) != (u8)p->data)
iopMemWrite8(p->addr, (u8)p->data);
break;
case SHORT_T:
if (iopMemRead16(p->addr) != (u16)p->data)
iopMemWrite16(p->addr, (u16)p->data);
break;
case WORD_T:
if (iopMemRead32(p->addr) != (u32)p->data)
iopMemWrite32(p->addr, (u32)p->data);
break;
default:
break;
}
break;
default:
break;
}
}
void _ApplyDynaPatch(const DynamicPatch& patch, u32 address)
{
for (const auto& pattern : patch.pattern)
{
if (*static_cast<u32*>(PSM(address + pattern.offset)) != pattern.value)
return;
}
PatchesCon->WriteLn("Applying Dynamic Patch to address 0x%08X", address);
// If everything passes, apply the patch.
for (const auto& replacement : patch.replacement)
{
memWrite32(address + replacement.offset, replacement.value);
}
}

View File

@ -100,8 +100,7 @@ namespace EmuFolders
std::string Langs;
std::string Logs;
std::string Cheats;
std::string CheatsWS;
std::string CheatsNI;
std::string Patches;
std::string Resources;
std::string Cache;
std::string Covers;
@ -1613,8 +1612,7 @@ void EmuFolders::SetDefaults(SettingsInterface& si)
si.SetStringValue("Folders", "MemoryCards", "memcards");
si.SetStringValue("Folders", "Logs", "logs");
si.SetStringValue("Folders", "Cheats", "cheats");
si.SetStringValue("Folders", "CheatsWS", "cheats_ws");
si.SetStringValue("Folders", "CheatsNI", "cheats_ni");
si.SetStringValue("Folders", "Patches", "patches");
si.SetStringValue("Folders", "Cache", "cache");
si.SetStringValue("Folders", "Textures", "textures");
si.SetStringValue("Folders", "InputProfiles", "inputprofiles");
@ -1637,8 +1635,7 @@ void EmuFolders::LoadConfig(SettingsInterface& si)
MemoryCards = LoadPathFromSettings(si, DataRoot, "MemoryCards", "memcards");
Logs = LoadPathFromSettings(si, DataRoot, "Logs", "logs");
Cheats = LoadPathFromSettings(si, DataRoot, "Cheats", "cheats");
CheatsWS = LoadPathFromSettings(si, DataRoot, "CheatsWS", "cheats_ws");
CheatsNI = LoadPathFromSettings(si, DataRoot, "CheatsNI", "cheats_ni");
Patches = LoadPathFromSettings(si, DataRoot, "Patches", "patches");
Covers = LoadPathFromSettings(si, DataRoot, "Covers", "covers");
GameSettings = LoadPathFromSettings(si, DataRoot, "GameSettings", "gamesettings");
Cache = LoadPathFromSettings(si, DataRoot, "Cache", "cache");
@ -1652,8 +1649,7 @@ void EmuFolders::LoadConfig(SettingsInterface& si)
Console.WriteLn("MemoryCards Directory: %s", MemoryCards.c_str());
Console.WriteLn("Logs Directory: %s", Logs.c_str());
Console.WriteLn("Cheats Directory: %s", Cheats.c_str());
Console.WriteLn("CheatsWS Directory: %s", CheatsWS.c_str());
Console.WriteLn("CheatsNI Directory: %s", CheatsNI.c_str());
Console.WriteLn("Patches Directory: %s", Patches.c_str());
Console.WriteLn("Covers Directory: %s", Covers.c_str());
Console.WriteLn("Game Settings Directory: %s", GameSettings.c_str());
Console.WriteLn("Cache Directory: %s", Cache.c_str());
@ -1671,8 +1667,7 @@ bool EmuFolders::EnsureFoldersExist()
result = FileSystem::CreateDirectoryPath(MemoryCards.c_str(), false) && result;
result = FileSystem::CreateDirectoryPath(Logs.c_str(), false) && result;
result = FileSystem::CreateDirectoryPath(Cheats.c_str(), false) && result;
result = FileSystem::CreateDirectoryPath(CheatsWS.c_str(), false) && result;
result = FileSystem::CreateDirectoryPath(CheatsNI.c_str(), false) && result;
result = FileSystem::CreateDirectoryPath(Patches.c_str(), false) && result;
result = FileSystem::CreateDirectoryPath(Covers.c_str(), false) && result;
result = FileSystem::CreateDirectoryPath(GameSettings.c_str(), false) && result;
result = FileSystem::CreateDirectoryPath(Cache.c_str(), false) && result;

View File

@ -15,57 +15,56 @@
#include "PrecompiledHeader.h"
#include "VMManager.h"
#include "Achievements.h"
#include "Counters.h"
#include "CDVD/CDVD.h"
#include "Counters.h"
#include "DEV9/DEV9.h"
#include "DebugTools/MIPSAnalyst.h"
#include "DebugTools/SymbolMap.h"
#include "Elfheader.h"
#include "FW.h"
#include "GameDatabase.h"
#include "GameList.h"
#include "GS.h"
#include "GS/Renderers/HW/GSTextureReplacements.h"
#include "GSDumpReplayer.h"
#include "GameDatabase.h"
#include "GameList.h"
#include "Host.h"
#include "ImGui/FullscreenUI.h"
#include "INISettingsInterface.h"
#include "ImGui/FullscreenUI.h"
#include "Input/InputManager.h"
#include "IopBios.h"
#include "LogSink.h"
#include "MTVU.h"
#include "MemoryCardFile.h"
#include "Patch.h"
#include "PerformanceMetrics.h"
#include "PINE.h"
#include "R5900.h"
#include "SPU2/spu2.h"
#include "DEV9/DEV9.h"
#include "USB/USB.h"
#include "PAD/Host/PAD.h"
#include "PCSX2Base.h"
#include "Sio.h"
#include "ps2/BiosTools.h"
#include "Recording/InputRecordingControls.h"
#include "DebugTools/MIPSAnalyst.h"
#include "DebugTools/SymbolMap.h"
#include "PINE.h"
#include "Patch.h"
#include "PerformanceMetrics.h"
#include "R5900.h"
#include "Recording/InputRecording.h"
#include "Recording/InputRecordingControls.h"
#include "SPU2/spu2.h"
#include "Sio.h"
#include "USB/USB.h"
#include "VMManager.h"
#include "ps2/BiosTools.h"
#include "common/Console.h"
#include "common/FileSystem.h"
#include "common/ScopedGuard.h"
#include "common/StringUtil.h"
#include "common/SettingsWrapper.h"
#include "common/Timer.h"
#include "common/StringUtil.h"
#include "common/Threading.h"
#include "common/Timer.h"
#include "common/emitter/tools.h"
#include "IconsFontAwesome5.h"
#include "fmt/core.h"
#include <atomic>
#include <sstream>
#include <mutex>
#include <sstream>
#ifdef _M_X86
#include "common/emitter/x86_intrin.h"
@ -104,19 +103,17 @@ namespace VMManager
static bool AutoDetectSource(const std::string& filename);
static bool ApplyBootParameters(VMBootParameters params, std::string* state_to_load);
static bool CheckBIOSAvailability();
static void LoadPatches(const std::string& serial, u32 crc,
bool show_messages, bool show_messages_when_disabled);
static void UpdateRunningGame(bool resetting, bool game_starting, bool swapping);
static std::string GetCurrentSaveStateFileName(s32 slot);
static bool DoLoadState(const char* filename);
static bool DoSaveState(const char* filename, s32 slot_for_message, bool zip_on_thread, bool backup_old_state);
static void ZipSaveState(std::unique_ptr<ArchiveEntryList> elist,
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key,
const char* filename, s32 slot_for_message);
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key, const char* filename,
s32 slot_for_message);
static void ZipSaveStateOnThread(std::unique_ptr<ArchiveEntryList> elist,
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key,
std::string filename, s32 slot_for_message);
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key, std::string filename,
s32 slot_for_message);
static void UpdateInhibitScreensaver(bool allow);
static void SaveSessionTime();
@ -155,12 +152,6 @@ static std::string s_game_name;
static std::string s_elf_override;
static std::string s_input_profile_name;
static u32 s_active_game_fixes = 0;
static std::vector<u8> s_widescreen_cheats_data;
static bool s_widescreen_cheats_loaded = false;
static std::vector<u8> s_no_interlacing_cheats_data;
static bool s_no_interlacing_cheats_loaded = false;
static s32 s_active_widescreen_patches = 0;
static u32 s_active_no_interlacing_patches = 0;
static u32 s_frame_advance_count = 0;
static u32 s_mxcsr_saved;
static bool s_gs_open_on_initialize = false;
@ -178,8 +169,7 @@ static bool s_discord_presence_active = false;
bool VMManager::PerformEarlyHardwareChecks(const char** error)
{
#define COMMON_DOWNLOAD_MESSAGE \
"PCSX2 builds can be downloaded from https://pcsx2.net/downloads/"
#define COMMON_DOWNLOAD_MESSAGE "PCSX2 builds can be downloaded from https://pcsx2.net/downloads/"
#if defined(_M_X86)
// On Windows, this gets called as a global object constructor, before any of our objects are constructed.
@ -189,16 +179,20 @@ bool VMManager::PerformEarlyHardwareChecks(const char** error)
if (!temp_x86_caps.hasStreamingSIMD4Extensions)
{
*error = "PCSX2 requires the Streaming SIMD 4.1 Extensions instruction set, which your CPU does not support.\n\n"
"SSE4.1 is now a minimum requirement for PCSX2. You should either upgrade your CPU, or use an older build such as 1.6.0.\n\n" COMMON_DOWNLOAD_MESSAGE;
*error =
"PCSX2 requires the Streaming SIMD 4.1 Extensions instruction set, which your CPU does not support.\n\n"
"SSE4.1 is now a minimum requirement for PCSX2. You should either upgrade your CPU, or use an older build "
"such as 1.6.0.\n\n" COMMON_DOWNLOAD_MESSAGE;
return false;
}
#if _M_SSE >= 0x0501
if (!temp_x86_caps.hasAVX || !temp_x86_caps.hasAVX2)
{
*error = "This build of PCSX2 requires the Advanced Vector Extensions 2 instruction set, which your CPU does not support.\n\n"
"You should download and run the SSE4.1 build of PCSX2 instead, or upgrade to a CPU that supports AVX2 to use this build.\n\n" COMMON_DOWNLOAD_MESSAGE;
*error = "This build of PCSX2 requires the Advanced Vector Extensions 2 instruction set, which your CPU does "
"not support.\n\n"
"You should download and run the SSE4.1 build of PCSX2 instead, or upgrade to a CPU that supports "
"AVX2 to use this build.\n\n" COMMON_DOWNLOAD_MESSAGE;
return false;
}
#endif
@ -345,11 +339,6 @@ void VMManager::Internal::CPUThreadShutdown()
InputManager::CloseSources();
WaitForSaveStateFlush();
std::vector<u8>().swap(s_widescreen_cheats_data);
s_widescreen_cheats_loaded = false;
std::vector<u8>().swap(s_no_interlacing_cheats_data);
s_no_interlacing_cheats_loaded = false;
s_cpu_provider_pack.reset();
s_vm_memory.reset();
@ -395,7 +384,8 @@ void VMManager::Internal::LoadStartupSettings()
#endif
}
void VMManager::SetDefaultSettings(SettingsInterface& si, bool folders, bool core, bool controllers, bool hotkeys, bool ui)
void VMManager::SetDefaultSettings(
SettingsInterface& si, bool folders, bool core, bool controllers, bool hotkeys, bool ui)
{
if (si.GetUIntValue("UI", "SettingsVersion", 0u) != SETTINGS_VERSION)
si.SetUIntValue("UI", "SettingsVersion", SETTINGS_VERSION);
@ -433,6 +423,7 @@ void VMManager::LoadSettings()
InputManager::ReloadSources(*si, lock);
InputManager::ReloadBindings(*si, *Host::GetSettingsInterfaceForBindings());
LogSink::UpdateLogging(*si);
Patch::ApplyPatchSettingOverrides();
// Achievements hardcore mode disallows setting some configuration options.
EnforceAchievementsChallengeModeSettings();
@ -441,20 +432,6 @@ void VMManager::LoadSettings()
EmuConfig.GS.MaskUserHacks();
EmuConfig.GS.MaskUpscalingHacks();
// Disable interlacing if we have no-interlacing patches active.
if (s_active_no_interlacing_patches > 0 && EmuConfig.GS.InterlaceMode == GSInterlaceMode::Automatic)
EmuConfig.GS.InterlaceMode = GSInterlaceMode::Off;
// Switch to 16:9 if widescreen patches are enabled, and AR is auto.
if (s_active_widescreen_patches > 0 && EmuConfig.GS.AspectRatio == AspectRatioType::RAuto4_3_3_2)
{
// Don't change when reloading settings in the middle of a FMV with switch.
if (EmuConfig.CurrentAspectRatio == EmuConfig.GS.AspectRatio)
EmuConfig.CurrentAspectRatio = AspectRatioType::R16_9;
EmuConfig.GS.AspectRatio = AspectRatioType::R16_9;
}
// Force MTVU off when playing back GS dumps, it doesn't get used.
if (GSDumpReplayer::IsReplayingDump())
EmuConfig.Speedhacks.vuThread = false;
@ -517,8 +494,7 @@ std::string VMManager::GetInputProfilePath(const std::string_view& name)
void VMManager::Internal::UpdateEmuFolders()
{
const std::string old_cheats_directory(EmuFolders::Cheats);
const std::string old_cheats_ws_directory(EmuFolders::CheatsWS);
const std::string old_cheats_ni_directory(EmuFolders::CheatsNI);
const std::string old_patches_directory(EmuFolders::Patches);
const std::string old_memcards_directory(EmuFolders::MemoryCards);
const std::string old_textures_directory(EmuFolders::Textures);
const std::string old_videos_directory(EmuFolders::Videos);
@ -529,11 +505,8 @@ void VMManager::Internal::UpdateEmuFolders()
if (VMManager::HasValidVM())
{
if (EmuFolders::Cheats != old_cheats_directory || EmuFolders::CheatsWS != old_cheats_ws_directory ||
EmuFolders::CheatsNI != old_cheats_ni_directory)
{
VMManager::ReloadPatches(true, true);
}
if (EmuFolders::Cheats != old_cheats_directory || EmuFolders::Patches != old_patches_directory)
Patch::ReloadPatches(s_game_serial, s_game_crc, true, false, true);
if (EmuFolders::MemoryCards != old_memcards_directory)
{
@ -679,7 +652,8 @@ bool VMManager::UpdateGameSettingsLayer()
}
}
Host::Internal::SetInputSettingsLayer(input_interface ? input_interface.get() : Host::Internal::GetBaseSettingsLayer());
Host::Internal::SetInputSettingsLayer(
input_interface ? input_interface.get() : Host::Internal::GetBaseSettingsLayer());
}
else
{
@ -692,144 +666,6 @@ bool VMManager::UpdateGameSettingsLayer()
return true;
}
void VMManager::LoadPatches(const std::string& serial, u32 crc, bool show_messages, bool show_messages_when_disabled)
{
const std::string crc_string(fmt::format("{:08X}", crc));
s_patches_crc = crc;
s_active_widescreen_patches = 0;
s_active_no_interlacing_patches = 0;
ForgetLoadedPatches();
std::string message;
int patch_count = 0;
if (EmuConfig.EnablePatches)
{
const GameDatabaseSchema::GameEntry* game = GameDatabase::findGame(serial);
if (game)
{
const std::string* patches = game->findPatch(crc);
if (patches && (patch_count = LoadPatchesFromString(*patches)) > 0)
{
PatchesCon->WriteLn(Color_Green, "(GameDB) Patches Loaded: %d", patch_count);
fmt::format_to(std::back_inserter(message), "{} game patches", patch_count);
}
LoadDynamicPatches(game->dynaPatches);
}
}
// regular cheat patches
int cheat_count = 0;
if (EmuConfig.EnableCheats)
{
cheat_count = LoadPatchesFromDir(crc_string, EmuFolders::Cheats, "Cheats", true);
if (cheat_count > 0)
{
PatchesCon->WriteLn(Color_Green, "Cheats Loaded: %d", cheat_count);
fmt::format_to(std::back_inserter(message), "{}{} cheat patches", (patch_count > 0) ? " and " : "", cheat_count);
}
}
// wide screen patches
if (EmuConfig.EnableWideScreenPatches && crc != 0)
{
if (!Achievements::ChallengeModeActive() && (s_active_widescreen_patches = LoadPatchesFromDir(crc_string, EmuFolders::CheatsWS, "Widescreen hacks", false)) > 0)
{
Console.WriteLn(Color_Gray, "Found widescreen patches in the cheats_ws folder --> skipping cheats_ws.zip");
}
else
{
// No ws cheat files found at the cheats_ws folder, try the ws cheats zip file.
if (!s_widescreen_cheats_loaded)
{
s_widescreen_cheats_loaded = true;
std::optional<std::vector<u8>> data = Host::ReadResourceFile("cheats_ws.zip");
if (data.has_value())
s_widescreen_cheats_data = std::move(data.value());
}
if (!s_widescreen_cheats_data.empty())
{
s_active_widescreen_patches = LoadPatchesFromZip(crc_string, s_widescreen_cheats_data.data(), s_widescreen_cheats_data.size());
PatchesCon->WriteLn(Color_Green, "(Wide Screen Cheats DB) Patches Loaded: %d", s_active_widescreen_patches);
}
}
if (s_active_widescreen_patches > 0)
{
fmt::format_to(std::back_inserter(message), "{}{} widescreen patches", (patch_count > 0 || cheat_count > 0) ? " and " : "", s_active_widescreen_patches);
// Switch to 16:9 if widescreen patches are enabled, and AR is auto.
if (EmuConfig.GS.AspectRatio == AspectRatioType::RAuto4_3_3_2)
{
// Don't change when reloading settings in the middle of a FMV with switch.
if (EmuConfig.CurrentAspectRatio == EmuConfig.GS.AspectRatio)
EmuConfig.CurrentAspectRatio = AspectRatioType::R16_9;
EmuConfig.GS.AspectRatio = AspectRatioType::R16_9;
}
}
}
// no-interlacing patches
if (EmuConfig.EnableNoInterlacingPatches && crc != 0)
{
if (!Achievements::ChallengeModeActive() && (s_active_no_interlacing_patches = LoadPatchesFromDir(crc_string, EmuFolders::CheatsNI, "No-interlacing patches", false)) > 0)
{
Console.WriteLn(Color_Gray, "Found no-interlacing patches in the cheats_ni folder --> skipping cheats_ni.zip");
}
else
{
// No ws cheat files found at the cheats_ws folder, try the ws cheats zip file.
if (!s_no_interlacing_cheats_loaded)
{
s_no_interlacing_cheats_loaded = true;
std::optional<std::vector<u8>> data = Host::ReadResourceFile("cheats_ni.zip");
if (data.has_value())
s_no_interlacing_cheats_data = std::move(data.value());
}
if (!s_no_interlacing_cheats_data.empty())
{
s_active_no_interlacing_patches = LoadPatchesFromZip(crc_string, s_no_interlacing_cheats_data.data(), s_no_interlacing_cheats_data.size());
PatchesCon->WriteLn(Color_Green, "(No-Interlacing Cheats DB) Patches Loaded: %u", s_active_no_interlacing_patches);
}
}
if (s_active_no_interlacing_patches > 0)
{
fmt::format_to(std::back_inserter(message), "{}{} no-interlacing patches", (patch_count > 0 || cheat_count > 0 || s_active_widescreen_patches > 0) ? " and " : "", s_active_no_interlacing_patches);
// Disable interlacing in GS if active.
if (EmuConfig.GS.InterlaceMode == GSInterlaceMode::Automatic)
{
EmuConfig.GS.InterlaceMode = GSInterlaceMode::Off;
GetMTGS().ApplySettings();
}
}
}
else
{
s_active_no_interlacing_patches = 0;
}
if (show_messages)
{
if (cheat_count > 0 || s_active_widescreen_patches > 0 || s_active_no_interlacing_patches > 0)
{
message += " are active.";
Host::AddIconOSDMessage("LoadPatches", ICON_FA_FILE_CODE, message, Host::OSD_INFO_DURATION);
}
else if (show_messages_when_disabled)
{
Host::AddIconOSDMessage("LoadPatches", ICON_FA_FILE_CODE, "No cheats or patches (widescreen, compatibility or others) are found / enabled.", Host::OSD_INFO_DURATION);
}
}
}
void VMManager::UpdateRunningGame(bool resetting, bool game_starting, bool swapping_disc)
{
// The CRC can be known before the game actually starts (at the bios), so when
@ -887,7 +723,16 @@ void VMManager::UpdateRunningGame(bool resetting, bool game_starting, bool swapp
Console.WriteLn(Color_StrongGreen, fmt::format(" Serial: {}", s_game_serial));
Console.WriteLn(Color_StrongGreen, fmt::format(" CRC: {:08X}", s_game_crc));
// When resetting, patches need to get removed here, because there's no entry point being compiled.
if (resetting)
Patch::ReloadPatches(s_game_serial, s_game_crc, false, false, false);
UpdateGameSettingsLayer();
// Must be done before ApplySettings(), so WS/NI configs are picked up.
// Actual patch files get loaded on the entry point compiling.
Patch::UpdateActivePatches(true, false, s_game_crc != 0);
ApplySettings();
if (!swapping_disc)
@ -897,11 +742,8 @@ void VMManager::UpdateRunningGame(bool resetting, bool game_starting, bool swapp
if (game_starting || resetting)
AutoEject::ClearAll();
// Check this here, for two cases: dynarec on, and when enable cheats is set per-game.
if (s_patches_crc != s_game_crc)
ReloadPatches(game_starting, false);
MIPSAnalyst::ScanForFunctions(R5900SymbolMap, ElfTextRange.first, ElfTextRange.first + ElfTextRange.second, true);
MIPSAnalyst::ScanForFunctions(
R5900SymbolMap, ElfTextRange.first, ElfTextRange.first + ElfTextRange.second, true);
R5900SymbolMap.UpdateActiveSymbols();
R3000SymbolMap.UpdateActiveSymbols();
}
@ -920,11 +762,6 @@ void VMManager::UpdateRunningGame(bool resetting, bool game_starting, bool swapp
Host::OnGameChanged(s_disc_path, s_elf_override, s_game_serial, s_game_name, s_game_crc);
}
void VMManager::ReloadPatches(bool verbose, bool show_messages_when_disabled)
{
LoadPatches(s_game_serial, s_game_crc, verbose, show_messages_when_disabled);
}
static LimiterModeType GetInitialLimiterMode()
{
return EmuConfig.GS.FrameLimitEnable ? LimiterModeType::Nominal : LimiterModeType::Unlimited;
@ -1068,8 +905,10 @@ bool VMManager::CheckBIOSAvailability()
// TODO: When we translate core strings, translate this.
const char* message = "PCSX2 requires a PS2 BIOS in order to run.\n\n"
"For legal reasons, you *must* obtain a BIOS from an actual PS2 unit that you own (borrowing doesn't count).\n\n"
"Once dumped, this BIOS image should be placed in the bios folder within the data directory (Tools Menu -> Open Data Directory).\n\n"
"For legal reasons, you *must* obtain a BIOS from an actual PS2 unit that you own (borrowing "
"doesn't count).\n\n"
"Once dumped, this BIOS image should be placed in the bios folder within the data directory "
"(Tools Menu -> Open Data Directory).\n\n"
"Please consult the FAQs and Guides for further instructions.";
Host::ReportErrorAsync("Startup Error", message);
@ -1168,9 +1007,7 @@ bool VMManager::Initialize(VMBootParameters boot_params)
Host::ReportErrorAsync("Startup Error", "Failed to initialize USB.");
return false;
}
ScopedGuard close_usb = []() {
USBclose();
};
ScopedGuard close_usb = []() { USBclose(); };
Console.WriteLn("Opening FW...");
if (FWopen() != 0)
@ -1204,7 +1041,6 @@ bool VMManager::Initialize(VMBootParameters boot_params)
SysClearExecutionCache();
memBindConditionalHandlers();
ForgetLoadedPatches();
gsUpdateFrequency(EmuConfig);
frameLimitReset();
cpuReset();
@ -1279,8 +1115,6 @@ void VMManager::Shutdown(bool save_resume_state)
Host::OnGameChanged(s_disc_path, s_elf_override, s_game_serial, s_game_name, 0);
}
s_active_game_fixes = 0;
s_active_widescreen_patches = 0;
s_active_no_interlacing_patches = 0;
UpdateGameSettingsLayer();
@ -1292,7 +1126,7 @@ void VMManager::Shutdown(bool save_resume_state)
a64_setfpcr(s_mxcsr_saved);
#endif
ForgetLoadedPatches();
Patch::UnloadPatches();
R3000A::ioman::reset();
vtlb_Shutdown();
USBclose();
@ -1350,8 +1184,6 @@ void VMManager::Reset()
const bool game_was_started = g_GameStarted;
s_active_game_fixes = 0;
s_active_widescreen_patches = 0;
s_active_no_interlacing_patches = 0;
SysClearExecutionCache();
memBindConditionalHandlers();
@ -1460,7 +1292,8 @@ bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip
if (!FileSystem::RenamePath(filename, backup_filename.c_str()))
{
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_EXCLAMATION_TRIANGLE,
fmt::format("Failed to back up old save state {}.", Path::GetFileName(filename)), Host::OSD_ERROR_DURATION);
fmt::format("Failed to back up old save state {}.", Path::GetFileName(filename)),
Host::OSD_ERROR_DURATION);
}
}
@ -1468,9 +1301,8 @@ bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip
{
// lock order here is important; the thread could exit before we resume here.
std::unique_lock lock(s_save_state_threads_mutex);
s_save_state_threads.emplace_back(&VMManager::ZipSaveStateOnThread,
std::move(elist), std::move(screenshot), std::move(osd_key), std::string(filename),
slot_for_message);
s_save_state_threads.emplace_back(&VMManager::ZipSaveStateOnThread, std::move(elist), std::move(screenshot),
std::move(osd_key), std::string(filename), slot_for_message);
}
else
{
@ -1482,35 +1314,36 @@ bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip
}
catch (Exception::BaseException& e)
{
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_EXCLAMATION_TRIANGLE, fmt::format("Failed to save save state: {}.", e.DiagMsg()),
Host::OSD_ERROR_DURATION);
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_EXCLAMATION_TRIANGLE,
fmt::format("Failed to save save state: {}.", e.DiagMsg()), Host::OSD_ERROR_DURATION);
return false;
}
}
void VMManager::ZipSaveState(std::unique_ptr<ArchiveEntryList> elist,
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key,
const char* filename, s32 slot_for_message)
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key, const char* filename,
s32 slot_for_message)
{
Common::Timer timer;
if (SaveState_ZipToDisk(std::move(elist), std::move(screenshot), filename))
{
if (slot_for_message >= 0 && VMManager::HasValidVM())
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_SAVE, fmt::format("State saved to slot {}.", slot_for_message),
Host::OSD_QUICK_DURATION);
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_SAVE,
fmt::format("State saved to slot {}.", slot_for_message), Host::OSD_QUICK_DURATION);
}
else
{
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_EXCLAMATION_TRIANGLE, fmt::format("Failed to save save state to slot {}.", slot_for_message),
Host::OSD_ERROR_DURATION);
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_EXCLAMATION_TRIANGLE,
fmt::format("Failed to save save state to slot {}.", slot_for_message), Host::OSD_ERROR_DURATION);
}
DevCon.WriteLn("Zipping save state to '%s' took %.2f ms", filename, timer.GetTimeMilliseconds());
}
void VMManager::ZipSaveStateOnThread(std::unique_ptr<ArchiveEntryList> elist, std::unique_ptr<SaveStateScreenshotData> screenshot,
std::string osd_key, std::string filename, s32 slot_for_message)
void VMManager::ZipSaveStateOnThread(std::unique_ptr<ArchiveEntryList> elist,
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key, std::string filename,
s32 slot_for_message)
{
ZipSaveState(std::move(elist), std::move(screenshot), std::move(osd_key), filename.c_str(), slot_for_message);
@ -1568,8 +1401,7 @@ u32 VMManager::DeleteSaveStates(const char* game_serial, u32 game_crc, bool also
bool VMManager::LoadState(const char* filename)
{
#ifdef ENABLE_ACHIEVEMENTS
if (Achievements::ChallengeModeActive() &&
!Achievements::ConfirmChallengeModeDisable("Loading state"))
if (Achievements::ChallengeModeActive() && !Achievements::ConfirmChallengeModeDisable("Loading state"))
{
return false;
}
@ -1588,19 +1420,20 @@ bool VMManager::LoadStateFromSlot(s32 slot)
const std::string filename(GetCurrentSaveStateFileName(slot));
if (filename.empty())
{
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE, fmt::format("There is no save state in slot {}.", slot), 5.0f);
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE,
fmt::format("There is no save state in slot {}.", slot), 5.0f);
return false;
}
#ifdef ENABLE_ACHIEVEMENTS
if (Achievements::ChallengeModeActive() &&
!Achievements::ConfirmChallengeModeDisable("Loading state"))
if (Achievements::ChallengeModeActive() && !Achievements::ConfirmChallengeModeDisable("Loading state"))
{
return false;
}
#endif
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_FOLDER_OPEN, fmt::format("Loading state from slot {}...", slot), Host::OSD_QUICK_DURATION);
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_FOLDER_OPEN,
fmt::format("Loading state from slot {}...", slot), Host::OSD_QUICK_DURATION);
return DoLoadState(filename.c_str());
}
@ -1616,7 +1449,8 @@ bool VMManager::SaveStateToSlot(s32 slot, bool zip_on_thread)
return false;
// if it takes more than a minute.. well.. wtf.
Host::AddIconOSDMessage(fmt::format("SaveStateSlot{}", slot), ICON_FA_SAVE, fmt::format("Saving state to slot {}...", slot), 60.0f);
Host::AddIconOSDMessage(
fmt::format("SaveStateSlot{}", slot), ICON_FA_SAVE, fmt::format("Saving state to slot {}...", slot), 60.0f);
return DoSaveState(filename.c_str(), slot, zip_on_thread, EmuConfig.BackupSavestate);
}
@ -1665,18 +1499,21 @@ bool VMManager::ChangeDisc(CDVD_SourceType source, std::string path)
if (source == CDVD_SourceType::NoDisc)
Host::AddIconOSDMessage("ChangeDisc", ICON_FA_COMPACT_DISC, "Disc removed.", Host::OSD_INFO_DURATION);
else
Host::AddIconOSDMessage("ChangeDisc", ICON_FA_COMPACT_DISC, fmt::format("Disc changed to '{}'.", display_name), Host::OSD_INFO_DURATION);
Host::AddIconOSDMessage("ChangeDisc", ICON_FA_COMPACT_DISC,
fmt::format("Disc changed to '{}'.", display_name), Host::OSD_INFO_DURATION);
}
else
{
Host::AddIconOSDMessage("ChangeDisc", ICON_FA_COMPACT_DISC, fmt::format("Failed to open new disc image '{}'. Reverting to old image.", display_name),
Host::AddIconOSDMessage("ChangeDisc", ICON_FA_COMPACT_DISC,
fmt::format("Failed to open new disc image '{}'. Reverting to old image.", display_name),
Host::OSD_ERROR_DURATION);
CDVDsys_ChangeSource(old_type);
if (!old_path.empty())
CDVDsys_SetFile(old_type, std::move(old_path));
if (!DoCDVDopen())
{
Host::AddIconOSDMessage("ChangeDisc", ICON_FA_COMPACT_DISC, "Failed to switch back to old disc image. Removing disc.", Host::OSD_CRITICAL_ERROR_DURATION);
Host::AddIconOSDMessage("ChangeDisc", ICON_FA_COMPACT_DISC,
"Failed to switch back to old disc image. Removing disc.", Host::OSD_CRITICAL_ERROR_DURATION);
CDVDsys_ChangeSource(CDVD_SourceType::NoDisc);
DoCDVDopen();
}
@ -1698,8 +1535,7 @@ bool VMManager::IsBlockDumpFileName(const std::string_view& path)
bool VMManager::IsGSDumpFileName(const std::string_view& path)
{
return (StringUtil::EndsWithNoCase(path, ".gs") ||
StringUtil::EndsWithNoCase(path, ".gs.xz") ||
return (StringUtil::EndsWithNoCase(path, ".gs") || StringUtil::EndsWithNoCase(path, ".gs.xz") ||
StringUtil::EndsWithNoCase(path, ".gs.zst"));
}
@ -1778,15 +1614,16 @@ void VMManager::Internal::EntryPointCompilingOnCPUThread()
// until the game entry point actually runs, because that can update settings, which
// can flush the JIT, etc. But we need to apply patches for games where the entry
// point is in the patch (e.g. WRC 4). So. Gross, but the only way to handle it really.
LoadPatches(SysGetDiscID(), ElfCRC, true, false);
ApplyLoadedPatches(PPT_ONCE_ON_LOAD);
Patch::ReloadPatches(SysGetDiscID(), ElfCRC, false, false, false);
Patch::ApplyLoadedPatches(Patch::PPT_ONCE_ON_LOAD);
}
void VMManager::Internal::GameStartingOnCPUThread()
{
// See note above.
UpdateRunningGame(false, true, false);
ApplyLoadedPatches(PPT_ONCE_ON_LOAD);
ApplyLoadedPatches(PPT_COMBINED_0_1);
Patch::ApplyLoadedPatches(Patch::PPT_ONCE_ON_LOAD);
Patch::ApplyLoadedPatches(Patch::PPT_COMBINED_0_1);
}
void VMManager::Internal::SwappingGameOnCPUThread()
@ -1797,8 +1634,8 @@ void VMManager::Internal::SwappingGameOnCPUThread()
void VMManager::Internal::VSyncOnCPUThread()
{
// TODO: Move frame limiting here to reduce CPU usage after sleeping...
ApplyLoadedPatches(PPT_CONTINUOUSLY);
ApplyLoadedPatches(PPT_COMBINED_0_1);
Patch::ApplyLoadedPatches(Patch::PPT_CONTINUOUSLY);
Patch::ApplyLoadedPatches(Patch::PPT_COMBINED_0_1);
// Frame advance must be done *before* pumping messages, because otherwise
// we'll immediately reduce the counter we just set.
@ -1841,10 +1678,8 @@ void VMManager::Internal::VSyncOnCPUThread()
void VMManager::CheckForCPUConfigChanges(const Pcsx2Config& old_config)
{
if (EmuConfig.Cpu == old_config.Cpu &&
EmuConfig.Gamefixes == old_config.Gamefixes &&
EmuConfig.Speedhacks == old_config.Speedhacks &&
EmuConfig.Profiler == old_config.Profiler)
if (EmuConfig.Cpu == old_config.Cpu && EmuConfig.Gamefixes == old_config.Gamefixes &&
EmuConfig.Speedhacks == old_config.Speedhacks && EmuConfig.Profiler == old_config.Profiler)
{
return;
}
@ -1904,12 +1739,18 @@ void VMManager::CheckForPatchConfigChanges(const Pcsx2Config& old_config)
{
if (EmuConfig.EnableCheats == old_config.EnableCheats &&
EmuConfig.EnableWideScreenPatches == old_config.EnableWideScreenPatches &&
EmuConfig.EnableNoInterlacingPatches == old_config.EnableNoInterlacingPatches &&
EmuConfig.EnablePatches == old_config.EnablePatches)
{
return;
}
ReloadPatches(true, true);
Patch::UpdateActivePatches(true, false, true);
// This is a bit messy, because the patch config update happens after the settings are loaded,
// if we disable widescreen patches, we have to reload the original settings again.
if (Patch::ReloadPatchAffectingOptions())
GetMTGS().ApplySettings();
}
void VMManager::CheckForDEV9ConfigChanges(const Pcsx2Config& old_config)
@ -1997,13 +1838,6 @@ void VMManager::CheckForConfigChanges(const Pcsx2Config& old_config)
CheckForDEV9ConfigChanges(old_config);
CheckForMemoryCardConfigChanges(old_config);
USB::CheckForConfigChanges(old_config);
if (EmuConfig.EnableCheats != old_config.EnableCheats ||
EmuConfig.EnableWideScreenPatches != old_config.EnableWideScreenPatches ||
EmuConfig.EnableNoInterlacingPatches != old_config.EnableNoInterlacingPatches)
{
VMManager::ReloadPatches(true, true);
}
}
// For the big picture UI, we still need to update GS settings, since it's running,
@ -2048,6 +1882,8 @@ bool VMManager::ReloadGameSettings()
if (!UpdateGameSettingsLayer())
return false;
// Patches must come first, because they can affect aspect ratio/interlacing.
Patch::UpdateActivePatches(true, false, true);
ApplySettings();
return true;
}
@ -2070,7 +1906,8 @@ void VMManager::EnforceAchievementsChallengeModeSettings()
// Can't use cheats.
if (EmuConfig.EnableCheats)
{
Host::AddKeyedOSDMessage("ChallengeDisableCheats", "Cheats have been disabled due to achievements hardcore mode.", Host::OSD_WARNING_DURATION);
Host::AddKeyedOSDMessage("ChallengeDisableCheats",
"Cheats have been disabled due to achievements hardcore mode.", Host::OSD_WARNING_DURATION);
EmuConfig.EnableCheats = false;
}
@ -2083,7 +1920,8 @@ void VMManager::EnforceAchievementsChallengeModeSettings()
EmuConfig.GS.FrameratePAL = Pcsx2Config::GSOptions::DEFAULT_FRAME_RATE_PAL;
// You can overclock, but not underclock (since that might slow down the game and make it easier).
EmuConfig.Speedhacks.EECycleRate = std::max<decltype(EmuConfig.Speedhacks.EECycleRate)>(EmuConfig.Speedhacks.EECycleRate, 0);
EmuConfig.Speedhacks.EECycleRate =
std::max<decltype(EmuConfig.Speedhacks.EECycleRate)>(EmuConfig.Speedhacks.EECycleRate, 0);
EmuConfig.Speedhacks.EECycleSkip = 0;
}
@ -2114,7 +1952,8 @@ void VMManager::WarnAboutUnsafeSettings()
if (EmuConfig.Speedhacks.fastCDVD)
messages += ICON_FA_COMPACT_DISC " Fast CDVD is enabled, this may break games.\n";
if (EmuConfig.Speedhacks.EECycleRate != 0 || EmuConfig.Speedhacks.EECycleSkip != 0)
messages += ICON_FA_TACHOMETER_ALT " Cycle rate/skip is not at default, this may crash or make games run too slow.\n";
messages +=
ICON_FA_TACHOMETER_ALT " Cycle rate/skip is not at default, this may crash or make games run too slow.\n";
if (EmuConfig.SPU2.SynchMode == Pcsx2Config::SPU2Options::SynchronizationMode::ASync)
messages += ICON_FA_VOLUME_MUTE " Audio is using async mix, expect desynchronization in FMVs.\n";
if (EmuConfig.GS.UpscaleMultiplier < 1.0f)
@ -2122,28 +1961,35 @@ void VMManager::WarnAboutUnsafeSettings()
if (EmuConfig.GS.HWMipmap != HWMipmapLevel::Automatic)
messages += ICON_FA_IMAGES " Mipmapping is not set to automatic. This may break rendering in some games.\n";
if (EmuConfig.GS.TextureFiltering != BiFiltering::PS2)
messages += ICON_FA_FILTER " Texture filtering is not set to Bilinear (PS2). This will break rendering in some games.\n";
messages += ICON_FA_FILTER
" Texture filtering is not set to Bilinear (PS2). This will break rendering in some games.\n";
if (EmuConfig.GS.TriFilter != TriFiltering::Automatic)
messages += ICON_FA_PAGER " Trilinear filtering is not set to automatic. This may break rendering in some games.\n";
messages +=
ICON_FA_PAGER " Trilinear filtering is not set to automatic. This may break rendering in some games.\n";
if (EmuConfig.GS.AccurateBlendingUnit <= AccBlendLevel::Minimum)
messages += ICON_FA_BLENDER " Blending is below basic, this may break effects in some games.\n";
if (EmuConfig.GS.HWDownloadMode != GSHardwareDownloadMode::Enabled)
messages += ICON_FA_DOWNLOAD " Hardware Download Mode is not set to Accurate, this may break rendering in some games.\n";
messages += ICON_FA_DOWNLOAD
" Hardware Download Mode is not set to Accurate, this may break rendering in some games.\n";
if (EmuConfig.Cpu.sseMXCSR.GetRoundMode() != SSEround_Chop)
messages += ICON_FA_MICROCHIP " EE FPU Round Mode is not set to default, this may break some games.\n";
if (!EmuConfig.Cpu.Recompiler.fpuOverflow || EmuConfig.Cpu.Recompiler.fpuExtraOverflow || EmuConfig.Cpu.Recompiler.fpuFullMode)
if (!EmuConfig.Cpu.Recompiler.fpuOverflow || EmuConfig.Cpu.Recompiler.fpuExtraOverflow ||
EmuConfig.Cpu.Recompiler.fpuFullMode)
messages += ICON_FA_MICROCHIP " EE FPU Clamp Mode is not set to default, this may break some games.\n";
if (EmuConfig.Cpu.sseVU0MXCSR.GetRoundMode() != SSEround_Chop || EmuConfig.Cpu.sseVU1MXCSR.GetRoundMode() != SSEround_Chop)
if (EmuConfig.Cpu.sseVU0MXCSR.GetRoundMode() != SSEround_Chop ||
EmuConfig.Cpu.sseVU1MXCSR.GetRoundMode() != SSEround_Chop)
messages += ICON_FA_MICROCHIP " VU Round Mode is not set to default, this may break some games.\n";
if (!EmuConfig.Cpu.Recompiler.vu0Overflow || EmuConfig.Cpu.Recompiler.vu0ExtraOverflow || EmuConfig.Cpu.Recompiler.vu0SignOverflow ||
!EmuConfig.Cpu.Recompiler.vu1Overflow || EmuConfig.Cpu.Recompiler.vu1ExtraOverflow || EmuConfig.Cpu.Recompiler.vu1SignOverflow)
if (!EmuConfig.Cpu.Recompiler.vu0Overflow || EmuConfig.Cpu.Recompiler.vu0ExtraOverflow ||
EmuConfig.Cpu.Recompiler.vu0SignOverflow || !EmuConfig.Cpu.Recompiler.vu1Overflow ||
EmuConfig.Cpu.Recompiler.vu1ExtraOverflow || EmuConfig.Cpu.Recompiler.vu1SignOverflow)
{
messages += ICON_FA_MICROCHIP " VU Clamp Mode is not set to default, this may break some games.\n";
}
if (!EmuConfig.EnableGameFixes)
messages += ICON_FA_GAMEPAD " Game Fixes are not enabled. Compatibility with some games may be affected.\n";
if (!EmuConfig.EnablePatches)
messages += ICON_FA_GAMEPAD " Compatibility Patches are not enabled. Compatibility with some games may be affected.\n";
messages +=
ICON_FA_GAMEPAD " Compatibility Patches are not enabled. Compatibility with some games may be affected.\n";
if (EmuConfig.GS.FramerateNTSC != Pcsx2Config::GSOptions::DEFAULT_FRAME_RATE_NTSC)
messages += ICON_FA_TV " Frame rate for NTSC is not default. This may break some games.\n";
if (EmuConfig.GS.FrameratePAL != Pcsx2Config::GSOptions::DEFAULT_FRAME_RATE_PAL)
@ -2164,13 +2010,17 @@ void VMManager::WarnAboutUnsafeSettings()
messages.clear();
if (!EmuConfig.Cpu.Recompiler.EnableEE)
messages += ICON_FA_EXCLAMATION_CIRCLE " EE Recompiler is not enabled, this will significantly reduce performance.\n";
messages +=
ICON_FA_EXCLAMATION_CIRCLE " EE Recompiler is not enabled, this will significantly reduce performance.\n";
if (!EmuConfig.Cpu.Recompiler.EnableVU0)
messages += ICON_FA_EXCLAMATION_CIRCLE " VU0 Recompiler is not enabled, this will significantly reduce performance.\n";
messages +=
ICON_FA_EXCLAMATION_CIRCLE " VU0 Recompiler is not enabled, this will significantly reduce performance.\n";
if (!EmuConfig.Cpu.Recompiler.EnableVU1)
messages += ICON_FA_EXCLAMATION_CIRCLE " VU1 Recompiler is not enabled, this will significantly reduce performance.\n";
messages +=
ICON_FA_EXCLAMATION_CIRCLE " VU1 Recompiler is not enabled, this will significantly reduce performance.\n";
if (!EmuConfig.Cpu.Recompiler.EnableIOP)
messages += ICON_FA_EXCLAMATION_CIRCLE " IOP Recompiler is not enabled, this will significantly reduce performance.\n";
messages +=
ICON_FA_EXCLAMATION_CIRCLE " IOP Recompiler is not enabled, this will significantly reduce performance.\n";
if (EmuConfig.Cpu.Recompiler.EnableEECache)
messages += ICON_FA_EXCLAMATION_CIRCLE " EE Cache is enabled, this will significantly reduce performance.\n";
if (!EmuConfig.Speedhacks.WaitLoop)
@ -2223,7 +2073,8 @@ void VMManager::SaveSessionTime()
if (!s_game_serial.empty() && s_game_crc != 0)
{
// round up to seconds
const std::time_t etime = static_cast<std::time_t>(std::round(Common::Timer::ConvertValueToSeconds(ctime - s_session_start_time)));
const std::time_t etime =
static_cast<std::time_t>(std::round(Common::Timer::ConvertValueToSeconds(ctime - s_session_start_time)));
const std::time_t wtime = std::time(nullptr);
GameList::AddPlayedTimeForSerial(s_game_serial, wtime, etime);
}
@ -2300,7 +2151,8 @@ static void InitializeCPUInfo()
return;
}
Console.WriteLn(Color_StrongYellow, "Processor count: %u cores, %u processors", cpuinfo_get_cores_count(), cpuinfo_get_processors_count());
Console.WriteLn(Color_StrongYellow, "Processor count: %u cores, %u processors", cpuinfo_get_cores_count(),
cpuinfo_get_processors_count());
Console.WriteLn(Color_StrongYellow, "Cluster count: %u", cluster_count);
static std::vector<const cpuinfo_processor*> ordered_processors;
@ -2319,9 +2171,10 @@ static void InitializeCPUInfo()
// find the large and small clusters based on frequency
// this is assuming the large cluster is always clocked higher
// sort based on core, so that hyperthreads get pushed down
std::sort(ordered_processors.begin(), ordered_processors.end(), [](const cpuinfo_processor* lhs, const cpuinfo_processor* rhs) {
return (lhs->core->frequency > rhs->core->frequency || lhs->smt_id < rhs->smt_id);
});
std::sort(ordered_processors.begin(), ordered_processors.end(),
[](const cpuinfo_processor* lhs, const cpuinfo_processor* rhs) {
return (lhs->core->frequency > rhs->core->frequency || lhs->smt_id < rhs->smt_id);
});
s_processor_list.reserve(ordered_processors.size());
std::stringstream ss;
@ -2357,14 +2210,15 @@ static void SetMTVUAndAffinityControlDefault(SettingsInterface& si)
for (u32 i = 0; i < cluster_count; i++)
{
const cpuinfo_cluster* cluster = cpuinfo_get_cluster(i);
Console.WriteLn(" Cluster %u: %u cores and %u processors at %u MHz",
i, cluster->core_count, cluster->processor_count, static_cast<u32>(cluster->frequency /* / 1000000u*/));
Console.WriteLn(" Cluster %u: %u cores and %u processors at %u MHz", i, cluster->core_count,
cluster->processor_count, static_cast<u32>(cluster->frequency /* / 1000000u*/));
}
const bool has_big_little = cluster_count > 1;
Console.WriteLn("Big-Little: %s", has_big_little ? "yes" : "no");
const u32 big_cores = cpuinfo_get_cluster(0)->core_count + ((cluster_count > 2) ? cpuinfo_get_cluster(1)->core_count : 0u);
const u32 big_cores =
cpuinfo_get_cluster(0)->core_count + ((cluster_count > 2) ? cpuinfo_get_cluster(1)->core_count : 0u);
Console.WriteLn("Guessing we have %u big/medium cores...", big_cores);
if (big_cores >= 3)
@ -2448,8 +2302,7 @@ void VMManager::SetEmuThreadAffinities()
return;
}
if (EmuConfig.Cpu.AffinityControlMode == 0 ||
s_processor_list.size() < (EmuConfig.Speedhacks.vuThread ? 3 : 2))
if (EmuConfig.Cpu.AffinityControlMode == 0 || s_processor_list.size() < (EmuConfig.Speedhacks.vuThread ? 3 : 2))
{
if (EmuConfig.Cpu.AffinityControlMode != 0)
Console.Error("Insufficient processors for affinity control.");
@ -2472,12 +2325,13 @@ void VMManager::SetEmuThreadAffinities()
};
// steal vu's thread if mtvu is off
const u8* this_proc_assigment = processor_assignment[EmuConfig.Cpu.AffinityControlMode][EmuConfig.Speedhacks.vuThread];
const u8* this_proc_assigment =
processor_assignment[EmuConfig.Cpu.AffinityControlMode][EmuConfig.Speedhacks.vuThread];
const u32 ee_index = s_processor_list[this_proc_assigment[0]];
const u32 vu_index = s_processor_list[this_proc_assigment[1]];
const u32 gs_index = s_processor_list[this_proc_assigment[2]];
Console.WriteLn("Processor order assignment: EE=%u, VU=%u, GS=%u",
this_proc_assigment[0], this_proc_assigment[1], this_proc_assigment[2]);
Console.WriteLn("Processor order assignment: EE=%u, VU=%u, GS=%u", this_proc_assigment[0], this_proc_assigment[1],
this_proc_assigment[2]);
const u64 ee_affinity = static_cast<u64>(1) << ee_index;
Console.WriteLn(Color_StrongGreen, "EE thread is on processor %u (0x%llx)", ee_index, ee_affinity);

View File

@ -107,9 +107,6 @@ namespace VMManager
/// Reloads game specific settings, and applys any changes present.
bool ReloadGameSettings();
/// Reloads cheats/patches. If verbose is set, the number of patches loaded will be shown in the OSD.
void ReloadPatches(bool verbose, bool show_messages_when_disabled);
/// Returns the save state filename for the given game serial/crc.
std::string GetSaveStateFileName(const char* game_serial, u32 game_crc, s32 slot);

View File

@ -259,7 +259,6 @@
<ClCompile Include="Mdec.cpp" />
<ClCompile Include="MultipartFileReader.cpp" />
<ClCompile Include="Patch.cpp" />
<ClCompile Include="Patch_Memory.cpp" />
<ClCompile Include="PrecompiledHeader.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>

View File

@ -692,9 +692,6 @@
<ClCompile Include="GameDatabase.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="Patch_Memory.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="IPU\IPUdma.cpp">
<Filter>System\Ps2\IPU</Filter>
</ClCompile>

View File

@ -1694,7 +1694,7 @@ void recompileNextInstruction(bool delayslot, bool swapped_delay_slot)
int count;
if (EmuConfig.EnablePatches)
ApplyDynamicPatches(pc);
Patch::ApplyDynamicPatches(pc);
// add breakpoint
if (!delayslot)

View File

@ -0,0 +1,73 @@
#!/usr/bin/env python3
# PCSX2 - PS2 Emulator for PCs
# Copyright (C) 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/>.
# pylint: disable=bare-except, disable=missing-function-docstring
import glob
import os
import sys
def merge_patches(srcdir, dstdir, label, desc, extralines=None):
for file in glob.glob(os.path.join(srcdir, "*.pnach")):
print(f"Reading {file}...")
name = os.path.basename(file)
with open(file, "rb") as f:
lines = f.read().decode().strip().split("\n")
gametitle_line = None
comment_line = None
for line in lines:
line = line.strip()
if line.startswith("gametitle=") and gametitle_line is None:
gametitle_line = line
elif line.startswith("comment=") and comment_line is None:
comment_line = line[8:]
# ignore gametitle if file already exists
outname = os.path.join(dstdir, name)
if os.path.exists(outname):
gametitle_line = None
with open(outname, "ab") as f:
if gametitle_line is not None:
f.write((gametitle_line + "\n\n").encode())
f.write(f"[{label}]\n".encode())
if desc is not None and comment_line is None:
f.write(f"description={desc}\n".encode())
if extralines is not None:
f.write(f"{extralines}\n".encode())
for line in lines:
line = line.strip()
if not line.startswith("gametitle="):
f.write((line + "\n").encode())
f.write("\n\n".encode())
print(f"Wrote/updated {outname}")
if __name__ == "__main__":
if len(sys.argv) < 4:
print(f"Usage: {sys.argv[0]} <ws directory> <ni directory> <output directory>")
sys.exit(1)
outdir = sys.argv[3]
if not os.path.isdir(outdir):
os.mkdir(outdir)
merge_patches(sys.argv[1], outdir, "Widescreen 16:9", "Renders the game in 16:9 aspect ratio, instead of 4:3.", "gsaspectratio=16:9")
merge_patches(sys.argv[2], outdir, "No-Interlacing", "Attempts to disable interlaced offset rendering.", "gsinterlacemode=1")