Large diffs are not rendered by default.

@@ -0,0 +1,72 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <vector>

#include <QTableWidget>

#include "Common/CommonTypes.h"

class QKeyEvent;
class QMouseEvent;
class QResizeEvent;

class CodeViewWidget : public QTableWidget
{
Q_OBJECT
public:
explicit CodeViewWidget();

u32 GetAddress() const;
u32 GetContextAddress() const;
void SetAddress(u32 address);

void Update();

void ToggleBreakpoint();
void AddBreakpoint();
signals:
void RequestPPCComparison(u32 addr);
void SymbolsChanged();
void BreakpointsChanged();

private:
void ReplaceAddress(u32 address, bool blr);

void resizeEvent(QResizeEvent*) override;
void keyPressEvent(QKeyEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void wheelEvent(QWheelEvent* event) override;

void OnContextMenu();

void OnFollowBranch();
void OnCopyAddress();
void OnCopyFunction();
void OnCopyCode();
void OnCopyHex();
void OnRenameSymbol();
void OnSetSymbolSize();
void OnSetSymbolEndAddress();
void OnRunToHere();
void OnAddFunction();
void OnPPCComparison();
void OnInsertBLR();
void OnInsertNOP();
void OnReplaceInstruction();

struct ReplStruct
{
u32 address;
u32 old_value;
};

std::vector<ReplStruct> m_repl_list;
bool m_updating = false;

u32 m_address = 0;
u32 m_context_address = 0;
};

Large diffs are not rendered by default.

@@ -0,0 +1,68 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <QDockWidget>
#include <QString>

class CodeViewWidget;
class QCloseEvent;
class QLineEdit;
class QSplitter;
class QListWidget;
class QTableWidget;
struct Symbol;

class CodeWidget : public QDockWidget
{
Q_OBJECT
public:
explicit CodeWidget(QWidget* parent = nullptr);
~CodeWidget();

void Step();
void StepOver();
void StepOut();
void Skip();
void ShowPC();
void SetPC();

void ToggleBreakpoint();
void AddBreakpoint();

signals:
void BreakpointsChanged();

private:
void CreateWidgets();
void ConnectWidgets();
void Update();
void UpdateCallstack();
void UpdateSymbols();
void UpdateFunctionCalls(Symbol* symbol);
void UpdateFunctionCallers(Symbol* symbol);

void OnSearchAddress();
void OnSearchSymbols();
void OnSelectSymbol();
void OnSelectCallstack();
void OnSelectFunctionCallers();
void OnSelectFunctionCalls();

void closeEvent(QCloseEvent*) override;

QLineEdit* m_search_address;
QLineEdit* m_search_symbols;

QListWidget* m_callstack_list;
QListWidget* m_symbols_list;
QListWidget* m_function_calls_list;
QListWidget* m_function_callers_list;
CodeViewWidget* m_code_view;
QSplitter* m_box_splitter;
QSplitter* m_code_splitter;

QString m_symbol_filter;
};
@@ -94,6 +94,8 @@
<QtMoc Include="TAS\StickWidget.h" />
<QtMoc Include="TAS\IRWidget.h" />
<QtMoc Include="Debugger\BreakpointWidget.h" />
<QtMoc Include="Debugger\CodeWidget.h" />
<QtMoc Include="Debugger\CodeViewWidget.h" />
<QtMoc Include="Debugger\NewBreakpointDialog.h" />
<QtMoc Include="Debugger\RegisterWidget.h" />
<QtMoc Include="Debugger\WatchWidget.h" />
@@ -135,6 +137,8 @@
<ClCompile Include="$(QtMocOutPrefix)AdvancedWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)BreakpointWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)CheatWarningWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)CodeViewWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)CodeWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)ControllersWindow.cpp" />
<ClCompile Include="$(QtMocOutPrefix)EnhancementsWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)FIFOPlayerWindow.cpp" />
@@ -215,6 +219,7 @@
<ClCompile Include="Config\Mapping\GCPadEmu.cpp" />
<ClCompile Include="Config\Mapping\GCPadWiiUConfigDialog.cpp" />
<ClCompile Include="Config\Mapping\Hotkey3D.cpp" />
<ClCompile Include="Config\Mapping\HotkeyDebugging.cpp" />
<ClCompile Include="Config\Mapping\HotkeyGeneral.cpp" />
<ClCompile Include="Config\Mapping\HotkeyGraphics.cpp" />
<ClCompile Include="Config\Mapping\HotkeyStates.cpp" />
@@ -237,6 +242,8 @@
<ClCompile Include="Config\PatchesWidget.cpp" />
<ClCompile Include="Config\PropertiesDialog.cpp" />
<ClCompile Include="Config\SettingsWindow.cpp" />
<ClCompile Include="Debugger\CodeViewWidget.cpp" />
<ClCompile Include="Debugger\CodeWidget.cpp" />
<ClCompile Include="FIFOPlayerWindow.cpp" />
<ClCompile Include="TAS\GCTASInputWindow.cpp" />
<ClCompile Include="TAS\WiiTASInputWindow.cpp" />
@@ -282,7 +289,7 @@
<ClCompile Include="Settings\InterfacePane.cpp" />
<ClCompile Include="Settings\PathPane.cpp" />
<ClCompile Include="Settings\WiiPane.cpp" />
<ClCompile Include="Settings\USBDeviceAddToWhitelistDialog.cpp" />
<ClCompile Include="Settings\USBDeviceAddToWhitelistDialog.cpp" />
<ClCompile Include="ToolBar.cpp" />
<ClCompile Include="Translation.cpp" />
<ClCompile Include="WiiUpdate.cpp" />
@@ -298,6 +305,8 @@
<ClInclude Include="Config\Mapping\HotkeyGraphics.h" />
<ClInclude Include="Config\Mapping\HotkeyStates.h" />
<ClInclude Include="Config\Mapping\HotkeyTAS.h" />
<ClInclude Include="Debugger\CodeViewWidget.h" />
<ClInclude Include="Debugger\CodeWidget.h" />
<ClInclude Include="TAS\Shared.h" />
<ClInclude Include="Config\Mapping\HotkeyWii.h" />
<ClInclude Include="Config\Mapping\MappingBool.h" />
@@ -9,7 +9,10 @@

#include "Common/Common.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/Debugger/PPCDebugInterface.h"
#include "Core/Host.h"
#include "Core/PowerPC/PowerPC.h"
#include "DolphinQt2/Settings.h"
#include "VideoCommon/RenderBase.h"

@@ -90,10 +93,16 @@ bool Host_RendererIsFullscreen()
{
return Host::GetInstance()->GetRenderFullscreen();
}

void Host_YieldToUI()
{
qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
}

void Host_UpdateDisasmDialog()
{
}

void Host_UpdateProgressDialog(const char* caption, int position, int total)
{
}
@@ -114,9 +123,6 @@ bool Host_UINeedsControllerState()
void Host_NotifyMapLoaded()
{
}
void Host_UpdateDisasmDialog()
{
}
void Host_ShowVideoConfig(void* parent, const std::string& backend_name)
{
}
@@ -194,7 +194,31 @@ void HotkeyScheduler::Run()
IsHotkey(HK_TRIGGER_SYNC_BUTTON, true));
}

