From 0826586f96c7de2b590f3420376f88bc8d18eb0c Mon Sep 17 00:00:00 2001 From: TryTwo Date: Mon, 30 Oct 2023 17:30:10 -0700 Subject: [PATCH] CodeDiffDialog: Add saving/loading function finder results. --- .../DolphinQt/Debugger/CodeDiffDialog.cpp | 166 ++++++++++++++++-- .../Core/DolphinQt/Debugger/CodeDiffDialog.h | 17 +- 2 files changed, 168 insertions(+), 15 deletions(-) diff --git a/Source/Core/DolphinQt/Debugger/CodeDiffDialog.cpp b/Source/Core/DolphinQt/Debugger/CodeDiffDialog.cpp index 6698a8289e..8eab6fd712 100644 --- a/Source/Core/DolphinQt/Debugger/CodeDiffDialog.cpp +++ b/Source/Core/DolphinQt/Debugger/CodeDiffDialog.cpp @@ -4,9 +4,11 @@ #include "DolphinQt/Debugger/CodeDiffDialog.h" #include +#include #include #include +#include #include #include #include @@ -16,7 +18,11 @@ #include #include +#include "Common/FileUtil.h" +#include "Common/IOFile.h" +#include "Common/MsgHandler.h" #include "Common/StringUtil.h" +#include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/Debugger/PPCDebugInterface.h" #include "Core/HW/CPU.h" @@ -55,12 +61,15 @@ void CodeDiffDialog::reject() void CodeDiffDialog::CreateWidgets() { + bool running = Core::GetState() != Core::State::Uninitialized; + auto* btns_layout = new QGridLayout; m_exclude_btn = new QPushButton(tr("Code did not get executed")); m_include_btn = new QPushButton(tr("Code has been executed")); m_record_btn = new QPushButton(tr("Start Recording")); m_record_btn->setCheckable(true); m_record_btn->setStyleSheet(RECORD_BUTTON_STYLESHEET); + m_record_btn->setEnabled(running); m_exclude_btn->setEnabled(false); m_include_btn->setEnabled(false); @@ -89,19 +98,30 @@ void CodeDiffDialog::CreateWidgets() m_matching_results_table->setColumnWidth(3, 210); m_matching_results_table->setColumnWidth(4, 65); m_matching_results_table->setCornerButtonEnabled(false); + m_autosave_check = new QCheckBox(tr("Auto Save")); + m_save_btn = new QPushButton(tr("Save")); + m_save_btn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_save_btn->setEnabled(running); + m_load_btn = new QPushButton(tr("Load")); + m_load_btn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_load_btn->setEnabled(running); m_reset_btn = new QPushButton(tr("Reset All")); m_reset_btn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_help_btn = new QPushButton(tr("Help")); m_help_btn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - auto* help_reset_layout = new QHBoxLayout; - help_reset_layout->addWidget(m_reset_btn, 0, Qt::AlignLeft); - help_reset_layout->addWidget(m_help_btn, 0, Qt::AlignRight); + auto* bottom_controls_layout = new QHBoxLayout; + bottom_controls_layout->addWidget(m_reset_btn, 0, Qt::AlignLeft); + bottom_controls_layout->addStretch(); + bottom_controls_layout->addWidget(m_autosave_check, 0, Qt::AlignRight); + bottom_controls_layout->addWidget(m_save_btn, 0, Qt::AlignRight); + bottom_controls_layout->addWidget(m_load_btn, 0, Qt::AlignRight); + bottom_controls_layout->addWidget(m_help_btn, 0, Qt::AlignRight); auto* layout = new QVBoxLayout(); layout->addLayout(btns_layout); layout->addLayout(labels_layout); layout->addWidget(m_matching_results_table); - layout->addLayout(help_reset_layout); + layout->addLayout(bottom_controls_layout); setLayout(layout); resize(515, 400); @@ -115,11 +135,14 @@ void CodeDiffDialog::ConnectWidgets() m_record_btn->setStyleSheet(RECORD_BUTTON_STYLESHEET); }); #endif - + connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, + [this](Core::State state) { UpdateButtons(state != Core::State::Uninitialized); }); connect(m_record_btn, &QPushButton::toggled, this, &CodeDiffDialog::OnRecord); - connect(m_include_btn, &QPushButton::pressed, [this]() { Update(true); }); - connect(m_exclude_btn, &QPushButton::pressed, [this]() { Update(false); }); + connect(m_include_btn, &QPushButton::pressed, [this]() { Update(UpdateType::Include); }); + connect(m_exclude_btn, &QPushButton::pressed, [this]() { Update(UpdateType::Exclude); }); connect(m_matching_results_table, &QTableWidget::itemClicked, [this]() { OnClickItem(); }); + connect(m_save_btn, &QPushButton::pressed, this, &CodeDiffDialog::SaveDataBackup); + connect(m_load_btn, &QPushButton::pressed, this, &CodeDiffDialog::LoadDataBackup); connect(m_reset_btn, &QPushButton::pressed, this, &CodeDiffDialog::ClearData); connect(m_help_btn, &QPushButton::pressed, this, &CodeDiffDialog::InfoDisp); connect(m_matching_results_table, &CodeDiffDialog::customContextMenuRequested, this, @@ -133,6 +156,103 @@ void CodeDiffDialog::OnClickItem() m_code_widget->SetAddress(address, CodeViewWidget::SetAddressUpdate::WithDetailedUpdate); } +void CodeDiffDialog::SaveDataBackup() +{ + if (Core::GetState() == Core::State::Uninitialized) + { + ModalMessageBox::information(this, tr("Code Diff Tool"), + tr("Emulation must be started before saving a file.")); + return; + } + + if (m_include.empty()) + return; + + std::string filename = + File::GetUserPath(D_LOGS_IDX) + SConfig::GetInstance().GetGameID() + "_CodeDiff.txt"; + File::IOFile f(filename, "w"); + if (!f) + { + ModalMessageBox::information( + this, tr("Code Diff Tool"), + tr("Failed to save file to: %1").arg(QString::fromStdString(filename))); + return; + } + + // Copy list of BLR tested functions: + std::set address_blr; + for (int i = 0; i < m_matching_results_table->rowCount(); i++) + { + if (m_matching_results_table->item(i, 4)->text() == QStringLiteral("X")) + address_blr.insert(m_matching_results_table->item(i, 4)->data(Qt::UserRole).toUInt()); + } + + for (const auto& line : m_include) + { + bool blr = address_blr.contains(line.addr); + f.WriteString( + fmt::format("{} {} {} {:d} {}\n", line.addr, line.hits, line.total_hits, blr, line.symbol)); + } +} + +void CodeDiffDialog::LoadDataBackup() +{ + if (Core::GetState() == Core::State::Uninitialized) + { + ModalMessageBox::information(this, tr("Code Diff Tool"), + tr("Emulation must be started before loading a file.")); + return; + } + + if (g_symbolDB.IsEmpty()) + { + ModalMessageBox::warning( + this, tr("Code Diff Tool"), + tr("Symbol map not found.\n\nIf one does not exist, you can generate one from " + "the Menu bar:\nSymbols -> Generate Symbols From ->\n\tAddress | Signature " + "Database | RSO Modules")); + return; + } + + std::string filename = + File::GetUserPath(D_LOGS_IDX) + SConfig::GetInstance().GetGameID() + "_CodeDiff.txt"; + File::IOFile f(filename, "r"); + if (!f) + { + ModalMessageBox::information( + this, tr("Code Diff Tool"), + tr("Failed to find or open file: %1").arg(QString::fromStdString(filename))); + return; + }; + + ClearData(); + + std::set blr_addresses; + char line[512]; + while (fgets(line, 512, f.GetHandle())) + { + bool blr = false; + Diff temp; + std::istringstream iss(line); + iss.imbue(std::locale::classic()); + iss >> temp.addr >> temp.hits >> temp.total_hits >> blr >> std::ws; + std::getline(iss, temp.symbol); + + if (blr) + blr_addresses.insert(temp.addr); + + m_include.push_back(std::move(temp)); + } + + Update(UpdateType::Backup); + + for (int i = 0; i < m_matching_results_table->rowCount(); i++) + { + if (blr_addresses.contains(m_matching_results_table->item(i, 4)->data(Qt::UserRole).toUInt())) + MarkRowBLR(i); + } +} + void CodeDiffDialog::ClearData() { if (m_record_btn->isChecked()) @@ -344,7 +464,7 @@ void CodeDiffDialog::RemoveMatchingSymbolsFromIncludes(const std::vector& m_include.end()); } -void CodeDiffDialog::Update(bool include) +void CodeDiffDialog::Update(UpdateType type) { // Wrap everything in a pause Core::State old_state = Core::GetState(); @@ -352,15 +472,18 @@ void CodeDiffDialog::Update(bool include) Core::SetState(Core::State::Paused); // Main process - if (include) + if (type == UpdateType::Include) { OnInclude(); } - else + else if (type == UpdateType::Exclude) { OnExclude(); } + if (type != UpdateType::Backup && m_autosave_check->isChecked() && !m_include.empty()) + SaveDataBackup(); + const auto create_item = [](const QString& string = {}, const u32 address = 0x00000000) { QTableWidgetItem* item = new QTableWidgetItem(string); item->setData(Qt::UserRole, address); @@ -428,7 +551,9 @@ void CodeDiffDialog::InfoDisp() "list " "and any includes left over will be displayed.\nYou can continue to use " "'Code did not get executed'/'Code has been executed' to narrow down the " - "results.")); + "results.\n\n" + "Saving will store the current list in Dolphin's Log folder (File -> Open User " + "Folder)")); ModalMessageBox::information( this, tr("Code Diff Tool Help"), tr("Example:\n" @@ -497,21 +622,27 @@ void CodeDiffDialog::OnSetBLR() if (!symbol) return; + MarkRowBLR(item->row()); + if (m_autosave_check->isChecked()) + SaveDataBackup(); + { auto& system = Core::System::GetInstance(); Core::CPUThreadGuard guard(system); system.GetPowerPC().GetDebugInterface().SetPatch(guard, symbol->address, 0x4E800020); } - int row = item->row(); + m_code_widget->Update(); +} + +void CodeDiffDialog::MarkRowBLR(int row) +{ m_matching_results_table->item(row, 0)->setForeground(QBrush(Qt::red)); m_matching_results_table->item(row, 1)->setForeground(QBrush(Qt::red)); m_matching_results_table->item(row, 2)->setForeground(QBrush(Qt::red)); m_matching_results_table->item(row, 3)->setForeground(QBrush(Qt::red)); m_matching_results_table->item(row, 4)->setForeground(QBrush(Qt::red)); m_matching_results_table->item(row, 4)->setText(QStringLiteral("X")); - - m_code_widget->Update(); } void CodeDiffDialog::UpdateItem() @@ -533,3 +664,10 @@ void CodeDiffDialog::UpdateItem() QString::fromStdString(symbolName).replace(QStringLiteral("\t"), QStringLiteral(" ")); m_matching_results_table->item(row, 3)->setText(newName); } + +void CodeDiffDialog::UpdateButtons(bool running) +{ + m_save_btn->setEnabled(running); + m_load_btn->setEnabled(running); + m_record_btn->setEnabled(running); +} diff --git a/Source/Core/DolphinQt/Debugger/CodeDiffDialog.h b/Source/Core/DolphinQt/Debugger/CodeDiffDialog.h index 3278359afe..c22c63e1ad 100644 --- a/Source/Core/DolphinQt/Debugger/CodeDiffDialog.h +++ b/Source/Core/DolphinQt/Debugger/CodeDiffDialog.h @@ -11,6 +11,7 @@ class CodeWidget; class QLabel; class QPushButton; +class QCheckBox; class QTableWidget; struct Diff @@ -32,8 +33,17 @@ public: void reject() override; private: + enum class UpdateType + { + Include, + Exclude, + Backup + }; + void CreateWidgets(); void ConnectWidgets(); + void SaveDataBackup(); + void LoadDataBackup(); void ClearData(); void ClearBlockCache(); void OnClickItem(); @@ -43,7 +53,7 @@ private: void OnExclude(); void RemoveMissingSymbolsFromIncludes(const std::vector& symbol_diff); void RemoveMatchingSymbolsFromIncludes(const std::vector& symbol_list); - void Update(bool include); + void Update(UpdateType type); void InfoDisp(); void OnContextMenu(); @@ -52,15 +62,20 @@ private: void OnDelete(); void OnSetBLR(); + void MarkRowBLR(int row); void UpdateItem(); + void UpdateButtons(bool running); QTableWidget* m_matching_results_table; + QCheckBox* m_autosave_check; QLabel* m_exclude_size_label; QLabel* m_include_size_label; QPushButton* m_exclude_btn; QPushButton* m_include_btn; QPushButton* m_record_btn; QPushButton* m_reset_btn; + QPushButton* m_save_btn; + QPushButton* m_load_btn; QPushButton* m_help_btn; CodeWidget* m_code_widget;