Merge pull request #9308 from smurf3tte/re23_patch

Patches for Resident Evil 2/3 audio issues
This commit is contained in:
Léo Lam 2021-01-06 01:52:15 +01:00 committed by GitHub
commit 4cdcbb6ab2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 244 additions and 74 deletions

View File

@ -0,0 +1,16 @@
# GHAE08 - Resident Evil 2
[OnFrame]
# Work around a game bug that causes background sounds to be zeroed during load.
# The bug was masked on real hardware by dcache. This patch fully fixes the bug
# and should also work on real hardware. Dolphin doesn't emulate dcache because
# the performance hit would be huge.
$Fix audio issues
# main.dol
0x800339E4:dword:0x60000000
# leon.rel
0x8055ACBC:dword:0x60000000:0x4BAA8445
# claire.rel
0x8055AB54:dword:0x60000000:0x4BAA85AD
[OnFrame_Enabled]
$Fix audio issues

View File

@ -0,0 +1,16 @@
# GHAJ08 - Biohazard 2
[OnFrame]
# Work around a game bug that causes background sounds to be zeroed during load.
# The bug was masked on real hardware by dcache. This patch fully fixes the bug
# and should also work on real hardware. Dolphin doesn't emulate dcache because
# the performance hit would be huge.
$Fix audio issues
# main.dol
0x80065FFC:dword:0x60000000
# leon.rel
0x805C5CC4:dword:0x60000000:0x4BA3D43D
# claire.rel
0x805C5BFC:dword:0x60000000:0x4BA3D505
[OnFrame_Enabled]
$Fix audio issues

View File

@ -0,0 +1,32 @@
# GHAP08 - Resident Evil 2
[OnFrame]
# Work around a game bug that causes background sounds to be zeroed during load.
# The bug was masked on real hardware by dcache. This patch fully fixes the bug
# and should also work on real hardware. Dolphin doesn't emulate dcache because
# the performance hit would be huge.
$Fix audio issues
# main.dol
0x80033D60:dword:0x60000000
# leon.rel
0x8055C5F8:dword:0x60000000:0x4BAA6B09
# claire.rel
0x8055C490:dword:0x60000000:0x4BAA6C71
# leon_g.rel
0x8055C3B8:dword:0x60000000:0x4BAA6D49
# claire_g.rel
0x8055C328:dword:0x60000000:0x4BAA6DD9
# leon_f.rel
0x8055D188:dword:0x60000000:0x4BAA5F79
# claire_f.rel
0x8055D068:dword:0x60000000:0x4BAA6099
# leon_s.rel
0x8055D100:dword:0x60000000:0x4BAA6001
# claire_s.rel
0x8055D064:dword:0x60000000:0x4BAA609D
# leon_i.rel
0x8055CFDC:dword:0x60000000:0x4BAA6125
# claire_i.rel
0x8055CEBC:dword:0x60000000:0x4BAA6245
[OnFrame_Enabled]
$Fix audio issues

View File

@ -0,0 +1,12 @@
# GLEE08 - Resident Evil 3: Nemesis
[OnFrame]
# Work around a game bug that causes background sounds to be zeroed during load.
# The bug was masked on real hardware by dcache. This patch fully fixes the bug
# and should also work on real hardware. Dolphin doesn't emulate dcache because
# the performance hit would be huge.
$Fix audio issues
# main.dol
0x80150E94:dword:0x60000000
[OnFrame_Enabled]
$Fix audio issues

View File

@ -0,0 +1,12 @@
# GLEJ08 - BioHazard 3: Last Escape
[OnFrame]
# Work around a game bug that causes background sounds to be zeroed during load.
# The bug was masked on real hardware by dcache. This patch fully fixes the bug
# and should also work on real hardware. Dolphin doesn't emulate dcache because
# the performance hit would be huge.
$Fix audio issues
# main.dol
0x8015110C:dword:0x60000000
[OnFrame_Enabled]
$Fix audio issues

View File