// TODO Debugging shortcuts (Separate PR)
if (IsHotkey(HK_STEP))
emit Step();

if (IsHotkey(HK_STEP_OVER))
emit StepOver();

if (IsHotkey(HK_STEP_OUT))
emit StepOut();

if (IsHotkey(HK_SKIP))
emit Skip();

if (IsHotkey(HK_SHOW_PC))
emit ShowPC();

if (IsHotkey(HK_SET_PC))
emit Skip();

if (IsHotkey(HK_BP_TOGGLE))
emit ToggleBreakpoint();

if (IsHotkey(HK_BP_ADD))
emit AddBreakpoint();

// TODO: HK_MBP_ADD

if (SConfig::GetInstance().bWii)
{
@@ -33,6 +33,17 @@ class HotkeyScheduler : public QObject
void ToggleReadOnlyMode();
void ConnectWiiRemote(int id);

void Step();
void StepOver();
void StepOut();
void Skip();

void ShowPC();
void SetPC();

void ToggleBreakpoint();
void AddBreakpoint();

private:
void Run();

@@ -51,6 +51,7 @@
#include "DolphinQt2/Config/Mapping/MappingWindow.h"
#include "DolphinQt2/Config/SettingsWindow.h"
#include "DolphinQt2/Debugger/BreakpointWidget.h"
#include "DolphinQt2/Debugger/CodeWidget.h"
#include "DolphinQt2/Debugger/RegisterWidget.h"
#include "DolphinQt2/Debugger/WatchWidget.h"
#include "DolphinQt2/FIFOPlayerWindow.h"
@@ -94,6 +95,7 @@ MainWindow::MainWindow(std::unique_ptr<BootParameters> boot_parameters) : QMainW
ConnectRenderWidget();
ConnectStack();
ConnectMenuBar();
ConnectHotkeys();

InitCoreCallbacks();

@@ -122,8 +124,6 @@ void MainWindow::InitControllers()
Wiimote::Initialize(Wiimote::InitializeMode::DO_NOT_WAIT_FOR_WIIMOTES);
m_hotkey_scheduler = new HotkeyScheduler();
m_hotkey_scheduler->Start();

ConnectHotkeys();
}

