2024-10-02 15:22:12 +00:00
|
|
|
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
|
|
|
// SPDX-License-Identifier: GPL-3.0+
|
|
|
|
|
|
|
|
#include "DebugAnalysisSettingsWidget.h"
|
|
|
|
|
|
|
|
#include "SettingsWindow.h"
|
|
|
|
#include "SettingWidgetBinder.h"
|
|
|
|
|
|
|
|
#include "DebugTools/SymbolImporter.h"
|
|
|
|
|
|
|
|
#include <QtWidgets/QFileDialog>
|
|
|
|
|
|
|
|
DebugAnalysisSettingsWidget::DebugAnalysisSettingsWidget(QWidget* parent)
|
|
|
|
: QWidget(parent)
|
|
|
|
{
|
|
|
|
m_ui.setupUi(this);
|
|
|
|
|
|
|
|
m_ui.automaticallyClearSymbols->setChecked(Host::GetBoolSettingValue("Debugger/Analysis", "AutomaticallySelectSymbolsToClear", true));
|
|
|
|
|
|
|
|
setupSymbolSourceGrid();
|
|
|
|
|
|
|
|
m_ui.importFromElf->setChecked(Host::GetBoolSettingValue("Debugger/Analysis", "ImportSymbolsFromELF", true));
|
|
|
|
m_ui.importSymFileFromDefaultLocation->setChecked(Host::GetBoolSettingValue("Debugger/Analysis", "ImportSymFileFromDefaultLocation", true));
|
|
|
|
m_ui.demangleSymbols->setChecked(Host::GetBoolSettingValue("Debugger/Analysis", "DemangleSymbols", true));
|
|
|
|
m_ui.demangleParameters->setChecked(Host::GetBoolSettingValue("Debugger/Analysis", "DemangleParameters", true));
|
|
|
|
|
|
|
|
setupSymbolFileList();
|
|
|
|
|
|
|
|
std::string function_scan_mode = Host::GetStringSettingValue("Debugger/Analysis", "FunctionScanMode");
|
|
|
|
for (int i = 0;; i++)
|
|
|
|
{
|
|
|
|
if (Pcsx2Config::DebugAnalysisOptions::FunctionScanModeNames[i] == nullptr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (function_scan_mode == Pcsx2Config::DebugAnalysisOptions::FunctionScanModeNames[i])
|
|
|
|
m_ui.functionScanMode->setCurrentIndex(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ui.customAddressRange->setChecked(Host::GetBoolSettingValue("Debugger/Analysis", "CustomFunctionScanRange", false));
|
2024-11-14 22:53:55 +00:00
|
|
|
m_ui.addressRangeStart->setText(QString::fromStdString(Host::GetStringSettingValue("Debugger/Analysis", "FunctionScanStartAddress", "")));
|
|
|
|
m_ui.addressRangeEnd->setText(QString::fromStdString(Host::GetStringSettingValue("Debugger/Analysis", "FunctionScanEndAddress", "")));
|
2024-10-02 15:22:12 +00:00
|
|
|
|
|
|
|
m_ui.grayOutOverwrittenFunctions->setChecked(Host::GetBoolSettingValue("Debugger/Analysis", "GenerateFunctionHashes", true));
|
|
|
|
|
|
|
|
connect(m_ui.automaticallyClearSymbols, &QCheckBox::checkStateChanged, this, &DebugAnalysisSettingsWidget::updateEnabledStates);
|
|
|
|
connect(m_ui.demangleSymbols, &QCheckBox::checkStateChanged, this, &DebugAnalysisSettingsWidget::updateEnabledStates);
|
|
|
|
connect(m_ui.customAddressRange, &QCheckBox::checkStateChanged, this, &DebugAnalysisSettingsWidget::updateEnabledStates);
|
|
|
|
|
|
|
|
updateEnabledStates();
|
|
|
|
}
|
|
|
|
|
|
|
|
DebugAnalysisSettingsWidget::DebugAnalysisSettingsWidget(SettingsWindow* dialog, QWidget* parent)
|
|
|
|
: QWidget(parent)
|
|
|
|
, m_dialog(dialog)
|
|
|
|
{
|
|
|
|
SettingsInterface* sif = dialog->getSettingsInterface();
|
|
|
|
|
|
|
|
m_ui.setupUi(this);
|
|
|
|
|
|
|
|
// Make sure the user doesn't select symbol sources from both the global
|
|
|
|
// settings and the per-game settings, as these settings will conflict with
|
|
|
|
// each other. It only really makes sense to modify these settings on a
|
|
|
|
// per-game basis anyway.
|
|
|
|
if (dialog->isPerGameSettings())
|
|
|
|
{
|
|
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(
|
|
|
|
sif, m_ui.automaticallyClearSymbols, "Debugger/Analysis", "AutomaticallySelectSymbolsToClear", true);
|
|
|
|
|
|
|
|
m_dialog->registerWidgetHelp(m_ui.automaticallyClearSymbols, tr("Automatically Select Symbols To Clear"), tr("Checked"),
|
|
|
|
tr("Automatically delete symbols that were generated by any previous analysis runs."));
|
|
|
|
|
|
|
|
setupSymbolSourceGrid();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_ui.clearExistingSymbolsGroup->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(
|
|
|
|
sif, m_ui.importFromElf, "Debugger/Analysis", "ImportSymbolsFromELF", true);
|
|
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(
|
|
|
|
sif, m_ui.importSymFileFromDefaultLocation, "Debugger/Analysis", "ImportSymFileFromDefaultLocation", true);
|
|
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(
|
|
|
|
sif, m_ui.demangleSymbols, "Debugger/Analysis", "DemangleSymbols", true);
|
|
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(
|
|
|
|
sif, m_ui.demangleParameters, "Debugger/Analysis", "DemangleParameters", true);
|
|
|
|
|
|
|
|
m_dialog->registerWidgetHelp(m_ui.importFromElf, tr("Import From ELF"), tr("Checked"),
|
|
|
|
tr("Import symbol tables stored in the game's boot ELF."));
|
|
|
|
m_dialog->registerWidgetHelp(m_ui.importSymFileFromDefaultLocation, tr("Import Default .sym File"), tr("Checked"),
|
|
|
|
tr("Import symbols from a .sym file with the same name as the loaded ISO file on disk if such a file exists."));
|
|
|
|
m_dialog->registerWidgetHelp(m_ui.demangleSymbols, tr("Demangle Symbols"), tr("Checked"),
|
|
|
|
tr("Demangle C++ symbols during the import process so that the function and global variable names shown in the "
|
|
|
|
"debugger are more readable."));
|
|
|
|
m_dialog->registerWidgetHelp(m_ui.demangleParameters, tr("Demangle Parameters"), tr("Checked"),
|
|
|
|
tr("Include parameter lists in demangled function names."));
|
|
|
|
|
|
|
|
// Same as above. It only makes sense to load extra symbol files on a
|
|
|
|
// per-game basis.
|
|
|
|
if (dialog->isPerGameSettings())
|
|
|
|
{
|
|
|
|
setupSymbolFileList();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_ui.symbolFileLabel->hide();
|
2024-11-12 23:03:04 +00:00
|
|
|
m_ui.symbolFileTable->hide();
|
2024-10-02 15:22:12 +00:00
|
|
|
m_ui.importSymbolFileButtons->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(
|
|
|
|
sif, m_ui.functionScanMode, "Debugger/Analysis", "FunctionScanMode",
|
|
|
|
Pcsx2Config::DebugAnalysisOptions::FunctionScanModeNames, DebugFunctionScanMode::SCAN_ELF);
|
|
|
|
|
|
|
|
m_dialog->registerWidgetHelp(m_ui.functionScanMode, tr("Scan Mode"), tr("Scan ELF"),
|
|
|
|
tr("Choose where the function scanner looks to find functions. This option can be useful if the application "
|
|
|
|
"loads additional code at runtime."));
|
|
|
|
|
|
|
|
// Same as above. It only makes sense to set a custom memory range on a
|
|
|
|
// per-game basis.
|
|
|
|
if (dialog->isPerGameSettings())
|
|
|
|
{
|
|
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(
|
|
|
|
sif, m_ui.customAddressRange, "Debugger/Analysis", "CustomFunctionScanRange", false);
|
|
|
|
connect(m_ui.addressRangeStart, &QLineEdit::textChanged, this, &DebugAnalysisSettingsWidget::functionScanRangeChanged);
|
|
|
|
connect(m_ui.addressRangeEnd, &QLineEdit::textChanged, this, &DebugAnalysisSettingsWidget::functionScanRangeChanged);
|
|
|
|
|
|
|
|
m_dialog->registerWidgetHelp(m_ui.customAddressRange, tr("Custom Address Range"), tr("Unchecked"),
|
|
|
|
tr("Whether to look for functions from the address range specified (Checked), or from the ELF segment "
|
|
|
|
"containing the entry point (Unchecked)."));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_ui.customAddressRange->hide();
|
|
|
|
m_ui.customAddressRangeLineEdits->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(
|
|
|
|
sif, m_ui.grayOutOverwrittenFunctions, "Debugger/Analysis", "GenerateFunctionHashes", true);
|
|
|
|
|
|
|
|
m_dialog->registerWidgetHelp(m_ui.grayOutOverwrittenFunctions, tr("Gray Out Symbols For Overwritten Functions"), tr("Checked"),
|
|
|
|
tr("Generate hashes for all the detected functions, and gray out the symbols displayed in the debugger for "
|
|
|
|
"functions that no longer match."));
|
|
|
|
|
|
|
|
connect(m_ui.automaticallyClearSymbols, &QCheckBox::checkStateChanged, this, &DebugAnalysisSettingsWidget::updateEnabledStates);
|
|
|
|
connect(m_ui.demangleSymbols, &QCheckBox::checkStateChanged, this, &DebugAnalysisSettingsWidget::updateEnabledStates);
|
|
|
|
connect(m_ui.customAddressRange, &QCheckBox::checkStateChanged, this, &DebugAnalysisSettingsWidget::updateEnabledStates);
|
|
|
|
|
|
|
|
updateEnabledStates();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebugAnalysisSettingsWidget::parseSettingsFromWidgets(Pcsx2Config::DebugAnalysisOptions& output)
|
|
|
|
{
|
|
|
|
output.AutomaticallySelectSymbolsToClear = m_ui.automaticallyClearSymbols->isChecked();
|
|
|
|
|
|
|
|
for (const auto& [name, temp] : m_symbol_sources)
|
|
|
|
{
|
|
|
|
DebugSymbolSource& source = output.SymbolSources.emplace_back();
|
|
|
|
source.Name = name;
|
|
|
|
source.ClearDuringAnalysis = temp.check_box->isChecked();
|
|
|
|
}
|
|
|
|
|
|
|
|
output.ImportSymbolsFromELF = m_ui.importFromElf->isChecked();
|
|
|
|
output.ImportSymFileFromDefaultLocation = m_ui.importSymFileFromDefaultLocation->isChecked();
|
|
|
|
output.DemangleSymbols = m_ui.demangleSymbols->isChecked();
|
|
|
|
output.DemangleParameters = m_ui.demangleParameters->isChecked();
|
|
|
|
|
2024-11-12 23:03:04 +00:00
|
|
|
for (int i = 0; i < m_symbol_file_model->rowCount(); i++)
|
2024-10-02 15:22:12 +00:00
|
|
|
{
|
|
|
|
DebugExtraSymbolFile& file = output.ExtraSymbolFiles.emplace_back();
|
2024-11-12 23:03:04 +00:00
|
|
|
|
|
|
|
file.Path = m_symbol_file_model->item(i, PATH_COLUMN)->text().toStdString();
|
|
|
|
file.BaseAddress = m_symbol_file_model->item(i, BASE_ADDRESS_COLUMN)->text().toStdString();
|
|
|
|
file.Condition = m_symbol_file_model->item(i, CONDITION_COLUMN)->text().toStdString();
|
2024-10-02 15:22:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
output.FunctionScanMode = static_cast<DebugFunctionScanMode>(m_ui.functionScanMode->currentIndex());
|
|
|
|
output.CustomFunctionScanRange = m_ui.customAddressRange->isChecked();
|
|
|
|
output.FunctionScanStartAddress = m_ui.addressRangeStart->text().toStdString();
|
|
|
|
output.FunctionScanEndAddress = m_ui.addressRangeEnd->text().toStdString();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebugAnalysisSettingsWidget::setupSymbolSourceGrid()
|
|
|
|
{
|
|
|
|
QGridLayout* layout = new QGridLayout(m_ui.symbolSourceGrid);
|
|
|
|
|
|
|
|
if (!m_dialog || m_dialog->getSerial() == QtHost::GetCurrentGameSerial().toStdString())
|
|
|
|
{
|
|
|
|
// Add symbol sources for which the user has already selected whether or
|
|
|
|
// not they should be cleared.
|
2024-11-12 23:03:04 +00:00
|
|
|
int existing_symbol_source_count = getIntSettingValue("Debugger/Analysis/SymbolSources", "Count", 0);
|
2024-10-02 15:22:12 +00:00
|
|
|
for (int i = 0; i < existing_symbol_source_count; i++)
|
|
|
|
{
|
|
|
|
std::string section = "Debugger/Analysis/SymbolSources/" + std::to_string(i);
|
2024-11-12 23:03:04 +00:00
|
|
|
std::string name = getStringSettingValue(section.c_str(), "Name", "");
|
|
|
|
bool value = getBoolSettingValue(section.c_str(), "ClearDuringAnalysis", false);
|
2024-10-02 15:22:12 +00:00
|
|
|
|
|
|
|
SymbolSourceTemp& source = m_symbol_sources[name];
|
|
|
|
source.previous_value = value;
|
|
|
|
source.modified_by_user = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add any more symbol sources for which the user hasn't made a
|
|
|
|
// selection. These are separate since we don't want to have to store
|
|
|
|
// configuration data for them.
|
|
|
|
R5900SymbolGuardian.Read([&](const ccc::SymbolDatabase& database) {
|
|
|
|
for (const ccc::SymbolSource& symbol_source : database.symbol_sources)
|
|
|
|
{
|
|
|
|
if (m_symbol_sources.find(symbol_source.name()) == m_symbol_sources.end() && symbol_source.name() != "Built-In")
|
|
|
|
{
|
|
|
|
SymbolSourceTemp& source = m_symbol_sources[symbol_source.name()];
|
|
|
|
source.previous_value = SymbolImporter::ShouldClearSymbolsFromSourceByDefault(symbol_source.name());
|
|
|
|
source.modified_by_user = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (m_symbol_sources.empty())
|
|
|
|
{
|
|
|
|
m_ui.symbolSourceErrorMessage->setText(tr("<i>No symbol sources in database.</i>"));
|
|
|
|
m_ui.symbolSourceScrollArea->hide();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the check boxes.
|
|
|
|
int i = 0;
|
|
|
|
for (auto& [name, temp] : m_symbol_sources)
|
|
|
|
{
|
|
|
|
temp.check_box = new QCheckBox(QString::fromStdString(name));
|
|
|
|
temp.check_box->setChecked(temp.previous_value);
|
|
|
|
layout->addWidget(temp.check_box, i / 2, i % 2);
|
|
|
|
|
|
|
|
connect(temp.check_box, &QCheckBox::checkStateChanged, this, &DebugAnalysisSettingsWidget::symbolSourceCheckStateChanged);
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_ui.symbolSourceErrorMessage->setText(tr("<i>Start this game to modify the symbol sources list.</i>"));
|
|
|
|
m_ui.symbolSourceScrollArea->hide();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ui.symbolSourceErrorMessage->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebugAnalysisSettingsWidget::symbolSourceCheckStateChanged()
|
|
|
|
{
|
2024-10-22 00:14:12 +00:00
|
|
|
QCheckBox* check_box = qobject_cast<QCheckBox*>(sender());
|
|
|
|
if (!check_box)
|
2024-10-02 15:22:12 +00:00
|
|
|
return;
|
|
|
|
|
2024-10-22 00:14:12 +00:00
|
|
|
auto temp = m_symbol_sources.find(check_box->text().toStdString());
|
2024-10-02 15:22:12 +00:00
|
|
|
if (temp == m_symbol_sources.end())
|
|
|
|
return;
|
|
|
|
|
|
|
|
temp->second.modified_by_user = true;
|
|
|
|
|
|
|
|
saveSymbolSources();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebugAnalysisSettingsWidget::saveSymbolSources()
|
|
|
|
{
|
|
|
|
if (!m_dialog)
|
|
|
|
return;
|
|
|
|
|
|
|
|
SettingsInterface* sif = m_dialog->getSettingsInterface();
|
|
|
|
if (!sif)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Clean up old configuration entries.
|
|
|
|
int old_count = sif->GetIntValue("Debugger/Analysis/SymbolSources", "Count");
|
|
|
|
for (int i = 0; i < old_count; i++)
|
|
|
|
{
|
|
|
|
std::string section = "Debugger/Analysis/SymbolSources/" + std::to_string(i);
|
|
|
|
sif->RemoveSection(section.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
sif->RemoveSection("Debugger/Analysis/SymbolSources");
|
|
|
|
|
|
|
|
int symbol_sources_to_save = 0;
|
|
|
|
for (auto& [name, temp] : m_symbol_sources)
|
|
|
|
if (temp.modified_by_user)
|
|
|
|
symbol_sources_to_save++;
|
|
|
|
|
|
|
|
if (symbol_sources_to_save == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Make new configuration entries.
|
|
|
|
sif->SetIntValue("Debugger/Analysis/SymbolSources", "Count", symbol_sources_to_save);
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
for (auto& [name, temp] : m_symbol_sources)
|
|
|
|
{
|
|
|
|
if (!temp.modified_by_user)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
std::string section = "Debugger/Analysis/SymbolSources/" + std::to_string(i);
|
2024-10-22 00:14:12 +00:00
|
|
|
sif->SetStringValue(section.c_str(), "Name", name.c_str());
|
2024-10-02 15:22:12 +00:00
|
|
|
sif->SetBoolValue(section.c_str(), "ClearDuringAnalysis", temp.check_box->isChecked());
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebugAnalysisSettingsWidget::setupSymbolFileList()
|
|
|
|
{
|
2024-11-12 23:03:04 +00:00
|
|
|
m_symbol_file_model = new QStandardItemModel(0, SYMBOL_FILE_COLUMN_COUNT, m_ui.symbolFileTable);
|
|
|
|
|
|
|
|
QStringList headers;
|
|
|
|
headers.emplace_back(tr("Path"));
|
|
|
|
headers.emplace_back(tr("Base Address"));
|
|
|
|
headers.emplace_back(tr("Condition"));
|
|
|
|
m_symbol_file_model->setHorizontalHeaderLabels(headers);
|
|
|
|
|
|
|
|
m_ui.symbolFileTable->setModel(m_symbol_file_model);
|
2024-10-02 15:22:12 +00:00
|
|
|
|
2024-11-12 23:03:04 +00:00
|
|
|
m_ui.symbolFileTable->horizontalHeader()->setSectionResizeMode(PATH_COLUMN, QHeaderView::Stretch);
|
|
|
|
m_ui.symbolFileTable->horizontalHeader()->setSectionResizeMode(BASE_ADDRESS_COLUMN, QHeaderView::Fixed);
|
|
|
|
m_ui.symbolFileTable->horizontalHeader()->setSectionResizeMode(CONDITION_COLUMN, QHeaderView::Fixed);
|
|
|
|
|
|
|
|
m_ui.symbolFileTable->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
|
|
|
|
|
|
|
int extra_symbol_file_count = getIntSettingValue("Debugger/Analysis/ExtraSymbolFiles", "Count", 0);
|
2024-10-02 15:22:12 +00:00
|
|
|
for (int i = 0; i < extra_symbol_file_count; i++)
|
|
|
|
{
|
|
|
|
std::string section = "Debugger/Analysis/ExtraSymbolFiles/" + std::to_string(i);
|
|
|
|
|
2024-11-12 23:03:04 +00:00
|
|
|
int row = m_symbol_file_model->rowCount();
|
|
|
|
if (!m_symbol_file_model->insertRow(row))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
QStandardItem* path_item = new QStandardItem();
|
|
|
|
path_item->setText(QString::fromStdString(getStringSettingValue(section.c_str(), "Path", "")));
|
|
|
|
m_symbol_file_model->setItem(row, PATH_COLUMN, path_item);
|
|
|
|
|
|
|
|
QStandardItem* base_address_item = new QStandardItem();
|
|
|
|
base_address_item->setText(QString::fromStdString(getStringSettingValue(section.c_str(), "BaseAddress")));
|
|
|
|
m_symbol_file_model->setItem(row, BASE_ADDRESS_COLUMN, base_address_item);
|
|
|
|
|
|
|
|
QStandardItem* condition_item = new QStandardItem();
|
|
|
|
condition_item->setText(QString::fromStdString(getStringSettingValue(section.c_str(), "Condition")));
|
|
|
|
m_symbol_file_model->setItem(row, CONDITION_COLUMN, condition_item);
|
2024-10-02 15:22:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
connect(m_ui.addSymbolFile, &QPushButton::clicked, this, &DebugAnalysisSettingsWidget::addSymbolFile);
|
|
|
|
connect(m_ui.removeSymbolFile, &QPushButton::clicked, this, &DebugAnalysisSettingsWidget::removeSymbolFile);
|
2024-11-12 23:03:04 +00:00
|
|
|
|
|
|
|
connect(m_ui.symbolFileTable->selectionModel(), &QItemSelectionModel::selectionChanged,
|
|
|
|
this, &DebugAnalysisSettingsWidget::updateEnabledStates);
|
|
|
|
|
|
|
|
connect(m_symbol_file_model, &QStandardItemModel::dataChanged,
|
|
|
|
this, &DebugAnalysisSettingsWidget::saveSymbolFiles);
|
|
|
|
connect(m_symbol_file_model, &QStandardItemModel::dataChanged,
|
|
|
|
this, &DebugAnalysisSettingsWidget::updateEnabledStates);
|
2024-10-02 15:22:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DebugAnalysisSettingsWidget::addSymbolFile()
|
|
|
|
{
|
2024-11-12 23:03:04 +00:00
|
|
|
std::string path = Path::ToNativePath(QFileDialog::getOpenFileName(this, tr("Add Symbol File")).toStdString());
|
|
|
|
if (path.empty())
|
2024-10-02 15:22:12 +00:00
|
|
|
return;
|
|
|
|
|
2024-11-12 23:03:04 +00:00
|
|
|
std::string relative_path = Path::MakeRelative(path, EmuFolders::GameSettings);
|
|
|
|
if (!relative_path.starts_with(".."))
|
|
|
|
path = std::move(relative_path);
|
|
|
|
|
|
|
|
int row = m_symbol_file_model->rowCount();
|
|
|
|
if (!m_symbol_file_model->insertRow(row))
|
|
|
|
return;
|
|
|
|
|
|
|
|
QStandardItem* path_item = new QStandardItem();
|
|
|
|
path_item->setText(QString::fromStdString(path));
|
|
|
|
m_symbol_file_model->setItem(row, PATH_COLUMN, path_item);
|
|
|
|
|
|
|
|
QStandardItem* base_address_item = new QStandardItem();
|
|
|
|
base_address_item->setText("");
|
|
|
|
m_symbol_file_model->setItem(row, BASE_ADDRESS_COLUMN, base_address_item);
|
|
|
|
|
|
|
|
QStandardItem* condition_item = new QStandardItem();
|
|
|
|
condition_item->setText("");
|
|
|
|
m_symbol_file_model->setItem(row, CONDITION_COLUMN, condition_item);
|
2024-10-02 15:22:12 +00:00
|
|
|
|
|
|
|
saveSymbolFiles();
|
2024-11-12 23:03:04 +00:00
|
|
|
updateEnabledStates();
|
2024-10-02 15:22:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DebugAnalysisSettingsWidget::removeSymbolFile()
|
|
|
|
{
|
2024-11-12 23:03:04 +00:00
|
|
|
QItemSelectionModel* selection_model = m_ui.symbolFileTable->selectionModel();
|
|
|
|
if (!selection_model)
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (!selection_model->selectedIndexes().isEmpty())
|
|
|
|
{
|
|
|
|
QModelIndex index = selection_model->selectedIndexes().first();
|
|
|
|
m_symbol_file_model->removeRow(index.row(), index.parent());
|
|
|
|
}
|
2024-10-02 15:22:12 +00:00
|
|
|
|
|
|
|
saveSymbolFiles();
|
2024-11-12 23:03:04 +00:00
|
|
|
updateEnabledStates();
|
2024-10-02 15:22:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DebugAnalysisSettingsWidget::saveSymbolFiles()
|
|
|
|
{
|
|
|
|
if (!m_dialog)
|
|
|
|
return;
|
|
|
|
|
|
|
|
SettingsInterface* sif = m_dialog->getSettingsInterface();
|
|
|
|
if (!sif)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Clean up old configuration entries.
|
|
|
|
int old_count = sif->GetIntValue("Debugger/Analysis/ExtraSymbolFiles", "Count");
|
|
|
|
for (int i = 0; i < old_count; i++)
|
|
|
|
{
|
|
|
|
std::string section = "Debugger/Analysis/ExtraSymbolFiles/" + std::to_string(i);
|
|
|
|
sif->RemoveSection(section.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
sif->RemoveSection("Debugger/Analysis/ExtraSymbolFiles");
|
|
|
|
|
2024-11-12 23:03:04 +00:00
|
|
|
if (m_symbol_file_model->rowCount() == 0)
|
2024-10-02 15:22:12 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Make new configuration entries.
|
2024-11-12 23:03:04 +00:00
|
|
|
sif->SetIntValue("Debugger/Analysis/ExtraSymbolFiles", "Count", m_symbol_file_model->rowCount());
|
2024-10-02 15:22:12 +00:00
|
|
|
|
2024-11-12 23:03:04 +00:00
|
|
|
for (int i = 0; i < m_symbol_file_model->rowCount(); i++)
|
2024-10-02 15:22:12 +00:00
|
|
|
{
|
|
|
|
std::string section = "Debugger/Analysis/ExtraSymbolFiles/" + std::to_string(i);
|
2024-11-12 23:03:04 +00:00
|
|
|
|
|
|
|
if (QStandardItem* path_item = m_symbol_file_model->item(i, PATH_COLUMN))
|
|
|
|
sif->SetStringValue(section.c_str(), "Path", path_item->text().toStdString().c_str());
|
|
|
|
|
|
|
|
if (QStandardItem* base_address_item = m_symbol_file_model->item(i, BASE_ADDRESS_COLUMN))
|
|
|
|
sif->SetStringValue(section.c_str(), "BaseAddress", base_address_item->text().toStdString().c_str());
|
|
|
|
|
|
|
|
if (QStandardItem* condition_item = m_symbol_file_model->item(i, CONDITION_COLUMN))
|
|
|
|
sif->SetStringValue(section.c_str(), "Condition", condition_item->text().toStdString().c_str());
|
2024-10-02 15:22:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QtHost::SaveGameSettings(sif, true);
|
|
|
|
g_emu_thread->reloadGameSettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebugAnalysisSettingsWidget::functionScanRangeChanged()
|
|
|
|
{
|
|
|
|
if (!m_dialog)
|
|
|
|
return;
|
|
|
|
|
|
|
|
SettingsInterface* sif = m_dialog->getSettingsInterface();
|
|
|
|
if (!sif)
|
|
|
|
return;
|
|
|
|
|
|
|
|
QString start_address = m_ui.addressRangeStart->text();
|
|
|
|
QString end_address = m_ui.addressRangeEnd->text();
|
|
|
|
|
|
|
|
bool ok;
|
|
|
|
|
|
|
|
if (start_address.toUInt(&ok, 16), ok)
|
|
|
|
sif->SetStringValue("Debugger/Analysis", "FunctionScanStartAddress", start_address.toStdString().c_str());
|
|
|
|
|
|
|
|
if (end_address.toUInt(&ok, 16), ok)
|
|
|
|
sif->SetStringValue("Debugger/Analysis", "FunctionScanEndAddress", end_address.toStdString().c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebugAnalysisSettingsWidget::updateEnabledStates()
|
|
|
|
{
|
|
|
|
m_ui.symbolSourceScrollArea->setEnabled(!m_ui.automaticallyClearSymbols->isChecked());
|
|
|
|
m_ui.symbolSourceErrorMessage->setEnabled(!m_ui.automaticallyClearSymbols->isChecked());
|
|
|
|
m_ui.demangleParameters->setEnabled(m_ui.demangleSymbols->isChecked());
|
2024-11-12 23:03:04 +00:00
|
|
|
m_ui.removeSymbolFile->setEnabled(
|
|
|
|
m_ui.symbolFileTable->selectionModel() && m_ui.symbolFileTable->selectionModel()->hasSelection());
|
2024-10-02 15:22:12 +00:00
|
|
|
m_ui.customAddressRangeLineEdits->setEnabled(m_ui.customAddressRange->isChecked());
|
|
|
|
}
|
2024-11-12 23:03:04 +00:00
|
|
|
|
|
|
|
std::string DebugAnalysisSettingsWidget::getStringSettingValue(
|
|
|
|
const char* section, const char* key, const char* default_value)
|
|
|
|
{
|
|
|
|
if (m_dialog)
|
|
|
|
return m_dialog->getEffectiveStringValue(section, key, default_value);
|
|
|
|
|
|
|
|
return Host::GetStringSettingValue(section, key, default_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DebugAnalysisSettingsWidget::getBoolSettingValue(
|
|
|
|
const char* section, const char* key, bool default_value)
|
|
|
|
{
|
|
|
|
if (m_dialog)
|
|
|
|
return m_dialog->getEffectiveBoolValue(section, key, default_value);
|
|
|
|
|
|
|
|
return Host::GetBoolSettingValue(section, key, default_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
int DebugAnalysisSettingsWidget::getIntSettingValue(
|
|
|
|
const char* section, const char* key, int default_value)
|
|
|
|
{
|
|
|
|
if (m_dialog)
|
|
|
|
return m_dialog->getEffectiveIntValue(section, key, default_value);
|
|
|
|
|
|
|
|
return Host::GetIntSettingValue(section, key, default_value);
|
|
|
|
}
|