mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-22 22:05:38 +00:00
Qt: Split Cheat Manager and Memory Scanner
And make them persistent across system invocations.
This commit is contained in:
parent
e936e7498a
commit
4598bc789b
|
@ -32,9 +32,9 @@ set(SRCS
|
|||
cheatcodeeditordialog.cpp
|
||||
cheatcodeeditordialog.h
|
||||
cheatcodeeditordialog.ui
|
||||
cheatmanagerdialog.cpp
|
||||
cheatmanagerdialog.h
|
||||
cheatmanagerdialog.ui
|
||||
cheatmanagerwindow.cpp
|
||||
cheatmanagerwindow.h
|
||||
cheatmanagerwindow.ui
|
||||
collapsiblewidget.cpp
|
||||
collapsiblewidget.h
|
||||
colorpickerbutton.cpp
|
||||
|
@ -121,6 +121,9 @@ set(SRCS
|
|||
memorycardeditorwindow.ui
|
||||
memorycardsettingswidget.cpp
|
||||
memorycardsettingswidget.h
|
||||
memoryscannerwindow.cpp
|
||||
memoryscannerwindow.h
|
||||
memoryscannerwindow.ui
|
||||
memoryviewwidget.cpp
|
||||
memoryviewwidget.h
|
||||
postprocessingsettingswidget.cpp
|
||||
|
|
File diff suppressed because it is too large
Load diff
578
src/duckstation-qt/cheatmanagerwindow.cpp
Normal file
578
src/duckstation-qt/cheatmanagerwindow.cpp
Normal file
|
@ -0,0 +1,578 @@
|
|||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> and contributors.
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "cheatmanagerwindow.h"
|
||||
#include "cheatcodeeditordialog.h"
|
||||
#include "mainwindow.h"
|
||||
#include "qthost.h"
|
||||
#include "qtutils.h"
|
||||
|
||||
#include "core/bus.h"
|
||||
#include "core/cpu_core.h"
|
||||
#include "core/host.h"
|
||||
#include "core/system.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtGui/QColor>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QInputDialog>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QTreeWidgetItemIterator>
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
CheatManagerWindow::CheatManagerWindow() : QWidget()
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
connectUi();
|
||||
|
||||
updateCheatList();
|
||||
}
|
||||
|
||||
CheatManagerWindow::~CheatManagerWindow() = default;
|
||||
|
||||
void CheatManagerWindow::connectUi()
|
||||
{
|
||||
connect(m_ui.cheatList, &QTreeWidget::currentItemChanged, this, &CheatManagerWindow::cheatListCurrentItemChanged);
|
||||
connect(m_ui.cheatList, &QTreeWidget::itemActivated, this, &CheatManagerWindow::cheatListItemActivated);
|
||||
connect(m_ui.cheatList, &QTreeWidget::itemChanged, this, &CheatManagerWindow::cheatListItemChanged);
|
||||
connect(m_ui.cheatListNewCategory, &QPushButton::clicked, this, &CheatManagerWindow::newCategoryClicked);
|
||||
connect(m_ui.cheatListAdd, &QPushButton::clicked, this, &CheatManagerWindow::addCodeClicked);
|
||||
connect(m_ui.cheatListEdit, &QPushButton::clicked, this, &CheatManagerWindow::editCodeClicked);
|
||||
connect(m_ui.cheatListRemove, &QPushButton::clicked, this, &CheatManagerWindow::deleteCodeClicked);
|
||||
connect(m_ui.cheatListActivate, &QPushButton::clicked, this, &CheatManagerWindow::activateCodeClicked);
|
||||
connect(m_ui.cheatListImport, &QPushButton::clicked, this, &CheatManagerWindow::importClicked);
|
||||
connect(m_ui.cheatListExport, &QPushButton::clicked, this, &CheatManagerWindow::exportClicked);
|
||||
connect(m_ui.cheatListClear, &QPushButton::clicked, this, &CheatManagerWindow::clearClicked);
|
||||
connect(m_ui.cheatListReset, &QPushButton::clicked, this, &CheatManagerWindow::resetClicked);
|
||||
|
||||
connect(g_emu_thread, &EmuThread::cheatEnabled, this, &CheatManagerWindow::setCheatCheckState);
|
||||
connect(g_emu_thread, &EmuThread::runningGameChanged, this, &CheatManagerWindow::updateCheatList);
|
||||
}
|
||||
|
||||
void CheatManagerWindow::showEvent(QShowEvent* event)
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
resizeColumns();
|
||||
}
|
||||
|
||||
void CheatManagerWindow::closeEvent(QCloseEvent* event)
|
||||
{
|
||||
QWidget::closeEvent(event);
|
||||
emit closed();
|
||||
}
|
||||
|
||||
void CheatManagerWindow::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
resizeColumns();
|
||||
}
|
||||
|
||||
void CheatManagerWindow::resizeColumns()
|
||||
{
|
||||
QtUtils::ResizeColumnsForTreeView(m_ui.cheatList, {-1, 100, 150, 100});
|
||||
}
|
||||
|
||||
QTreeWidgetItem* CheatManagerWindow::getItemForCheatIndex(u32 index) const
|
||||
{
|
||||
QTreeWidgetItemIterator iter(m_ui.cheatList);
|
||||
while (*iter)
|
||||
{
|
||||
QTreeWidgetItem* item = *iter;
|
||||
const QVariant item_data(item->data(0, Qt::UserRole));
|
||||
if (item_data.isValid() && item_data.toUInt() == index)
|
||||
return item;
|
||||
|
||||
++iter;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QTreeWidgetItem* CheatManagerWindow::getItemForCheatGroup(const QString& group_name) const
|
||||
{
|
||||
const int count = m_ui.cheatList->topLevelItemCount();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
QTreeWidgetItem* item = m_ui.cheatList->topLevelItem(i);
|
||||
if (item->text(0) == group_name)
|
||||
return item;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QTreeWidgetItem* CheatManagerWindow::createItemForCheatGroup(const QString& group_name) const
|
||||
{
|
||||
QTreeWidgetItem* group = new QTreeWidgetItem();
|
||||
group->setFlags(group->flags() | Qt::ItemIsUserCheckable);
|
||||
group->setText(0, group_name);
|
||||
m_ui.cheatList->addTopLevelItem(group);
|
||||
return group;
|
||||
}
|
||||
|
||||
QStringList CheatManagerWindow::getCheatGroupNames() const
|
||||
{
|
||||
QStringList group_names;
|
||||
|
||||
const int count = m_ui.cheatList->topLevelItemCount();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
QTreeWidgetItem* item = m_ui.cheatList->topLevelItem(i);
|
||||
group_names.push_back(item->text(0));
|
||||
}
|
||||
|
||||
return group_names;
|
||||
}
|
||||
|
||||
static int getCheatIndexFromItem(QTreeWidgetItem* item)
|
||||
{
|
||||
QVariant item_data(item->data(0, Qt::UserRole));
|
||||
if (!item_data.isValid())
|
||||
return -1;
|
||||
|
||||
return static_cast<int>(item_data.toUInt());
|
||||
}
|
||||
|
||||
int CheatManagerWindow::getSelectedCheatIndex() const
|
||||
{
|
||||
QList<QTreeWidgetItem*> sel = m_ui.cheatList->selectedItems();
|
||||
if (sel.isEmpty())
|
||||
return -1;
|
||||
|
||||
return static_cast<int>(getCheatIndexFromItem(sel.first()));
|
||||
}
|
||||
|
||||
CheatList* CheatManagerWindow::getCheatList() const
|
||||
{
|
||||
return System::IsValid() ? System::GetCheatList() : nullptr;
|
||||
}
|
||||
|
||||
void CheatManagerWindow::updateCheatList()
|
||||
{
|
||||
QSignalBlocker sb(m_ui.cheatList);
|
||||
while (m_ui.cheatList->topLevelItemCount() > 0)
|
||||
delete m_ui.cheatList->takeTopLevelItem(0);
|
||||
|
||||
m_ui.cheatList->setEnabled(false);
|
||||
m_ui.cheatListAdd->setEnabled(false);
|
||||
m_ui.cheatListNewCategory->setEnabled(false);
|
||||
m_ui.cheatListEdit->setEnabled(false);
|
||||
m_ui.cheatListRemove->setEnabled(false);
|
||||
m_ui.cheatListActivate->setText(tr("Activate"));
|
||||
m_ui.cheatListActivate->setEnabled(false);
|
||||
m_ui.cheatListImport->setEnabled(false);
|
||||
m_ui.cheatListExport->setEnabled(false);
|
||||
m_ui.cheatListClear->setEnabled(false);
|
||||
m_ui.cheatListReset->setEnabled(false);
|
||||
|
||||
Host::RunOnCPUThread([]() {
|
||||
if (!System::IsValid())
|
||||
return;
|
||||
|
||||
CheatList* list = System::GetCheatList();
|
||||
if (!list)
|
||||
{
|
||||
System::LoadCheatList();
|
||||
list = System::GetCheatList();
|
||||
}
|
||||
if (!list)
|
||||
{
|
||||
System::LoadCheatListFromDatabase();
|
||||
list = System::GetCheatList();
|
||||
}
|
||||
if (!list)
|
||||
{
|
||||
System::SetCheatList(std::make_unique<CheatList>());
|
||||
list = System::GetCheatList();
|
||||
}
|
||||
|
||||
// still racey...
|
||||
QtHost::RunOnUIThread([list]() {
|
||||
if (!QtHost::IsSystemValid())
|
||||
return;
|
||||
|
||||
CheatManagerWindow* cm = g_main_window->getCheatManagerWindow();
|
||||
if (!cm)
|
||||
return;
|
||||
|
||||
QSignalBlocker sb(cm->m_ui.cheatList);
|
||||
|
||||
const std::vector<std::string> groups = list->GetCodeGroups();
|
||||
for (const std::string& group_name : groups)
|
||||
{
|
||||
QTreeWidgetItem* group = cm->createItemForCheatGroup(QString::fromStdString(group_name));
|
||||
|
||||
const u32 count = list->GetCodeCount();
|
||||
bool all_enabled = true;
|
||||
for (u32 i = 0; i < count; i++)
|
||||
{
|
||||
const CheatCode& code = list->GetCode(i);
|
||||
if (code.group != group_name)
|
||||
continue;
|
||||
|
||||
QTreeWidgetItem* item = new QTreeWidgetItem(group);
|
||||
cm->fillItemForCheatCode(item, i, code);
|
||||
|
||||
all_enabled &= code.enabled;
|
||||
}
|
||||
|
||||
group->setCheckState(0, all_enabled ? Qt::Checked : Qt::Unchecked);
|
||||
group->setExpanded(true);
|
||||
}
|
||||
|
||||
cm->m_ui.cheatList->setEnabled(true);
|
||||
cm->m_ui.cheatListAdd->setEnabled(true);
|
||||
cm->m_ui.cheatListNewCategory->setEnabled(true);
|
||||
cm->m_ui.cheatListImport->setEnabled(true);
|
||||
cm->m_ui.cheatListClear->setEnabled(true);
|
||||
cm->m_ui.cheatListReset->setEnabled(true);
|
||||
cm->m_ui.cheatListExport->setEnabled(cm->m_ui.cheatList->topLevelItemCount() > 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void CheatManagerWindow::fillItemForCheatCode(QTreeWidgetItem* item, u32 index, const CheatCode& code)
|
||||
{
|
||||
item->setData(0, Qt::UserRole, QVariant(static_cast<uint>(index)));
|
||||
if (code.IsManuallyActivated())
|
||||
{
|
||||
item->setFlags(item->flags() & ~(Qt::ItemIsUserCheckable));
|
||||
}
|
||||
else
|
||||
{
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(0, code.enabled ? Qt::Checked : Qt::Unchecked);
|
||||
}
|
||||
item->setText(0, QString::fromStdString(code.description));
|
||||
item->setText(1, qApp->translate("Cheats", CheatCode::GetTypeDisplayName(code.type)));
|
||||
item->setText(2, qApp->translate("Cheats", CheatCode::GetActivationDisplayName(code.activation)));
|
||||
item->setText(3, QString::number(static_cast<uint>(code.instructions.size())));
|
||||
}
|
||||
|
||||
void CheatManagerWindow::saveCheatList()
|
||||
{
|
||||
Host::RunOnCPUThread([]() { System::SaveCheatList(); });
|
||||
}
|
||||
|
||||
void CheatManagerWindow::cheatListCurrentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous)
|
||||
{
|
||||
const int cheat_index = current ? getCheatIndexFromItem(current) : -1;
|
||||
const bool has_current = (cheat_index >= 0);
|
||||
m_ui.cheatListEdit->setEnabled(has_current);
|
||||
m_ui.cheatListRemove->setEnabled(has_current);
|
||||
m_ui.cheatListActivate->setEnabled(has_current);
|
||||
|
||||
if (!has_current)
|
||||
{
|
||||
m_ui.cheatListActivate->setText(tr("Activate"));
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool manual_activation = getCheatList()->GetCode(static_cast<u32>(cheat_index)).IsManuallyActivated();
|
||||
m_ui.cheatListActivate->setText(manual_activation ? tr("Activate") : tr("Toggle"));
|
||||
}
|
||||
}
|
||||
|
||||
void CheatManagerWindow::cheatListItemActivated(QTreeWidgetItem* item)
|
||||
{
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
const int index = getCheatIndexFromItem(item);
|
||||
if (index >= 0)
|
||||
activateCheat(static_cast<u32>(index));
|
||||
}
|
||||
|
||||
void CheatManagerWindow::cheatListItemChanged(QTreeWidgetItem* item, int column)
|
||||
{
|
||||
if (!item || column != 0)
|
||||
return;
|
||||
|
||||
CheatList* list = getCheatList();
|
||||
|
||||
const int index = getCheatIndexFromItem(item);
|
||||
if (index < 0)
|
||||
{
|
||||
// we're probably a parent/group node
|
||||
const int child_count = item->childCount();
|
||||
const Qt::CheckState cs = item->checkState(0);
|
||||
for (int i = 0; i < child_count; i++)
|
||||
item->child(i)->setCheckState(0, cs);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (static_cast<u32>(index) >= list->GetCodeCount())
|
||||
return;
|
||||
|
||||
CheatCode& cc = list->GetCode(static_cast<u32>(index));
|
||||
if (cc.IsManuallyActivated())
|
||||
return;
|
||||
|
||||
const bool new_enabled = (item->checkState(0) == Qt::Checked);
|
||||
if (cc.enabled == new_enabled)
|
||||
return;
|
||||
|
||||
Host::RunOnCPUThread([index, new_enabled]() {
|
||||
System::GetCheatList()->SetCodeEnabled(static_cast<u32>(index), new_enabled);
|
||||
System::SaveCheatList();
|
||||
});
|
||||
}
|
||||
|
||||
void CheatManagerWindow::activateCheat(u32 index)
|
||||
{
|
||||
CheatList* list = getCheatList();
|
||||
if (index >= list->GetCodeCount())
|
||||
return;
|
||||
|
||||
CheatCode& cc = list->GetCode(index);
|
||||
if (cc.IsManuallyActivated())
|
||||
{
|
||||
g_emu_thread->applyCheat(index);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool new_enabled = !cc.enabled;
|
||||
setCheatCheckState(index, new_enabled);
|
||||
|
||||
Host::RunOnCPUThread([index, new_enabled]() {
|
||||
System::GetCheatList()->SetCodeEnabled(index, new_enabled);
|
||||
System::SaveCheatList();
|
||||
});
|
||||
}
|
||||
|
||||
void CheatManagerWindow::setCheatCheckState(u32 index, bool checked)
|
||||
{
|
||||
QTreeWidgetItem* item = getItemForCheatIndex(index);
|
||||
if (item)
|
||||
{
|
||||
QSignalBlocker sb(m_ui.cheatList);
|
||||
item->setCheckState(0, checked ? Qt::Checked : Qt::Unchecked);
|
||||
}
|
||||
}
|
||||
|
||||
void CheatManagerWindow::newCategoryClicked()
|
||||
{
|
||||
QString group_name = QInputDialog::getText(this, tr("Add Group"), tr("Group Name:"));
|
||||
if (group_name.isEmpty())
|
||||
return;
|
||||
|
||||
if (getItemForCheatGroup(group_name) != nullptr)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), tr("This group name already exists."));
|
||||
return;
|
||||
}
|
||||
|
||||
createItemForCheatGroup(group_name);
|
||||
}
|
||||
|
||||
void CheatManagerWindow::addCodeClicked()
|
||||
{
|
||||
CheatList* list = getCheatList();
|
||||
|
||||
CheatCode new_code;
|
||||
new_code.group = "Ungrouped";
|
||||
|
||||
CheatCodeEditorDialog editor(getCheatGroupNames(), &new_code, this);
|
||||
if (editor.exec() > 0)
|
||||
{
|
||||
const QString group_name_qstr(QString::fromStdString(new_code.group));
|
||||
QTreeWidgetItem* group_item = getItemForCheatGroup(group_name_qstr);
|
||||
if (!group_item)
|
||||
group_item = createItemForCheatGroup(group_name_qstr);
|
||||
|
||||
QTreeWidgetItem* item = new QTreeWidgetItem(group_item);
|
||||
fillItemForCheatCode(item, list->GetCodeCount(), new_code);
|
||||
group_item->setExpanded(true);
|
||||
|
||||
Host::RunOnCPUThread(
|
||||
[&new_code]() {
|
||||
System::GetCheatList()->AddCode(std::move(new_code));
|
||||
System::SaveCheatList();
|
||||
},
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
void CheatManagerWindow::editCodeClicked()
|
||||
{
|
||||
int index = getSelectedCheatIndex();
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
CheatList* list = getCheatList();
|
||||
if (static_cast<u32>(index) >= list->GetCodeCount())
|
||||
return;
|
||||
|
||||
CheatCode new_code = list->GetCode(static_cast<u32>(index));
|
||||
CheatCodeEditorDialog editor(getCheatGroupNames(), &new_code, this);
|
||||
if (editor.exec() > 0)
|
||||
{
|
||||
QTreeWidgetItem* item = getItemForCheatIndex(static_cast<u32>(index));
|
||||
if (item)
|
||||
{
|
||||
if (new_code.group != list->GetCode(static_cast<u32>(index)).group)
|
||||
{
|
||||
item = item->parent()->takeChild(item->parent()->indexOfChild(item));
|
||||
|
||||
const QString group_name_qstr(QString::fromStdString(new_code.group));
|
||||
QTreeWidgetItem* group_item = getItemForCheatGroup(group_name_qstr);
|
||||
if (!group_item)
|
||||
group_item = createItemForCheatGroup(group_name_qstr);
|
||||
group_item->addChild(item);
|
||||
group_item->setExpanded(true);
|
||||
}
|
||||
|
||||
fillItemForCheatCode(item, static_cast<u32>(index), new_code);
|
||||
}
|
||||
else
|
||||
{
|
||||
// shouldn't happen...
|
||||
updateCheatList();
|
||||
}
|
||||
|
||||
Host::RunOnCPUThread(
|
||||
[index, &new_code]() {
|
||||
System::GetCheatList()->SetCode(static_cast<u32>(index), std::move(new_code));
|
||||
System::SaveCheatList();
|
||||
},
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
void CheatManagerWindow::deleteCodeClicked()
|
||||
{
|
||||
int index = getSelectedCheatIndex();
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
CheatList* list = getCheatList();
|
||||
if (static_cast<u32>(index) >= list->GetCodeCount())
|
||||
return;
|
||||
|
||||
if (QMessageBox::question(this, tr("Delete Code"),
|
||||
tr("Are you sure you wish to delete the selected code? This action is not reversible."),
|
||||
QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Host::RunOnCPUThread(
|
||||
[index]() {
|
||||
System::GetCheatList()->RemoveCode(static_cast<u32>(index));
|
||||
System::SaveCheatList();
|
||||
},
|
||||
true);
|
||||
updateCheatList();
|
||||
}
|
||||
|
||||
void CheatManagerWindow::activateCodeClicked()
|
||||
{
|
||||
int index = getSelectedCheatIndex();
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
activateCheat(static_cast<u32>(index));
|
||||
}
|
||||
|
||||
void CheatManagerWindow::importClicked()
|
||||
{
|
||||
QMenu menu(this);
|
||||
connect(menu.addAction(tr("From File...")), &QAction::triggered, this, &CheatManagerWindow::importFromFileTriggered);
|
||||
connect(menu.addAction(tr("From Text...")), &QAction::triggered, this, &CheatManagerWindow::importFromTextTriggered);
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
void CheatManagerWindow::importFromFileTriggered()
|
||||
{
|
||||
const QString filter(tr("PCSXR/Libretro Cheat Files (*.cht *.txt);;All Files (*.*)"));
|
||||
const QString filename =
|
||||
QDir::toNativeSeparators(QFileDialog::getOpenFileName(this, tr("Import Cheats"), QString(), filter));
|
||||
if (filename.isEmpty())
|
||||
return;
|
||||
|
||||
CheatList new_cheats;
|
||||
if (!new_cheats.LoadFromFile(filename.toUtf8().constData(), CheatList::Format::Autodetect))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), tr("Failed to parse cheat file. The log may contain more information."));
|
||||
return;
|
||||
}
|
||||
|
||||
Host::RunOnCPUThread(
|
||||
[&new_cheats]() {
|
||||
DebugAssert(System::HasCheatList());
|
||||
System::GetCheatList()->MergeList(new_cheats);
|
||||
System::SaveCheatList();
|
||||
},
|
||||
true);
|
||||
updateCheatList();
|
||||
}
|
||||
|
||||
void CheatManagerWindow::importFromTextTriggered()
|
||||
{
|
||||
const QString text = QInputDialog::getMultiLineText(this, tr("Import Cheats"), tr("Cheat File Text:"));
|
||||
if (text.isEmpty())
|
||||
return;
|
||||
|
||||
CheatList new_cheats;
|
||||
if (!new_cheats.LoadFromString(text.toStdString(), CheatList::Format::Autodetect))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), tr("Failed to parse cheat file. The log may contain more information."));
|
||||
return;
|
||||
}
|
||||
|
||||
Host::RunOnCPUThread(
|
||||
[&new_cheats]() {
|
||||
DebugAssert(System::HasCheatList());
|
||||
System::GetCheatList()->MergeList(new_cheats);
|
||||
System::SaveCheatList();
|
||||
},
|
||||
true);
|
||||
updateCheatList();
|
||||
}
|
||||
|
||||
void CheatManagerWindow::exportClicked()
|
||||
{
|
||||
const QString filter(tr("PCSXR Cheat Files (*.cht);;All Files (*.*)"));
|
||||
const QString filename =
|
||||
QDir::toNativeSeparators(QFileDialog::getSaveFileName(this, tr("Export Cheats"), QString(), filter));
|
||||
if (filename.isEmpty())
|
||||
return;
|
||||
|
||||
if (!getCheatList()->SaveToPCSXRFile(filename.toUtf8().constData()))
|
||||
QMessageBox::critical(this, tr("Error"), tr("Failed to save cheat file. The log may contain more information."));
|
||||
}
|
||||
|
||||
void CheatManagerWindow::clearClicked()
|
||||
{
|
||||
if (QMessageBox::question(this, tr("Confirm Clear"),
|
||||
tr("Are you sure you want to remove all cheats? This is not reversible.")) !=
|
||||
QMessageBox::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Host::RunOnCPUThread([] { System::ClearCheatList(true); }, true);
|
||||
updateCheatList();
|
||||
}
|
||||
|
||||
void CheatManagerWindow::resetClicked()
|
||||
{
|
||||
if (QMessageBox::question(
|
||||
this, tr("Confirm Reset"),
|
||||
tr(
|
||||
"Are you sure you want to reset the cheat list? Any cheats not in the DuckStation database WILL BE LOST.")) !=
|
||||
QMessageBox::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Host::RunOnCPUThread([] { System::DeleteCheatList(); }, true);
|
||||
updateCheatList();
|
||||
}
|
|
@ -1,32 +1,38 @@
|
|||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui_cheatmanagerwindow.h"
|
||||
|
||||
#include "core/cheats.h"
|
||||
#include "ui_cheatmanagerdialog.h"
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtWidgets/QComboBox>
|
||||
#include <QtWidgets/QDialog>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QPushButton>
|
||||
#include <QtWidgets/QTableWidget>
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <optional>
|
||||
|
||||
class CheatManagerDialog : public QDialog
|
||||
class CheatManagerWindow : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CheatManagerDialog(QWidget* parent);
|
||||
~CheatManagerDialog();
|
||||
CheatManagerWindow();
|
||||
~CheatManagerWindow();
|
||||
|
||||
Q_SIGNALS:
|
||||
void closed();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event);
|
||||
void closeEvent(QCloseEvent* event);
|
||||
void resizeEvent(QResizeEvent* event);
|
||||
|
||||
private Q_SLOTS:
|
||||
void resizeColumns();
|
||||
|
||||
private Q_SLOTS:
|
||||
CheatList* getCheatList() const;
|
||||
void updateCheatList();
|
||||
void saveCheatList();
|
||||
|
@ -47,29 +53,13 @@ private Q_SLOTS:
|
|||
void clearClicked();
|
||||
void resetClicked();
|
||||
|
||||
void addToWatchClicked();
|
||||
void addManualWatchAddressClicked();
|
||||
void removeWatchClicked();
|
||||
void scanCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous);
|
||||
void watchCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous);
|
||||
void scanItemChanged(QTableWidgetItem* item);
|
||||
void watchItemChanged(QTableWidgetItem* item);
|
||||
void updateScanValue();
|
||||
void updateScanUi();
|
||||
|
||||
private:
|
||||
enum : int
|
||||
{
|
||||
MAX_DISPLAYED_SCAN_RESULTS = 5000
|
||||
};
|
||||
|
||||
void setupAdditionalUi();
|
||||
void connectUi();
|
||||
void setUpdateTimerEnabled(bool enabled);
|
||||
void updateResults();
|
||||
void updateResultsValues();
|
||||
void updateWatch();
|
||||
void updateWatchValues();
|
||||
void fillItemForCheatCode(QTreeWidgetItem* item, u32 index, const CheatCode& code);
|
||||
|
||||
QTreeWidgetItem* getItemForCheatIndex(u32 index) const;
|
||||
|
@ -77,15 +67,8 @@ private:
|
|||
QTreeWidgetItem* createItemForCheatGroup(const QString& group_name) const;
|
||||
QStringList getCheatGroupNames() const;
|
||||
int getSelectedCheatIndex() const;
|
||||
int getSelectedResultIndexFirst() const;
|
||||
int getSelectedResultIndexLast() const;
|
||||
int getSelectedWatchIndexFirst() const;
|
||||
int getSelectedWatchIndexLast() const;
|
||||
|
||||
Ui::CheatManagerDialog m_ui;
|
||||
|
||||
MemoryScan m_scanner;
|
||||
MemoryWatchList m_watch;
|
||||
Ui::CheatManagerWindow m_ui;
|
||||
|
||||
QTimer* m_update_timer = nullptr;
|
||||
};
|
|
@ -1,18 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CheatManagerDialog</class>
|
||||
<widget class="QDialog" name="CheatManagerDialog">
|
||||
<class>CheatManagerWindow</class>
|
||||
<widget class="QWidget" name="CheatManagerWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1046</width>
|
||||
<height>778</height>
|
||||
<width>882</width>
|
||||
<height>572</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Cheat Manager</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset>
|
||||
<normaloff>:/icons/duck.png</normaloff>:/icons/duck.png</iconset>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
|
@ -101,7 +105,7 @@
|
|||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
|
@ -116,10 +120,10 @@
|
|||
<item row="0" column="0">
|
||||
<widget class="QTreeWidget" name="cheatList">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
<enum>QAbstractItemView::SelectionMode::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
<enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
|
@ -153,7 +157,7 @@
|
|||
<item row="0" column="0">
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="layoutWidget2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
|
@ -163,10 +167,10 @@
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ContiguousSelection</enum>
|
||||
<enum>QAbstractItemView::SelectionMode::ContiguousSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
<enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
|
@ -477,24 +481,24 @@
|
|||
</size>
|
||||
</property>
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhNone</set>
|
||||
<set>Qt::InputMethodHint::ImhNone</set>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
<enum>QFrame::Shape::NoFrame</enum>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
<enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
<enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::NoTextInteraction</set>
|
||||
<set>Qt::TextInteractionFlag::NoTextInteraction</set>
|
||||
</property>
|
||||
<property name="text">
|
||||
<property name="text" stdset="0">
|
||||
<string>Number of Results (Display limited to first 5000) : </string>
|
||||
</property>
|
||||
</widget>
|
||||
|
@ -511,16 +515,16 @@
|
|||
</size>
|
||||
</property>
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhNone</set>
|
||||
<set>Qt::InputMethodHint::ImhNone</set>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
<enum>QFrame::Shape::NoFrame</enum>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
<enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
<enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
|
@ -529,12 +533,12 @@
|
|||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::NoTextInteraction</set>
|
||||
<set>Qt::TextInteractionFlag::NoTextInteraction</set>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<property name="text" stdset="0">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</widget>
|
||||
|
@ -544,7 +548,7 @@
|
|||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
|
@ -566,10 +570,10 @@
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ContiguousSelection</enum>
|
||||
<enum>QAbstractItemView::SelectionMode::ContiguousSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
<enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
|
@ -658,7 +662,7 @@
|
|||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
|
@ -9,7 +9,7 @@
|
|||
<ClCompile Include="audiosettingswidget.cpp" />
|
||||
<ClCompile Include="autoupdaterdialog.cpp" />
|
||||
<ClCompile Include="biossettingswidget.cpp" />
|
||||
<ClCompile Include="cheatmanagerdialog.cpp" />
|
||||
<ClCompile Include="cheatmanagerwindow.cpp" />
|
||||
<ClCompile Include="cheatcodeeditordialog.cpp" />
|
||||
<ClCompile Include="collapsiblewidget.cpp" />
|
||||
<ClCompile Include="colorpickerbutton.cpp" />
|
||||
|
@ -30,6 +30,7 @@
|
|||
<ClCompile Include="inputbindingdialog.cpp" />
|
||||
<ClCompile Include="inputbindingwidgets.cpp" />
|
||||
<ClCompile Include="logwindow.cpp" />
|
||||
<ClCompile Include="memoryscannerwindow.cpp" />
|
||||
<ClCompile Include="memoryviewwidget.cpp" />
|
||||
<ClCompile Include="displaywidget.cpp" />
|
||||
<ClCompile Include="gamelistsettingswidget.cpp" />
|
||||
|
@ -58,7 +59,7 @@
|
|||
<QtMoc Include="aboutdialog.h" />
|
||||
<QtMoc Include="audiosettingswidget.h" />
|
||||
<QtMoc Include="biossettingswidget.h" />
|
||||
<QtMoc Include="cheatmanagerdialog.h" />
|
||||
<QtMoc Include="cheatmanagerwindow.h" />
|
||||
<QtMoc Include="cheatcodeeditordialog.h" />
|
||||
<QtMoc Include="coverdownloaddialog.h" />
|
||||
<QtMoc Include="memorycardsettingswidget.h" />
|
||||
|
@ -86,6 +87,7 @@
|
|||
<QtMoc Include="memoryviewwidget.h" />
|
||||
<QtMoc Include="logwindow.h" />
|
||||
<QtMoc Include="graphicssettingswidget.h" />
|
||||
<QtMoc Include="memoryscannerwindow.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="settingwidgetbinder.h" />
|
||||
|
@ -141,7 +143,7 @@
|
|||
<QtUi Include="memorycardeditorwindow.ui">
|
||||
<FileType>Document</FileType>
|
||||
</QtUi>
|
||||
<QtUi Include="cheatmanagerdialog.ui">
|
||||
<QtUi Include="cheatmanagerwindow.ui">
|
||||
<FileType>Document</FileType>
|
||||
</QtUi>
|
||||
<QtUi Include="cheatcodeeditordialog.ui">
|
||||
|
@ -224,7 +226,7 @@
|
|||
<ClCompile Include="$(IntDir)moc_autoupdaterdialog.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_advancedsettingswidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_biossettingswidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_cheatmanagerdialog.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_cheatmanagerwindow.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_cheatcodeeditordialog.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_collapsiblewidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_colorpickerbutton.cpp" />
|
||||
|
@ -255,6 +257,7 @@
|
|||
<ClCompile Include="$(IntDir)moc_mainwindow.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_memorycardsettingswidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_memorycardeditorwindow.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_memoryscannerwindow.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_memoryviewwidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_postprocessingsettingswidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_qthost.cpp" />
|
||||
|
@ -329,6 +332,9 @@
|
|||
<QtUi Include="graphicssettingswidget.ui">
|
||||
<FileType>Document</FileType>
|
||||
</QtUi>
|
||||
<QtUi Include="memoryscannerwindow.ui">
|
||||
<FileType>Document</FileType>
|
||||
</QtUi>
|
||||
<None Include="translations\duckstation-qt_es-es.ts" />
|
||||
<None Include="translations\duckstation-qt_tr.ts" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<ClCompile Include="biossettingswidget.cpp" />
|
||||
<ClCompile Include="memorycardeditorwindow.cpp" />
|
||||
<ClCompile Include="postprocessingsettingswidget.cpp" />
|
||||
<ClCompile Include="cheatmanagerdialog.cpp" />
|
||||
<ClCompile Include="cheatmanagerwindow.cpp" />
|
||||
<ClCompile Include="cheatcodeeditordialog.cpp" />
|
||||
<ClCompile Include="debuggerwindow.cpp" />
|
||||
<ClCompile Include="debuggermodels.cpp" />
|
||||
|
@ -73,7 +73,7 @@
|
|||
<ClCompile Include="$(IntDir)moc_cheatcodeeditordialog.cpp">
|
||||
<Filter>moc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(IntDir)moc_cheatmanagerdialog.cpp">
|
||||
<ClCompile Include="$(IntDir)moc_cheatmanagerwindow.cpp">
|
||||
<Filter>moc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(IntDir)moc_collapsiblewidget.cpp">
|
||||
|
@ -182,6 +182,10 @@
|
|||
<ClCompile Include="$(IntDir)moc_interfacesettingswidget.cpp">
|
||||
<Filter>moc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="memoryscannerwindow.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_memoryscannerwindow.cpp">
|
||||
<Filter>moc</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="qtutils.h" />
|
||||
|
@ -226,7 +230,7 @@
|
|||
<QtMoc Include="biossettingswidget.h" />
|
||||
<QtMoc Include="memorycardeditorwindow.h" />
|
||||
<QtMoc Include="postprocessingsettingswidget.h" />
|
||||
<QtMoc Include="cheatmanagerdialog.h" />
|
||||
<QtMoc Include="cheatmanagerwindow.h" />
|
||||
<QtMoc Include="cheatcodeeditordialog.h" />
|
||||
<QtMoc Include="debuggermodels.h" />
|
||||
<QtMoc Include="debuggerwindow.h" />
|
||||
|
@ -246,6 +250,7 @@
|
|||
<QtMoc Include="setupwizarddialog.h" />
|
||||
<QtMoc Include="logwindow.h" />
|
||||
<QtMoc Include="graphicssettingswidget.h" />
|
||||
<QtMoc Include="memoryscannerwindow.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<QtUi Include="consolesettingswidget.ui" />
|
||||
|
@ -261,7 +266,7 @@
|
|||
<QtUi Include="biossettingswidget.ui" />
|
||||
<QtUi Include="postprocessingsettingswidget.ui" />
|
||||
<QtUi Include="memorycardeditorwindow.ui" />
|
||||
<QtUi Include="cheatmanagerdialog.ui" />
|
||||
<QtUi Include="cheatmanagerwindow.ui" />
|
||||
<QtUi Include="cheatcodeeditordialog.ui" />
|
||||
<QtUi Include="emulationsettingswidget.ui" />
|
||||
<QtUi Include="achievementsettingswidget.ui" />
|
||||
|
@ -287,6 +292,7 @@
|
|||
<QtUi Include="controllerledsettingsdialog.ui" />
|
||||
<QtUi Include="debuggeraddbreakpointdialog.ui" />
|
||||
<QtUi Include="graphicssettingswidget.ui" />
|
||||
<QtUi Include="memoryscannerwindow.ui" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="qt5.natvis" />
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "aboutdialog.h"
|
||||
#include "achievementlogindialog.h"
|
||||
#include "autoupdaterdialog.h"
|
||||
#include "cheatmanagerdialog.h"
|
||||
#include "cheatmanagerwindow.h"
|
||||
#include "coverdownloaddialog.h"
|
||||
#include "debuggerwindow.h"
|
||||
#include "displaywidget.h"
|
||||
|
@ -14,6 +14,7 @@
|
|||
#include "interfacesettingswidget.h"
|
||||
#include "logwindow.h"
|
||||
#include "memorycardeditorwindow.h"
|
||||
#include "memoryscannerwindow.h"
|
||||
#include "qthost.h"
|
||||
#include "qtutils.h"
|
||||
#include "settingswindow.h"
|
||||
|
@ -601,12 +602,6 @@ void MainWindow::onSystemDestroyed()
|
|||
// reload played time
|
||||
if (m_game_list_widget->isShowingGameList())
|
||||
m_game_list_widget->refresh(false);
|
||||
|
||||
if (m_cheat_manager_dialog)
|
||||
{
|
||||
delete m_cheat_manager_dialog;
|
||||
m_cheat_manager_dialog = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onRunningGameChanged(const QString& filename, const QString& game_serial, const QString& game_title)
|
||||
|
@ -752,33 +747,12 @@ void MainWindow::recreate()
|
|||
|
||||
void MainWindow::destroySubWindows()
|
||||
{
|
||||
if (m_debugger_window)
|
||||
{
|
||||
m_debugger_window->close();
|
||||
m_debugger_window->deleteLater();
|
||||
m_debugger_window = nullptr;
|
||||
}
|
||||
|
||||
if (m_memory_card_editor_window)
|
||||
{
|
||||
m_memory_card_editor_window->close();
|
||||
m_memory_card_editor_window->deleteLater();
|
||||
m_memory_card_editor_window = nullptr;
|
||||
}
|
||||
|
||||
if (m_controller_settings_window)
|
||||
{
|
||||
m_controller_settings_window->close();
|
||||
m_controller_settings_window->deleteLater();
|
||||
m_controller_settings_window = nullptr;
|
||||
}
|
||||
|
||||
if (m_settings_window)
|
||||
{
|
||||
m_settings_window->close();
|
||||
m_settings_window->deleteLater();
|
||||
m_settings_window = nullptr;
|
||||
}
|
||||
QtUtils::CloseAndDeleteWindow(m_memory_scanner_window);
|
||||
QtUtils::CloseAndDeleteWindow(m_debugger_window);
|
||||
QtUtils::CloseAndDeleteWindow(m_cheat_manager_window);
|
||||
QtUtils::CloseAndDeleteWindow(m_memory_card_editor_window);
|
||||
QtUtils::CloseAndDeleteWindow(m_controller_settings_window);
|
||||
QtUtils::CloseAndDeleteWindow(m_settings_window);
|
||||
|
||||
SettingsWindow::closeGamePropertiesDialogs();
|
||||
|
||||
|
@ -1805,6 +1779,7 @@ void MainWindow::updateEmulationActions(bool starting, bool running, bool cheevo
|
|||
m_ui.menuChangeDisc->setDisabled(starting || !running);
|
||||
m_ui.menuCheats->setDisabled(starting || !running || cheevos_challenge_mode);
|
||||
m_ui.actionCPUDebugger->setDisabled(cheevos_challenge_mode);
|
||||
m_ui.actionMemoryScanner->setDisabled(cheevos_challenge_mode);
|
||||
m_ui.actionDumpRAM->setDisabled(starting || !running || cheevos_challenge_mode);
|
||||
m_ui.actionDumpVRAM->setDisabled(starting || !running || cheevos_challenge_mode);
|
||||
m_ui.actionDumpSPURAM->setDisabled(starting || !running || cheevos_challenge_mode);
|
||||
|
@ -2094,7 +2069,8 @@ void MainWindow::connectSignals()
|
|||
connect(m_ui.actionAboutQt, &QAction::triggered, qApp, &QApplication::aboutQt);
|
||||
connect(m_ui.actionAbout, &QAction::triggered, this, &MainWindow::onAboutActionTriggered);
|
||||
connect(m_ui.actionCheckForUpdates, &QAction::triggered, this, &MainWindow::onCheckForUpdatesActionTriggered);
|
||||
connect(m_ui.actionMemory_Card_Editor, &QAction::triggered, this, &MainWindow::onToolsMemoryCardEditorTriggered);
|
||||
connect(m_ui.actionMemoryCardEditor, &QAction::triggered, this, &MainWindow::onToolsMemoryCardEditorTriggered);
|
||||
connect(m_ui.actionMemoryScanner, &QAction::triggered, this, &MainWindow::onToolsMemoryScannerTriggered);
|
||||
connect(m_ui.actionCoverDownloader, &QAction::triggered, this, &MainWindow::onToolsCoverDownloaderTriggered);
|
||||
connect(m_ui.actionCPUDebugger, &QAction::triggered, this, &MainWindow::openCPUDebugger);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionEnableGDBServer, "Debug", "EnableGDBServer", false);
|
||||
|
@ -2571,17 +2547,7 @@ SettingsWindow* MainWindow::getSettingsDialog()
|
|||
void MainWindow::doSettings(const char* category /* = nullptr */)
|
||||
{
|
||||
SettingsWindow* dlg = getSettingsDialog();
|
||||
if (!dlg->isVisible())
|
||||
{
|
||||
dlg->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
dlg->raise();
|
||||
dlg->activateWindow();
|
||||
dlg->setFocus();
|
||||
}
|
||||
|
||||
QtUtils::ShowOrRaiseWindow(dlg);
|
||||
if (category)
|
||||
dlg->setCategory(category);
|
||||
}
|
||||
|
@ -2592,17 +2558,7 @@ void MainWindow::doControllerSettings(
|
|||
if (!m_controller_settings_window)
|
||||
m_controller_settings_window = new ControllerSettingsWindow();
|
||||
|
||||
if (!m_controller_settings_window->isVisible())
|
||||
{
|
||||
m_controller_settings_window->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_controller_settings_window->raise();
|
||||
m_controller_settings_window->activateWindow();
|
||||
m_controller_settings_window->setFocus();
|
||||
}
|
||||
|
||||
QtUtils::ShowOrRaiseWindow(m_controller_settings_window);
|
||||
if (category != ControllerSettingsWindow::Category::Count)
|
||||
m_controller_settings_window->setCategory(category);
|
||||
}
|
||||
|
@ -2933,16 +2889,7 @@ void MainWindow::openMemoryCardEditor(const QString& card_a_path, const QString&
|
|||
if (!m_memory_card_editor_window)
|
||||
m_memory_card_editor_window = new MemoryCardEditorWindow();
|
||||
|
||||
if (!m_memory_card_editor_window->isVisible())
|
||||
{
|
||||
m_memory_card_editor_window->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_memory_card_editor_window->raise();
|
||||
m_memory_card_editor_window->activateWindow();
|
||||
m_memory_card_editor_window->setFocus();
|
||||
}
|
||||
QtUtils::ShowOrRaiseWindow(m_memory_card_editor_window);
|
||||
|
||||
if (!card_a_path.isEmpty())
|
||||
{
|
||||
|
@ -2987,19 +2934,9 @@ void MainWindow::onAchievementsChallengeModeChanged(bool enabled)
|
|||
{
|
||||
if (enabled)
|
||||
{
|
||||
if (m_cheat_manager_dialog)
|
||||
{
|
||||
m_cheat_manager_dialog->close();
|
||||
delete m_cheat_manager_dialog;
|
||||
m_cheat_manager_dialog = nullptr;
|
||||
}
|
||||
|
||||
if (m_debugger_window)
|
||||
{
|
||||
m_debugger_window->close();
|
||||
delete m_debugger_window;
|
||||
m_debugger_window = nullptr;
|
||||
}
|
||||
QtUtils::CloseAndDeleteWindow(m_cheat_manager_window);
|
||||
QtUtils::CloseAndDeleteWindow(m_debugger_window);
|
||||
QtUtils::CloseAndDeleteWindow(m_memory_scanner_window);
|
||||
}
|
||||
|
||||
updateEmulationActions(false, System::IsValid(), enabled);
|
||||
|
@ -3019,44 +2956,52 @@ void MainWindow::onToolsCoverDownloaderTriggered()
|
|||
dlg.exec();
|
||||
}
|
||||
|
||||
void MainWindow::onToolsMemoryScannerTriggered()
|
||||
{
|
||||
if (Achievements::IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
if (!m_memory_scanner_window)
|
||||
{
|
||||
m_memory_scanner_window = new MemoryScannerWindow();
|
||||
connect(m_memory_scanner_window, &MemoryScannerWindow::closed, this, [this]() {
|
||||
m_memory_scanner_window->deleteLater();
|
||||
m_memory_scanner_window = nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
QtUtils::ShowOrRaiseWindow(m_memory_scanner_window);
|
||||
}
|
||||
|
||||
void MainWindow::openCheatManager()
|
||||
{
|
||||
if (!m_cheat_manager_dialog)
|
||||
m_cheat_manager_dialog = new CheatManagerDialog(this);
|
||||
if (Achievements::IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
if (!m_cheat_manager_dialog->isVisible())
|
||||
if (!m_cheat_manager_window)
|
||||
{
|
||||
m_cheat_manager_dialog->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cheat_manager_dialog->raise();
|
||||
m_cheat_manager_dialog->activateWindow();
|
||||
m_cheat_manager_dialog->setFocus();
|
||||
m_cheat_manager_window = new CheatManagerWindow();
|
||||
connect(m_cheat_manager_window, &CheatManagerWindow::closed, this, [this]() {
|
||||
m_cheat_manager_window->deleteLater();
|
||||
m_cheat_manager_window = nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
QtUtils::ShowOrRaiseWindow(m_cheat_manager_window);
|
||||
}
|
||||
|
||||
void MainWindow::openCPUDebugger()
|
||||
{
|
||||
if (m_debugger_window)
|
||||
if (!m_debugger_window)
|
||||
{
|
||||
m_debugger_window->raise();
|
||||
m_debugger_window->activateWindow();
|
||||
m_debugger_window->setFocus();
|
||||
return;
|
||||
}
|
||||
|
||||
Assert(!m_debugger_window);
|
||||
m_debugger_window = new DebuggerWindow();
|
||||
connect(m_debugger_window, &DebuggerWindow::closed, this, &MainWindow::onCPUDebuggerClosed);
|
||||
m_debugger_window->show();
|
||||
}
|
||||
|
||||
void MainWindow::onCPUDebuggerClosed()
|
||||
{
|
||||
Assert(m_debugger_window);
|
||||
connect(m_debugger_window, &DebuggerWindow::closed, this, [this]() {
|
||||
m_debugger_window->deleteLater();
|
||||
m_debugger_window = nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
QtUtils::ShowOrRaiseWindow(m_debugger_window);
|
||||
}
|
||||
|
||||
void MainWindow::onToolsOpenDataDirectoryTriggered()
|
||||
|
@ -3067,13 +3012,9 @@ void MainWindow::onToolsOpenDataDirectoryTriggered()
|
|||
void MainWindow::onSettingsTriggeredFromToolbar()
|
||||
{
|
||||
if (s_system_valid)
|
||||
{
|
||||
m_settings_toolbar_menu->exec(QCursor::pos());
|
||||
}
|
||||
else
|
||||
{
|
||||
doSettings();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::checkForUpdates(bool display_message)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
@ -24,13 +24,14 @@ class QLabel;
|
|||
class QThread;
|
||||
class QProgressBar;
|
||||
|
||||
class MainWindow;
|
||||
class GameListWidget;
|
||||
class EmuThread;
|
||||
class AutoUpdaterDialog;
|
||||
class MemoryCardEditorWindow;
|
||||
class CheatManagerDialog;
|
||||
class CheatManagerWindow;
|
||||
class DebuggerWindow;
|
||||
class MainWindow;
|
||||
class MemoryScannerWindow;
|
||||
|
||||
class GPUDevice;
|
||||
namespace Achievements {
|
||||
|
@ -96,6 +97,9 @@ public:
|
|||
ALWAYS_INLINE QLabel* getStatusFPSWidget() const { return m_status_fps_widget; }
|
||||
ALWAYS_INLINE QLabel* getStatusVPSWidget() const { return m_status_vps_widget; }
|
||||
|
||||
/// Accessors for child windows.
|
||||
CheatManagerWindow* getCheatManagerWindow() const { return m_cheat_manager_window; }
|
||||
|
||||
public Q_SLOTS:
|
||||
/// Updates debug menu visibility (hides if disabled).
|
||||
void updateDebugMenuVisibility();
|
||||
|
@ -166,6 +170,7 @@ private Q_SLOTS:
|
|||
void onAboutActionTriggered();
|
||||
void onCheckForUpdatesActionTriggered();
|
||||
void onToolsMemoryCardEditorTriggered();
|
||||
void onToolsMemoryScannerTriggered();
|
||||
void onToolsCoverDownloaderTriggered();
|
||||
void onToolsOpenDataDirectoryTriggered();
|
||||
void onSettingsTriggeredFromToolbar();
|
||||
|
@ -180,7 +185,6 @@ private Q_SLOTS:
|
|||
|
||||
void openCheatManager();
|
||||
void openCPUDebugger();
|
||||
void onCPUDebuggerClosed();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
@ -290,8 +294,9 @@ private:
|
|||
|
||||
AutoUpdaterDialog* m_auto_updater_dialog = nullptr;
|
||||
MemoryCardEditorWindow* m_memory_card_editor_window = nullptr;
|
||||
CheatManagerDialog* m_cheat_manager_dialog = nullptr;
|
||||
CheatManagerWindow* m_cheat_manager_window = nullptr;
|
||||
DebuggerWindow* m_debugger_window = nullptr;
|
||||
MemoryScannerWindow* m_memory_scanner_window = nullptr;
|
||||
|
||||
bool m_was_paused_by_focus_loss = false;
|
||||
bool m_open_debugger_on_start = false;
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>21</height>
|
||||
<height>33</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuSystem">
|
||||
|
@ -42,8 +42,7 @@
|
|||
<string>Change Disc</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="disc-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="disc-line"/>
|
||||
</property>
|
||||
<actiongroup name="actionGroupChangeDiscSubImages"/>
|
||||
<addaction name="actionChangeDiscFromFile"/>
|
||||
|
@ -57,8 +56,7 @@
|
|||
<string>Cheats</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="cheats-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="cheats-line"/>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuLoadState">
|
||||
|
@ -66,8 +64,7 @@
|
|||
<string>Load State</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="folder-open-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="folder-open-line"/>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuSaveState">
|
||||
|
@ -75,8 +72,7 @@
|
|||
<string>Save State</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="save-3-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="save-3-line"/>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="actionStartFile"/>
|
||||
|
@ -109,8 +105,7 @@
|
|||
<string>Theme</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="paint-brush-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="paint-brush-line"/>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuSettingsLanguage">
|
||||
|
@ -118,8 +113,7 @@
|
|||
<string>Language</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="global-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="global-line"/>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="actionViewGameProperties"/>
|
||||
|
@ -157,7 +151,7 @@
|
|||
<addaction name="separator"/>
|
||||
<addaction name="actionCheckForUpdates"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionViewThirdPartyNotices" />
|
||||
<addaction name="actionViewThirdPartyNotices"/>
|
||||
<addaction name="actionAboutQt"/>
|
||||
<addaction name="actionAbout"/>
|
||||
</widget>
|
||||
|
@ -235,9 +229,11 @@
|
|||
<property name="title">
|
||||
<string>&Tools</string>
|
||||
</property>
|
||||
<addaction name="actionMemory_Card_Editor"/>
|
||||
<addaction name="actionMemoryCardEditor"/>
|
||||
<addaction name="actionCoverDownloader"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionMemoryScanner"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionOpenDataDirectory"/>
|
||||
</widget>
|
||||
<addaction name="menuSystem"/>
|
||||
|
@ -258,7 +254,7 @@
|
|||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
<enum>Qt::ToolButtonStyle::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
|
@ -287,8 +283,7 @@
|
|||
<widget class="QStatusBar" name="statusBar"/>
|
||||
<action name="actionStartFile">
|
||||
<property name="icon">
|
||||
<iconset theme="file-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="file-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start &File...</string>
|
||||
|
@ -296,8 +291,7 @@
|
|||
</action>
|
||||
<action name="actionStartDisc">
|
||||
<property name="icon">
|
||||
<iconset theme="disc-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="disc-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start &Disc...</string>
|
||||
|
@ -305,8 +299,7 @@
|
|||
</action>
|
||||
<action name="actionStartBios">
|
||||
<property name="icon">
|
||||
<iconset theme="chip-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="chip-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start &BIOS</string>
|
||||
|
@ -314,8 +307,7 @@
|
|||
</action>
|
||||
<action name="actionScanForNewGames">
|
||||
<property name="icon">
|
||||
<iconset theme="file-search-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="file-search-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Scan For New Games</string>
|
||||
|
@ -323,8 +315,7 @@
|
|||
</action>
|
||||
<action name="actionRescanAllGames">
|
||||
<property name="icon">
|
||||
<iconset theme="refresh-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="refresh-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Rescan All Games</string>
|
||||
|
@ -332,8 +323,7 @@
|
|||
</action>
|
||||
<action name="actionPowerOff">
|
||||
<property name="icon">
|
||||
<iconset theme="shut-down-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="shut-down-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Power &Off</string>
|
||||
|
@ -341,8 +331,7 @@
|
|||
</action>
|
||||
<action name="actionReset">
|
||||
<property name="icon">
|
||||
<iconset theme="restart-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="restart-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Reset</string>
|
||||
|
@ -353,8 +342,7 @@
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="pause-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="pause-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Pause</string>
|
||||
|
@ -362,8 +350,7 @@
|
|||
</action>
|
||||
<action name="actionLoadState">
|
||||
<property name="icon">
|
||||
<iconset theme="folder-open-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="folder-open-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Load State</string>
|
||||
|
@ -371,8 +358,7 @@
|
|||
</action>
|
||||
<action name="actionSaveState">
|
||||
<property name="icon">
|
||||
<iconset theme="save-3-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="save-3-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Save State</string>
|
||||
|
@ -380,8 +366,7 @@
|
|||
</action>
|
||||
<action name="actionExit">
|
||||
<property name="icon">
|
||||
<iconset theme="door-open-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="door-open-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>E&xit</string>
|
||||
|
@ -389,8 +374,7 @@
|
|||
</action>
|
||||
<action name="actionBIOSSettings">
|
||||
<property name="icon">
|
||||
<iconset theme="chip-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="chip-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>B&IOS</string>
|
||||
|
@ -398,8 +382,7 @@
|
|||
</action>
|
||||
<action name="actionConsoleSettings">
|
||||
<property name="icon">
|
||||
<iconset theme="chip-2-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="chip-2-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>C&onsole</string>
|
||||
|
@ -407,8 +390,7 @@
|
|||
</action>
|
||||
<action name="actionEmulationSettings">
|
||||
<property name="icon">
|
||||
<iconset theme="emulation-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="emulation-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>E&mulation</string>
|
||||
|
@ -416,8 +398,7 @@
|
|||
</action>
|
||||
<action name="actionControllerSettings">
|
||||
<property name="icon">
|
||||
<iconset theme="controller-digital-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="controller-digital-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Controllers</string>
|
||||
|
@ -425,8 +406,7 @@
|
|||
</action>
|
||||
<action name="actionHotkeySettings">
|
||||
<property name="icon">
|
||||
<iconset theme="keyboard-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="keyboard-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Hotkeys</string>
|
||||
|
@ -434,8 +414,7 @@
|
|||
</action>
|
||||
<action name="actionGraphicsSettings">
|
||||
<property name="icon">
|
||||
<iconset theme="image-fill">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="image-fill"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Graphics</string>
|
||||
|
@ -443,8 +422,7 @@
|
|||
</action>
|
||||
<action name="actionPostProcessingSettings">
|
||||
<property name="icon">
|
||||
<iconset theme="sun-fill">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="sun-fill"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Post-Processing</string>
|
||||
|
@ -452,8 +430,7 @@
|
|||
</action>
|
||||
<action name="actionFullscreen">
|
||||
<property name="icon">
|
||||
<iconset theme="fullscreen-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="fullscreen-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Fullscreen</string>
|
||||
|
@ -493,8 +470,7 @@
|
|||
</action>
|
||||
<action name="actionCheckForUpdates">
|
||||
<property name="icon">
|
||||
<iconset theme="download-2-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="download-2-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Check for &Updates...</string>
|
||||
|
@ -525,8 +501,7 @@
|
|||
</action>
|
||||
<action name="actionChangeDisc">
|
||||
<property name="icon">
|
||||
<iconset theme="disc-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="disc-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Change Disc...</string>
|
||||
|
@ -534,8 +509,7 @@
|
|||
</action>
|
||||
<action name="actionCheats">
|
||||
<property name="icon">
|
||||
<iconset theme="cheats-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="cheats-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cheats...</string>
|
||||
|
@ -543,8 +517,7 @@
|
|||
</action>
|
||||
<action name="actionCheatsToolbar">
|
||||
<property name="icon">
|
||||
<iconset theme="cheats-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="cheats-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cheats</string>
|
||||
|
@ -552,8 +525,7 @@
|
|||
</action>
|
||||
<action name="actionAudioSettings">
|
||||
<property name="icon">
|
||||
<iconset theme="volume-up-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="volume-up-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Audio</string>
|
||||
|
@ -561,8 +533,7 @@
|
|||
</action>
|
||||
<action name="actionAchievementSettings">
|
||||
<property name="icon">
|
||||
<iconset theme="trophy-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="trophy-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Achievements</string>
|
||||
|
@ -570,8 +541,7 @@
|
|||
</action>
|
||||
<action name="actionFolderSettings">
|
||||
<property name="icon">
|
||||
<iconset theme="folder-open-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="folder-open-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Folders</string>
|
||||
|
@ -579,8 +549,7 @@
|
|||
</action>
|
||||
<action name="actionGameListSettings">
|
||||
<property name="icon">
|
||||
<iconset theme="folder-open-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="folder-open-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Game List</string>
|
||||
|
@ -588,8 +557,7 @@
|
|||
</action>
|
||||
<action name="actionInterfaceSettings">
|
||||
<property name="icon">
|
||||
<iconset theme="settings-3-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="settings-3-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Interface</string>
|
||||
|
@ -597,8 +565,7 @@
|
|||
</action>
|
||||
<action name="actionAdvancedSettings">
|
||||
<property name="icon">
|
||||
<iconset theme="alert-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="alert-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Advanced</string>
|
||||
|
@ -606,8 +573,7 @@
|
|||
</action>
|
||||
<action name="actionAddGameDirectory">
|
||||
<property name="icon">
|
||||
<iconset theme="folder-add-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="folder-add-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add Game Directory...</string>
|
||||
|
@ -615,26 +581,24 @@
|
|||
</action>
|
||||
<action name="actionSettings">
|
||||
<property name="icon">
|
||||
<iconset theme="settings-3-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="settings-3-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Settings</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::PreferencesRole</enum>
|
||||
<enum>QAction::MenuRole::PreferencesRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSettings2">
|
||||
<property name="icon">
|
||||
<iconset theme="settings-3-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="settings-3-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Settings</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::PreferencesRole</enum>
|
||||
<enum>QAction::MenuRole::PreferencesRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionChangeDiscFromFile">
|
||||
|
@ -788,8 +752,7 @@
|
|||
</action>
|
||||
<action name="actionScreenshot">
|
||||
<property name="icon">
|
||||
<iconset theme="screenshot-2-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="screenshot-2-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Screenshot</string>
|
||||
|
@ -797,8 +760,7 @@
|
|||
</action>
|
||||
<action name="actionMemoryCardSettings">
|
||||
<property name="icon">
|
||||
<iconset theme="memcard-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="memcard-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Memory Cards</string>
|
||||
|
@ -806,8 +768,7 @@
|
|||
</action>
|
||||
<action name="actionResumeLastState">
|
||||
<property name="icon">
|
||||
<iconset theme="play-circle-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="play-circle-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Resume</string>
|
||||
|
@ -851,8 +812,7 @@
|
|||
</action>
|
||||
<action name="actionViewGameList">
|
||||
<property name="icon">
|
||||
<iconset theme="list-check">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="list-check"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Game &List</string>
|
||||
|
@ -871,14 +831,13 @@
|
|||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="file-settings-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="file-settings-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Game &Properties</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMemory_Card_Editor">
|
||||
<action name="actionMemoryCardEditor">
|
||||
<property name="text">
|
||||
<string>Memory &Card Editor</string>
|
||||
</property>
|
||||
|
@ -898,8 +857,7 @@
|
|||
</action>
|
||||
<action name="actionViewGameGrid">
|
||||
<property name="icon">
|
||||
<iconset theme="function-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="function-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Game &Grid</string>
|
||||
|
@ -949,8 +907,7 @@
|
|||
</action>
|
||||
<action name="actionPowerOffWithoutSaving">
|
||||
<property name="icon">
|
||||
<iconset theme="close-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="close-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Power Off &Without Saving</string>
|
||||
|
@ -958,8 +915,7 @@
|
|||
</action>
|
||||
<action name="actionStartFullscreenUI">
|
||||
<property name="icon">
|
||||
<iconset theme="tv-2-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="tv-2-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start Big Picture Mode</string>
|
||||
|
@ -967,8 +923,7 @@
|
|||
</action>
|
||||
<action name="actionStartFullscreenUI2">
|
||||
<property name="icon">
|
||||
<iconset theme="tv-2-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="tv-2-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Big Picture</string>
|
||||
|
@ -979,6 +934,11 @@
|
|||
<string>Cover Downloader</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMemoryScanner">
|
||||
<property name="text">
|
||||
<string>Memory &Scanner</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="resources/resources.qrc"/>
|
||||
|
|
592
src/duckstation-qt/memoryscannerwindow.cpp
Normal file
592
src/duckstation-qt/memoryscannerwindow.cpp
Normal file
|
@ -0,0 +1,592 @@
|
|||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> and contributors.
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "memoryscannerwindow.h"
|
||||
#include "cheatcodeeditordialog.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/bus.h"
|
||||
#include "core/cpu_core.h"
|
||||
#include "core/host.h"
|
||||
#include "core/system.h"
|
||||
#include "qthost.h"
|
||||
#include "qtutils.h"
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtGui/QColor>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QInputDialog>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QTreeWidgetItemIterator>
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
static constexpr std::array<const char*, 6> s_size_strings = {
|
||||
{TRANSLATE_NOOP("MemoryScannerWindow", "Byte"), TRANSLATE_NOOP("MemoryScannerWindow", "Halfword"),
|
||||
TRANSLATE_NOOP("MemoryScannerWindow", "Word"), TRANSLATE_NOOP("MemoryScannerWindow", "Signed Byte"),
|
||||
TRANSLATE_NOOP("MemoryScannerWindow", "Signed Halfword"), TRANSLATE_NOOP("MemoryScannerWindow", "Signed Word")}};
|
||||
|
||||
static QString formatHexValue(u32 value, u8 size)
|
||||
{
|
||||
return QStringLiteral("0x%1").arg(static_cast<uint>(value), size, 16, QChar('0'));
|
||||
}
|
||||
|
||||
static QString formatHexAndDecValue(u32 value, u8 size, bool is_signed)
|
||||
{
|
||||
|
||||
if (is_signed)
|
||||
{
|
||||
u32 value_raw = value;
|
||||
if (size == 2)
|
||||
value_raw &= 0xFF;
|
||||
else if (size == 4)
|
||||
value_raw &= 0xFFFF;
|
||||
return QStringLiteral("0x%1 (%2)")
|
||||
.arg(static_cast<u32>(value_raw), size, 16, QChar('0'))
|
||||
.arg(static_cast<int>(value));
|
||||
}
|
||||
else
|
||||
return QStringLiteral("0x%1 (%2)").arg(static_cast<u32>(value), size, 16, QChar('0')).arg(static_cast<uint>(value));
|
||||
}
|
||||
|
||||
static QString formatCheatCode(u32 address, u32 value, const MemoryAccessSize size)
|
||||
{
|
||||
|
||||
if (size == MemoryAccessSize::Byte && address <= 0x00200000)
|
||||
return QStringLiteral("CHEAT CODE: %1 %2")
|
||||
.arg(static_cast<u32>(address) + 0x30000000, 8, 16, QChar('0'))
|
||||
.toUpper()
|
||||
.arg(static_cast<u16>(value), 4, 16, QChar('0'))
|
||||
.toUpper();
|
||||
else if (size == MemoryAccessSize::HalfWord && address <= 0x001FFFFE)
|
||||
return QStringLiteral("CHEAT CODE: %1 %2")
|
||||
.arg(static_cast<u32>(address) + 0x80000000, 8, 16, QChar('0'))
|
||||
.toUpper()
|
||||
.arg(static_cast<u16>(value), 4, 16, QChar('0'))
|
||||
.toUpper();
|
||||
else if (size == MemoryAccessSize::Word && address <= 0x001FFFFC)
|
||||
return QStringLiteral("CHEAT CODE: %1 %2")
|
||||
.arg(static_cast<u32>(address) + 0x90000000, 8, 16, QChar('0'))
|
||||
.toUpper()
|
||||
.arg(static_cast<u32>(value), 8, 16, QChar('0'))
|
||||
.toUpper();
|
||||
else
|
||||
return QStringLiteral("OUTSIDE RAM RANGE. POKE %1 with %2")
|
||||
.arg(static_cast<u32>(address), 8, 16, QChar('0'))
|
||||
.toUpper()
|
||||
.arg(static_cast<u16>(value), 8, 16, QChar('0'))
|
||||
.toUpper();
|
||||
}
|
||||
|
||||
static QString formatValue(u32 value, bool is_signed)
|
||||
{
|
||||
if (is_signed)
|
||||
return QString::number(static_cast<int>(value));
|
||||
else
|
||||
return QString::number(static_cast<uint>(value));
|
||||
}
|
||||
|
||||
MemoryScannerWindow::MemoryScannerWindow() : QWidget()
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
connectUi();
|
||||
}
|
||||
|
||||
MemoryScannerWindow::~MemoryScannerWindow() = default;
|
||||
|
||||
void MemoryScannerWindow::connectUi()
|
||||
{
|
||||
m_ui.scanStartAddress->setText(formatHexValue(m_scanner.GetStartAddress(), 8));
|
||||
m_ui.scanEndAddress->setText(formatHexValue(m_scanner.GetEndAddress(), 8));
|
||||
|
||||
connect(m_ui.scanValue, &QLineEdit::textChanged, this, &MemoryScannerWindow::updateScanValue);
|
||||
connect(m_ui.scanValueBase, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
[this](int index) { updateScanValue(); });
|
||||
connect(m_ui.scanSize, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
|
||||
m_scanner.SetSize(static_cast<MemoryAccessSize>(index));
|
||||
m_scanner.ResetSearch();
|
||||
updateResults();
|
||||
});
|
||||
connect(m_ui.scanValueSigned, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
|
||||
m_scanner.SetValueSigned(index == 0);
|
||||
m_scanner.ResetSearch();
|
||||
updateResults();
|
||||
});
|
||||
connect(m_ui.scanOperator, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
[this](int index) { m_scanner.SetOperator(static_cast<MemoryScan::Operator>(index)); });
|
||||
connect(m_ui.scanStartAddress, &QLineEdit::textChanged, [this](const QString& value) {
|
||||
uint address;
|
||||
if (value.startsWith(QStringLiteral("0x")) && value.length() > 2)
|
||||
address = value.mid(2).toUInt(nullptr, 16);
|
||||
else
|
||||
address = value.toUInt(nullptr, 16);
|
||||
m_scanner.SetStartAddress(static_cast<PhysicalMemoryAddress>(address));
|
||||
});
|
||||
connect(m_ui.scanEndAddress, &QLineEdit::textChanged, [this](const QString& value) {
|
||||
uint address;
|
||||
if (value.startsWith(QStringLiteral("0x")) && value.length() > 2)
|
||||
address = value.mid(2).toUInt(nullptr, 16);
|
||||
else
|
||||
address = value.toUInt(nullptr, 16);
|
||||
m_scanner.SetEndAddress(static_cast<PhysicalMemoryAddress>(address));
|
||||
});
|
||||
connect(m_ui.scanPresetRange, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
|
||||
if (index == 0)
|
||||
{
|
||||
m_ui.scanStartAddress->setText(formatHexValue(0, 8));
|
||||
m_ui.scanEndAddress->setText(formatHexValue(Bus::g_ram_size, 8));
|
||||
}
|
||||
else if (index == 1)
|
||||
{
|
||||
m_ui.scanStartAddress->setText(formatHexValue(CPU::SCRATCHPAD_ADDR, 8));
|
||||
m_ui.scanEndAddress->setText(formatHexValue(CPU::SCRATCHPAD_ADDR + CPU::SCRATCHPAD_SIZE, 8));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui.scanStartAddress->setText(formatHexValue(Bus::BIOS_BASE, 8));
|
||||
m_ui.scanEndAddress->setText(formatHexValue(Bus::BIOS_BASE + Bus::BIOS_SIZE, 8));
|
||||
}
|
||||
});
|
||||
connect(m_ui.scanNewSearch, &QPushButton::clicked, [this]() {
|
||||
m_scanner.Search();
|
||||
updateResults();
|
||||
});
|
||||
connect(m_ui.scanSearchAgain, &QPushButton::clicked, [this]() {
|
||||
m_scanner.SearchAgain();
|
||||
updateResults();
|
||||
});
|
||||
connect(m_ui.scanResetSearch, &QPushButton::clicked, [this]() {
|
||||
m_scanner.ResetSearch();
|
||||
updateResults();
|
||||
});
|
||||
connect(m_ui.scanAddWatch, &QPushButton::clicked, this, &MemoryScannerWindow::addToWatchClicked);
|
||||
connect(m_ui.scanAddManualAddress, &QPushButton::clicked, this, &MemoryScannerWindow::addManualWatchAddressClicked);
|
||||
connect(m_ui.scanRemoveWatch, &QPushButton::clicked, this, &MemoryScannerWindow::removeWatchClicked);
|
||||
connect(m_ui.scanTable, &QTableWidget::currentItemChanged, this, &MemoryScannerWindow::scanCurrentItemChanged);
|
||||
connect(m_ui.watchTable, &QTableWidget::currentItemChanged, this, &MemoryScannerWindow::watchCurrentItemChanged);
|
||||
connect(m_ui.scanTable, &QTableWidget::itemChanged, this, &MemoryScannerWindow::scanItemChanged);
|
||||
connect(m_ui.watchTable, &QTableWidget::itemChanged, this, &MemoryScannerWindow::watchItemChanged);
|
||||
|
||||
m_update_timer = new QTimer(this);
|
||||
connect(m_update_timer, &QTimer::timeout, this, &MemoryScannerWindow::updateScanUi);
|
||||
|
||||
connect(g_emu_thread, &EmuThread::systemStarted, this, &MemoryScannerWindow::onSystemStarted);
|
||||
connect(g_emu_thread, &EmuThread::systemDestroyed, this, &MemoryScannerWindow::onSystemDestroyed);
|
||||
|
||||
if (QtHost::IsSystemValid())
|
||||
onSystemStarted();
|
||||
else
|
||||
enableUi(false);
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::enableUi(bool enabled)
|
||||
{
|
||||
const bool has_results = (m_scanner.GetResultCount() > 0);
|
||||
|
||||
m_ui.scanValue->setEnabled(enabled);
|
||||
m_ui.scanValueBase->setEnabled(enabled);
|
||||
m_ui.scanValueSigned->setEnabled(enabled);
|
||||
m_ui.scanSize->setEnabled(enabled);
|
||||
m_ui.scanOperator->setEnabled(enabled);
|
||||
m_ui.scanStartAddress->setEnabled(enabled);
|
||||
m_ui.scanEndAddress->setEnabled(enabled);
|
||||
m_ui.scanPresetRange->setEnabled(enabled);
|
||||
m_ui.scanResultCount->setEnabled(enabled);
|
||||
m_ui.scanNewSearch->setEnabled(enabled);
|
||||
m_ui.scanSearchAgain->setEnabled(enabled && has_results);
|
||||
m_ui.scanResetSearch->setEnabled(enabled && has_results);
|
||||
m_ui.scanAddWatch->setEnabled(enabled && !m_ui.scanTable->selectedItems().empty());
|
||||
m_ui.watchTable->setEnabled(enabled);
|
||||
m_ui.scanAddManualAddress->setEnabled(enabled);
|
||||
m_ui.scanRemoveWatch->setEnabled(enabled && !m_ui.watchTable->selectedItems().empty());
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::showEvent(QShowEvent* event)
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
resizeColumns();
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::closeEvent(QCloseEvent* event)
|
||||
{
|
||||
QWidget::closeEvent(event);
|
||||
emit closed();
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
resizeColumns();
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::resizeColumns()
|
||||
{
|
||||
QtUtils::ResizeColumnsForTableView(m_ui.scanTable, {-1, 130, 130});
|
||||
QtUtils::ResizeColumnsForTableView(m_ui.watchTable, {-1, 100, 100, 100, 40});
|
||||
}
|
||||
|
||||
int MemoryScannerWindow::getSelectedResultIndexFirst() const
|
||||
{
|
||||
QList<QTableWidgetSelectionRange> sel = m_ui.scanTable->selectedRanges();
|
||||
if (sel.isEmpty())
|
||||
return -1;
|
||||
|
||||
return sel.front().topRow();
|
||||
}
|
||||
|
||||
int MemoryScannerWindow::getSelectedResultIndexLast() const
|
||||
{
|
||||
QList<QTableWidgetSelectionRange> sel = m_ui.scanTable->selectedRanges();
|
||||
if (sel.isEmpty())
|
||||
return -1;
|
||||
|
||||
return sel.front().bottomRow();
|
||||
}
|
||||
|
||||
int MemoryScannerWindow::getSelectedWatchIndexFirst() const
|
||||
{
|
||||
QList<QTableWidgetSelectionRange> sel = m_ui.watchTable->selectedRanges();
|
||||
if (sel.isEmpty())
|
||||
return -1;
|
||||
|
||||
return sel.front().topRow();
|
||||
}
|
||||
|
||||
int MemoryScannerWindow::getSelectedWatchIndexLast() const
|
||||
{
|
||||
QList<QTableWidgetSelectionRange> sel = m_ui.watchTable->selectedRanges();
|
||||
if (sel.isEmpty())
|
||||
return -1;
|
||||
|
||||
return sel.front().bottomRow();
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::onSystemStarted()
|
||||
{
|
||||
if (!m_update_timer->isActive())
|
||||
m_update_timer->start(SCAN_INTERVAL);
|
||||
|
||||
enableUi(true);
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::onSystemDestroyed()
|
||||
{
|
||||
if (m_update_timer->isActive())
|
||||
m_update_timer->stop();
|
||||
|
||||
enableUi(false);
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::addToWatchClicked()
|
||||
{
|
||||
const int indexFirst = getSelectedResultIndexFirst();
|
||||
const int indexLast = getSelectedResultIndexLast();
|
||||
if (indexFirst < 0)
|
||||
return;
|
||||
|
||||
for (int index = indexFirst; index <= indexLast; index++)
|
||||
{
|
||||
const MemoryScan::Result& res = m_scanner.GetResults()[static_cast<u32>(index)];
|
||||
m_watch.AddEntry(fmt::format("0x{:08x}", res.address), res.address, m_scanner.GetSize(), m_scanner.GetValueSigned(),
|
||||
false);
|
||||
updateWatch();
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::addManualWatchAddressClicked()
|
||||
{
|
||||
std::optional<unsigned> address = QtUtils::PromptForAddress(this, windowTitle(), tr("Enter manual address:"), false);
|
||||
if (!address.has_value())
|
||||
return;
|
||||
|
||||
QStringList items;
|
||||
for (const char* title : s_size_strings)
|
||||
items.append(tr(title));
|
||||
|
||||
bool ok = false;
|
||||
QString selected_item(QInputDialog::getItem(this, windowTitle(), tr("Select data size:"), items, 0, false, &ok));
|
||||
int index = items.indexOf(selected_item);
|
||||
if (index < 0 || !ok)
|
||||
return;
|
||||
|
||||
if (index == 1 || index == 4)
|
||||
address.value() &= 0xFFFFFFFE;
|
||||
else if (index == 2 || index == 5)
|
||||
address.value() &= 0xFFFFFFFC;
|
||||
|
||||
m_watch.AddEntry(fmt::format("0x{:08x}", address.value()), address.value(), static_cast<MemoryAccessSize>(index % 3),
|
||||
(index > 3), false);
|
||||
updateWatch();
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::removeWatchClicked()
|
||||
{
|
||||
const int indexFirst = getSelectedWatchIndexFirst();
|
||||
const int indexLast = getSelectedWatchIndexLast();
|
||||
if (indexFirst < 0)
|
||||
return;
|
||||
|
||||
for (int index = indexLast; index >= indexFirst; index--)
|
||||
{
|
||||
m_watch.RemoveEntry(static_cast<u32>(index));
|
||||
updateWatch();
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::scanCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous)
|
||||
{
|
||||
m_ui.scanAddWatch->setEnabled((current != nullptr));
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::watchCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous)
|
||||
{
|
||||
m_ui.scanRemoveWatch->setEnabled((current != nullptr));
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::scanItemChanged(QTableWidgetItem* item)
|
||||
{
|
||||
const u32 index = static_cast<u32>(item->row());
|
||||
switch (item->column())
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
bool value_ok = false;
|
||||
if (m_scanner.GetValueSigned())
|
||||
{
|
||||
int value = item->text().toInt(&value_ok);
|
||||
if (value_ok)
|
||||
m_scanner.SetResultValue(index, static_cast<u32>(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
uint value = item->text().toUInt(&value_ok);
|
||||
if (value_ok)
|
||||
m_scanner.SetResultValue(index, static_cast<u32>(value));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::watchItemChanged(QTableWidgetItem* item)
|
||||
{
|
||||
const u32 index = static_cast<u32>(item->row());
|
||||
if (index >= m_watch.GetEntryCount())
|
||||
return;
|
||||
|
||||
switch (item->column())
|
||||
{
|
||||
case 4:
|
||||
{
|
||||
m_watch.SetEntryFreeze(index, (item->checkState() == Qt::Checked));
|
||||
}
|
||||
break;
|
||||
|
||||
case 0:
|
||||
{
|
||||
m_watch.SetEntryDescription(index, item->text().toStdString());
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
{
|
||||
const MemoryWatchList::Entry& entry = m_watch.GetEntry(index);
|
||||
bool value_ok = false;
|
||||
if (entry.is_signed)
|
||||
{
|
||||
int value = item->text().toInt(&value_ok);
|
||||
if (value_ok)
|
||||
m_watch.SetEntryValue(index, static_cast<u32>(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
uint value;
|
||||
if (item->text()[1] == 'x' || item->text()[1] == 'X')
|
||||
value = item->text().toUInt(&value_ok, 16);
|
||||
else
|
||||
value = item->text().toUInt(&value_ok);
|
||||
if (value_ok)
|
||||
m_watch.SetEntryValue(index, static_cast<u32>(value));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::updateScanValue()
|
||||
{
|
||||
QString value = m_ui.scanValue->text();
|
||||
if (value.startsWith(QStringLiteral("0x")))
|
||||
value.remove(0, 2);
|
||||
|
||||
bool ok = false;
|
||||
uint uint_value = value.toUInt(&ok, (m_ui.scanValueBase->currentIndex() > 0) ? 16 : 10);
|
||||
if (ok)
|
||||
m_scanner.SetValue(uint_value);
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::updateResults()
|
||||
{
|
||||
QSignalBlocker sb(m_ui.scanTable);
|
||||
m_ui.scanTable->setRowCount(0);
|
||||
|
||||
int row = 0;
|
||||
const MemoryScan::ResultVector& results = m_scanner.GetResults();
|
||||
for (const MemoryScan::Result& res : results)
|
||||
{
|
||||
if (row == MAX_DISPLAYED_SCAN_RESULTS)
|
||||
break;
|
||||
|
||||
m_ui.scanTable->insertRow(row);
|
||||
|
||||
QTableWidgetItem* address_item = new QTableWidgetItem(formatHexValue(res.address, 8));
|
||||
address_item->setFlags(address_item->flags() & ~(Qt::ItemIsEditable));
|
||||
m_ui.scanTable->setItem(row, 0, address_item);
|
||||
|
||||
QTableWidgetItem* value_item;
|
||||
if (m_ui.scanValueBase->currentIndex() == 0)
|
||||
value_item = new QTableWidgetItem(formatValue(res.value, m_scanner.GetValueSigned()));
|
||||
else if (m_scanner.GetSize() == MemoryAccessSize::Byte)
|
||||
value_item = new QTableWidgetItem(formatHexValue(res.value, 2));
|
||||
else if (m_scanner.GetSize() == MemoryAccessSize::HalfWord)
|
||||
value_item = new QTableWidgetItem(formatHexValue(res.value, 4));
|
||||
else
|
||||
value_item = new QTableWidgetItem(formatHexValue(res.value, 8));
|
||||
m_ui.scanTable->setItem(row, 1, value_item);
|
||||
|
||||
QTableWidgetItem* previous_item;
|
||||
if (m_ui.scanValueBase->currentIndex() == 0)
|
||||
previous_item = new QTableWidgetItem(formatValue(res.last_value, m_scanner.GetValueSigned()));
|
||||
else if (m_scanner.GetSize() == MemoryAccessSize::Byte)
|
||||
previous_item = new QTableWidgetItem(formatHexValue(res.last_value, 2));
|
||||
else if (m_scanner.GetSize() == MemoryAccessSize::HalfWord)
|
||||
previous_item = new QTableWidgetItem(formatHexValue(res.last_value, 4));
|
||||
else
|
||||
previous_item = new QTableWidgetItem(formatHexValue(res.last_value, 8));
|
||||
|
||||
previous_item->setFlags(address_item->flags() & ~(Qt::ItemIsEditable));
|
||||
m_ui.scanTable->setItem(row, 2, previous_item);
|
||||
row++;
|
||||
}
|
||||
|
||||
m_ui.scanResultCount->setText((row < static_cast<int>(results.size())) ?
|
||||
tr("%1 (only showing first %2)").arg(results.size()).arg(row) :
|
||||
QString::number(m_scanner.GetResultCount()));
|
||||
|
||||
m_ui.scanResetSearch->setEnabled(!results.empty());
|
||||
m_ui.scanSearchAgain->setEnabled(!results.empty());
|
||||
m_ui.scanAddWatch->setEnabled(false);
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::updateResultsValues()
|
||||
{
|
||||
QSignalBlocker sb(m_ui.scanTable);
|
||||
|
||||
int row = 0;
|
||||
for (const MemoryScan::Result& res : m_scanner.GetResults())
|
||||
{
|
||||
if (res.value_changed)
|
||||
{
|
||||
QTableWidgetItem* item = m_ui.scanTable->item(row, 1);
|
||||
if (m_ui.scanValueBase->currentIndex() == 0)
|
||||
item->setText(formatValue(res.value, m_scanner.GetValueSigned()));
|
||||
else if (m_scanner.GetSize() == MemoryAccessSize::Byte)
|
||||
item->setText(formatHexValue(res.value, 2));
|
||||
else if (m_scanner.GetSize() == MemoryAccessSize::HalfWord)
|
||||
item->setText(formatHexValue(res.value, 4));
|
||||
else
|
||||
item->setText(formatHexValue(res.value, 8));
|
||||
item->setForeground(Qt::red);
|
||||
}
|
||||
|
||||
row++;
|
||||
if (row == MAX_DISPLAYED_SCAN_RESULTS)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::updateWatch()
|
||||
{
|
||||
m_watch.UpdateValues();
|
||||
|
||||
QSignalBlocker sb(m_ui.watchTable);
|
||||
m_ui.watchTable->setRowCount(0);
|
||||
|
||||
const MemoryWatchList::EntryVector& entries = m_watch.GetEntries();
|
||||
if (!entries.empty())
|
||||
{
|
||||
int row = 0;
|
||||
for (const MemoryWatchList::Entry& res : entries)
|
||||
{
|
||||
m_ui.watchTable->insertRow(row);
|
||||
|
||||
QTableWidgetItem* description_item = new QTableWidgetItem(formatCheatCode(res.address, res.value, res.size));
|
||||
m_ui.watchTable->setItem(row, 0, description_item);
|
||||
|
||||
QTableWidgetItem* address_item = new QTableWidgetItem(formatHexValue(res.address, 8));
|
||||
address_item->setFlags(address_item->flags() & ~(Qt::ItemIsEditable));
|
||||
m_ui.watchTable->setItem(row, 1, address_item);
|
||||
|
||||
QTableWidgetItem* size_item =
|
||||
new QTableWidgetItem(tr(s_size_strings[static_cast<u32>(res.size) + (res.is_signed ? 3 : 0)]));
|
||||
size_item->setFlags(address_item->flags() & ~(Qt::ItemIsEditable));
|
||||
m_ui.watchTable->setItem(row, 2, size_item);
|
||||
|
||||
QTableWidgetItem* value_item;
|
||||
if (res.size == MemoryAccessSize::Byte)
|
||||
value_item = new QTableWidgetItem(formatHexAndDecValue(res.value, 2, res.is_signed));
|
||||
else if (res.size == MemoryAccessSize::HalfWord)
|
||||
value_item = new QTableWidgetItem(formatHexAndDecValue(res.value, 4, res.is_signed));
|
||||
else
|
||||
value_item = new QTableWidgetItem(formatHexAndDecValue(res.value, 8, res.is_signed));
|
||||
|
||||
m_ui.watchTable->setItem(row, 3, value_item);
|
||||
|
||||
QTableWidgetItem* freeze_item = new QTableWidgetItem();
|
||||
freeze_item->setFlags(freeze_item->flags() | (Qt::ItemIsEditable | Qt::ItemIsUserCheckable));
|
||||
freeze_item->setCheckState(res.freeze ? Qt::Checked : Qt::Unchecked);
|
||||
m_ui.watchTable->setItem(row, 4, freeze_item);
|
||||
|
||||
row++;
|
||||
}
|
||||
}
|
||||
|
||||
m_ui.scanSaveWatch->setEnabled(!entries.empty());
|
||||
m_ui.scanRemoveWatch->setEnabled(false);
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::updateWatchValues()
|
||||
{
|
||||
QSignalBlocker sb(m_ui.watchTable);
|
||||
int row = 0;
|
||||
for (const MemoryWatchList::Entry& res : m_watch.GetEntries())
|
||||
{
|
||||
if (res.changed)
|
||||
{
|
||||
if (m_ui.scanValueBase->currentIndex() == 0)
|
||||
m_ui.watchTable->item(row, 3)->setText(formatValue(res.value, res.is_signed));
|
||||
else if (m_scanner.GetSize() == MemoryAccessSize::Byte)
|
||||
m_ui.watchTable->item(row, 3)->setText(formatHexValue(res.value, 2));
|
||||
else if (m_scanner.GetSize() == MemoryAccessSize::HalfWord)
|
||||
m_ui.watchTable->item(row, 3)->setText(formatHexValue(res.value, 4));
|
||||
else
|
||||
m_ui.watchTable->item(row, 3)->setText(formatHexValue(res.value, 8));
|
||||
}
|
||||
row++;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryScannerWindow::updateScanUi()
|
||||
{
|
||||
m_scanner.UpdateResultsValues();
|
||||
m_watch.UpdateValues();
|
||||
|
||||
updateResultsValues();
|
||||
updateWatchValues();
|
||||
}
|
74
src/duckstation-qt/memoryscannerwindow.h
Normal file
74
src/duckstation-qt/memoryscannerwindow.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui_memoryscannerwindow.h"
|
||||
|
||||
#include "core/cheats.h"
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtWidgets/QComboBox>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QPushButton>
|
||||
#include <QtWidgets/QTableWidget>
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <optional>
|
||||
|
||||
class MemoryScannerWindow : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MemoryScannerWindow();
|
||||
~MemoryScannerWindow();
|
||||
|
||||
Q_SIGNALS:
|
||||
void closed();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event);
|
||||
void closeEvent(QCloseEvent* event);
|
||||
void resizeEvent(QResizeEvent* event);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onSystemStarted();
|
||||
void onSystemDestroyed();
|
||||
|
||||
void addToWatchClicked();
|
||||
void addManualWatchAddressClicked();
|
||||
void removeWatchClicked();
|
||||
void scanCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous);
|
||||
void watchCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous);
|
||||
void scanItemChanged(QTableWidgetItem* item);
|
||||
void watchItemChanged(QTableWidgetItem* item);
|
||||
void updateScanValue();
|
||||
void updateScanUi();
|
||||
|
||||
private:
|
||||
enum : int
|
||||
{
|
||||
MAX_DISPLAYED_SCAN_RESULTS = 5000,
|
||||
SCAN_INTERVAL = 100,
|
||||
};
|
||||
|
||||
void connectUi();
|
||||
void enableUi(bool enabled);
|
||||
void resizeColumns();
|
||||
void updateResults();
|
||||
void updateResultsValues();
|
||||
void updateWatch();
|
||||
void updateWatchValues();
|
||||
|
||||
int getSelectedResultIndexFirst() const;
|
||||
int getSelectedResultIndexLast() const;
|
||||
int getSelectedWatchIndexFirst() const;
|
||||
int getSelectedWatchIndexLast() const;
|
||||
|
||||
Ui::MemoryScannerWindow m_ui;
|
||||
|
||||
MemoryScan m_scanner;
|
||||
MemoryWatchList m_watch;
|
||||
|
||||
QTimer* m_update_timer = nullptr;
|
||||
};
|
470
src/duckstation-qt/memoryscannerwindow.ui
Normal file
470
src/duckstation-qt/memoryscannerwindow.ui
Normal file
|
@ -0,0 +1,470 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MemoryScannerWindow</class>
|
||||
<widget class="QWidget" name="MemoryScannerWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>833</width>
|
||||
<height>610</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Memory Scanner</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset>
|
||||
<normaloff>:/icons/duck.png</normaloff>:/icons/duck.png</iconset>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTableWidget" name="scanTable">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SelectionMode::ContiguousSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Address</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Previous Value</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Search Parameters</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Value:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="scanValue"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="scanValueSigned">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Signed</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Unsigned</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="scanValueBase">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Decimal</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Hex</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Data Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="scanSize">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Byte (1 byte)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Halfword (2 bytes)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Word (4 bytes)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Operator:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="scanOperator">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Equal to...</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Not Equal to...</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Greater Than...</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Greater or Equal...</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Less Than...</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Less or Equal...</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Increased By...</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Decreased By...</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Changed By...</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Equal to Previous (Unchanged Value)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Not Equal to Previous (Changed Value)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Greater Than Previous</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Greater or Equal to Previous</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Less Than Previous</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Less or Equal to Previous</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Any Value</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Start Address:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLineEdit" name="scanStartAddress"/>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>End Address:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLineEdit" name="scanEndAddress"/>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Preset Range:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QComboBox" name="scanPresetRange">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>RAM</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Scratchpad</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BIOS</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Result Count:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QLabel" name="scanResultCount">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QPushButton" name="scanNewSearch">
|
||||
<property name="text">
|
||||
<string>New Search</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="scanSearchAgain">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Search Again</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="scanResetSearch">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Clear Results</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="10" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="scanAddWatch">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add Selected Results To Watch List</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0" colspan="2">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTableWidget" name="watchTable">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SelectionMode::ContiguousSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Simple Cheat Code or Description</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Address</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Freeze</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QPushButton" name="scanLoadWatch">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Load Watch</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="scanSaveWatch">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save Watch</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="scanAddManualAddress">
|
||||
<property name="text">
|
||||
<string>Add Manual Address</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="scanRemoveWatch">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove Selected Entries from Watch List</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -64,6 +64,23 @@ QWidget* GetRootWidget(QWidget* widget, bool stop_at_window_or_dialog)
|
|||
return widget;
|
||||
}
|
||||
|
||||
void ShowOrRaiseWindow(QWidget* window)
|
||||
{
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
if (!window->isVisible())
|
||||
{
|
||||
window->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
window->raise();
|
||||
window->activateWindow();
|
||||
window->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ALWAYS_INLINE_RELEASE static void ResizeColumnsForView(T* view, const std::initializer_list<int>& widths)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
@ -11,6 +11,7 @@
|
|||
#include <QtCore/QMetaType>
|
||||
#include <QtCore/QString>
|
||||
#include <QtGui/QIcon>
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <optional>
|
||||
|
@ -46,6 +47,25 @@ QFrame* CreateHorizontalLine(QWidget* parent);
|
|||
/// Returns the greatest parent of a widget, i.e. its dialog/window.
|
||||
QWidget* GetRootWidget(QWidget* widget, bool stop_at_window_or_dialog = true);
|
||||
|
||||
/// Shows or raises a window (brings it to the front).
|
||||
void ShowOrRaiseWindow(QWidget* window);
|
||||
|
||||
/// Closes and deletes a window later, outside of this event pump.
|
||||
template<typename T>
|
||||
[[maybe_unused]] static void CloseAndDeleteWindow(T*& window)
|
||||
{
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
window->close();
|
||||
|
||||
// Some windows delete themselves.
|
||||
if (window)
|
||||
window->deleteLater();
|
||||
|
||||
window = nullptr;
|
||||
}
|
||||
|
||||
/// Resizes columns of the table view to at the specified widths. A negative width will stretch the column to use the
|
||||
/// remaining space.
|
||||
void ResizeColumnsForTableView(QTableView* view, const std::initializer_list<int>& widths);
|
||||
|
|
Loading…
Reference in a new issue