void MainWindow::ShutdownControllers()
@@ -196,11 +196,14 @@ void MainWindow::CreateComponents()
m_register_widget = new RegisterWidget(this);
m_watch_widget = new WatchWidget(this);
m_breakpoint_widget = new BreakpointWidget(this);
m_code_widget = new CodeWidget(this);

connect(m_watch_widget, &WatchWidget::RequestMemoryBreakpoint,
[this](u32 addr) { m_breakpoint_widget->AddAddressMBP(addr); });
connect(m_register_widget, &RegisterWidget::RequestMemoryBreakpoint,
[this](u32 addr) { m_breakpoint_widget->AddAddressMBP(addr); });
connect(m_code_widget, &CodeWidget::BreakpointsChanged, m_breakpoint_widget,
&BreakpointWidget::Update);

#if defined(HAVE_XRANDR) && HAVE_XRANDR
m_graphics_window = new GraphicsWindow(
@@ -313,11 +316,27 @@ void MainWindow::ConnectHotkeys()
Movie::SetReadOnly(read_only);
emit ReadOnlyModeChanged(read_only);
});

connect(m_hotkey_scheduler, &HotkeyScheduler::Step, m_code_widget, &CodeWidget::Step);
connect(m_hotkey_scheduler, &HotkeyScheduler::StepOver, m_code_widget, &CodeWidget::StepOver);
connect(m_hotkey_scheduler, &HotkeyScheduler::StepOut, m_code_widget, &CodeWidget::StepOut);
connect(m_hotkey_scheduler, &HotkeyScheduler::Skip, m_code_widget, &CodeWidget::Skip);

connect(m_hotkey_scheduler, &HotkeyScheduler::ShowPC, m_code_widget, &CodeWidget::ShowPC);
connect(m_hotkey_scheduler, &HotkeyScheduler::SetPC, m_code_widget, &CodeWidget::SetPC);

connect(m_hotkey_scheduler, &HotkeyScheduler::ToggleBreakpoint, m_code_widget,
&CodeWidget::ToggleBreakpoint);
connect(m_hotkey_scheduler, &HotkeyScheduler::AddBreakpoint, m_code_widget,
&CodeWidget::AddBreakpoint);
}

void MainWindow::ConnectToolBar()
{
addToolBar(m_tool_bar);

connect(m_tool_bar, &ToolBar::OpenPressed, this, &MainWindow::Open);

connect(m_tool_bar, &ToolBar::OpenPressed, this, &MainWindow::Open);
connect(m_tool_bar, &ToolBar::PlayPressed, this, [this]() { Play(); });
connect(m_tool_bar, &ToolBar::PausePressed, this, &MainWindow::Pause);
@@ -327,6 +346,13 @@ void MainWindow::ConnectToolBar()
connect(m_tool_bar, &ToolBar::SettingsPressed, this, &MainWindow::ShowSettingsWindow);
connect(m_tool_bar, &ToolBar::ControllersPressed, this, &MainWindow::ShowControllersWindow);
connect(m_tool_bar, &ToolBar::GraphicsPressed, this, &MainWindow::ShowGraphicsWindow);

connect(m_tool_bar, &ToolBar::StepPressed, m_code_widget, &CodeWidget::Step);
connect(m_tool_bar, &ToolBar::StepOverPressed, m_code_widget, &CodeWidget::StepOver);
connect(m_tool_bar, &ToolBar::StepOutPressed, m_code_widget, &CodeWidget::StepOut);
connect(m_tool_bar, &ToolBar::SkipPressed, m_code_widget, &CodeWidget::Skip);
connect(m_tool_bar, &ToolBar::ShowPCPressed, m_code_widget, &CodeWidget::ShowPC);
connect(m_tool_bar, &ToolBar::SetPCPressed, m_code_widget, &CodeWidget::SetPC);
}

