Merge pull request #9308 from smurf3tte/re23_patch
Patches for Resident Evil 2/3 audio issues
This commit is contained in:
commit
4cdcbb6ab2
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -27,6 +27,8 @@ struct PatchEntry
|
|||
PatchType type = PatchType::Patch8Bit;
|
||||
u32 address = 0;
|
||||
u32 value = 0;
|
||||
u32 comparand = 0;
|
||||
bool conditional = false;
|
||||
};
|
||||
|
||||
struct Patch
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue