mirror of https://github.com/PCSX2/pcsx2.git
Debugger: Adds loading breakpoints/saved addresses from settings
Adds `/inis/debuggersettings/` settings folder to contain settings specifically for the debugger. Adds functionality to manually save (to settings) Breakpoints/Saved addresses and automatically load them upon launching the debugger.
This commit is contained in:
parent
b402f6a404
commit
52ccc609cd
|
@ -80,6 +80,7 @@ oprofile_data/
|
||||||
/bin/gamesettings
|
/bin/gamesettings
|
||||||
/bin/help
|
/bin/help
|
||||||
/bin/inis
|
/bin/inis
|
||||||
|
/bin/inis/debuggersettings
|
||||||
/bin/logs
|
/bin/logs
|
||||||
/bin/memcards
|
/bin/memcards
|
||||||
/bin/plugins
|
/bin/plugins
|
||||||
|
|
|
@ -149,6 +149,8 @@ target_sources(pcsx2-qt PRIVATE
|
||||||
Debugger/CpuWidget.cpp
|
Debugger/CpuWidget.cpp
|
||||||
Debugger/CpuWidget.h
|
Debugger/CpuWidget.h
|
||||||
Debugger/CpuWidget.ui
|
Debugger/CpuWidget.ui
|
||||||
|
Debugger/DebuggerSettingsManager.cpp
|
||||||
|
Debugger/DebuggerSettingsManager.h
|
||||||
Debugger/DebuggerWindow.cpp
|
Debugger/DebuggerWindow.cpp
|
||||||
Debugger/DebuggerWindow.h
|
Debugger/DebuggerWindow.h
|
||||||
Debugger/DebuggerWindow.ui
|
Debugger/DebuggerWindow.ui
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "Models/BreakpointModel.h"
|
#include "Models/BreakpointModel.h"
|
||||||
#include "Models/ThreadModel.h"
|
#include "Models/ThreadModel.h"
|
||||||
#include "Models/SavedAddressesModel.h"
|
#include "Models/SavedAddressesModel.h"
|
||||||
|
#include "Debugger/DebuggerSettingsManager.h"
|
||||||
|
|
||||||
#include "DebugTools/DebugInterface.h"
|
#include "DebugTools/DebugInterface.h"
|
||||||
#include "DebugTools/Breakpoints.h"
|
#include "DebugTools/Breakpoints.h"
|
||||||
|
@ -45,6 +46,15 @@ CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
|
||||||
m_ui.setupUi(this);
|
m_ui.setupUi(this);
|
||||||
|
|
||||||
connect(g_emu_thread, &EmuThread::onVMPaused, this, &CpuWidget::onVMPaused);
|
connect(g_emu_thread, &EmuThread::onVMPaused, this, &CpuWidget::onVMPaused);
|
||||||
|
connect(g_emu_thread, &EmuThread::onGameChanged, [this](const QString& title) {
|
||||||
|
if (title.isEmpty())
|
||||||
|
return;
|
||||||
|
// Don't overwrite users BPs/Saved Addresses unless they have a clean state.
|
||||||
|
if (m_bpModel.rowCount() == 0)
|
||||||
|
DebuggerSettingsManager::loadGameSettings(&m_bpModel);
|
||||||
|
if (m_savedAddressesModel.rowCount() == 0)
|
||||||
|
DebuggerSettingsManager::loadGameSettings(&m_savedAddressesModel);
|
||||||
|
});
|
||||||
|
|
||||||
connect(m_ui.registerWidget, &RegisterWidget::gotoInDisasm, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddress);
|
connect(m_ui.registerWidget, &RegisterWidget::gotoInDisasm, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddress);
|
||||||
connect(m_ui.memoryviewWidget, &MemoryViewWidget::gotoInDisasm, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddress);
|
connect(m_ui.memoryviewWidget, &MemoryViewWidget::gotoInDisasm, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddress);
|
||||||
|
@ -146,6 +156,9 @@ CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
|
||||||
connect(m_ui.savedAddressesList->model(), &QAbstractItemModel::dataChanged, [savedAddressesTableView](const QModelIndex& topLeft) {
|
connect(m_ui.savedAddressesList->model(), &QAbstractItemModel::dataChanged, [savedAddressesTableView](const QModelIndex& topLeft) {
|
||||||
savedAddressesTableView->resizeColumnToContents(topLeft.column());
|
savedAddressesTableView->resizeColumnToContents(topLeft.column());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
DebuggerSettingsManager::loadGameSettings(&m_bpModel);
|
||||||
|
DebuggerSettingsManager::loadGameSettings(&m_savedAddressesModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
CpuWidget::~CpuWidget() = default;
|
CpuWidget::~CpuWidget() = default;
|
||||||
|
@ -301,10 +314,9 @@ void CpuWidget::onBPListDoubleClicked(const QModelIndex& index)
|
||||||
|
|
||||||
void CpuWidget::onBPListContextMenu(QPoint pos)
|
void CpuWidget::onBPListContextMenu(QPoint pos)
|
||||||
{
|
{
|
||||||
if (!m_cpu.isAlive())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QMenu* contextMenu = new QMenu(tr("Breakpoint List Context Menu"), m_ui.breakpointList);
|
QMenu* contextMenu = new QMenu(tr("Breakpoint List Context Menu"), m_ui.breakpointList);
|
||||||
|
if (m_cpu.isAlive())
|
||||||
|
{
|
||||||
|
|
||||||
QAction* newAction = new QAction(tr("New"), m_ui.breakpointList);
|
QAction* newAction = new QAction(tr("New"), m_ui.breakpointList);
|
||||||
connect(newAction, &QAction::triggered, this, &CpuWidget::contextBPListNew);
|
connect(newAction, &QAction::triggered, this, &CpuWidget::contextBPListNew);
|
||||||
|
@ -329,19 +341,37 @@ void CpuWidget::onBPListContextMenu(QPoint pos)
|
||||||
connect(deleteAction, &QAction::triggered, this, &CpuWidget::contextBPListDelete);
|
connect(deleteAction, &QAction::triggered, this, &CpuWidget::contextBPListDelete);
|
||||||
contextMenu->addAction(deleteAction);
|
contextMenu->addAction(deleteAction);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
contextMenu->addSeparator();
|
contextMenu->addSeparator();
|
||||||
|
if (m_bpModel.rowCount() > 0)
|
||||||
|
{
|
||||||
QAction* actionExport = new QAction(tr("Copy all as CSV"), m_ui.breakpointList);
|
QAction* actionExport = new QAction(tr("Copy all as CSV"), m_ui.breakpointList);
|
||||||
connect(actionExport, &QAction::triggered, [this]() {
|
connect(actionExport, &QAction::triggered, [this]() {
|
||||||
// It's important to use the Export Role here to allow pasting to be translation agnostic
|
// It's important to use the Export Role here to allow pasting to be translation agnostic
|
||||||
QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.breakpointList->model(), BreakpointModel::ExportRole, true));
|
QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.breakpointList->model(), BreakpointModel::ExportRole, true));
|
||||||
});
|
});
|
||||||
contextMenu->addAction(actionExport);
|
contextMenu->addAction(actionExport);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_cpu.isAlive())
|
||||||
|
{
|
||||||
QAction* actionImport = new QAction(tr("Paste from CSV"), m_ui.breakpointList);
|
QAction* actionImport = new QAction(tr("Paste from CSV"), m_ui.breakpointList);
|
||||||
connect(actionImport, &QAction::triggered, this, &CpuWidget::contextBPListPasteCSV);
|
connect(actionImport, &QAction::triggered, this, &CpuWidget::contextBPListPasteCSV);
|
||||||
contextMenu->addAction(actionImport);
|
contextMenu->addAction(actionImport);
|
||||||
|
|
||||||
|
QAction* actionLoad = new QAction(tr("Load from Settings"), m_ui.breakpointList);
|
||||||
|
connect(actionLoad, &QAction::triggered, [this]() {
|
||||||
|
m_bpModel.clear();
|
||||||
|
DebuggerSettingsManager::loadGameSettings(&m_bpModel);
|
||||||
|
});
|
||||||
|
contextMenu->addAction(actionLoad);
|
||||||
|
|
||||||
|
QAction* actionSave = new QAction(tr("Save to Settings"), m_ui.breakpointList);
|
||||||
|
connect(actionSave, &QAction::triggered, this, &CpuWidget::saveBreakpointsToDebuggerSettings);
|
||||||
|
contextMenu->addAction(actionSave);
|
||||||
|
}
|
||||||
|
|
||||||
contextMenu->popup(m_ui.breakpointList->viewport()->mapToGlobal(pos));
|
contextMenu->popup(m_ui.breakpointList->viewport()->mapToGlobal(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,98 +445,7 @@ void CpuWidget::contextBPListPasteCSV()
|
||||||
QString matchedValue = match.captured(0);
|
QString matchedValue = match.captured(0);
|
||||||
fields << matchedValue.mid(1, matchedValue.length() - 2);
|
fields << matchedValue.mid(1, matchedValue.length() - 2);
|
||||||
}
|
}
|
||||||
|
m_bpModel.loadBreakpointFromFieldList(fields);
|
||||||
if (fields.size() != BreakpointModel::BreakpointColumns::COLUMN_COUNT)
|
|
||||||
{
|
|
||||||
Console.WriteLn("Debugger CSV Import: Invalid number of columns, skipping");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ok;
|
|
||||||
const int type = fields[BreakpointModel::BreakpointColumns::TYPE].toUInt(&ok);
|
|
||||||
if (!ok)
|
|
||||||
{
|
|
||||||
Console.WriteLn("Debugger CSV Import: Failed to parse type '%s', skipping", fields[BreakpointModel::BreakpointColumns::TYPE].toUtf8().constData());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is how we differentiate between breakpoints and memchecks
|
|
||||||
if (type == MEMCHECK_INVALID)
|
|
||||||
{
|
|
||||||
BreakPoint bp;
|
|
||||||
|
|
||||||
// Address
|
|
||||||
bp.addr = fields[BreakpointModel::BreakpointColumns::OFFSET].toUInt(&ok, 16);
|
|
||||||
if (!ok)
|
|
||||||
{
|
|
||||||
Console.WriteLn("Debugger CSV Import: Failed to parse address '%s', skipping", fields[BreakpointModel::BreakpointColumns::OFFSET].toUtf8().constData());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Condition
|
|
||||||
if (!fields[BreakpointModel::BreakpointColumns::CONDITION].isEmpty())
|
|
||||||
{
|
|
||||||
PostfixExpression expr;
|
|
||||||
bp.hasCond = true;
|
|
||||||
bp.cond.debug = &m_cpu;
|
|
||||||
|
|
||||||
if (!m_cpu.initExpression(fields[BreakpointModel::BreakpointColumns::CONDITION].toUtf8().constData(), expr))
|
|
||||||
{
|
|
||||||
Console.WriteLn("Debugger CSV Import: Failed to parse cond '%s', skipping", fields[BreakpointModel::BreakpointColumns::CONDITION].toUtf8().constData());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
bp.cond.expression = expr;
|
|
||||||
strncpy(&bp.cond.expressionString[0], fields[BreakpointModel::BreakpointColumns::CONDITION].toUtf8().constData(), sizeof(bp.cond.expressionString));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enabled
|
|
||||||
bp.enabled = fields[BreakpointModel::BreakpointColumns::ENABLED].toUInt(&ok);
|
|
||||||
if (!ok)
|
|
||||||
{
|
|
||||||
Console.WriteLn("Debugger CSV Import: Failed to parse enable flag '%s', skipping", fields[BreakpointModel::BreakpointColumns::ENABLED].toUtf8().constData());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_bpModel.insertBreakpointRows(0, 1, {bp});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MemCheck mc;
|
|
||||||
// Mode
|
|
||||||
if (type >= MEMCHECK_INVALID)
|
|
||||||
{
|
|
||||||
Console.WriteLn("Debugger CSV Import: Failed to parse cond type '%s', skipping", fields [BreakpointModel::BreakpointColumns::TYPE].toUtf8().constData());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mc.cond = static_cast<MemCheckCondition>(type);
|
|
||||||
|
|
||||||
// Address
|
|
||||||
mc.start = fields[BreakpointModel::BreakpointColumns::OFFSET].toUInt(&ok, 16);
|
|
||||||
if (!ok)
|
|
||||||
{
|
|
||||||
Console.WriteLn("Debugger CSV Import: Failed to parse address '%s', skipping", fields[BreakpointModel::BreakpointColumns::OFFSET].toUtf8().constData());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size
|
|
||||||
mc.end = fields[BreakpointModel::BreakpointColumns::SIZE_LABEL].toUInt(&ok) + mc.start;
|
|
||||||
if (!ok)
|
|
||||||
{
|
|
||||||
Console.WriteLn("Debugger CSV Import: Failed to parse length '%s', skipping", fields[BreakpointModel::BreakpointColumns::SIZE_LABEL].toUtf8().constData());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Result
|
|
||||||
const int result = fields [BreakpointModel::BreakpointColumns::ENABLED].toUInt(&ok);
|
|
||||||
if (!ok)
|
|
||||||
{
|
|
||||||
Console.WriteLn("Debugger CSV Import: Failed to parse result flag '%s', skipping", fields [BreakpointModel::BreakpointColumns::ENABLED].toUtf8().constData());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mc.result = static_cast<MemCheckResult>(result);
|
|
||||||
|
|
||||||
m_bpModel.insertBreakpointRows(0, 1, {mc});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -561,7 +500,19 @@ void CpuWidget::onSavedAddressesListContextMenu(QPoint pos)
|
||||||
connect(actionImportCSV, &QAction::triggered, this, &CpuWidget::contextSavedAddressesListPasteCSV);
|
connect(actionImportCSV, &QAction::triggered, this, &CpuWidget::contextSavedAddressesListPasteCSV);
|
||||||
contextMenu->addAction(actionImportCSV);
|
contextMenu->addAction(actionImportCSV);
|
||||||
|
|
||||||
contextMenu->popup(m_ui.savedAddressesList->viewport()->mapToGlobal(pos));
|
if (m_cpu.isAlive())
|
||||||
|
{
|
||||||
|
QAction* actionLoad = new QAction(tr("Load from Settings"), m_ui.savedAddressesList);
|
||||||
|
connect(actionLoad, &QAction::triggered, [this]() {
|
||||||
|
m_savedAddressesModel.clear();
|
||||||
|
DebuggerSettingsManager::loadGameSettings(&m_savedAddressesModel);
|
||||||
|
});
|
||||||
|
contextMenu->addAction(actionLoad);
|
||||||
|
|
||||||
|
QAction* actionSave = new QAction(tr("Save to Settings"), m_ui.savedAddressesList);
|
||||||
|
connect(actionSave, &QAction::triggered, this, &CpuWidget::saveSavedAddressesToDebuggerSettings);
|
||||||
|
contextMenu->addAction(actionSave);
|
||||||
|
}
|
||||||
|
|
||||||
if (isIndexValid)
|
if (isIndexValid)
|
||||||
{
|
{
|
||||||
|
@ -571,6 +522,8 @@ void CpuWidget::onSavedAddressesListContextMenu(QPoint pos)
|
||||||
});
|
});
|
||||||
contextMenu->addAction(deleteAction);
|
contextMenu->addAction(deleteAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contextMenu->popup(m_ui.savedAddressesList->viewport()->mapToGlobal(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CpuWidget::contextSavedAddressesListPasteCSV()
|
void CpuWidget::contextSavedAddressesListPasteCSV()
|
||||||
|
@ -594,24 +547,7 @@ void CpuWidget::contextSavedAddressesListPasteCSV()
|
||||||
fields << matchedValue.mid(1, matchedValue.length() - 2);
|
fields << matchedValue.mid(1, matchedValue.length() - 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields.size() != SavedAddressesModel::HeaderColumns::COLUMN_COUNT)
|
m_savedAddressesModel.loadSavedAddressFromFieldList(fields);
|
||||||
{
|
|
||||||
Console.WriteLn("Debugger CSV Import: Invalid number of columns, skipping");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ok;
|
|
||||||
const u32 address = fields[SavedAddressesModel::HeaderColumns::ADDRESS].toUInt(&ok, 16);
|
|
||||||
if (!ok)
|
|
||||||
{
|
|
||||||
Console.WriteLn("Debugger CSV Import: Failed to parse address '%s', skipping", fields[SavedAddressesModel::HeaderColumns::ADDRESS].toUtf8().constData());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString label = fields[SavedAddressesModel::HeaderColumns::LABEL];
|
|
||||||
const QString description = fields[SavedAddressesModel::HeaderColumns::DESCRIPTION];
|
|
||||||
const SavedAddressesModel::SavedAddress importedAddress = {address, label, description};
|
|
||||||
m_savedAddressesModel.addRow(importedAddress);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1392,3 +1328,12 @@ void CpuWidget::loadSearchResults()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CpuWidget::saveBreakpointsToDebuggerSettings()
|
||||||
|
{
|
||||||
|
DebuggerSettingsManager::saveGameSettings(&m_bpModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CpuWidget::saveSavedAddressesToDebuggerSettings()
|
||||||
|
{
|
||||||
|
DebuggerSettingsManager::saveGameSettings(&m_savedAddressesModel);
|
||||||
|
}
|
||||||
|
|
|
@ -118,6 +118,9 @@ public slots:
|
||||||
void contextRemoveSearchResult();
|
void contextRemoveSearchResult();
|
||||||
void onListSearchResultsContextMenu(QPoint pos);
|
void onListSearchResultsContextMenu(QPoint pos);
|
||||||
|
|
||||||
|
void saveBreakpointsToDebuggerSettings();
|
||||||
|
void saveSavedAddressesToDebuggerSettings();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<QTableWidget*> m_registerTableViews;
|
std::vector<QTableWidget*> m_registerTableViews;
|
||||||
std::vector<u32> m_searchResults;
|
std::vector<u32> m_searchResults;
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0+
|
||||||
|
|
||||||
|
#include "DebuggerSettingsManager.h"
|
||||||
|
|
||||||
|
#include <QtCore/QJsonDocument>
|
||||||
|
#include <QtCore/QJsonObject>
|
||||||
|
#include <QtCore/QJsonArray>
|
||||||
|
#include <QtCore/QFile>
|
||||||
|
|
||||||
|
#include "common/Console.h"
|
||||||
|
#include "fmt/core.h"
|
||||||
|
#include "VMManager.h"
|
||||||
|
#include "Models/BreakpointModel.h"
|
||||||
|
|
||||||
|
std::mutex DebuggerSettingsManager::writeLock;
|
||||||
|
const QString DebuggerSettingsManager::settingsFileVersion = "0.00";
|
||||||
|
|
||||||
|
QJsonObject DebuggerSettingsManager::loadGameSettingsJSON() {
|
||||||
|
std::string path = VMManager::GetDebuggerSettingsFilePathForCurrentGame();
|
||||||
|
QFile file(QString::fromStdString(path));
|
||||||
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
|
{
|
||||||
|
Console.WriteLnFmt("Debugger Settings Manager: No Debugger Settings file found for game at: '{}'", path);
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
QByteArray fileContent = file.readAll();
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
const QJsonDocument jsonDoc(QJsonDocument::fromJson(fileContent));
|
||||||
|
if (jsonDoc.isNull() || !jsonDoc.isObject())
|
||||||
|
{
|
||||||
|
Console.WriteLnFmt("Debugger Settings Manager: Failed to load contents of settings file for file at: '{}'", path);
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonDoc.object();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebuggerSettingsManager::writeJSONToPath(std::string path, QJsonDocument jsonDocument)
|
||||||
|
{
|
||||||
|
QFile file(QString::fromStdString(path));
|
||||||
|
if (!file.open(QIODevice::WriteOnly))
|
||||||
|
{
|
||||||
|
Console.WriteLnFmt("Debugger Settings Manager: Failed to write Debugger Settings file to path: '{}'", path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
file.write(jsonDocument.toJson(QJsonDocument::Indented));
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebuggerSettingsManager::loadGameSettings(BreakpointModel* bpModel)
|
||||||
|
{
|
||||||
|
const std::string path = VMManager::GetDebuggerSettingsFilePathForCurrentGame();
|
||||||
|
if (path.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QJsonValue breakpointsValue = loadGameSettingsJSON().value("Breakpoints");
|
||||||
|
const QString valueToLoad = breakpointsValue.toString();
|
||||||
|
if (breakpointsValue.isUndefined() || !breakpointsValue.isArray())
|
||||||
|
{
|
||||||
|
Console.WriteLnFmt("Debugger Settings Manager: Failed to read Breakpoints array from settings file: '{}'", path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QJsonArray breakpointsArray = breakpointsValue.toArray();
|
||||||
|
for (u32 row = 0; row < breakpointsArray.size(); row++)
|
||||||
|
{
|
||||||
|
const QJsonValue rowValue = breakpointsArray.at(row);
|
||||||
|
if (rowValue.isUndefined() || !rowValue.isObject())
|
||||||
|
{
|
||||||
|
Console.WriteLn("Debugger Settings Manager: Failed to load invalid Breakpoint object.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const QJsonObject rowObject = rowValue.toObject();
|
||||||
|
|
||||||
|
QStringList fields;
|
||||||
|
u32 col = 0;
|
||||||
|
for (auto iter = rowObject.begin(); iter != rowObject.end(); iter++, col++)
|
||||||
|
{
|
||||||
|
QString headerColKey = bpModel->headerData(col, Qt::Horizontal, Qt::UserRole).toString();
|
||||||
|
fields << rowObject.value(headerColKey).toString();
|
||||||
|
}
|
||||||
|
bpModel->loadBreakpointFromFieldList(fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebuggerSettingsManager::loadGameSettings(SavedAddressesModel* savedAddressesModel)
|
||||||
|
{
|
||||||
|
const std::string path = VMManager::GetDebuggerSettingsFilePathForCurrentGame();
|
||||||
|
if (path.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QJsonValue savedAddressesValue = loadGameSettingsJSON().value("SavedAddresses");
|
||||||
|
QString valueToLoad = savedAddressesValue.toString();
|
||||||
|
if (savedAddressesValue.isUndefined() || !savedAddressesValue.isArray())
|
||||||
|
{
|
||||||
|
Console.WriteLnFmt("Debugger Settings Manager: Failed to read Saved Addresses array from settings file: '{}'", path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QJsonArray breakpointsArray = savedAddressesValue.toArray();
|
||||||
|
|
||||||
|
for (u32 row = 0; row < breakpointsArray.size(); row++)
|
||||||
|
{
|
||||||
|
const QJsonValue rowValue = breakpointsArray.at(row);
|
||||||
|
if (rowValue.isUndefined() || !rowValue.isObject())
|
||||||
|
{
|
||||||
|
Console.WriteLn("Debugger Settings Manager: Failed to load invalid Breakpoint object.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const QJsonObject rowObject = rowValue.toObject();
|
||||||
|
QStringList fields;
|
||||||
|
u32 col = 0;
|
||||||
|
for (auto iter = rowObject.begin(); iter != rowObject.end(); iter++, col++)
|
||||||
|
{
|
||||||
|
QString headerColKey = savedAddressesModel->headerData(col, Qt::Horizontal, Qt::UserRole).toString();
|
||||||
|
fields << rowObject.value(headerColKey).toString();
|
||||||
|
}
|
||||||
|
savedAddressesModel->loadSavedAddressFromFieldList(fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebuggerSettingsManager::saveGameSettings(BreakpointModel* bpModel)
|
||||||
|
{
|
||||||
|
saveGameSettings(bpModel, "Breakpoints", BreakpointModel::ExportRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebuggerSettingsManager::saveGameSettings(SavedAddressesModel* savedAddressesModel)
|
||||||
|
{
|
||||||
|
saveGameSettings(savedAddressesModel, "SavedAddresses", Qt::DisplayRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebuggerSettingsManager::saveGameSettings(QAbstractTableModel* abstractTableModel, QString settingsKey, u32 role)
|
||||||
|
{
|
||||||
|
const std::string path = VMManager::GetDebuggerSettingsFilePathForCurrentGame();
|
||||||
|
if (path.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const std::lock_guard<std::mutex> lock(writeLock);
|
||||||
|
QJsonObject loadedSettings = loadGameSettingsJSON();
|
||||||
|
QJsonArray rowsArray;
|
||||||
|
QStringList keys;
|
||||||
|
for (u32 col = 0; col < abstractTableModel->columnCount(); ++col)
|
||||||
|
{
|
||||||
|
keys << abstractTableModel->headerData(col, Qt::Horizontal, Qt::UserRole).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 row = 0; row < abstractTableModel->rowCount(); row++)
|
||||||
|
{
|
||||||
|
QJsonObject rowObject;
|
||||||
|
for (u32 col = 0; col < abstractTableModel->columnCount(); col++)
|
||||||
|
{
|
||||||
|
const QModelIndex index = abstractTableModel->index(row, col);
|
||||||
|
const QString data = abstractTableModel->data(index, role).toString();
|
||||||
|
rowObject.insert(keys[col], QJsonValue::fromVariant(data));
|
||||||
|
}
|
||||||
|
rowsArray.append(rowObject);
|
||||||
|
}
|
||||||
|
loadedSettings.insert(settingsKey, rowsArray);
|
||||||
|
loadedSettings.insert("Version", settingsFileVersion);
|
||||||
|
QJsonDocument doc(loadedSettings);
|
||||||
|
writeJSONToPath(path, doc);
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0+
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <QtWidgets/QDialog>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "Models/BreakpointModel.h"
|
||||||
|
#include "Models/SavedAddressesModel.h"
|
||||||
|
|
||||||
|
class DebuggerSettingsManager final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DebuggerSettingsManager(QWidget* parent = nullptr);
|
||||||
|
~DebuggerSettingsManager();
|
||||||
|
|
||||||
|
static void loadGameSettings(BreakpointModel* bpModel);
|
||||||
|
static void loadGameSettings(SavedAddressesModel* savedAddressesModel);
|
||||||
|
static void saveGameSettings(BreakpointModel* bpModel);
|
||||||
|
static void saveGameSettings(SavedAddressesModel* savedAddressesModel);
|
||||||
|
static void saveGameSettings(QAbstractTableModel* abstractTableModel, QString settingsKey, u32 role);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::mutex writeLock;
|
||||||
|
static void writeJSONToPath(std::string path, QJsonDocument jsonDocument);
|
||||||
|
static QJsonObject loadGameSettingsJSON();
|
||||||
|
const static QString settingsFileVersion;
|
||||||
|
};
|
|
@ -6,6 +6,7 @@
|
||||||
#include "DebugTools/DebugInterface.h"
|
#include "DebugTools/DebugInterface.h"
|
||||||
#include "DebugTools/Breakpoints.h"
|
#include "DebugTools/Breakpoints.h"
|
||||||
#include "DebugTools/DisassemblyManager.h"
|
#include "DebugTools/DisassemblyManager.h"
|
||||||
|
#include "common/Console.h"
|
||||||
|
|
||||||
#include "QtHost.h"
|
#include "QtHost.h"
|
||||||
#include "QtUtils.h"
|
#include "QtUtils.h"
|
||||||
|
@ -226,6 +227,28 @@ QVariant BreakpointModel::headerData(int section, Qt::Orientation orientation, i
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (role == Qt::UserRole && orientation == Qt::Horizontal)
|
||||||
|
{
|
||||||
|
switch (section)
|
||||||
|
{
|
||||||
|
case BreakpointColumns::TYPE:
|
||||||
|
return "TYPE";
|
||||||
|
case BreakpointColumns::OFFSET:
|
||||||
|
return "OFFSET";
|
||||||
|
case BreakpointColumns::SIZE_LABEL:
|
||||||
|
return "SIZE / LABEL";
|
||||||
|
case BreakpointColumns::OPCODE:
|
||||||
|
return "INSTRUCTION";
|
||||||
|
case BreakpointColumns::CONDITION:
|
||||||
|
return "CONDITION";
|
||||||
|
case BreakpointColumns::HITS:
|
||||||
|
return "HITS";
|
||||||
|
case BreakpointColumns::ENABLED:
|
||||||
|
return "X";
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,3 +425,106 @@ void BreakpointModel::refreshData()
|
||||||
|
|
||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BreakpointModel::loadBreakpointFromFieldList(QStringList fields)
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
if (fields.size() != BreakpointModel::BreakpointColumns::COLUMN_COUNT)
|
||||||
|
{
|
||||||
|
Console.WriteLn("Debugger Breakpoint Model: Invalid number of columns, skipping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int type = fields[BreakpointModel::BreakpointColumns::TYPE].toUInt(&ok);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
Console.WriteLn("Debugger Breakpoint Model: Failed to parse type '%s', skipping", fields[BreakpointModel::BreakpointColumns::TYPE].toUtf8().constData());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is how we differentiate between breakpoints and memchecks
|
||||||
|
if (type == MEMCHECK_INVALID)
|
||||||
|
{
|
||||||
|
BreakPoint bp;
|
||||||
|
|
||||||
|
// Address
|
||||||
|
bp.addr = fields[BreakpointModel::BreakpointColumns::OFFSET].toUInt(&ok, 16);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
Console.WriteLn("Debugger Breakpoint Model: Failed to parse address '%s', skipping", fields[BreakpointModel::BreakpointColumns::OFFSET].toUtf8().constData());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Condition
|
||||||
|
if (!fields[BreakpointModel::BreakpointColumns::CONDITION].isEmpty())
|
||||||
|
{
|
||||||
|
PostfixExpression expr;
|
||||||
|
bp.hasCond = true;
|
||||||
|
bp.cond.debug = &m_cpu;
|
||||||
|
|
||||||
|
if (!m_cpu.initExpression(fields[BreakpointModel::BreakpointColumns::CONDITION].toUtf8().constData(), expr))
|
||||||
|
{
|
||||||
|
Console.WriteLn("Debugger Breakpoint Model: Failed to parse cond '%s', skipping", fields[BreakpointModel::BreakpointColumns::CONDITION].toUtf8().constData());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bp.cond.expression = expr;
|
||||||
|
strncpy(&bp.cond.expressionString[0], fields[BreakpointModel::BreakpointColumns::CONDITION].toUtf8().constData(), sizeof(bp.cond.expressionString));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled
|
||||||
|
bp.enabled = fields[BreakpointModel::BreakpointColumns::ENABLED].toUInt(&ok);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
Console.WriteLn("Debugger Breakpoint Model: Failed to parse enable flag '%s', skipping", fields[BreakpointModel::BreakpointColumns::ENABLED].toUtf8().constData());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
insertBreakpointRows(0, 1, {bp});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MemCheck mc;
|
||||||
|
// Mode
|
||||||
|
if (type >= MEMCHECK_INVALID)
|
||||||
|
{
|
||||||
|
Console.WriteLn("Debugger Breakpoint Model: Failed to parse cond type '%s', skipping", fields[BreakpointModel::BreakpointColumns::TYPE].toUtf8().constData());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mc.cond = static_cast<MemCheckCondition>(type);
|
||||||
|
|
||||||
|
// Address
|
||||||
|
QString test = fields[BreakpointModel::BreakpointColumns::OFFSET];
|
||||||
|
mc.start = fields[BreakpointModel::BreakpointColumns::OFFSET].toUInt(&ok, 16);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
Console.WriteLn("Debugger Breakpoint Model: Failed to parse address '%s', skipping", fields[BreakpointModel::BreakpointColumns::OFFSET].toUtf8().constData());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size
|
||||||
|
mc.end = fields[BreakpointModel::BreakpointColumns::SIZE_LABEL].toUInt(&ok) + mc.start;
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
Console.WriteLn("Debugger Breakpoint Model: Failed to parse length '%s', skipping", fields[BreakpointModel::BreakpointColumns::SIZE_LABEL].toUtf8().constData());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result
|
||||||
|
const int result = fields[BreakpointModel::BreakpointColumns::ENABLED].toUInt(&ok);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
Console.WriteLn("Debugger Breakpoint Model: Failed to parse result flag '%s', skipping", fields[BreakpointModel::BreakpointColumns::ENABLED].toUtf8().constData());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mc.result = static_cast<MemCheckResult>(result);
|
||||||
|
|
||||||
|
insertBreakpointRows(0, 1, {mc});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakpointModel::clear()
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_breakpoints.clear();
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
|
@ -55,10 +55,12 @@ public:
|
||||||
bool setData(const QModelIndex& index, const QVariant& value, int role) override;
|
bool setData(const QModelIndex& index, const QVariant& value, int role) override;
|
||||||
bool removeRows(int row, int count, const QModelIndex& index = QModelIndex()) override;
|
bool removeRows(int row, int count, const QModelIndex& index = QModelIndex()) override;
|
||||||
bool insertBreakpointRows(int row, int count, std::vector<BreakpointMemcheck> breakpoints, const QModelIndex& index = QModelIndex());
|
bool insertBreakpointRows(int row, int count, std::vector<BreakpointMemcheck> breakpoints, const QModelIndex& index = QModelIndex());
|
||||||
|
void loadBreakpointFromFieldList(QStringList breakpointFields);
|
||||||
|
|
||||||
BreakpointMemcheck at(int row) const { return m_breakpoints.at(row); };
|
BreakpointMemcheck at(int row) const { return m_breakpoints.at(row); };
|
||||||
|
|
||||||
void refreshData();
|
void refreshData();
|
||||||
|
void clear();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DebugInterface& m_cpu;
|
DebugInterface& m_cpu;
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include "PrecompiledHeader.h"
|
#include "PrecompiledHeader.h"
|
||||||
#include "SavedAddressesModel.h"
|
#include "SavedAddressesModel.h"
|
||||||
|
|
||||||
|
#include "common/Console.h"
|
||||||
|
|
||||||
SavedAddressesModel::SavedAddressesModel(DebugInterface& cpu, QObject* parent)
|
SavedAddressesModel::SavedAddressesModel(DebugInterface& cpu, QObject* parent)
|
||||||
: QAbstractTableModel(parent)
|
: QAbstractTableModel(parent)
|
||||||
, m_cpu(cpu)
|
, m_cpu(cpu)
|
||||||
|
@ -96,9 +98,11 @@ bool SavedAddressesModel::setData(const QModelIndex& index, const QVariant& valu
|
||||||
|
|
||||||
QVariant SavedAddressesModel::headerData(int section, Qt::Orientation orientation, int role) const
|
QVariant SavedAddressesModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
{
|
{
|
||||||
if ((role != Qt::DisplayRole && role != Qt::EditRole) || orientation != Qt::Horizontal)
|
if (orientation != Qt::Horizontal)
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
||||||
|
if (role == Qt::DisplayRole)
|
||||||
|
{
|
||||||
switch (section)
|
switch (section)
|
||||||
{
|
{
|
||||||
case SavedAddressesModel::ADDRESS:
|
case SavedAddressesModel::ADDRESS:
|
||||||
|
@ -110,6 +114,22 @@ QVariant SavedAddressesModel::headerData(int section, Qt::Orientation orientatio
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (role == Qt::UserRole)
|
||||||
|
{
|
||||||
|
switch (section)
|
||||||
|
{
|
||||||
|
case SavedAddressesModel::ADDRESS:
|
||||||
|
return "MEMORY ADDRESS";
|
||||||
|
case SavedAddressesModel::LABEL:
|
||||||
|
return "LABEL";
|
||||||
|
case SavedAddressesModel::DESCRIPTION:
|
||||||
|
return "DESCRIPTION";
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
Qt::ItemFlags SavedAddressesModel::flags(const QModelIndex& index) const
|
Qt::ItemFlags SavedAddressesModel::flags(const QModelIndex& index) const
|
||||||
|
@ -150,3 +170,32 @@ int SavedAddressesModel::columnCount(const QModelIndex&) const
|
||||||
{
|
{
|
||||||
return HeaderColumns::COLUMN_COUNT;
|
return HeaderColumns::COLUMN_COUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SavedAddressesModel::loadSavedAddressFromFieldList(QStringList fields)
|
||||||
|
{
|
||||||
|
if (fields.size() != SavedAddressesModel::HeaderColumns::COLUMN_COUNT)
|
||||||
|
{
|
||||||
|
Console.WriteLn("Debugger Saved Addresses Model: Invalid number of columns, skipping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
const u32 address = fields[SavedAddressesModel::HeaderColumns::ADDRESS].toUInt(&ok, 16);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
Console.WriteLn("Debugger Saved Addresses Model: Failed to parse address '%s', skipping", fields[SavedAddressesModel::HeaderColumns::ADDRESS].toUtf8().constData());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString label = fields[SavedAddressesModel::HeaderColumns::LABEL];
|
||||||
|
const QString description = fields[SavedAddressesModel::HeaderColumns::DESCRIPTION];
|
||||||
|
const SavedAddressesModel::SavedAddress importedAddress = {address, label, description};
|
||||||
|
addRow(importedAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SavedAddressesModel::clear()
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_savedAddresses.clear();
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ public:
|
||||||
QString description;
|
QString description;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum HeaderColumns : int
|
enum HeaderColumns: int
|
||||||
{
|
{
|
||||||
ADDRESS = 0,
|
ADDRESS = 0,
|
||||||
LABEL,
|
LABEL,
|
||||||
|
@ -45,6 +45,8 @@ public:
|
||||||
void addRow(SavedAddress addresstoSave);
|
void addRow(SavedAddress addresstoSave);
|
||||||
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
|
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
|
||||||
bool setData(const QModelIndex& index, const QVariant& value, int role) override;
|
bool setData(const QModelIndex& index, const QVariant& value, int role) override;
|
||||||
|
void loadSavedAddressFromFieldList(QStringList fields);
|
||||||
|
void clear();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DebugInterface& m_cpu;
|
DebugInterface& m_cpu;
|
||||||
|
|
|
@ -106,6 +106,7 @@
|
||||||
<ClCompile Include="Debugger\MemoryViewWidget.cpp" />
|
<ClCompile Include="Debugger\MemoryViewWidget.cpp" />
|
||||||
<ClCompile Include="Debugger\RegisterWidget.cpp" />
|
<ClCompile Include="Debugger\RegisterWidget.cpp" />
|
||||||
<ClCompile Include="Debugger\BreakpointDialog.cpp" />
|
<ClCompile Include="Debugger\BreakpointDialog.cpp" />
|
||||||
|
<ClCompile Include="Debugger\DebuggerSettingsManager.cpp" />
|
||||||
<ClCompile Include="Debugger\Models\BreakpointModel.cpp" />
|
<ClCompile Include="Debugger\Models\BreakpointModel.cpp" />
|
||||||
<ClCompile Include="Debugger\Models\ThreadModel.cpp" />
|
<ClCompile Include="Debugger\Models\ThreadModel.cpp" />
|
||||||
<ClCompile Include="Debugger\Models\StackModel.cpp" />
|
<ClCompile Include="Debugger\Models\StackModel.cpp" />
|
||||||
|
@ -196,6 +197,7 @@
|
||||||
<QtMoc Include="Debugger\MemoryViewWidget.h" />
|
<QtMoc Include="Debugger\MemoryViewWidget.h" />
|
||||||
<QtMoc Include="Debugger\RegisterWidget.h" />
|
<QtMoc Include="Debugger\RegisterWidget.h" />
|
||||||
<QtMoc Include="Debugger\BreakpointDialog.h" />
|
<QtMoc Include="Debugger\BreakpointDialog.h" />
|
||||||
|
<ClInclude Include="Debugger\DebuggerSettingsManager.h" />
|
||||||
<QtMoc Include="Debugger\Models\BreakpointModel.h" />
|
<QtMoc Include="Debugger\Models\BreakpointModel.h" />
|
||||||
<QtMoc Include="Debugger\Models\ThreadModel.h" />
|
<QtMoc Include="Debugger\Models\ThreadModel.h" />
|
||||||
<QtMoc Include="Debugger\Models\StackModel.h" />
|
<QtMoc Include="Debugger\Models\StackModel.h" />
|
||||||
|
|
|
@ -346,6 +346,9 @@
|
||||||
<ClCompile Include="$(IntDir)moc_LogWindow.cpp">
|
<ClCompile Include="$(IntDir)moc_LogWindow.cpp">
|
||||||
<Filter>moc</Filter>
|
<Filter>moc</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="Debugger\DebuggerSettingsManager.cpp">
|
||||||
|
<Filter>Debugger</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Manifest Include="..\pcsx2\windows\PCSX2.manifest">
|
<Manifest Include="..\pcsx2\windows\PCSX2.manifest">
|
||||||
|
@ -365,6 +368,9 @@
|
||||||
<ClInclude Include="Settings\MemoryCardConvertWorker.h">
|
<ClInclude Include="Settings\MemoryCardConvertWorker.h">
|
||||||
<Filter>Settings</Filter>
|
<Filter>Settings</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="Debugger\DebuggerSettingsManager.h">
|
||||||
|
<Filter>Debugger</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<QtMoc Include="MainWindow.h" />
|
<QtMoc Include="MainWindow.h" />
|
||||||
|
|
|
@ -1210,6 +1210,7 @@ namespace EmuFolders
|
||||||
extern std::string AppRoot;
|
extern std::string AppRoot;
|
||||||
extern std::string DataRoot;
|
extern std::string DataRoot;
|
||||||
extern std::string Settings;
|
extern std::string Settings;
|
||||||
|
extern std::string DebuggerSettings;
|
||||||
extern std::string Bios;
|
extern std::string Bios;
|
||||||
extern std::string Snapshots;
|
extern std::string Snapshots;
|
||||||
extern std::string Savestates;
|
extern std::string Savestates;
|
||||||
|
|
|
@ -150,6 +150,7 @@ namespace EmuFolders
|
||||||
std::string AppRoot;
|
std::string AppRoot;
|
||||||
std::string DataRoot;
|
std::string DataRoot;
|
||||||
std::string Settings;
|
std::string Settings;
|
||||||
|
std::string DebuggerSettings;
|
||||||
std::string Bios;
|
std::string Bios;
|
||||||
std::string Snapshots;
|
std::string Snapshots;
|
||||||
std::string Savestates;
|
std::string Savestates;
|
||||||
|
@ -2004,6 +2005,7 @@ void EmuFolders::LoadConfig(SettingsInterface& si)
|
||||||
Textures = LoadPathFromSettings(si, DataRoot, "Textures", "textures");
|
Textures = LoadPathFromSettings(si, DataRoot, "Textures", "textures");
|
||||||
InputProfiles = LoadPathFromSettings(si, DataRoot, "InputProfiles", "inputprofiles");
|
InputProfiles = LoadPathFromSettings(si, DataRoot, "InputProfiles", "inputprofiles");
|
||||||
Videos = LoadPathFromSettings(si, DataRoot, "Videos", "videos");
|
Videos = LoadPathFromSettings(si, DataRoot, "Videos", "videos");
|
||||||
|
DebuggerSettings = LoadPathFromSettings(si, Settings, "DebuggerSettings", "debuggersettings");
|
||||||
|
|
||||||
Console.WriteLn("BIOS Directory: %s", Bios.c_str());
|
Console.WriteLn("BIOS Directory: %s", Bios.c_str());
|
||||||
Console.WriteLn("Snapshots Directory: %s", Snapshots.c_str());
|
Console.WriteLn("Snapshots Directory: %s", Snapshots.c_str());
|
||||||
|
@ -2020,6 +2022,7 @@ void EmuFolders::LoadConfig(SettingsInterface& si)
|
||||||
Console.WriteLn("Textures Directory: %s", Textures.c_str());
|
Console.WriteLn("Textures Directory: %s", Textures.c_str());
|
||||||
Console.WriteLn("Input Profile Directory: %s", InputProfiles.c_str());
|
Console.WriteLn("Input Profile Directory: %s", InputProfiles.c_str());
|
||||||
Console.WriteLn("Video Dumping Directory: %s", Videos.c_str());
|
Console.WriteLn("Video Dumping Directory: %s", Videos.c_str());
|
||||||
|
Console.WriteLn("Debugger Settings Directory: %s", DebuggerSettings.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EmuFolders::EnsureFoldersExist()
|
bool EmuFolders::EnsureFoldersExist()
|
||||||
|
@ -2035,6 +2038,7 @@ bool EmuFolders::EnsureFoldersExist()
|
||||||
result = FileSystem::CreateDirectoryPath(Covers.c_str(), false) && result;
|
result = FileSystem::CreateDirectoryPath(Covers.c_str(), false) && result;
|
||||||
result = FileSystem::CreateDirectoryPath(GameSettings.c_str(), false) && result;
|
result = FileSystem::CreateDirectoryPath(GameSettings.c_str(), false) && result;
|
||||||
result = FileSystem::CreateDirectoryPath(UserResources.c_str(), false) && result;
|
result = FileSystem::CreateDirectoryPath(UserResources.c_str(), false) && result;
|
||||||
|
result = FileSystem::CreateDirectoryPath(DebuggerSettings.c_str(), false) && result;
|
||||||
result = FileSystem::CreateDirectoryPath(Cache.c_str(), false) && result;
|
result = FileSystem::CreateDirectoryPath(Cache.c_str(), false) && result;
|
||||||
result = FileSystem::CreateDirectoryPath(Textures.c_str(), false) && result;
|
result = FileSystem::CreateDirectoryPath(Textures.c_str(), false) && result;
|
||||||
result = FileSystem::CreateDirectoryPath(InputProfiles.c_str(), false) && result;
|
result = FileSystem::CreateDirectoryPath(InputProfiles.c_str(), false) && result;
|
||||||
|
|
|
@ -729,6 +729,22 @@ std::string VMManager::GetInputProfilePath(const std::string_view& name)
|
||||||
return Path::Combine(EmuFolders::InputProfiles, fmt::format("{}.ini", name));
|
return Path::Combine(EmuFolders::InputProfiles, fmt::format("{}.ini", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string VMManager::GetDebuggerSettingsFilePath(const std::string_view& game_serial, u32 game_crc)
|
||||||
|
{
|
||||||
|
std::string path;
|
||||||
|
if (!game_serial.empty() && game_crc != 0)
|
||||||
|
{
|
||||||
|
auto lock = Host::GetSettingsLock();
|
||||||
|
return Path::Combine(EmuFolders::DebuggerSettings, fmt::format("{}_{:08X}.json", game_serial, game_crc));
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string VMManager::GetDebuggerSettingsFilePathForCurrentGame()
|
||||||
|
{
|
||||||
|
return GetDebuggerSettingsFilePath(s_disc_serial, s_current_crc);
|
||||||
|
}
|
||||||
|
|
||||||
void VMManager::Internal::UpdateEmuFolders()
|
void VMManager::Internal::UpdateEmuFolders()
|
||||||
{
|
{
|
||||||
const std::string old_cheats_directory(EmuFolders::Cheats);
|
const std::string old_cheats_directory(EmuFolders::Cheats);
|
||||||
|
|
|
@ -196,6 +196,12 @@ namespace VMManager
|
||||||
/// Returns the path for the input profile ini file with the specified name (may not exist).
|
/// Returns the path for the input profile ini file with the specified name (may not exist).
|
||||||
std::string GetInputProfilePath(const std::string_view& name);
|
std::string GetInputProfilePath(const std::string_view& name);
|
||||||
|
|
||||||
|
/// Returns the path for the debugger settings json file for the specified game serial and CRC.
|
||||||
|
std::string GetDebuggerSettingsFilePath(const std::string_view& game_serial, u32 game_crc);
|
||||||
|
|
||||||
|
/// Returns the path for the debugger settings json file for the current game.
|
||||||
|
std::string GetDebuggerSettingsFilePathForCurrentGame();
|
||||||
|
|
||||||
/// Resizes the render window to the display size, with an optional scale.
|
/// Resizes the render window to the display size, with an optional scale.
|
||||||
/// If the scale is set to 0, the internal resolution will be used, otherwise it is treated as a multiplier to 1x.
|
/// If the scale is set to 0, the internal resolution will be used, otherwise it is treated as a multiplier to 1x.
|
||||||
void RequestDisplaySize(float scale = 0.0f);
|
void RequestDisplaySize(float scale = 0.0f);
|
||||||
|
|
Loading…
Reference in New Issue