void MainWindow::ConnectGameList()
@@ -353,11 +379,13 @@ void MainWindow::ConnectStack()
setTabPosition(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea, QTabWidget::North);
addDockWidget(Qt::RightDockWidgetArea, m_log_widget);
addDockWidget(Qt::RightDockWidgetArea, m_log_config_widget);
addDockWidget(Qt::RightDockWidgetArea, m_code_widget);
addDockWidget(Qt::RightDockWidgetArea, m_register_widget);
addDockWidget(Qt::RightDockWidgetArea, m_watch_widget);
addDockWidget(Qt::RightDockWidgetArea, m_breakpoint_widget);

tabifyDockWidget(m_log_widget, m_log_config_widget);
tabifyDockWidget(m_log_widget, m_code_widget);
tabifyDockWidget(m_log_widget, m_register_widget);
tabifyDockWidget(m_log_widget, m_watch_widget);
tabifyDockWidget(m_log_widget, m_breakpoint_widget);
@@ -20,6 +20,7 @@

class BreakpointWidget;
struct BootParameters;
class CodeWidget;
class FIFOPlayerWindow;
class HotkeyScheduler;
class LogConfigWidget;
@@ -157,6 +158,7 @@ class MainWindow final : public QMainWindow
std::array<WiiTASInputWindow*, num_wii_controllers> m_wii_tas_input_windows{};

BreakpointWidget* m_breakpoint_widget;
CodeWidget* m_code_widget;
LogWidget* m_log_widget;
LogConfigWidget* m_log_config_widget;
FIFOPlayerWindow* m_fifo_window;
@@ -9,6 +9,8 @@
#include <QAction>
#include <QDesktopServices>
#include <QFileDialog>
#include <QFontDialog>
#include <QInputDialog>
#include <QMap>
#include <QMessageBox>
#include <QUrl>
@@ -17,15 +19,22 @@
#include "Common/FileUtil.h"
#include "Common/StringUtil.h"

#include "Core/Boot/Boot.h"
#include "Core/CommonTitles.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/Debugger/RSO.h"
#include "Core/HLE/HLE.h"
#include "Core/HW/WiiSaveCrypted.h"
#include "Core/HW/Wiimote.h"
#include "Core/Host.h"
#include "Core/IOS/ES/ES.h"
#include "Core/IOS/IOS.h"
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
#include "Core/Movie.h"
#include "Core/PowerPC/PPCAnalyst.h"
#include "Core/PowerPC/PPCSymbolDB.h"
#include "Core/PowerPC/SignatureDB/SignatureDB.h"
#include "Core/State.h"
#include "Core/TitleDatabase.h"
#include "Core/WiiUtils.h"
@@ -47,6 +56,7 @@ MenuBar::MenuBar(QWidget* parent) : QMenuBar(parent)
AddOptionsMenu();
AddToolsMenu();
AddViewMenu();
AddSymbolsMenu();
AddHelpMenu();

connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
@@ -84,6 +94,9 @@ void MenuBar::OnEmulationStateChanged(Core::State state)
m_recording_stop->setEnabled(false);
m_recording_play->setEnabled(!running);

// Symbols
m_symbols->setEnabled(running);

UpdateStateSlotMenu();
UpdateToolsMenu(running);

@@ -92,9 +105,21 @@ void MenuBar::OnEmulationStateChanged(Core::State state)

