mirror of
https://github.com/PCSX2/pcsx2.git
synced 2025-12-16 04:08:48 +00:00
Patch: Restore original behaviour of PPT_ONCE_ON_LOAD, add new PPT_ON_LOAD_OR_WHEN_ENABLED place option, and display when a patch is applied in the GUI (#13698)
Some checks failed
🐧 Linux Builds / AppImage (push) Waiting to run
🐧 Linux Builds / Flatpak (push) Waiting to run
🍎 MacOS Builds / Defaults (push) Waiting to run
🖥️ Windows Builds / Lint VS Project Files (push) Waiting to run
🖥️ Windows Builds / SSE4 (push) Blocked by required conditions
🖥️ Windows Builds / AVX2 (push) Blocked by required conditions
🖥️ Windows Builds / CMake (push) Waiting to run
🏭 Update Controller Database / update-controller-db (push) Has been cancelled
Some checks failed
🐧 Linux Builds / AppImage (push) Waiting to run
🐧 Linux Builds / Flatpak (push) Waiting to run
🍎 MacOS Builds / Defaults (push) Waiting to run
🖥️ Windows Builds / Lint VS Project Files (push) Waiting to run
🖥️ Windows Builds / SSE4 (push) Blocked by required conditions
🖥️ Windows Builds / AVX2 (push) Blocked by required conditions
🖥️ Windows Builds / CMake (push) Waiting to run
🏭 Update Controller Database / update-controller-db (push) Has been cancelled
This commit is contained in:
parent
0180ec060b
commit
cd120c3cfd
@ -15,19 +15,23 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
GamePatchDetailsWidget::GamePatchDetailsWidget(std::string name, const std::string& author,
|
GamePatchDetailsWidget::GamePatchDetailsWidget(const Patch::PatchInfo& info, bool tristate, Qt::CheckState checkState, SettingsWindow* dialog, QWidget* parent)
|
||||||
const std::string& description, bool tristate, Qt::CheckState checkState, SettingsWindow* dialog, QWidget* parent)
|
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, m_dialog(dialog)
|
, m_dialog(dialog)
|
||||||
, m_name(name)
|
, m_name(info.name)
|
||||||
{
|
{
|
||||||
m_ui.setupUi(this);
|
m_ui.setupUi(this);
|
||||||
|
|
||||||
m_ui.name->setText(QString::fromStdString(name));
|
const QString name = QString::fromStdString(info.name);
|
||||||
|
const QString author = !info.author.empty() ? QString::fromStdString(info.author) : tr("Unknown");
|
||||||
|
const QString place = QString::fromUtf8(PlaceToString(info.place));
|
||||||
|
const QString description = !info.description.empty() ? QString::fromStdString(info.description) : tr("No description provided.");
|
||||||
|
m_ui.name->setText(name);
|
||||||
m_ui.description->setText(
|
m_ui.description->setText(
|
||||||
tr("<strong>Author: </strong>%1<br>%2")
|
tr("<strong>Author:</strong> %1<br><strong>Applied:</strong> %2<br>%3")
|
||||||
.arg(author.empty() ? tr("Unknown") : QString::fromStdString(author))
|
.arg(author)
|
||||||
.arg(description.empty() ? tr("No description provided.") : QString::fromStdString(description)));
|
.arg(place)
|
||||||
|
.arg(description));
|
||||||
|
|
||||||
pxAssert(dialog->getSettingsInterface());
|
pxAssert(dialog->getSettingsInterface());
|
||||||
m_ui.enabled->setTristate(tristate);
|
m_ui.enabled->setTristate(tristate);
|
||||||
@ -178,7 +182,7 @@ void GamePatchSettingsWidget::reloadList()
|
|||||||
}
|
}
|
||||||
|
|
||||||
GamePatchDetailsWidget* it =
|
GamePatchDetailsWidget* it =
|
||||||
new GamePatchDetailsWidget(std::move(pi.name), pi.author, pi.description, globally_toggleable_option, check_state, dialog(), container);
|
new GamePatchDetailsWidget(pi, globally_toggleable_option, check_state, dialog(), container);
|
||||||
layout->addWidget(it);
|
layout->addWidget(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ class GamePatchDetailsWidget : public QWidget
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GamePatchDetailsWidget(std::string name, const std::string& author, const std::string& description, bool tristate, Qt::CheckState checkState,
|
GamePatchDetailsWidget(const Patch::PatchInfo& info, bool tristate, Qt::CheckState checkState,
|
||||||
SettingsWindow* dialog, QWidget* parent);
|
SettingsWindow* dialog, QWidget* parent);
|
||||||
~GamePatchDetailsWidget();
|
~GamePatchDetailsWidget();
|
||||||
|
|
||||||
|
|||||||
@ -630,9 +630,9 @@ namespace FullscreenUI
|
|||||||
static std::unique_ptr<GameList::Entry> s_game_settings_entry;
|
static std::unique_ptr<GameList::Entry> s_game_settings_entry;
|
||||||
static std::vector<std::pair<std::string, bool>> s_game_list_directories_cache;
|
static std::vector<std::pair<std::string, bool>> s_game_list_directories_cache;
|
||||||
static std::vector<GSAdapterInfo> s_graphics_adapter_list_cache;
|
static std::vector<GSAdapterInfo> s_graphics_adapter_list_cache;
|
||||||
static Patch::PatchInfoList s_game_patch_list;
|
static std::vector<Patch::PatchInfo> s_game_patch_list;
|
||||||
static std::vector<std::string> s_enabled_game_patch_cache;
|
static std::vector<std::string> s_enabled_game_patch_cache;
|
||||||
static Patch::PatchInfoList s_game_cheats_list;
|
static std::vector<Patch::PatchInfo> s_game_cheats_list;
|
||||||
static std::vector<std::string> s_enabled_game_cheat_cache;
|
static std::vector<std::string> s_enabled_game_cheat_cache;
|
||||||
static u32 s_game_cheat_unlabelled_count = 0;
|
static u32 s_game_cheat_unlabelled_count = 0;
|
||||||
static std::vector<const HotkeyInfo*> s_hotkey_list_cache;
|
static std::vector<const HotkeyInfo*> s_hotkey_list_cache;
|
||||||
@ -3637,7 +3637,7 @@ void FullscreenUI::PopulateGameListDirectoryCache(SettingsInterface* si)
|
|||||||
|
|
||||||
void FullscreenUI::PopulatePatchesAndCheatsList(const std::string_view serial, u32 crc)
|
void FullscreenUI::PopulatePatchesAndCheatsList(const std::string_view serial, u32 crc)
|
||||||
{
|
{
|
||||||
constexpr auto sort_patches = [](Patch::PatchInfoList& list) {
|
constexpr auto sort_patches = [](std::vector<Patch::PatchInfo>& list) {
|
||||||
std::sort(list.begin(), list.end(), [](const Patch::PatchInfo& lhs, const Patch::PatchInfo& rhs) { return lhs.name < rhs.name; });
|
std::sort(list.begin(), list.end(), [](const Patch::PatchInfo& lhs, const Patch::PatchInfo& rhs) { return lhs.name < rhs.name; });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -6690,7 +6690,7 @@ void FullscreenUI::DrawPatchesOrCheatsSettingsPage(bool cheats)
|
|||||||
{
|
{
|
||||||
SettingsInterface* bsi = GetEditingSettingsInterface();
|
SettingsInterface* bsi = GetEditingSettingsInterface();
|
||||||
|
|
||||||
const Patch::PatchInfoList& patch_list = cheats ? s_game_cheats_list : s_game_patch_list;
|
const std::vector<Patch::PatchInfo>& patch_list = cheats ? s_game_cheats_list : s_game_patch_list;
|
||||||
std::vector<std::string>& enable_list = cheats ? s_enabled_game_cheat_cache : s_enabled_game_patch_cache;
|
std::vector<std::string>& enable_list = cheats ? s_enabled_game_cheat_cache : s_enabled_game_patch_cache;
|
||||||
const char* section = cheats ? Patch::CHEATS_CONFIG_SECTION : Patch::PATCHES_CONFIG_SECTION;
|
const char* section = cheats ? Patch::CHEATS_CONFIG_SECTION : Patch::PATCHES_CONFIG_SECTION;
|
||||||
const bool master_enable = cheats ? GetEffectiveBoolSetting(bsi, "EmuCore", "EnableCheats", false) : true;
|
const bool master_enable = cheats ? GetEffectiveBoolSetting(bsi, "EmuCore", "EnableCheats", false) : true;
|
||||||
|
|||||||
186
pcsx2/Patch.cpp
186
pcsx2/Patch.cpp
@ -51,7 +51,7 @@ namespace Patch
|
|||||||
BYTES_T
|
BYTES_T
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr std::array<const char*, 3> s_place_to_string = {{"0", "1", "2"}};
|
static constexpr std::array<const char*, 4> s_place_to_string = {{"0", "1", "2", "3"}};
|
||||||
static constexpr std::array<const char*, 2> s_cpu_to_string = {{"EE", "IOP"}};
|
static constexpr std::array<const char*, 2> s_cpu_to_string = {{"EE", "IOP"}};
|
||||||
static constexpr std::array<const char*, 9> s_type_to_string = {
|
static constexpr std::array<const char*, 9> s_type_to_string = {
|
||||||
{"byte", "short", "word", "double", "extended", "beshort", "beword", "bedouble", "bytes"}};
|
{"byte", "short", "word", "double", "extended", "beshort", "beword", "bedouble", "bytes"}};
|
||||||
@ -125,10 +125,6 @@ namespace Patch
|
|||||||
void (*func)(PatchGroup* group, const std::string_view cmd, const std::string_view param);
|
void (*func)(PatchGroup* group, const std::string_view cmd, const std::string_view param);
|
||||||
};
|
};
|
||||||
|
|
||||||
using PatchList = std::vector<PatchGroup>;
|
|
||||||
using ActivePatchList = std::vector<const PatchCommand*>;
|
|
||||||
using EnablePatchList = std::vector<std::string>;
|
|
||||||
|
|
||||||
namespace PatchFunc
|
namespace PatchFunc
|
||||||
{
|
{
|
||||||
static void patch(PatchGroup* group, const std::string_view cmd, const std::string_view param);
|
static void patch(PatchGroup* group, const std::string_view cmd, const std::string_view param);
|
||||||
@ -141,23 +137,23 @@ namespace Patch
|
|||||||
static int PatchTableExecute(PatchGroup* group, const std::string_view lhs, const std::string_view rhs,
|
static int PatchTableExecute(PatchGroup* group, const std::string_view lhs, const std::string_view rhs,
|
||||||
const std::span<const PatchTextTable>& Table);
|
const std::span<const PatchTextTable>& Table);
|
||||||
static void LoadPatchLine(PatchGroup* group, const std::string_view line);
|
static void LoadPatchLine(PatchGroup* group, const std::string_view line);
|
||||||
static u32 LoadPatchesFromString(PatchList* patch_list, const std::string& patch_file);
|
static u32 LoadPatchesFromString(std::vector<PatchGroup>* patch_list, const std::string& patch_file);
|
||||||
static bool OpenPatchesZip();
|
static bool OpenPatchesZip();
|
||||||
static std::string GetPnachTemplate(
|
static std::string GetPnachTemplate(
|
||||||
const std::string_view serial, u32 crc, bool include_serial, bool add_wildcard, bool all_crcs);
|
const std::string_view serial, u32 crc, bool include_serial, bool add_wildcard, bool all_crcs);
|
||||||
static std::vector<std::string> FindPatchFilesOnDisk(
|
static std::vector<std::string> FindPatchFilesOnDisk(
|
||||||
const std::string_view serial, u32 crc, bool cheats, bool all_crcs);
|
const std::string_view serial, u32 crc, bool cheats, bool all_crcs);
|
||||||
|
|
||||||
static bool ContainsPatchName(const PatchInfoList& patches, const std::string_view patchName);
|
static bool ContainsPatchName(const std::vector<PatchInfo>& patches, const std::string_view patchName);
|
||||||
static bool ContainsPatchName(const PatchList& patches, const std::string_view patchName);
|
static bool ContainsPatchName(const std::vector<PatchGroup>& patches, const std::string_view patchName);
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
static void EnumeratePnachFiles(const std::string_view serial, u32 crc, bool cheats, bool for_ui, const F& f);
|
static void EnumeratePnachFiles(const std::string_view serial, u32 crc, bool cheats, bool for_ui, const F& f);
|
||||||
|
|
||||||
static bool PatchStringHasUnlabelledPatch(const std::string& pnach_data);
|
static bool PatchStringHasUnlabelledPatch(const std::string& pnach_data);
|
||||||
static void ExtractPatchInfo(PatchInfoList* dst, const std::string& pnach_data, u32* num_unlabelled_patches);
|
static void ExtractPatchInfo(std::vector<PatchInfo>* dst, const std::string& pnach_data, u32* num_unlabelled_patches);
|
||||||
static void ReloadEnabledLists();
|
static void ReloadEnabledLists();
|
||||||
static u32 EnablePatches(const PatchList& patches, const EnablePatchList& enable_list, const EnablePatchList& enable_immediately_list);
|
static u32 EnablePatches(const std::vector<PatchGroup>* patches, const std::vector<std::string>& enable_list, const std::vector<std::string>* enable_immediately_list);
|
||||||
|
|
||||||
static void ApplyPatch(const PatchCommand* p);
|
static void ApplyPatch(const PatchCommand* p);
|
||||||
static void ApplyDynaPatch(const DynamicPatch& patch, u32 address);
|
static void ApplyDynaPatch(const DynamicPatch& patch, u32 address);
|
||||||
@ -175,21 +171,21 @@ namespace Patch
|
|||||||
const char* PATCH_DISABLE_CONFIG_KEY = "Disable";
|
const char* PATCH_DISABLE_CONFIG_KEY = "Disable";
|
||||||
|
|
||||||
static zip_t* s_patches_zip;
|
static zip_t* s_patches_zip;
|
||||||
static PatchList s_gamedb_patches;
|
static std::vector<PatchGroup> s_gamedb_patches;
|
||||||
static PatchList s_game_patches;
|
static std::vector<PatchGroup> s_game_patches;
|
||||||
static PatchList s_cheat_patches;
|
static std::vector<PatchGroup> s_cheat_patches;
|
||||||
|
|
||||||
static u32 s_gamedb_counts = 0;
|
static u32 s_gamedb_counts = 0;
|
||||||
static u32 s_patches_counts = 0;
|
static u32 s_patches_counts = 0;
|
||||||
static u32 s_cheats_counts = 0;
|
static u32 s_cheats_counts = 0;
|
||||||
|
|
||||||
static ActivePatchList s_active_patches;
|
static std::vector<const PatchCommand*> s_active_patches;
|
||||||
static std::vector<DynamicPatch> s_active_gamedb_dynamic_patches;
|
static std::vector<DynamicPatch> s_active_gamedb_dynamic_patches;
|
||||||
static std::vector<DynamicPatch> s_active_pnach_dynamic_patches;
|
static std::vector<DynamicPatch> s_active_pnach_dynamic_patches;
|
||||||
static EnablePatchList s_enabled_cheats;
|
static std::vector<std::string> s_enabled_cheats;
|
||||||
static EnablePatchList s_enabled_patches;
|
static std::vector<std::string> s_enabled_patches;
|
||||||
static EnablePatchList s_just_enabled_cheats;
|
static std::vector<std::string> s_just_enabled_cheats;
|
||||||
static EnablePatchList s_just_enabled_patches;
|
static std::vector<std::string> s_just_enabled_patches;
|
||||||
static u32 s_patches_crc;
|
static u32 s_patches_crc;
|
||||||
static std::optional<float> s_override_aspect_ratio;
|
static std::optional<float> s_override_aspect_ratio;
|
||||||
static std::optional<GSInterlaceMode> s_override_interlace_mode;
|
static std::optional<GSInterlaceMode> s_override_interlace_mode;
|
||||||
@ -218,7 +214,7 @@ void Patch::TrimPatchLine(std::string& buffer)
|
|||||||
buffer.erase(pos);
|
buffer.erase(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Patch::ContainsPatchName(const PatchList& patch_list, const std::string_view patch_name)
|
bool Patch::ContainsPatchName(const std::vector<PatchGroup>& patch_list, const std::string_view patch_name)
|
||||||
{
|
{
|
||||||
return std::find_if(patch_list.begin(), patch_list.end(), [&patch_name](const PatchGroup& patch) {
|
return std::find_if(patch_list.begin(), patch_list.end(), [&patch_name](const PatchGroup& patch) {
|
||||||
return patch.name == patch_name;
|
return patch.name == patch_name;
|
||||||
@ -253,7 +249,7 @@ void Patch::LoadPatchLine(PatchGroup* group, const std::string_view line)
|
|||||||
PatchTableExecute(group, key, value, s_patch_commands);
|
PatchTableExecute(group, key, value, s_patch_commands);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Patch::LoadPatchesFromString(PatchList* patch_list, const std::string& patch_file)
|
u32 Patch::LoadPatchesFromString(std::vector<PatchGroup>* patch_list, const std::string& patch_file)
|
||||||
{
|
{
|
||||||
const size_t before = patch_list->size();
|
const size_t before = patch_list->size();
|
||||||
|
|
||||||
@ -264,7 +260,7 @@ u32 Patch::LoadPatchesFromString(PatchList* patch_list, const std::string& patch
|
|||||||
// Ungrouped/legacy patches should merge with other ungrouped patches.
|
// Ungrouped/legacy patches should merge with other ungrouped patches.
|
||||||
if (current_patch_group.name.empty())
|
if (current_patch_group.name.empty())
|
||||||
{
|
{
|
||||||
const PatchList::iterator ungrouped_patch = std::find_if(patch_list->begin(), patch_list->end(),
|
const std::vector<PatchGroup>::iterator ungrouped_patch = std::find_if(patch_list->begin(), patch_list->end(),
|
||||||
[](const PatchGroup& pg) { return pg.name.empty(); });
|
[](const PatchGroup& pg) { return pg.name.empty(); });
|
||||||
if (ungrouped_patch != patch_list->end())
|
if (ungrouped_patch != patch_list->end())
|
||||||
{
|
{
|
||||||
@ -407,7 +403,7 @@ std::vector<std::string> Patch::FindPatchFilesOnDisk(const std::string_view seri
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Patch::ContainsPatchName(const PatchInfoList& patches, const std::string_view patchName)
|
bool Patch::ContainsPatchName(const std::vector<PatchInfo>& patches, const std::string_view patchName)
|
||||||
{
|
{
|
||||||
return std::find_if(patches.begin(), patches.end(), [&patchName](const PatchInfo& patch) {
|
return std::find_if(patches.begin(), patches.end(), [&patchName](const PatchInfo& patch) {
|
||||||
return patch.name == patchName;
|
return patch.name == patchName;
|
||||||
@ -491,11 +487,15 @@ bool Patch::PatchStringHasUnlabelledPatch(const std::string& pnach_data)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Patch::ExtractPatchInfo(PatchInfoList* dst, const std::string& pnach_data, u32* num_unlabelled_patches)
|
void Patch::ExtractPatchInfo(std::vector<PatchInfo>* dst, const std::string& pnach_data, u32* num_unlabelled_patches)
|
||||||
{
|
{
|
||||||
std::istringstream ss(pnach_data);
|
std::istringstream ss(pnach_data);
|
||||||
std::string line;
|
std::string line;
|
||||||
PatchInfo current_patch;
|
PatchInfo current_patch;
|
||||||
|
|
||||||
|
std::optional<patch_place_type> last_place;
|
||||||
|
bool unknown_place = false;
|
||||||
|
|
||||||
while (std::getline(ss, line))
|
while (std::getline(ss, line))
|
||||||
{
|
{
|
||||||
TrimPatchLine(line);
|
TrimPatchLine(line);
|
||||||
@ -522,6 +522,8 @@ void Patch::ExtractPatchInfo(PatchInfoList* dst, const std::string& pnach_data,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
current_patch = {};
|
current_patch = {};
|
||||||
|
last_place = std::nullopt;
|
||||||
|
unknown_place = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
current_patch.name = line.substr(1, line.length() - 2);
|
current_patch.name = line.substr(1, line.length() - 2);
|
||||||
@ -534,13 +536,51 @@ void Patch::ExtractPatchInfo(PatchInfoList* dst, const std::string& pnach_data,
|
|||||||
// Just ignore other directives, who knows what rubbish people have in here.
|
// Just ignore other directives, who knows what rubbish people have in here.
|
||||||
// Use comment for description if it hasn't been otherwise specified.
|
// Use comment for description if it hasn't been otherwise specified.
|
||||||
if (key == "author")
|
if (key == "author")
|
||||||
|
{
|
||||||
current_patch.author = value;
|
current_patch.author = value;
|
||||||
|
}
|
||||||
else if (key == "description")
|
else if (key == "description")
|
||||||
|
{
|
||||||
current_patch.description = value;
|
current_patch.description = value;
|
||||||
|
}
|
||||||
else if (key == "comment" && current_patch.description.empty())
|
else if (key == "comment" && current_patch.description.empty())
|
||||||
|
{
|
||||||
current_patch.description = value;
|
current_patch.description = value;
|
||||||
else if (key == "patch" && !has_patch && num_unlabelled_patches)
|
}
|
||||||
(*num_unlabelled_patches)++;
|
else if (key == "patch")
|
||||||
|
{
|
||||||
|
if (!has_patch && num_unlabelled_patches)
|
||||||
|
(*num_unlabelled_patches)++;
|
||||||
|
|
||||||
|
// Try to extract the place value of the patch lines so we can
|
||||||
|
// display them in the GUI if they all match. TODO: Don't duplicate
|
||||||
|
// all this parsing logic twice.
|
||||||
|
if (unknown_place)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string::size_type comma_pos = value.find(",");
|
||||||
|
if (comma_pos == std::string::npos)
|
||||||
|
comma_pos = 0;
|
||||||
|
const std::string_view padded_place = value.substr(0, comma_pos);
|
||||||
|
const std::string_view place_string = StringUtil::StripWhitespace(padded_place);
|
||||||
|
const std::optional<patch_place_type> place = LookupEnumName<patch_place_type>(
|
||||||
|
place_string, s_place_to_string);
|
||||||
|
if (!place.has_value() || (last_place.has_value() && place != last_place))
|
||||||
|
{
|
||||||
|
// This group contains patch lines with different or invalid
|
||||||
|
// place values.
|
||||||
|
current_patch.place = std::nullopt;
|
||||||
|
unknown_place = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_patch.place = place;
|
||||||
|
last_place = place;
|
||||||
|
}
|
||||||
|
else if (key == "dpatch")
|
||||||
|
{
|
||||||
|
current_patch.place = std::nullopt;
|
||||||
|
unknown_place = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last one.
|
// Last one.
|
||||||
@ -570,9 +610,9 @@ std::string_view Patch::PatchInfo::GetNameParentPart() const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
Patch::PatchInfoList Patch::GetPatchInfo(const std::string_view serial, u32 crc, bool cheats, bool showAllCRCS, u32* num_unlabelled_patches)
|
std::vector<Patch::PatchInfo> Patch::GetPatchInfo(const std::string_view serial, u32 crc, bool cheats, bool showAllCRCS, u32* num_unlabelled_patches)
|
||||||
{
|
{
|
||||||
PatchInfoList ret;
|
std::vector<PatchInfo> ret;
|
||||||
|
|
||||||
if (num_unlabelled_patches)
|
if (num_unlabelled_patches)
|
||||||
*num_unlabelled_patches = 0;
|
*num_unlabelled_patches = 0;
|
||||||
@ -592,14 +632,14 @@ std::string Patch::GetPnachFilename(const std::string_view serial, u32 crc, bool
|
|||||||
|
|
||||||
void Patch::ReloadEnabledLists()
|
void Patch::ReloadEnabledLists()
|
||||||
{
|
{
|
||||||
const EnablePatchList prev_enabled_cheats = std::move(s_enabled_cheats);
|
const std::vector<std::string> prev_enabled_cheats = std::move(s_enabled_cheats);
|
||||||
if (EmuConfig.EnableCheats && !Achievements::IsHardcoreModeActive())
|
if (EmuConfig.EnableCheats && !Achievements::IsHardcoreModeActive())
|
||||||
s_enabled_cheats = Host::GetStringListSetting(CHEATS_CONFIG_SECTION, PATCH_ENABLE_CONFIG_KEY);
|
s_enabled_cheats = Host::GetStringListSetting(CHEATS_CONFIG_SECTION, PATCH_ENABLE_CONFIG_KEY);
|
||||||
else
|
else
|
||||||
s_enabled_cheats = {};
|
s_enabled_cheats = {};
|
||||||
|
|
||||||
const EnablePatchList prev_enabled_patches = std::exchange(s_enabled_patches, Host::GetStringListSetting(PATCHES_CONFIG_SECTION, PATCH_ENABLE_CONFIG_KEY));
|
const std::vector<std::string> prev_enabled_patches = std::exchange(s_enabled_patches, Host::GetStringListSetting(PATCHES_CONFIG_SECTION, PATCH_ENABLE_CONFIG_KEY));
|
||||||
const EnablePatchList disabled_patches = Host::GetStringListSetting(PATCHES_CONFIG_SECTION, PATCH_DISABLE_CONFIG_KEY);
|
const std::vector<std::string> disabled_patches = Host::GetStringListSetting(PATCHES_CONFIG_SECTION, PATCH_DISABLE_CONFIG_KEY);
|
||||||
|
|
||||||
// Name based matching for widescreen/NI settings.
|
// Name based matching for widescreen/NI settings.
|
||||||
if (EmuConfig.EnableWideScreenPatches)
|
if (EmuConfig.EnableWideScreenPatches)
|
||||||
@ -649,12 +689,10 @@ void Patch::ReloadEnabledLists()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Patch::EnablePatches(const PatchList& patches, const EnablePatchList& enable_list, const EnablePatchList& enable_immediately_list)
|
u32 Patch::EnablePatches(const std::vector<PatchGroup>* patches, const std::vector<std::string>& enable_list, const std::vector<std::string>* enable_immediately_list)
|
||||||
{
|
{
|
||||||
ActivePatchList patches_to_apply_immediately;
|
|
||||||
|
|
||||||
u32 count = 0;
|
u32 count = 0;
|
||||||
for (const PatchGroup& p : patches)
|
for (const PatchGroup& p : *patches)
|
||||||
{
|
{
|
||||||
// For compatibility, we auto enable anything that's not labelled.
|
// For compatibility, we auto enable anything that's not labelled.
|
||||||
// Also for gamedb patches.
|
// Also for gamedb patches.
|
||||||
@ -664,7 +702,6 @@ u32 Patch::EnablePatches(const PatchList& patches, const EnablePatchList& enable
|
|||||||
Console.WriteLn(Color_Green, fmt::format("Enabled patch: {}",
|
Console.WriteLn(Color_Green, fmt::format("Enabled patch: {}",
|
||||||
p.name.empty() ? std::string_view("<unknown>") : std::string_view(p.name)));
|
p.name.empty() ? std::string_view("<unknown>") : std::string_view(p.name)));
|
||||||
|
|
||||||
const bool apply_immediately = std::find(enable_immediately_list.begin(), enable_immediately_list.end(), p.name) != enable_immediately_list.end();
|
|
||||||
for (const PatchCommand& ip : p.patches)
|
for (const PatchCommand& ip : p.patches)
|
||||||
{
|
{
|
||||||
// print the actual patch lines only in verbose mode (even in devel)
|
// print the actual patch lines only in verbose mode (even in devel)
|
||||||
@ -672,8 +709,6 @@ u32 Patch::EnablePatches(const PatchList& patches, const EnablePatchList& enable
|
|||||||
DevCon.WriteLnFmt(" {}", ip.ToString());
|
DevCon.WriteLnFmt(" {}", ip.ToString());
|
||||||
|
|
||||||
s_active_patches.push_back(&ip);
|
s_active_patches.push_back(&ip);
|
||||||
if (apply_immediately && ip.placetopatch == PPT_ONCE_ON_LOAD)
|
|
||||||
patches_to_apply_immediately.push_back(&ip);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const DynamicPatch& dp : p.dpatches)
|
for (const DynamicPatch& dp : p.dpatches)
|
||||||
@ -690,12 +725,28 @@ u32 Patch::EnablePatches(const PatchList& patches, const EnablePatchList& enable
|
|||||||
count += p.name.empty() ? (static_cast<u32>(p.patches.size()) + static_cast<u32>(p.dpatches.size())) : 1;
|
count += p.name.empty() ? (static_cast<u32>(p.patches.size()) + static_cast<u32>(p.dpatches.size())) : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!patches_to_apply_immediately.empty())
|
// Apply PPT_ON_LOAD_OR_WHEN_ENABLED patches immediately.
|
||||||
|
if (enable_immediately_list && !enable_immediately_list->empty())
|
||||||
{
|
{
|
||||||
Host::RunOnCPUThread([patches = std::move(patches_to_apply_immediately)]() {
|
// Don't pass pointers to patch objects themselves here just in case the
|
||||||
for (const PatchCommand* i : patches)
|
// patches are reloaded twice in a row before this event makes it.
|
||||||
|
Host::RunOnCPUThread([patches, enable_immediately_list]() {
|
||||||
|
for (const PatchGroup& group : *patches)
|
||||||
{
|
{
|
||||||
ApplyPatch(i);
|
const bool apply_immediately = std::find(
|
||||||
|
enable_immediately_list->begin(),
|
||||||
|
enable_immediately_list->end(),
|
||||||
|
group.name) != enable_immediately_list->end();
|
||||||
|
if (!apply_immediately)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (const PatchCommand& command : group.patches)
|
||||||
|
{
|
||||||
|
if (command.placetopatch != PPT_ON_LOAD_OR_WHEN_ENABLED)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ApplyPatch(&command);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -762,19 +813,23 @@ void Patch::UpdateActivePatches(bool reload_enabled_list, bool verbose, bool ver
|
|||||||
u32 gp_count = 0;
|
u32 gp_count = 0;
|
||||||
if (EmuConfig.EnablePatches)
|
if (EmuConfig.EnablePatches)
|
||||||
{
|
{
|
||||||
gp_count = EnablePatches(s_gamedb_patches, EnablePatchList(), EnablePatchList());
|
gp_count = EnablePatches(&s_gamedb_patches, std::vector<std::string>(), nullptr);
|
||||||
s_gamedb_counts = gp_count;
|
s_gamedb_counts = gp_count;
|
||||||
if (gp_count > 0)
|
if (gp_count > 0)
|
||||||
message.append(TRANSLATE_PLURAL_STR("Patch", "%n GameDB patches are active.", "OSD Message", gp_count));
|
message.append(TRANSLATE_PLURAL_STR("Patch", "%n GameDB patches are active.", "OSD Message", gp_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 p_count = EnablePatches(s_game_patches, s_enabled_patches, apply_new_patches ? s_just_enabled_patches : EnablePatchList());
|
const u32 p_count = EnablePatches(
|
||||||
|
&s_game_patches, s_enabled_patches, apply_new_patches ? &s_just_enabled_patches : nullptr);
|
||||||
s_patches_counts = p_count;
|
s_patches_counts = p_count;
|
||||||
if (p_count > 0)
|
if (p_count > 0)
|
||||||
message.append_format("{}{}", message.empty() ? "" : "\n",
|
message.append_format("{}{}", message.empty() ? "" : "\n",
|
||||||
TRANSLATE_PLURAL_STR("Patch", "%n game patches are active.", "OSD Message", p_count));
|
TRANSLATE_PLURAL_STR("Patch", "%n game patches are active.", "OSD Message", p_count));
|
||||||
|
|
||||||
const u32 c_count = EmuConfig.EnableCheats ? EnablePatches(s_cheat_patches, s_enabled_cheats, apply_new_patches ? s_just_enabled_cheats : EnablePatchList()) : 0;
|
u32 c_count = 0;
|
||||||
|
if (EmuConfig.EnableCheats)
|
||||||
|
c_count = EnablePatches(
|
||||||
|
&s_cheat_patches, s_enabled_cheats, apply_new_patches ? &s_just_enabled_cheats : nullptr);
|
||||||
s_cheats_counts = c_count;
|
s_cheats_counts = c_count;
|
||||||
if (c_count > 0)
|
if (c_count > 0)
|
||||||
message.append_format("{}{}", message.empty() ? "" : "\n",
|
message.append_format("{}{}", message.empty() ? "" : "\n",
|
||||||
@ -892,7 +947,7 @@ void Patch::PatchFunc::patch(PatchGroup* group, const std::string_view cmd, cons
|
|||||||
|
|
||||||
if (!placetopatch.has_value())
|
if (!placetopatch.has_value())
|
||||||
{
|
{
|
||||||
PATCH_ERROR("Invalid 'place' value '{}' (0 - once on startup, 1: continuously)", pieces[0]);
|
PATCH_ERROR("Invalid 'place' value '{}' (0: on boot only, 1: continuously, 2: on boot and continuously, 3: on boot and when enabled in the GUI)", pieces[0]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!addr.has_value() || !addr_end.empty())
|
if (!addr.has_value() || !addr_end.empty())
|
||||||
@ -1083,6 +1138,19 @@ void Patch::PatchFunc::dpatch(PatchGroup* group, const std::string_view cmd, con
|
|||||||
group->dpatches.push_back(dpatch);
|
group->dpatches.push_back(dpatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Patch::ApplyBootPatches()
|
||||||
|
{
|
||||||
|
ApplyLoadedPatches(PPT_ONCE_ON_LOAD);
|
||||||
|
ApplyLoadedPatches(PPT_COMBINED_0_1);
|
||||||
|
ApplyLoadedPatches(PPT_ON_LOAD_OR_WHEN_ENABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Patch::ApplyVsyncPatches()
|
||||||
|
{
|
||||||
|
ApplyLoadedPatches(PPT_CONTINUOUSLY);
|
||||||
|
ApplyLoadedPatches(PPT_COMBINED_0_1);
|
||||||
|
}
|
||||||
|
|
||||||
// This is for applying patches directly to memory
|
// This is for applying patches directly to memory
|
||||||
void Patch::ApplyLoadedPatches(patch_place_type place)
|
void Patch::ApplyLoadedPatches(patch_place_type place)
|
||||||
{
|
{
|
||||||
@ -1731,3 +1799,31 @@ void Patch::ApplyDynaPatch(const DynamicPatch& patch, u32 address)
|
|||||||
memWrite32(address + replacement.offset, replacement.value);
|
memWrite32(address + replacement.offset, replacement.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* Patch::PlaceToString(std::optional<patch_place_type> place)
|
||||||
|
{
|
||||||
|
if (!place.has_value())
|
||||||
|
//: Time when a patch is applied.
|
||||||
|
return TRANSLATE("Patch", "Unknown");
|
||||||
|
|
||||||
|
switch (*place)
|
||||||
|
{
|
||||||
|
case Patch::PPT_ONCE_ON_LOAD:
|
||||||
|
//: Time when a patch is applied.
|
||||||
|
return TRANSLATE("Patch", "Only On Startup");
|
||||||
|
case Patch::PPT_CONTINUOUSLY:
|
||||||
|
//: Time when a patch is applied.
|
||||||
|
return TRANSLATE("Patch", "Every Frame");
|
||||||
|
case Patch::PPT_COMBINED_0_1:
|
||||||
|
//: Time when a patch is applied.
|
||||||
|
return TRANSLATE("Patch", "On Startup & Every Frame");
|
||||||
|
case Patch::PPT_ON_LOAD_OR_WHEN_ENABLED:
|
||||||
|
//: Time when a patch is applied.
|
||||||
|
return TRANSLATE("Patch", "On Startup & When Enabled");
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|||||||
@ -29,23 +29,22 @@ namespace Patch
|
|||||||
// In PCSX2 it indicates how/when/where the patch line should be applied. If
|
// 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.
|
// place is not one of the supported values then the patch line is never applied.
|
||||||
// PCSX2 currently supports the following values:
|
// PCSX2 currently supports the following values:
|
||||||
// 0 - apply the patch line once on game boot/startup
|
// 0 - apply the patch line once on game boot only
|
||||||
// 1 - apply the patch line continuously (technically - on every vsync)
|
// 1 - apply the patch line continuously (technically - on every vsync)
|
||||||
// 2 - effect of 0 and 1 combined, see below
|
// 2 - effect of 0 and 1 combined, see below
|
||||||
|
// 3 - apply the patch line once on game boot or when enabled in the GUI
|
||||||
// Note:
|
// Note:
|
||||||
// - while it may seem that a value of 1 does the same as 0, but also later
|
// - 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.
|
// continues to apply the patch on every vsync - it's not.
|
||||||
// The current (and past) behavior is that these patches are applied at different
|
// 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
|
// 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.
|
// 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 : u8
|
enum patch_place_type : u8
|
||||||
{
|
{
|
||||||
PPT_ONCE_ON_LOAD = 0,
|
PPT_ONCE_ON_LOAD = 0,
|
||||||
PPT_CONTINUOUSLY = 1,
|
PPT_CONTINUOUSLY = 1,
|
||||||
PPT_COMBINED_0_1 = 2,
|
PPT_COMBINED_0_1 = 2,
|
||||||
|
PPT_ON_LOAD_OR_WHEN_ENABLED = 3,
|
||||||
|
|
||||||
PPT_END_MARKER
|
PPT_END_MARKER
|
||||||
};
|
};
|
||||||
@ -56,12 +55,14 @@ namespace Patch
|
|||||||
std::string description;
|
std::string description;
|
||||||
std::string author;
|
std::string author;
|
||||||
|
|
||||||
|
// This is only populated if all the patch lines in a given group have
|
||||||
|
// the same place value.
|
||||||
|
std::optional<patch_place_type> place;
|
||||||
|
|
||||||
std::string_view GetNamePart() const;
|
std::string_view GetNamePart() const;
|
||||||
std::string_view GetNameParentPart() const;
|
std::string_view GetNameParentPart() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
using PatchInfoList = std::vector<PatchInfo>;
|
|
||||||
|
|
||||||
struct DynamicPatchEntry
|
struct DynamicPatchEntry
|
||||||
{
|
{
|
||||||
u32 offset;
|
u32 offset;
|
||||||
@ -80,7 +81,7 @@ namespace Patch
|
|||||||
extern const char* PATCH_ENABLE_CONFIG_KEY;
|
extern const char* PATCH_ENABLE_CONFIG_KEY;
|
||||||
extern const char* PATCH_DISABLE_CONFIG_KEY;
|
extern const char* PATCH_DISABLE_CONFIG_KEY;
|
||||||
|
|
||||||
extern PatchInfoList GetPatchInfo(const std::string_view serial, u32 crc, bool cheats, bool showAllCRCS, u32* num_unlabelled_patches);
|
extern std::vector<PatchInfo> GetPatchInfo(const std::string_view serial, u32 crc, bool cheats, bool showAllCRCS, u32* num_unlabelled_patches);
|
||||||
|
|
||||||
/// Returns the path to a new cheat/patch pnach for the specified serial and CRC.
|
/// Returns the path to a new cheat/patch pnach for the specified serial and CRC.
|
||||||
extern std::string GetPnachFilename(const std::string_view serial, u32 crc, bool cheats);
|
extern std::string GetPnachFilename(const std::string_view serial, u32 crc, bool cheats);
|
||||||
@ -97,6 +98,13 @@ namespace Patch
|
|||||||
extern void LoadDynamicPatches(const std::vector<DynamicPatch>& patches);
|
extern void LoadDynamicPatches(const std::vector<DynamicPatch>& patches);
|
||||||
extern void ApplyDynamicPatches(u32 pc);
|
extern void ApplyDynamicPatches(u32 pc);
|
||||||
|
|
||||||
|
// Apply all loaded patches that should be applied when the entry point is
|
||||||
|
// being recompiled.
|
||||||
|
extern void ApplyBootPatches();
|
||||||
|
|
||||||
|
// Apply all loaded patches that should be applied during vsync.
|
||||||
|
extern void ApplyVsyncPatches();
|
||||||
|
|
||||||
// Patches the emulation memory by applying all the loaded patches with a specific place 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
|
// 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,
|
// are enabled (e.g. ws patches, auto game fixes, etc) before calling ApplyLoadedPatches,
|
||||||
@ -112,4 +120,6 @@ namespace Patch
|
|||||||
extern u32 GetAllActivePatchesCount();
|
extern u32 GetAllActivePatchesCount();
|
||||||
|
|
||||||
extern bool IsGloballyToggleablePatch(const PatchInfo& patch_info);
|
extern bool IsGloballyToggleablePatch(const PatchInfo& patch_info);
|
||||||
|
|
||||||
|
extern const char* PlaceToString(std::optional<patch_place_type> place);
|
||||||
} // namespace Patch
|
} // namespace Patch
|
||||||
|
|||||||
@ -2832,8 +2832,8 @@ void VMManager::Internal::EntryPointCompilingOnCPUThread()
|
|||||||
|
|
||||||
HandleELFChange(true);
|
HandleELFChange(true);
|
||||||
|
|
||||||
Patch::ApplyLoadedPatches(Patch::PPT_ONCE_ON_LOAD);
|
Patch::ApplyBootPatches();
|
||||||
Patch::ApplyLoadedPatches(Patch::PPT_COMBINED_0_1);
|
|
||||||
// If the config changes at this point, it's a reset, so the game doesn't currently know about the memcard
|
// If the config changes at this point, it's a reset, so the game doesn't currently know about the memcard
|
||||||
// so there's no need to leave the eject running.
|
// so there's no need to leave the eject running.
|
||||||
FileMcd_CancelEject();
|
FileMcd_CancelEject();
|
||||||
@ -2849,8 +2849,7 @@ void VMManager::Internal::VSyncOnCPUThread()
|
|||||||
{
|
{
|
||||||
Pad::UpdateMacroButtons();
|
Pad::UpdateMacroButtons();
|
||||||
|
|
||||||
Patch::ApplyLoadedPatches(Patch::PPT_CONTINUOUSLY);
|
Patch::ApplyVsyncPatches();
|
||||||
Patch::ApplyLoadedPatches(Patch::PPT_COMBINED_0_1);
|
|
||||||
|
|
||||||
// Frame advance must be done *before* pumping messages, because otherwise
|
// Frame advance must be done *before* pumping messages, because otherwise
|
||||||
// we'll immediately reduce the counter we just set.
|
// we'll immediately reduce the counter we just set.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user