@ -0,0 +1,20 @@
# GLEP08 - Resident Evil 3: Nemesis
[OnFrame]
# Work around a game bug that causes background sounds to be zeroed during load.
# The bug was masked on real hardware by dcache. This patch fully fixes the bug
# and should also work on real hardware. Dolphin doesn't emulate dcache because
# the performance hit would be huge.
$Fix audio issues
# eng.rel
0x8058C174:dword:0x60000000:0x4BA76F8D
# ger.rel
0x8058CE40:dword:0x60000000:0x4BA762C1
# fra.rel
0x8058D03C:dword:0x60000000:0x4BA760C5
# spa.rel
0x8058D024:dword:0x60000000:0x4BA760DD
# ita.rel
0x8058CEA4:dword:0x60000000:0x4BA7625D
[OnFrame_Enabled]
$Fix audio issues

View File

@ -90,6 +90,11 @@ void LoadPatchSection(const std::string& section, std::vector<Patch>& patches, I
bool success = true; bool success = true;
success &= TryParse(items[0], &pE.address); success &= TryParse(items[0], &pE.address);
success &= TryParse(items[2], &pE.value); success &= TryParse(items[2], &pE.value);
if (items.size() >= 4)
{
success &= TryParse(items[3], &pE.comparand);
pE.conditional = true;
}
const auto iter = const auto iter =
std::find(s_patch_type_strings.begin(), s_patch_type_strings.end(), items[1]); std::find(s_patch_type_strings.begin(), s_patch_type_strings.end(), items[1]);
@ -184,16 +189,20 @@ static void ApplyPatches(const std::vector<Patch>& patches)
{ {
u32 addr = entry.address; u32 addr = entry.address;
u32 value = entry.value; u32 value = entry.value;
u32 comparand = entry.comparand;
switch (entry.type) switch (entry.type)
{ {
case PatchType::Patch8Bit: case PatchType::Patch8Bit:
PowerPC::HostWrite_U8(static_cast<u8>(value), addr); if (!entry.conditional || PowerPC::HostRead_U8(addr) == static_cast<u8>(comparand))
PowerPC::HostWrite_U8(static_cast<u8>(value), addr);
break; break;
case PatchType::Patch16Bit: case PatchType::Patch16Bit:
PowerPC::HostWrite_U16(static_cast<u16>(value), addr); if (!entry.conditional || PowerPC::HostRead_U16(addr) == static_cast<u16>(comparand))
PowerPC::HostWrite_U16(static_cast<u16>(value), addr);
break; break;
case PatchType::Patch32Bit: case PatchType::Patch32Bit:
PowerPC::HostWrite_U32(value, addr); if (!entry.conditional || PowerPC::HostRead_U32(addr) == comparand)
PowerPC::HostWrite_U32(value, addr);
break; break;
default: default:
// unknown patchtype // unknown patchtype

View File

@ -27,6 +27,8 @@ struct PatchEntry
PatchType type = PatchType::Patch8Bit; PatchType type = PatchType::Patch8Bit;
u32 address = 0; u32 address = 0;
u32 value = 0; u32 value = 0;
u32 comparand = 0;
bool conditional = false;
}; };
struct Patch struct Patch

View File

@ -4,6 +4,7 @@
#include "DolphinQt/Config/NewPatchDialog.h" #include "DolphinQt/Config/NewPatchDialog.h"
#include <QCheckBox>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QGridLayout> #include <QGridLayout>
#include <QGroupBox> #include <QGroupBox>
@ -17,6 +18,23 @@
#include "Core/PatchEngine.h" #include "Core/PatchEngine.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h" #include "DolphinQt/QtUtils/ModalMessageBox.h"
struct NewPatchEntry
{
NewPatchEntry() = default;
// These entries share the lifetime of their associated text widgets, and because they are
// captured by pointer by various edit handlers, they should not copied or moved.
NewPatchEntry(const NewPatchEntry&) = delete;
NewPatchEntry& operator=(const NewPatchEntry&) = delete;
NewPatchEntry(NewPatchEntry&&) = delete;
NewPatchEntry& operator=(NewPatchEntry&&) = delete;
QLineEdit* address = nullptr;
QLineEdit* value = nullptr;
QLineEdit* comparand = nullptr;
PatchEngine::PatchEntry entry;
};
NewPatchDialog::NewPatchDialog(QWidget* parent, PatchEngine::Patch& patch) NewPatchDialog::NewPatchDialog(QWidget* parent, PatchEngine::Patch& patch)
: QDialog(parent), m_patch(patch) : QDialog(parent), m_patch(patch)
{ {
@ -38,6 +56,8 @@ NewPatchDialog::NewPatchDialog(QWidget* parent, PatchEngine::Patch& patch)
} }
} }
NewPatchDialog::~NewPatchDialog() = default;
void NewPatchDialog::CreateWidgets() void NewPatchDialog::CreateWidgets()
{ {
m_name_edit = new QLineEdit; m_name_edit = new QLineEdit;
@ -79,26 +99,29 @@ void NewPatchDialog::ConnectWidgets()
void NewPatchDialog::AddEntry() void NewPatchDialog::AddEntry()
{ {
m_patch.entries.emplace_back(); m_entry_layout->addWidget(CreateEntry({}));
m_entry_layout->addWidget(CreateEntry(m_patch.entries[m_patch.entries.size() - 1]));
} }
static bool PatchEq(const PatchEngine::PatchEntry& a, const PatchEngine::PatchEntry& b) static u32 OnTextEdited(QLineEdit* edit, const QString& text)
{ {
if (a.address != b.address) bool okay = false;
return false; u32 value = text.toUInt(&okay, 16);
if (a.type != b.type) QFont font;
return false; QPalette palette;
if (a.value != b.value) font.setBold(!okay);
return false;
return true; if (!okay)
palette.setColor(QPalette::Text, Qt::red);
edit->setFont(font);
edit->setPalette(palette);
return value;
} }
QGroupBox* NewPatchDialog::CreateEntry(PatchEngine::PatchEntry& entry) QGroupBox* NewPatchDialog::CreateEntry(const PatchEngine::PatchEntry& entry)
{ {
QGroupBox* box = new QGroupBox(); QGroupBox* box = new QGroupBox();
@ -115,95 +138,90 @@ QGroupBox* NewPatchDialog::CreateEntry(PatchEngine::PatchEntry& entry)
type_layout->addWidget(dword); type_layout->addWidget(dword);
type->setLayout(type_layout); type->setLayout(type_layout);
auto* offset = new QLineEdit; auto* address = new QLineEdit;
auto* value = new QLineEdit; auto* value = new QLineEdit;
auto* comparand = new QLineEdit;
m_edits.push_back(offset); auto* new_entry = m_entries.emplace_back(std::make_unique<NewPatchEntry>()).get();
m_edits.push_back(value); new_entry->address = address;
new_entry->value = value;
new_entry->comparand = comparand;
new_entry->entry = entry;
auto* conditional = new QCheckBox(tr("Conditional"));
auto* comparand_label = new QLabel(tr("Comparand:"));
auto* layout = new QGridLayout; auto* layout = new QGridLayout;
layout->addWidget(type, 0, 0, 1, -1); layout->addWidget(type, 0, 0, 1, -1);
layout->addWidget(new QLabel(tr("Offset:")), 1, 0); layout->addWidget(new QLabel(tr("Address:")), 1, 0);
layout->addWidget(offset, 1, 1); layout->addWidget(address, 1, 1);
layout->addWidget(new QLabel(tr("Value:")), 2, 0); layout->addWidget(new QLabel(tr("Value:")), 2, 0);
layout->addWidget(value, 2, 1); layout->addWidget(value, 2, 1);
layout->addWidget(remove, 3, 0, 1, -1); layout->addWidget(conditional, 3, 0, 1, -1);
layout->addWidget(comparand_label, 4, 0);
layout->addWidget(comparand, 4, 1);
layout->addWidget(remove, 5, 0, 1, -1);
box->setLayout(layout); box->setLayout(layout);
connect(offset, qOverload<const QString&>(&QLineEdit::textEdited), connect(address, qOverload<const QString&>(&QLineEdit::textEdited),
[&entry, offset](const QString& text) { [new_entry](const QString& text) {
bool okay = true; new_entry->entry.address = OnTextEdited(new_entry->address, text);
entry.address = text.toUInt(&okay, 16);
QFont font;
QPalette palette;
font.setBold(!okay);
if (!okay)
palette.setColor(QPalette::Text, Qt::red);
offset->setFont(font);
offset->setPalette(palette);
}); });
connect(value, qOverload<const QString&>(&QLineEdit::textEdited), connect(value, qOverload<const QString&>(&QLineEdit::textEdited),
[&entry, value](const QString& text) { [new_entry](const QString& text) {
bool okay; new_entry->entry.value = OnTextEdited(new_entry->value, text);
entry.value = text.toUInt(&okay, 16);
QFont font;
QPalette palette;
font.setBold(!okay);
if (!okay)
palette.setColor(QPalette::Text, Qt::red);
value->setFont(font);
value->setPalette(palette);
}); });
connect(remove, &QPushButton::clicked, [this, box, offset, value, entry] { connect(comparand, qOverload<const QString&>(&QLineEdit::textEdited),
if (m_patch.entries.size() > 1) [new_entry](const QString& text) {
new_entry->entry.comparand = OnTextEdited(new_entry->comparand, text);
});
connect(remove, &QPushButton::clicked, [this, box, new_entry] {
if (m_entries.size() > 1)
{ {
box->setVisible(false); box->setVisible(false);
m_entry_layout->removeWidget(box); m_entry_layout->removeWidget(box);
box->deleteLater(); box->deleteLater();
m_patch.entries.erase( m_entries.erase(std::find_if(m_entries.begin(), m_entries.end(),
std::find_if(m_patch.entries.begin(), m_patch.entries.end(), [new_entry](const auto& e) { return e.get() == new_entry; }));
[entry](const PatchEngine::PatchEntry& e) { return PatchEq(e, entry); }));
const auto it =
std::remove_if(m_edits.begin(), m_edits.end(), [offset, value](QLineEdit* line_edit) {
return line_edit == offset || line_edit == value;
});
m_edits.erase(it, m_edits.end());
} }
}); });
connect(byte, &QRadioButton::toggled, [&entry](bool checked) { connect(byte, &QRadioButton::toggled, [new_entry](bool checked) {
if (checked) if (checked)
entry.type = PatchEngine::PatchType::Patch8Bit; new_entry->entry.type = PatchEngine::PatchType::Patch8Bit;
}); });
connect(word, &QRadioButton::toggled, [&entry](bool checked) { connect(word, &QRadioButton::toggled, [new_entry](bool checked) {
if (checked) if (checked)
entry.type = PatchEngine::PatchType::Patch16Bit; new_entry->entry.type = PatchEngine::PatchType::Patch16Bit;
}); });
connect(dword, &QRadioButton::toggled, [&entry](bool checked) { connect(dword, &QRadioButton::toggled, [new_entry](bool checked) {
if (checked) if (checked)
entry.type = PatchEngine::PatchType::Patch32Bit; new_entry->entry.type = PatchEngine::PatchType::Patch32Bit;
}); });
byte->setChecked(entry.type == PatchEngine::PatchType::Patch8Bit); byte->setChecked(entry.type == PatchEngine::PatchType::Patch8Bit);
word->setChecked(entry.type == PatchEngine::PatchType::Patch16Bit); word->setChecked(entry.type == PatchEngine::PatchType::Patch16Bit);
dword->setChecked(entry.type == PatchEngine::PatchType::Patch32Bit); dword->setChecked(entry.type == PatchEngine::PatchType::Patch32Bit);
offset->setText(QStringLiteral("%1").arg(entry.address, 8, 16, QLatin1Char('0'))); connect(conditional, &QCheckBox::toggled, [new_entry, comparand_label, comparand](bool checked) {
new_entry->entry.conditional = checked;
comparand_label->setVisible(checked);
comparand->setVisible(checked);
});
conditional->setChecked(entry.conditional);
comparand_label->setVisible(entry.conditional);
comparand->setVisible(entry.conditional);
address->setText(QStringLiteral("%1").arg(entry.address, 8, 16, QLatin1Char('0')));
value->setText(QStringLiteral("%1").arg(entry.value, 8, 16, QLatin1Char('0'))); value->setText(QStringLiteral("%1").arg(entry.value, 8, 16, QLatin1Char('0')));
comparand->setText(QStringLiteral("%1").arg(entry.comparand, 8, 16, QLatin1Char('0')));
return box; return box;
} }
@ -218,11 +236,22 @@ void NewPatchDialog::accept()
bool valid = true; bool valid = true;
for (const auto* edit : m_edits) for (const auto& entry : m_entries)
{ {
edit->text().toUInt(&valid, 16); entry->address->text().toUInt(&valid, 16);
if (!valid) if (!valid)
break; break;
entry->value->text().toUInt(&valid, 16);
if (!valid)
break;
if (entry->entry.conditional)
{
entry->comparand->text().toUInt(&valid, 16);
if (!valid)
break;
}
} }
if (!valid) if (!valid)
@ -233,5 +262,12 @@ void NewPatchDialog::accept()
return; return;
} }
m_patch.entries.clear();
for (const auto& entry : m_entries)
{
m_patch.entries.emplace_back(entry->entry);
}
QDialog::accept(); QDialog::accept();
} }

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include <memory>
#include <vector> #include <vector>
#include <QDialog> #include <QDialog>
@ -21,10 +22,13 @@ class QLineEdit;
class QVBoxLayout; class QVBoxLayout;
class QPushButton; class QPushButton;
struct NewPatchEntry;
class NewPatchDialog : public QDialog class NewPatchDialog : public QDialog
{ {
public: public:
explicit NewPatchDialog(QWidget* parent, PatchEngine::Patch& patch); explicit NewPatchDialog(QWidget* parent, PatchEngine::Patch& patch);
~NewPatchDialog() override;
private: private:
void CreateWidgets(); void CreateWidgets();
@ -33,7 +37,7 @@ private:
void accept() override; void accept() override;
QGroupBox* CreateEntry(PatchEngine::PatchEntry& entry); QGroupBox* CreateEntry(const PatchEngine::PatchEntry& entry);
QLineEdit* m_name_edit; QLineEdit* m_name_edit;
QWidget* m_entry_widget; QWidget* m_entry_widget;
@ -41,7 +45,7 @@ private:
QPushButton* m_add_button; QPushButton* m_add_button;
QDialogButtonBox* m_button_box; QDialogButtonBox* m_button_box;
std::vector<QLineEdit*> m_edits; std::vector<std::unique_ptr<NewPatchEntry>> m_entries;
PatchEngine::Patch& m_patch; PatchEngine::Patch& m_patch;
}; };

View File

@ -8,6 +8,8 @@
#include <QListWidget> #include <QListWidget>
#include <QPushButton> #include <QPushButton>
#include <fmt/format.h>
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/IniFile.h" #include "Common/IniFile.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
@ -143,8 +145,17 @@ void PatchesWidget::SavePatches()
for (const auto& entry : patch.entries) for (const auto& entry : patch.entries)
{ {
lines.emplace_back(StringFromFormat("0x%08X:%s:0x%08X", entry.address, if (!entry.conditional)
PatchEngine::PatchTypeAsString(entry.type), entry.value)); {
lines.emplace_back(fmt::format("0x{:08X}:{}:0x{:08X}", entry.address,
PatchEngine::PatchTypeAsString(entry.type), entry.value));
}
else
{
lines.emplace_back(fmt::format("0x{:08X}:{}:0x{:08X}:0x{:08X}", entry.address,
PatchEngine::PatchTypeAsString(entry.type), entry.value,
entry.comparand));
}
} }
} }