void MenuBar::OnDebugModeToggled(bool enabled)
{
// Options
m_boot_to_pause->setVisible(enabled);
m_automatic_start->setVisible(enabled);
m_change_font->setVisible(enabled);

// View
m_show_code->setVisible(enabled);
m_show_registers->setVisible(enabled);
m_show_watch->setVisible(enabled);
m_show_breakpoints->setVisible(enabled);

if (enabled)
addMenu(m_symbols);
else
removeAction(m_symbols->menuAction());
}

void MenuBar::AddFileMenu()
@@ -290,6 +315,14 @@ void MenuBar::AddViewMenu()

view_menu->addSeparator();

m_show_code = view_menu->addAction(tr("&Code"));
m_show_code->setCheckable(true);
m_show_code->setChecked(Settings::Instance().IsCodeVisible());

connect(m_show_code, &QAction::toggled, &Settings::Instance(), &Settings::SetCodeVisible);
connect(&Settings::Instance(), &Settings::CodeVisibilityChanged, m_show_code,
&QAction::setChecked);

m_show_registers = view_menu->addAction(tr("&Registers"));
m_show_registers->setCheckable(true);
m_show_registers->setChecked(Settings::Instance().IsRegistersVisible());
@@ -335,6 +368,25 @@ void MenuBar::AddOptionsMenu()
AddAction(options_menu, tr("&Audio Settings"), this, &MenuBar::ConfigureAudio);
AddAction(options_menu, tr("&Controller Settings"), this, &MenuBar::ConfigureControllers);
AddAction(options_menu, tr("&Hotkey Settings"), this, &MenuBar::ConfigureHotkeys);

options_menu->addSeparator();

// Debugging mode only
m_boot_to_pause = options_menu->addAction(tr("Boot To Pause"));
m_boot_to_pause->setCheckable(true);
m_boot_to_pause->setChecked(SConfig::GetInstance().bBootToPause);

connect(m_boot_to_pause, &QAction::toggled, this,
[this](bool enable) { SConfig::GetInstance().bBootToPause = enable; });

m_automatic_start = options_menu->addAction(tr("&Automatic Start"));
m_automatic_start->setCheckable(true);
m_automatic_start->setChecked(SConfig::GetInstance().bAutomaticStart);

connect(m_automatic_start, &QAction::toggled, this,
[this](bool enable) { SConfig::GetInstance().bAutomaticStart = enable; });

m_change_font = AddAction(options_menu, tr("Font..."), this, &MenuBar::ChangeDebugFont);
}

void MenuBar::AddHelpMenu()
@@ -538,6 +590,35 @@ void MenuBar::AddMovieMenu()
[](bool value) { SConfig::GetInstance().m_DumpAudio = value; });
}

void MenuBar::AddSymbolsMenu()
{
m_symbols = addMenu(tr("Symbols"));

AddAction(m_symbols, tr("&Clear Symbols"), this, &MenuBar::ClearSymbols);

auto* generate = m_symbols->addMenu(tr("Generate Symbols From"));
AddAction(generate, tr("Address"), this, &MenuBar::GenerateSymbolsFromAddress);
AddAction(generate, tr("Signature Database"), this, &MenuBar::GenerateSymbolsFromSignatureDB);
AddAction(generate, tr("RSO Modules"), this, &MenuBar::GenerateSymbolsFromRSO);
m_symbols->addSeparator();

AddAction(m_symbols, tr("&Load Symbol Map"), this, &MenuBar::LoadSymbolMap);
AddAction(m_symbols, tr("&Save Symbol Map"), this, &MenuBar::SaveSymbolMap);
m_symbols->addSeparator();

AddAction(m_symbols, tr("&Load &Other Map File..."), this, &MenuBar::LoadOtherSymbolMap);
AddAction(m_symbols, tr("Save Symbol Map &As..."), this, &MenuBar::SaveSymbolMapAs);
m_symbols->addSeparator();

AddAction(m_symbols, tr("Save Code"), this, &MenuBar::SaveCode);
m_symbols->addSeparator();

AddAction(m_symbols, tr("&Create Signature File..."), this, &MenuBar::CreateSignatureFile);
m_symbols->addSeparator();

AddAction(m_symbols, tr("&Patch HLE Functions"), this, &MenuBar::PatchHLEFunctions);
}

