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;
success &= TryParse(items[0], &pE.address);
success &= TryParse(items[2], &pE.value);
if (items.size() >= 4)
{
success &= TryParse(items[3], &pE.comparand);
pE.conditional = true;
}
const auto iter =
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 value = entry.value;
u32 comparand = entry.comparand;
switch (entry.type)
{
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;
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;
case PatchType::Patch32Bit:
PowerPC::HostWrite_U32(value, addr);
if (!entry.conditional || PowerPC::HostRead_U32(addr) == comparand)
PowerPC::HostWrite_U32(value, addr);
break;
default:
// unknown patchtype

View File

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

View File

@ -4,6 +4,7 @@
#include "DolphinQt/Config/NewPatchDialog.h"
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QGridLayout>
#include <QGroupBox>
@ -17,6 +18,23 @@
#include "Core/PatchEngine.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)
: QDialog(parent), m_patch(patch)
{
@ -38,6 +56,8 @@ NewPatchDialog::NewPatchDialog(QWidget* parent, PatchEngine::Patch& patch)
}
}
NewPatchDialog::~NewPatchDialog() = default;
void NewPatchDialog::CreateWidgets()
{
m_name_edit = new QLineEdit;
@ -79,26 +99,29 @@ void NewPatchDialog::ConnectWidgets()
void NewPatchDialog::AddEntry()
{
m_patch.entries.emplace_back();
m_entry_layout->addWidget(CreateEntry(m_patch.entries[m_patch.entries.size() - 1]));
m_entry_layout->addWidget(CreateEntry({}));
}
static bool PatchEq(const PatchEngine::PatchEntry& a, const PatchEngine::PatchEntry& b)
static u32 OnTextEdited(QLineEdit* edit, const QString& text)
{
if (a.address != b.address)
return false;
bool okay = false;
u32 value = text.toUInt(&okay, 16);
if (a.type != b.type)
return false;
QFont font;
QPalette palette;
if (a.value != b.value)
return false;
font.setBold(!okay);
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();
@ -115,95 +138,90 @@ QGroupBox* NewPatchDialog::CreateEntry(PatchEngine::PatchEntry& entry)
type_layout->addWidget(dword);
type->setLayout(type_layout);
auto* offset = new QLineEdit;
auto* address = new QLineEdit;
auto* value = new QLineEdit;
auto* comparand = new QLineEdit;
m_edits.push_back(offset);
m_edits.push_back(value);
auto* new_entry = m_entries.emplace_back(std::make_unique<NewPatchEntry>()).get();
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;
layout->addWidget(type, 0, 0, 1, -1);
layout->addWidget(new QLabel(tr("Offset:")), 1, 0);
layout->addWidget(offset, 1, 1);
layout->addWidget(new QLabel(tr("Address:")), 1, 0);
layout->addWidget(address, 1, 1);
layout->addWidget(new QLabel(tr("Value:")), 2, 0);
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);
connect(offset, qOverload<const QString&>(&QLineEdit::textEdited),
[&entry, offset](const QString& text) {
bool okay = true;
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(address, qOverload<const QString&>(&QLineEdit::textEdited),
[new_entry](const QString& text) {
new_entry->entry.address = OnTextEdited(new_entry->address, text);
});
connect(value, qOverload<const QString&>(&QLineEdit::textEdited),
[&entry, value](const QString& text) {
bool okay;
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);
[new_entry](const QString& text) {
new_entry->entry.value = OnTextEdited(new_entry->value, text);
});
connect(remove, &QPushButton::clicked, [this, box, offset, value, entry] {
if (m_patch.entries.size() > 1)
connect(comparand, qOverload<const QString&>(&QLineEdit::textEdited),
[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);
m_entry_layout->removeWidget(box);
box->deleteLater();
m_patch.entries.erase(
std::find_if(m_patch.entries.begin(), m_patch.entries.end(),
[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());
m_entries.erase(std::find_if(m_entries.begin(), m_entries.end(),
[new_entry](const auto& e) { return e.get() == new_entry; }));
}
});
connect(byte, &QRadioButton::toggled, [&entry](bool checked) {
connect(byte, &QRadioButton::toggled, [new_entry](bool 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)
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)
entry.type = PatchEngine::PatchType::Patch32Bit;
new_entry->entry.type = PatchEngine::PatchType::Patch32Bit;
});
byte->setChecked(entry.type == PatchEngine::PatchType::Patch8Bit);
word->setChecked(entry.type == PatchEngine::PatchType::Patch16Bit);
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')));
comparand->setText(QStringLiteral("%1").arg(entry.comparand, 8, 16, QLatin1Char('0')));
return box;
}
@ -218,11 +236,22 @@ void NewPatchDialog::accept()
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)
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)
@ -233,5 +262,12 @@ void NewPatchDialog::accept()
return;
}
m_patch.entries.clear();
for (const auto& entry : m_entries)
{
m_patch.entries.emplace_back(entry->entry);
}
QDialog::accept();
}

View File

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

View File

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