void MenuBar::UpdateToolsMenu(bool emulation_started)
{
m_boot_sysmenu->setEnabled(!emulation_started);
@@ -718,3 +799,181 @@ void MenuBar::OnReadOnlyModeChanged(bool read_only)
{
m_recording_read_only->setChecked(read_only);
}

void MenuBar::ChangeDebugFont()
{
bool okay;
QFont font = QFontDialog::getFont(&okay, Settings::Instance().GetDebugFont(), this,
tr("Pick a debug font"));

if (okay)
Settings::Instance().SetDebugFont(font);
}

void MenuBar::ClearSymbols()
{
auto result = QMessageBox::warning(this, tr("Confirmation"),
tr("Do you want to clear the list of symbol names?"),
QMessageBox::Yes | QMessageBox::Cancel);

if (result == QMessageBox::Cancel)
return;

g_symbolDB.Clear();
Host_NotifyMapLoaded();
}

void MenuBar::GenerateSymbolsFromAddress()
{
PPCAnalyst::FindFunctions(0x80000000, 0x81800000, &g_symbolDB);
Host_NotifyMapLoaded();
}

void MenuBar::GenerateSymbolsFromSignatureDB()
{
PPCAnalyst::FindFunctions(0x80000000, 0x81800000, &g_symbolDB);
SignatureDB db(SignatureDB::HandlerType::DSY);
if (db.Load(File::GetSysDirectory() + TOTALDB))
{
db.Apply(&g_symbolDB);
QMessageBox::information(
this, tr("Information"),
tr("Generated symbol names from '%1'").arg(QString::fromStdString(TOTALDB)));
db.List();
}
else
{
QMessageBox::critical(
this, tr("Error"),
tr("'%1' not found, no symbol names generated").arg(QString::fromStdString(TOTALDB)));
}

Host_NotifyMapLoaded();
}

void MenuBar::GenerateSymbolsFromRSO()
{
QString text = QInputDialog::getText(this, tr("Input"), tr("Enter the RSO module address:"));
bool good;
uint address = text.toUInt(&good, 16);

if (!good)
{
QMessageBox::warning(this, tr("Error"), tr("Invalid RSO module address: %1").arg(text));
return;
}

RSOChainView rso_chain;
if (rso_chain.Load(static_cast<u32>(address)))
{
rso_chain.Apply(&g_symbolDB);
Host_NotifyMapLoaded();
}
else
{
QMessageBox::warning(this, tr("Error"), tr("Failed to load RSO module at %1").arg(text));
}
}

void MenuBar::LoadSymbolMap()
{
std::string existing_map_file, writable_map_file;
bool map_exists = CBoot::FindMapFile(&existing_map_file, &writable_map_file);

if (!map_exists)
{
g_symbolDB.Clear();
PPCAnalyst::FindFunctions(0x81300000, 0x81800000, &g_symbolDB);
SignatureDB db(SignatureDB::HandlerType::DSY);
if (db.Load(File::GetSysDirectory() + TOTALDB))
db.Apply(&g_symbolDB);

QMessageBox::warning(this, tr("Warning"),
tr("'%1' not found, scanning for common functions instead")
.arg(QString::fromStdString(writable_map_file)));
}
else
{
g_symbolDB.LoadMap(existing_map_file);
QMessageBox::information(
this, tr("Information"),
tr("Loaded symbols from '%1'").arg(QString::fromStdString(existing_map_file.c_str())));
}

HLE::PatchFunctions();
Host_NotifyMapLoaded();
}

void MenuBar::SaveSymbolMap()
{
std::string existing_map_file, writable_map_file;
CBoot::FindMapFile(&existing_map_file, &writable_map_file);

g_symbolDB.SaveSymbolMap(writable_map_file);
}

void MenuBar::LoadOtherSymbolMap()
{
QString file = QFileDialog::getOpenFileName(this, tr("Load map file"),
QString::fromStdString(File::GetUserPath(D_MAPS_IDX)),
tr("Dolphin Map File (*.map)"));

if (file.isEmpty())
return;

g_symbolDB.LoadMap(file.toStdString());
HLE::PatchFunctions();
Host_NotifyMapLoaded();
}

void MenuBar::SaveSymbolMapAs()
{
const std::string& title_id_str = SConfig::GetInstance().m_debugger_game_id;
QString file = QFileDialog::getSaveFileName(
this, tr("Save map file"),
QString::fromStdString(File::GetUserPath(D_MAPS_IDX) + "/" + title_id_str + ".map"),
tr("Dolphin Map File (*.map)"));

if (file.isEmpty())
return;

g_symbolDB.SaveSymbolMap(file.toStdString());
}

void MenuBar::SaveCode()
{
std::string existing_map_file, writable_map_file;
CBoot::FindMapFile(&existing_map_file, &writable_map_file);

const std::string path =
writable_map_file.substr(0, writable_map_file.find_last_of(".")) + "_code.map";

g_symbolDB.SaveCodeMap(path);
}

void MenuBar::CreateSignatureFile()
{
QString text = QInputDialog::getText(
this, tr("Input"), tr("Only export symbols with prefix:\n(Blank for all symbols)"));

if (text.isEmpty())
return;

std::string prefix = text.toStdString();

QString file = QFileDialog::getSaveFileName(this, tr("Save signature file"));

if (file.isEmpty())
return;

std::string save_path = file.toStdString();
SignatureDB db(save_path);
db.Populate(&g_symbolDB, prefix);
db.Save(save_path);
db.List();
}

void MenuBar::PatchHLEFunctions()
{
HLE::PatchFunctions();
}
@@ -117,12 +117,27 @@ class MenuBar final : public QMenuBar
void AddToolsMenu();
void AddHelpMenu();
void AddMovieMenu();
void AddSymbolsMenu();

void InstallWAD();
void ImportWiiSave();
void ExportWiiSaves();
void CheckNAND();
void NANDExtractCertificates();
void ChangeDebugFont();

// Debugging UI
void ClearSymbols();
void GenerateSymbolsFromAddress();
void GenerateSymbolsFromSignatureDB();
void GenerateSymbolsFromRSO();
void LoadSymbolMap();
void LoadOtherSymbolMap();
void SaveSymbolMap();
void SaveSymbolMapAs();
void SaveCode();
void CreateSignatureFile();
void PatchHLEFunctions();

void OnSelectionChanged(std::shared_ptr<const UICommon::GameFile> game_file);
void OnRecordingStatusChanged(bool recording);
@@ -168,8 +183,17 @@ class MenuBar final : public QMenuBar
QAction* m_recording_stop;
QAction* m_recording_read_only;

// Options
QAction* m_boot_to_pause;
QAction* m_automatic_start;
QAction* m_change_font;

// View
QAction* m_show_code;
QAction* m_show_registers;
QAction* m_show_watch;
QAction* m_show_breakpoints;

// Symbols
QMenu* m_symbols;
};
@@ -279,3 +279,36 @@ void Settings::SetControllerStateNeeded(bool needed)
{
m_controller_state_needed = needed;
}

void Settings::SetCodeVisible(bool enabled)
{
if (IsCodeVisible() != enabled)
{
QSettings().setValue(QStringLiteral("debugger/showcode"), enabled);

emit CodeVisibilityChanged(enabled);
}
}

bool Settings::IsCodeVisible() const
{
return QSettings().value(QStringLiteral("debugger/showcode")).toBool();
}

void Settings::SetDebugFont(QFont font)
{
if (GetDebugFont() != font)
{
QSettings().setValue(QStringLiteral("debugger/font"), font);

emit DebugFontChanged(font);
}
}

QFont Settings::GetDebugFont() const
{
QFont default_font = QFont(QStringLiteral("Monospace"));
default_font.setStyleHint(QFont::TypeWriter);

return QSettings().value(QStringLiteral("debugger/font"), default_font).value<QFont>();
}
@@ -6,6 +6,7 @@

#include <memory>

#include <QFont>
#include <QObject>
#include <QVector>

@@ -24,6 +25,7 @@ enum class Language;

class GameListModel;
class InputConfig;
class QFont;

// UI settings to be stored in the config directory.
class Settings final : public QObject
@@ -91,6 +93,10 @@ class Settings final : public QObject
bool IsWatchVisible() const;
void SetBreakpointsVisible(bool enabled);
bool IsBreakpointsVisible() const;
void SetCodeVisible(bool enabled);
bool IsCodeVisible() const;
QFont GetDebugFont() const;
void SetDebugFont(QFont font);

// Other
GameListModel* GetGameListModel() const;
@@ -110,7 +116,9 @@ class Settings final : public QObject
void EnableCheatsChanged(bool enabled);
void WatchVisibilityChanged(bool visible);
void BreakpointsVisibilityChanged(bool visible);
void CodeVisibilityChanged(bool visible);
void DebugModeToggled(bool enabled);
void DebugFontChanged(QFont font);

private:
bool m_controller_state_needed = false;
@@ -28,7 +28,11 @@ ToolBar::ToolBar(QWidget* parent) : QToolBar(parent)

connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
[this](Core::State state) { OnEmulationStateChanged(state); });

connect(&Settings::Instance(), &Settings::DebugModeToggled, this, &ToolBar::OnDebugModeToggled);

OnEmulationStateChanged(Core::GetState());
OnDebugModeToggled(Settings::Instance().IsDebugModeEnabled());
}

void ToolBar::OnEmulationStateChanged(Core::State state)
@@ -45,8 +49,25 @@ void ToolBar::OnEmulationStateChanged(Core::State state)
m_pause_action->setVisible(playing);
}

void ToolBar::OnDebugModeToggled(bool enabled)
{
m_step_action->setVisible(enabled);
m_step_over_action->setVisible(enabled);
m_step_out_action->setVisible(enabled);
m_skip_action->setVisible(enabled);
m_show_pc_action->setVisible(enabled);
m_set_pc_action->setVisible(enabled);
}

void ToolBar::MakeActions()
{
m_step_action = AddAction(this, tr("Step"), this, &ToolBar::StepPressed);
m_step_over_action = AddAction(this, tr("Step Over"), this, &ToolBar::StepOverPressed);
m_step_out_action = AddAction(this, tr("Step Out"), this, &ToolBar::StepOutPressed);
m_skip_action = AddAction(this, tr("Skip"), this, &ToolBar::SkipPressed);
m_show_pc_action = AddAction(this, tr("Show PC"), this, &ToolBar::ShowPCPressed);
m_set_pc_action = AddAction(this, tr("Set PC"), this, &ToolBar::SetPCPressed);

m_open_action = AddAction(this, tr("Open"), this, &ToolBar::OpenPressed);
m_play_action = AddAction(this, tr("Play"), this, &ToolBar::PlayPressed);
m_pause_action = AddAction(this, tr("Pause"), this, &ToolBar::PausePressed);
@@ -63,9 +84,11 @@ void ToolBar::MakeActions()

// Ensure every button has about the same width
std::vector<QWidget*> items;
for (const auto& action : {m_open_action, m_play_action, m_pause_action, m_stop_action,
m_stop_action, m_fullscreen_action, m_screenshot_action,
m_config_action, m_graphics_action, m_controllers_action})
for (const auto& action :
{m_open_action, m_play_action, m_pause_action, m_stop_action, m_stop_action,
m_fullscreen_action, m_screenshot_action, m_config_action, m_graphics_action,
m_controllers_action, m_step_action, m_step_over_action, m_step_out_action, m_skip_action,
m_show_pc_action, m_set_pc_action})
{
items.emplace_back(widgetForAction(action));
}
@@ -32,8 +32,16 @@ class ToolBar final : public QToolBar
void ControllersPressed();
void GraphicsPressed();

void StepPressed();
void StepOverPressed();
void StepOutPressed();
void SkipPressed();
void ShowPCPressed();
void SetPCPressed();

private:
void OnEmulationStateChanged(Core::State state);
void OnDebugModeToggled(bool enabled);

void MakeActions();
void UpdateIcons();
@@ -47,4 +55,11 @@ class ToolBar final : public QToolBar
QAction* m_config_action;
QAction* m_controllers_action;
QAction* m_graphics_action;

QAction* m_step_action;
QAction* m_step_over_action;
QAction* m_step_out_action;
QAction* m_skip_action;
QAction* m_show_pc_action;
QAction* m_set_pc_action;
};