1 // Copyright 2015 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4
5 #include "DolphinQt/MenuBar.h"
6
7 #include <cinttypes>
8
9 #include <QAction>
10 #include <QDesktopServices>
11 #include <QFileDialog>
12 #include <QFontDialog>
13 #include <QInputDialog>
14 #include <QMap>
15 #include <QUrl>
16
17 #include "Common/CommonPaths.h"
18 #include "Common/FileUtil.h"
19 #include "Common/StringUtil.h"
20
21 #include "Common/CDUtils.h"
22 #include "Core/Boot/Boot.h"
23 #include "Core/CommonTitles.h"
24 #include "Core/ConfigManager.h"
25 #include "Core/Core.h"
26 #include "Core/Debugger/RSO.h"
27 #include "Core/HLE/HLE.h"
28 #include "Core/HW/AddressSpace.h"
29 #include "Core/HW/Memmap.h"
30 #include "Core/HW/WiiSave.h"
31 #include "Core/HW/Wiimote.h"
32 #include "Core/IOS/ES/ES.h"
33 #include "Core/IOS/IOS.h"
34 #include "Core/IOS/USB/Bluetooth/BTEmu.h"
35 #include "Core/Movie.h"
36 #include "Core/NetPlayProto.h"
37 #include "Core/PowerPC/JitInterface.h"
38 #include "Core/PowerPC/MMU.h"
39 #include "Core/PowerPC/PPCAnalyst.h"
40 #include "Core/PowerPC/PPCSymbolDB.h"
41 #include "Core/PowerPC/PowerPC.h"
42 #include "Core/PowerPC/SignatureDB/SignatureDB.h"
43 #include "Core/State.h"
44 #include "Core/TitleDatabase.h"
45 #include "Core/WiiUtils.h"
46
47 #include "DiscIO/Enums.h"
48 #include "DiscIO/NANDImporter.h"
49 #include "DiscIO/WiiSaveBanner.h"
50
51 #include "DolphinQt/AboutDialog.h"
52 #include "DolphinQt/Host.h"
53 #include "DolphinQt/QtUtils/ModalMessageBox.h"
54 #include "DolphinQt/Settings.h"
55 #include "DolphinQt/Updater.h"
56
57 #include "UICommon/AutoUpdate.h"
58 #include "UICommon/GameFile.h"
59
60 QPointer<MenuBar> MenuBar::s_menu_bar;
61
GetSignatureSelector() const62 QString MenuBar::GetSignatureSelector() const
63 {
64 return QStringLiteral("%1 (*.dsy);; %2 (*.csv);; %3 (*.mega)")
65 .arg(tr("Dolphin Signature File"), tr("Dolphin Signature CSV File"),
66 tr("WiiTools Signature MEGA File"));
67 }
68
MenuBar(QWidget * parent)69 MenuBar::MenuBar(QWidget* parent) : QMenuBar(parent)
70 {
71 s_menu_bar = this;
72
73 AddFileMenu();
74 AddEmulationMenu();
75 AddMovieMenu();
76 AddOptionsMenu();
77 AddToolsMenu();
78 AddViewMenu();
79 AddJITMenu();
80 AddSymbolsMenu();
81 AddHelpMenu();
82
83 connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
84 [=](Core::State state) { OnEmulationStateChanged(state); });
85 connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this,
86 [this] { OnEmulationStateChanged(Core::GetState()); });
87
88 OnEmulationStateChanged(Core::GetState());
89 connect(&Settings::Instance(), &Settings::DebugModeToggled, this, &MenuBar::OnDebugModeToggled);
90
91 connect(this, &MenuBar::SelectionChanged, this, &MenuBar::OnSelectionChanged);
92 connect(this, &MenuBar::RecordingStatusChanged, this, &MenuBar::OnRecordingStatusChanged);
93 connect(this, &MenuBar::ReadOnlyModeChanged, this, &MenuBar::OnReadOnlyModeChanged);
94 }
95
OnEmulationStateChanged(Core::State state)96 void MenuBar::OnEmulationStateChanged(Core::State state)
97 {
98 bool running = state != Core::State::Uninitialized;
99 bool playing = running && state != Core::State::Paused;
100
101 // File
102 m_eject_disc->setEnabled(running);
103 m_change_disc->setEnabled(running);
104
105 // Emulation
106 m_play_action->setEnabled(!playing);
107 m_play_action->setVisible(!playing);
108 m_pause_action->setEnabled(playing);
109 m_pause_action->setVisible(playing);
110 m_stop_action->setEnabled(running);
111 m_stop_action->setVisible(running);
112 m_reset_action->setEnabled(running);
113 m_fullscreen_action->setEnabled(running);
114 m_frame_advance_action->setEnabled(running);
115 m_screenshot_action->setEnabled(running);
116 m_state_load_menu->setEnabled(running);
117 m_state_save_menu->setEnabled(running);
118
119 // Movie
120 m_recording_read_only->setEnabled(running);
121 if (!running)
122 {
123 m_recording_stop->setEnabled(false);
124 m_recording_export->setEnabled(false);
125 }
126 m_recording_play->setEnabled(m_game_selected && !running);
127 m_recording_start->setEnabled((m_game_selected || running) && !Movie::IsPlayingInput());
128
129 // Options
130 m_controllers_action->setEnabled(NetPlay::IsNetPlayRunning() ? !running : true);
131
132 // Tools
133 m_show_cheat_manager->setEnabled(Settings::Instance().GetCheatsEnabled() && running);
134
135 // JIT
136 m_jit_interpreter_core->setEnabled(running);
137 m_jit_block_linking->setEnabled(!running);
138 m_jit_disable_cache->setEnabled(!running);
139 m_jit_disable_fastmem->setEnabled(!running);
140 m_jit_clear_cache->setEnabled(running);
141 m_jit_log_coverage->setEnabled(!running);
142 m_jit_search_instruction->setEnabled(running);
143
144 for (QAction* action :
145 {m_jit_off, m_jit_loadstore_off, m_jit_loadstore_lbzx_off, m_jit_loadstore_lxz_off,
146 m_jit_loadstore_lwz_off, m_jit_loadstore_floating_off, m_jit_loadstore_paired_off,
147 m_jit_floatingpoint_off, m_jit_integer_off, m_jit_paired_off, m_jit_systemregisters_off,
148 m_jit_branch_off, m_jit_register_cache_off})
149 {
150 action->setEnabled(running && !playing);
151 }
152
153 // Symbols
154 m_symbols->setEnabled(running);
155
156 UpdateStateSlotMenu();
157 UpdateToolsMenu(running);
158
159 OnDebugModeToggled(Settings::Instance().IsDebugModeEnabled());
160 }
161
OnDebugModeToggled(bool enabled)162 void MenuBar::OnDebugModeToggled(bool enabled)
163 {
164 // Options
165 m_boot_to_pause->setVisible(enabled);
166 m_automatic_start->setVisible(enabled);
167 m_change_font->setVisible(enabled);
168
169 // View
170 m_show_code->setVisible(enabled);
171 m_show_registers->setVisible(enabled);
172 m_show_threads->setVisible(enabled);
173 m_show_watch->setVisible(enabled);
174 m_show_breakpoints->setVisible(enabled);
175 m_show_memory->setVisible(enabled);
176 m_show_network->setVisible(enabled);
177 m_show_jit->setVisible(enabled);
178
179 if (enabled)
180 {
181 addMenu(m_jit);
182 addMenu(m_symbols);
183 }
184 else
185 {
186 removeAction(m_jit->menuAction());
187 removeAction(m_symbols->menuAction());
188 }
189 }
190
AddDVDBackupMenu(QMenu * file_menu)191 void MenuBar::AddDVDBackupMenu(QMenu* file_menu)
192 {
193 m_backup_menu = file_menu->addMenu(tr("&Boot from DVD Backup"));
194
195 const std::vector<std::string> drives = Common::GetCDDevices();
196 // Windows Limitation of 24 character drives
197 for (size_t i = 0; i < drives.size() && i < 24; i++)
198 {
199 auto drive = QString::fromStdString(drives[i]);
200 m_backup_menu->addAction(drive, this, [this, drive] { emit BootDVDBackup(drive); });
201 }
202 }
203
AddFileMenu()204 void MenuBar::AddFileMenu()
205 {
206 QMenu* file_menu = addMenu(tr("&File"));
207 m_open_action = file_menu->addAction(tr("&Open..."), this, &MenuBar::Open, QKeySequence::Open);
208
209 file_menu->addSeparator();
210
211 m_change_disc = file_menu->addAction(tr("Change &Disc..."), this, &MenuBar::ChangeDisc);
212 m_eject_disc = file_menu->addAction(tr("&Eject Disc"), this, &MenuBar::EjectDisc);
213
214 AddDVDBackupMenu(file_menu);
215
216 file_menu->addSeparator();
217
218 m_exit_action = file_menu->addAction(tr("E&xit"), this, &MenuBar::Exit);
219 m_exit_action->setShortcuts({QKeySequence::Quit, QKeySequence(Qt::ALT + Qt::Key_F4)});
220 }
221
AddToolsMenu()222 void MenuBar::AddToolsMenu()
223 {
224 QMenu* tools_menu = addMenu(tr("&Tools"));
225
226 tools_menu->addAction(tr("&Resource Pack Manager"), this,
227 [this] { emit ShowResourcePackManager(); });
228
229 m_show_cheat_manager =
230 tools_menu->addAction(tr("&Cheats Manager"), this, [this] { emit ShowCheatsManager(); });
231
232 connect(&Settings::Instance(), &Settings::EnableCheatsChanged, this, [this](bool enabled) {
233 m_show_cheat_manager->setEnabled(Core::GetState() != Core::State::Uninitialized && enabled);
234 });
235
236 tools_menu->addAction(tr("FIFO Player"), this, &MenuBar::ShowFIFOPlayer);
237
238 tools_menu->addSeparator();
239
240 tools_menu->addAction(tr("Start &NetPlay..."), this, &MenuBar::StartNetPlay);
241 tools_menu->addAction(tr("Browse &NetPlay Sessions...."), this, &MenuBar::BrowseNetPlay);
242
243 tools_menu->addSeparator();
244
245 QMenu* gc_ipl = tools_menu->addMenu(tr("Load GameCube Main Menu"));
246
247 m_ntscj_ipl = gc_ipl->addAction(tr("NTSC-J"), this,
248 [this] { emit BootGameCubeIPL(DiscIO::Region::NTSC_J); });
249 m_ntscu_ipl = gc_ipl->addAction(tr("NTSC-U"), this,
250 [this] { emit BootGameCubeIPL(DiscIO::Region::NTSC_U); });
251 m_pal_ipl =
252 gc_ipl->addAction(tr("PAL"), this, [this] { emit BootGameCubeIPL(DiscIO::Region::PAL); });
253
254 tools_menu->addAction(tr("Memory Card Manager"), this, [this] { emit ShowMemcardManager(); });
255
256 tools_menu->addSeparator();
257
258 // Label will be set by a NANDRefresh later
259 m_boot_sysmenu = tools_menu->addAction(QString{}, this, [this] { emit BootWiiSystemMenu(); });
260 m_wad_install_action = tools_menu->addAction(tr("Install WAD..."), this, &MenuBar::InstallWAD);
261 m_manage_nand_menu = tools_menu->addMenu(tr("Manage NAND"));
262 m_import_backup = m_manage_nand_menu->addAction(tr("Import BootMii NAND Backup..."), this,
263 [this] { emit ImportNANDBackup(); });
264 m_check_nand = m_manage_nand_menu->addAction(tr("Check NAND..."), this, &MenuBar::CheckNAND);
265 m_extract_certificates = m_manage_nand_menu->addAction(tr("Extract Certificates from NAND"), this,
266 &MenuBar::NANDExtractCertificates);
267
268 m_boot_sysmenu->setEnabled(false);
269
270 connect(&Settings::Instance(), &Settings::NANDRefresh, this, [this] { UpdateToolsMenu(false); });
271
272 m_perform_online_update_menu = tools_menu->addMenu(tr("Perform Online System Update"));
273 m_perform_online_update_for_current_region = m_perform_online_update_menu->addAction(
274 tr("Current Region"), this, [this] { emit PerformOnlineUpdate(""); });
275 m_perform_online_update_menu->addSeparator();
276 m_perform_online_update_menu->addAction(tr("Europe"), this,
277 [this] { emit PerformOnlineUpdate("EUR"); });
278 m_perform_online_update_menu->addAction(tr("Japan"), this,
279 [this] { emit PerformOnlineUpdate("JPN"); });
280 m_perform_online_update_menu->addAction(tr("Korea"), this,
281 [this] { emit PerformOnlineUpdate("KOR"); });
282 m_perform_online_update_menu->addAction(tr("United States"), this,
283 [this] { emit PerformOnlineUpdate("USA"); });
284
285 tools_menu->addSeparator();
286
287 tools_menu->addAction(tr("Import Wii Save..."), this, &MenuBar::ImportWiiSave);
288 tools_menu->addAction(tr("Export All Wii Saves"), this, &MenuBar::ExportWiiSaves);
289
290 QMenu* menu = new QMenu(tr("Connect Wii Remotes"), tools_menu);
291
292 tools_menu->addSeparator();
293 tools_menu->addMenu(menu);
294
295 for (int i = 0; i < 4; i++)
296 {
297 m_wii_remotes[i] = menu->addAction(tr("Connect Wii Remote %1").arg(i + 1), this,
298 [this, i] { emit ConnectWiiRemote(i); });
299 m_wii_remotes[i]->setCheckable(true);
300 }
301
302 menu->addSeparator();
303
304 m_wii_remotes[4] =
305 menu->addAction(tr("Connect Balance Board"), this, [this] { emit ConnectWiiRemote(4); });
306 m_wii_remotes[4]->setCheckable(true);
307 }
308
AddEmulationMenu()309 void MenuBar::AddEmulationMenu()
310 {
311 QMenu* emu_menu = addMenu(tr("&Emulation"));
312 m_play_action = emu_menu->addAction(tr("&Play"), this, &MenuBar::Play);
313 m_pause_action = emu_menu->addAction(tr("&Pause"), this, &MenuBar::Pause);
314 m_stop_action = emu_menu->addAction(tr("&Stop"), this, &MenuBar::Stop);
315 m_reset_action = emu_menu->addAction(tr("&Reset"), this, &MenuBar::Reset);
316 m_fullscreen_action = emu_menu->addAction(tr("Toggle &Fullscreen"), this, &MenuBar::Fullscreen);
317 m_frame_advance_action = emu_menu->addAction(tr("&Frame Advance"), this, &MenuBar::FrameAdvance);
318
319 m_screenshot_action = emu_menu->addAction(tr("Take Screenshot"), this, &MenuBar::Screenshot);
320
321 emu_menu->addSeparator();
322
323 AddStateLoadMenu(emu_menu);
324 AddStateSaveMenu(emu_menu);
325 AddStateSlotMenu(emu_menu);
326 UpdateStateSlotMenu();
327
328 for (QMenu* menu : {m_state_load_menu, m_state_save_menu, m_state_slot_menu})
329 connect(menu, &QMenu::aboutToShow, this, &MenuBar::UpdateStateSlotMenu);
330 }
331
AddStateLoadMenu(QMenu * emu_menu)332 void MenuBar::AddStateLoadMenu(QMenu* emu_menu)
333 {
334 m_state_load_menu = emu_menu->addMenu(tr("&Load State"));
335 m_state_load_menu->addAction(tr("Load State from File"), this, &MenuBar::StateLoad);
336 m_state_load_menu->addAction(tr("Load State from Selected Slot"), this, &MenuBar::StateLoadSlot);
337 m_state_load_slots_menu = m_state_load_menu->addMenu(tr("Load State from Slot"));
338 m_state_load_menu->addAction(tr("Undo Load State"), this, &MenuBar::StateLoadUndo);
339
340 for (int i = 1; i <= 10; i++)
341 {
342 QAction* action = m_state_load_slots_menu->addAction(QString{});
343
344 connect(action, &QAction::triggered, this, [=]() { emit StateLoadSlotAt(i); });
345 }
346 }
347
AddStateSaveMenu(QMenu * emu_menu)348 void MenuBar::AddStateSaveMenu(QMenu* emu_menu)
349 {
350 m_state_save_menu = emu_menu->addMenu(tr("Sa&ve State"));
351 m_state_save_menu->addAction(tr("Save State to File"), this, &MenuBar::StateSave);
352 m_state_save_menu->addAction(tr("Save State to Selected Slot"), this, &MenuBar::StateSaveSlot);
353 m_state_save_menu->addAction(tr("Save State to Oldest Slot"), this, &MenuBar::StateSaveOldest);
354 m_state_save_slots_menu = m_state_save_menu->addMenu(tr("Save State to Slot"));
355 m_state_save_menu->addAction(tr("Undo Save State"), this, &MenuBar::StateSaveUndo);
356
357 for (int i = 1; i <= 10; i++)
358 {
359 QAction* action = m_state_save_slots_menu->addAction(QString{});
360
361 connect(action, &QAction::triggered, this, [=]() { emit StateSaveSlotAt(i); });
362 }
363 }
364
AddStateSlotMenu(QMenu * emu_menu)365 void MenuBar::AddStateSlotMenu(QMenu* emu_menu)
366 {
367 m_state_slot_menu = emu_menu->addMenu(tr("Select State Slot"));
368 m_state_slots = new QActionGroup(this);
369
370 for (int i = 1; i <= 10; i++)
371 {
372 QAction* action = m_state_slot_menu->addAction(QString{});
373 action->setCheckable(true);
374 action->setActionGroup(m_state_slots);
375 if (Settings::Instance().GetStateSlot() == i)
376 action->setChecked(true);
377
378 connect(action, &QAction::triggered, this, [=]() { emit SetStateSlot(i); });
379 }
380 }
381
UpdateStateSlotMenu()382 void MenuBar::UpdateStateSlotMenu()
383 {
384 QList<QAction*> actions_slot = m_state_slots->actions();
385 QList<QAction*> actions_load = m_state_load_slots_menu->actions();
386 QList<QAction*> actions_save = m_state_save_slots_menu->actions();
387 for (int i = 0; i < actions_slot.length(); i++)
388 {
389 int slot = i + 1;
390 QString info = QString::fromStdString(State::GetInfoStringOfSlot(slot));
391 actions_load.at(i)->setText(tr("Load from Slot %1 - %2").arg(slot).arg(info));
392 actions_save.at(i)->setText(tr("Save to Slot %1 - %2").arg(slot).arg(info));
393 actions_slot.at(i)->setText(tr("Select Slot %1 - %2").arg(slot).arg(info));
394 }
395 }
396
AddViewMenu()397 void MenuBar::AddViewMenu()
398 {
399 QMenu* view_menu = addMenu(tr("&View"));
400 QAction* show_log = view_menu->addAction(tr("Show &Log"));
401 show_log->setCheckable(true);
402 show_log->setChecked(Settings::Instance().IsLogVisible());
403
404 connect(show_log, &QAction::toggled, &Settings::Instance(), &Settings::SetLogVisible);
405
406 QAction* show_log_config = view_menu->addAction(tr("Show Log &Configuration"));
407 show_log_config->setCheckable(true);
408 show_log_config->setChecked(Settings::Instance().IsLogConfigVisible());
409
410 connect(show_log_config, &QAction::toggled, &Settings::Instance(),
411 &Settings::SetLogConfigVisible);
412
413 QAction* show_toolbar = view_menu->addAction(tr("Show &Toolbar"));
414 show_toolbar->setCheckable(true);
415 show_toolbar->setChecked(Settings::Instance().IsToolBarVisible());
416
417 connect(show_toolbar, &QAction::toggled, &Settings::Instance(), &Settings::SetToolBarVisible);
418
419 connect(&Settings::Instance(), &Settings::LogVisibilityChanged, show_log, &QAction::setChecked);
420 connect(&Settings::Instance(), &Settings::LogConfigVisibilityChanged, show_log_config,
421 &QAction::setChecked);
422 connect(&Settings::Instance(), &Settings::ToolBarVisibilityChanged, show_toolbar,
423 &QAction::setChecked);
424
425 QAction* lock_widgets = view_menu->addAction(tr("&Lock Widgets In Place"));
426 lock_widgets->setCheckable(true);
427 lock_widgets->setChecked(Settings::Instance().AreWidgetsLocked());
428
429 connect(lock_widgets, &QAction::toggled, &Settings::Instance(), &Settings::SetWidgetsLocked);
430
431 view_menu->addSeparator();
432
433 m_show_code = view_menu->addAction(tr("&Code"));
434 m_show_code->setCheckable(true);
435 m_show_code->setChecked(Settings::Instance().IsCodeVisible());
436
437 connect(m_show_code, &QAction::toggled, &Settings::Instance(), &Settings::SetCodeVisible);
438 connect(&Settings::Instance(), &Settings::CodeVisibilityChanged, m_show_code,
439 &QAction::setChecked);
440
441 m_show_registers = view_menu->addAction(tr("&Registers"));
442 m_show_registers->setCheckable(true);
443 m_show_registers->setChecked(Settings::Instance().IsRegistersVisible());
444
445 connect(m_show_registers, &QAction::toggled, &Settings::Instance(),
446 &Settings::SetRegistersVisible);
447 connect(&Settings::Instance(), &Settings::RegistersVisibilityChanged, m_show_registers,
448 &QAction::setChecked);
449
450 m_show_threads = view_menu->addAction(tr("&Threads"));
451 m_show_threads->setCheckable(true);
452 m_show_threads->setChecked(Settings::Instance().IsThreadsVisible());
453
454 connect(m_show_threads, &QAction::toggled, &Settings::Instance(), &Settings::SetThreadsVisible);
455 connect(&Settings::Instance(), &Settings::ThreadsVisibilityChanged, m_show_threads,
456 &QAction::setChecked);
457
458 // i18n: This kind of "watch" is used for watching emulated memory.
459 // It's not related to timekeeping devices.
460 m_show_watch = view_menu->addAction(tr("&Watch"));
461 m_show_watch->setCheckable(true);
462 m_show_watch->setChecked(Settings::Instance().IsWatchVisible());
463
464 connect(m_show_watch, &QAction::toggled, &Settings::Instance(), &Settings::SetWatchVisible);
465 connect(&Settings::Instance(), &Settings::WatchVisibilityChanged, m_show_watch,
466 &QAction::setChecked);
467
468 m_show_breakpoints = view_menu->addAction(tr("&Breakpoints"));
469 m_show_breakpoints->setCheckable(true);
470 m_show_breakpoints->setChecked(Settings::Instance().IsBreakpointsVisible());
471
472 connect(m_show_breakpoints, &QAction::toggled, &Settings::Instance(),
473 &Settings::SetBreakpointsVisible);
474 connect(&Settings::Instance(), &Settings::BreakpointsVisibilityChanged, m_show_breakpoints,
475 &QAction::setChecked);
476
477 m_show_memory = view_menu->addAction(tr("&Memory"));
478 m_show_memory->setCheckable(true);
479 m_show_memory->setChecked(Settings::Instance().IsMemoryVisible());
480
481 connect(m_show_memory, &QAction::toggled, &Settings::Instance(), &Settings::SetMemoryVisible);
482 connect(&Settings::Instance(), &Settings::MemoryVisibilityChanged, m_show_memory,
483 &QAction::setChecked);
484
485 m_show_network = view_menu->addAction(tr("&Network"));
486 m_show_network->setCheckable(true);
487 m_show_network->setChecked(Settings::Instance().IsNetworkVisible());
488
489 connect(m_show_network, &QAction::toggled, &Settings::Instance(), &Settings::SetNetworkVisible);
490 connect(&Settings::Instance(), &Settings::NetworkVisibilityChanged, m_show_network,
491 &QAction::setChecked);
492
493 m_show_jit = view_menu->addAction(tr("&JIT"));
494 m_show_jit->setCheckable(true);
495 m_show_jit->setChecked(Settings::Instance().IsJITVisible());
496 connect(m_show_jit, &QAction::toggled, &Settings::Instance(), &Settings::SetJITVisible);
497 connect(&Settings::Instance(), &Settings::JITVisibilityChanged, m_show_jit, &QAction::setChecked);
498
499 view_menu->addSeparator();
500
501 AddGameListTypeSection(view_menu);
502 view_menu->addSeparator();
503 AddListColumnsMenu(view_menu);
504 view_menu->addSeparator();
505 AddShowPlatformsMenu(view_menu);
506 AddShowRegionsMenu(view_menu);
507
508 view_menu->addSeparator();
509 view_menu->addAction(tr("Purge Game List Cache"), this, &MenuBar::PurgeGameListCache);
510 view_menu->addSeparator();
511 view_menu->addAction(tr("Search"), this, &MenuBar::ShowSearch, QKeySequence::Find);
512 }
513
AddOptionsMenu()514 void MenuBar::AddOptionsMenu()
515 {
516 QMenu* options_menu = addMenu(tr("&Options"));
517 options_menu->addAction(tr("Co&nfiguration"), this, &MenuBar::Configure,
518 QKeySequence::Preferences);
519 options_menu->addSeparator();
520 options_menu->addAction(tr("&Graphics Settings"), this, &MenuBar::ConfigureGraphics);
521 options_menu->addAction(tr("&Audio Settings"), this, &MenuBar::ConfigureAudio);
522 m_controllers_action =
523 options_menu->addAction(tr("&Controller Settings"), this, &MenuBar::ConfigureControllers);
524 options_menu->addAction(tr("&Hotkey Settings"), this, &MenuBar::ConfigureHotkeys);
525
526 options_menu->addSeparator();
527
528 // Debugging mode only
529 m_boot_to_pause = options_menu->addAction(tr("Boot to Pause"));
530 m_boot_to_pause->setCheckable(true);
531 m_boot_to_pause->setChecked(SConfig::GetInstance().bBootToPause);
532
533 connect(m_boot_to_pause, &QAction::toggled, this,
534 [](bool enable) { SConfig::GetInstance().bBootToPause = enable; });
535
536 m_automatic_start = options_menu->addAction(tr("&Automatic Start"));
537 m_automatic_start->setCheckable(true);
538 m_automatic_start->setChecked(SConfig::GetInstance().bAutomaticStart);
539
540 connect(m_automatic_start, &QAction::toggled, this,
541 [](bool enable) { SConfig::GetInstance().bAutomaticStart = enable; });
542
543 m_change_font = options_menu->addAction(tr("&Font..."), this, &MenuBar::ChangeDebugFont);
544 }
545
InstallUpdateManually()546 void MenuBar::InstallUpdateManually()
547 {
548 auto& track = SConfig::GetInstance().m_auto_update_track;
549 auto previous_value = track;
550
551 track = "dev";
552
553 auto* updater = new Updater(this->parentWidget());
554
555 if (!updater->CheckForUpdate())
556 {
557 ModalMessageBox::information(
558 this, tr("Update"),
559 tr("You are running the latest version available on this update track."));
560 }
561
562 track = previous_value;
563 }
564
AddHelpMenu()565 void MenuBar::AddHelpMenu()
566 {
567 QMenu* help_menu = addMenu(tr("&Help"));
568
569 QAction* website = help_menu->addAction(tr("&Website"));
570 connect(website, &QAction::triggered, this,
571 []() { QDesktopServices::openUrl(QUrl(QStringLiteral("https://dolphin-emu.org/"))); });
572 QAction* documentation = help_menu->addAction(tr("Online &Documentation"));
573 connect(documentation, &QAction::triggered, this, []() {
574 QDesktopServices::openUrl(QUrl(QStringLiteral("https://dolphin-emu.org/docs/guides")));
575 });
576 QAction* github = help_menu->addAction(tr("&GitHub Repository"));
577 connect(github, &QAction::triggered, this, []() {
578 QDesktopServices::openUrl(QUrl(QStringLiteral("https://github.com/dolphin-emu/dolphin")));
579 });
580 QAction* bugtracker = help_menu->addAction(tr("&Bug Tracker"));
581 connect(bugtracker, &QAction::triggered, this, []() {
582 QDesktopServices::openUrl(
583 QUrl(QStringLiteral("https://bugs.dolphin-emu.org/projects/emulator")));
584 });
585
586 if (AutoUpdateChecker::SystemSupportsAutoUpdates())
587 {
588 help_menu->addSeparator();
589
590 help_menu->addAction(tr("&Check for Updates..."), this, &MenuBar::InstallUpdateManually);
591 }
592
593 #ifndef __APPLE__
594 help_menu->addSeparator();
595 #endif
596
597 help_menu->addAction(tr("&About"), this, &MenuBar::ShowAboutDialog);
598 }
599
AddGameListTypeSection(QMenu * view_menu)600 void MenuBar::AddGameListTypeSection(QMenu* view_menu)
601 {
602 QAction* list_view = view_menu->addAction(tr("List View"));
603 list_view->setCheckable(true);
604
605 QAction* grid_view = view_menu->addAction(tr("Grid View"));
606 grid_view->setCheckable(true);
607
608 QActionGroup* list_group = new QActionGroup(this);
609 list_group->addAction(list_view);
610 list_group->addAction(grid_view);
611
612 bool prefer_list = Settings::Instance().GetPreferredView();
613 list_view->setChecked(prefer_list);
614 grid_view->setChecked(!prefer_list);
615
616 connect(list_view, &QAction::triggered, this, &MenuBar::ShowList);
617 connect(grid_view, &QAction::triggered, this, &MenuBar::ShowGrid);
618 }
619
AddListColumnsMenu(QMenu * view_menu)620 void MenuBar::AddListColumnsMenu(QMenu* view_menu)
621 {
622 static const QMap<QString, bool*> columns{
623 {tr("Platform"), &SConfig::GetInstance().m_showSystemColumn},
624 {tr("Banner"), &SConfig::GetInstance().m_showBannerColumn},
625 {tr("Title"), &SConfig::GetInstance().m_showTitleColumn},
626 {tr("Description"), &SConfig::GetInstance().m_showDescriptionColumn},
627 {tr("Maker"), &SConfig::GetInstance().m_showMakerColumn},
628 {tr("File Name"), &SConfig::GetInstance().m_showFileNameColumn},
629 {tr("File Path"), &SConfig::GetInstance().m_showFilePathColumn},
630 {tr("Game ID"), &SConfig::GetInstance().m_showIDColumn},
631 {tr("Region"), &SConfig::GetInstance().m_showRegionColumn},
632 {tr("File Size"), &SConfig::GetInstance().m_showSizeColumn},
633 {tr("File Format"), &SConfig::GetInstance().m_showFileFormatColumn},
634 {tr("Block Size"), &SConfig::GetInstance().m_showBlockSizeColumn},
635 {tr("Compression"), &SConfig::GetInstance().m_showCompressionColumn},
636 {tr("Tags"), &SConfig::GetInstance().m_showTagsColumn}};
637
638 QActionGroup* column_group = new QActionGroup(this);
639 m_cols_menu = view_menu->addMenu(tr("List Columns"));
640 column_group->setExclusive(false);
641
642 for (const auto& key : columns.keys())
643 {
644 bool* config = columns[key];
645 QAction* action = column_group->addAction(m_cols_menu->addAction(key));
646 action->setCheckable(true);
647 action->setChecked(*config);
648 connect(action, &QAction::toggled, [this, config, key](bool value) {
649 *config = value;
650 emit ColumnVisibilityToggled(key, value);
651 });
652 }
653 }
654
AddShowPlatformsMenu(QMenu * view_menu)655 void MenuBar::AddShowPlatformsMenu(QMenu* view_menu)
656 {
657 static const QMap<QString, bool*> platform_map{
658 {tr("Show Wii"), &SConfig::GetInstance().m_ListWii},
659 {tr("Show GameCube"), &SConfig::GetInstance().m_ListGC},
660 {tr("Show WAD"), &SConfig::GetInstance().m_ListWad},
661 {tr("Show ELF/DOL"), &SConfig::GetInstance().m_ListElfDol}};
662
663 QActionGroup* platform_group = new QActionGroup(this);
664 QMenu* plat_menu = view_menu->addMenu(tr("Show Platforms"));
665 platform_group->setExclusive(false);
666
667 for (const auto& key : platform_map.keys())
668 {
669 bool* config = platform_map[key];
670 QAction* action = platform_group->addAction(plat_menu->addAction(key));
671 action->setCheckable(true);
672 action->setChecked(*config);
673 connect(action, &QAction::toggled, [this, config, key](bool value) {
674 *config = value;
675 emit GameListPlatformVisibilityToggled(key, value);
676 });
677 }
678 }
679
AddShowRegionsMenu(QMenu * view_menu)680 void MenuBar::AddShowRegionsMenu(QMenu* view_menu)
681 {
682 static const QMap<QString, bool*> region_map{
683 {tr("Show JAP"), &SConfig::GetInstance().m_ListJap},
684 {tr("Show PAL"), &SConfig::GetInstance().m_ListPal},
685 {tr("Show USA"), &SConfig::GetInstance().m_ListUsa},
686 {tr("Show Australia"), &SConfig::GetInstance().m_ListAustralia},
687 {tr("Show France"), &SConfig::GetInstance().m_ListFrance},
688 {tr("Show Germany"), &SConfig::GetInstance().m_ListGermany},
689 {tr("Show Italy"), &SConfig::GetInstance().m_ListItaly},
690 {tr("Show Korea"), &SConfig::GetInstance().m_ListKorea},
691 {tr("Show Netherlands"), &SConfig::GetInstance().m_ListNetherlands},
692 {tr("Show Russia"), &SConfig::GetInstance().m_ListRussia},
693 {tr("Show Spain"), &SConfig::GetInstance().m_ListSpain},
694 {tr("Show Taiwan"), &SConfig::GetInstance().m_ListTaiwan},
695 {tr("Show World"), &SConfig::GetInstance().m_ListWorld},
696 {tr("Show Unknown"), &SConfig::GetInstance().m_ListUnknown}};
697
698 QActionGroup* region_group = new QActionGroup(this);
699 QMenu* region_menu = view_menu->addMenu(tr("Show Regions"));
700 region_group->setExclusive(false);
701
702 for (const auto& key : region_map.keys())
703 {
704 bool* config = region_map[key];
705 QAction* action = region_group->addAction(region_menu->addAction(key));
706 action->setCheckable(true);
707 action->setChecked(*config);
708 connect(action, &QAction::toggled, [this, config, key](bool value) {
709 *config = value;
710 emit GameListRegionVisibilityToggled(key, value);
711 });
712 }
713 }
714
AddMovieMenu()715 void MenuBar::AddMovieMenu()
716 {
717 auto* movie_menu = addMenu(tr("&Movie"));
718 m_recording_start =
719 movie_menu->addAction(tr("Start Re&cording Input"), this, [this] { emit StartRecording(); });
720 m_recording_play =
721 movie_menu->addAction(tr("P&lay Input Recording..."), this, [this] { emit PlayRecording(); });
722 m_recording_stop = movie_menu->addAction(tr("Stop Playing/Recording Input"), this,
723 [this] { emit StopRecording(); });
724 m_recording_export =
725 movie_menu->addAction(tr("Export Recording..."), this, [this] { emit ExportRecording(); });
726
727 m_recording_start->setEnabled(false);
728 m_recording_play->setEnabled(false);
729 m_recording_stop->setEnabled(false);
730 m_recording_export->setEnabled(false);
731
732 m_recording_read_only = movie_menu->addAction(tr("&Read-Only Mode"));
733 m_recording_read_only->setCheckable(true);
734 m_recording_read_only->setChecked(Movie::IsReadOnly());
735 connect(m_recording_read_only, &QAction::toggled, [](bool value) { Movie::SetReadOnly(value); });
736
737 movie_menu->addAction(tr("TAS Input"), this, [this] { emit ShowTASInput(); });
738
739 movie_menu->addSeparator();
740
741 auto* pause_at_end = movie_menu->addAction(tr("Pause at End of Movie"));
742 pause_at_end->setCheckable(true);
743 pause_at_end->setChecked(SConfig::GetInstance().m_PauseMovie);
744 connect(pause_at_end, &QAction::toggled,
745 [](bool value) { SConfig::GetInstance().m_PauseMovie = value; });
746
747 auto* lag_counter = movie_menu->addAction(tr("Show Lag Counter"));
748 lag_counter->setCheckable(true);
749 lag_counter->setChecked(SConfig::GetInstance().m_ShowLag);
750 connect(lag_counter, &QAction::toggled,
751 [](bool value) { SConfig::GetInstance().m_ShowLag = value; });
752
753 auto* frame_counter = movie_menu->addAction(tr("Show Frame Counter"));
754 frame_counter->setCheckable(true);
755 frame_counter->setChecked(SConfig::GetInstance().m_ShowFrameCount);
756 connect(frame_counter, &QAction::toggled,
757 [](bool value) { SConfig::GetInstance().m_ShowFrameCount = value; });
758
759 auto* input_display = movie_menu->addAction(tr("Show Input Display"));
760 input_display->setCheckable(true);
761 input_display->setChecked(SConfig::GetInstance().m_ShowInputDisplay);
762 connect(input_display, &QAction::toggled,
763 [](bool value) { SConfig::GetInstance().m_ShowInputDisplay = value; });
764
765 auto* system_clock = movie_menu->addAction(tr("Show System Clock"));
766 system_clock->setCheckable(true);
767 system_clock->setChecked(SConfig::GetInstance().m_ShowRTC);
768 connect(system_clock, &QAction::toggled,
769 [](bool value) { SConfig::GetInstance().m_ShowRTC = value; });
770
771 movie_menu->addSeparator();
772
773 auto* dump_frames = movie_menu->addAction(tr("Dump Frames"));
774 dump_frames->setCheckable(true);
775 dump_frames->setChecked(SConfig::GetInstance().m_DumpFrames);
776 connect(dump_frames, &QAction::toggled,
777 [](bool value) { SConfig::GetInstance().m_DumpFrames = value; });
778
779 auto* dump_audio = movie_menu->addAction(tr("Dump Audio"));
780 dump_audio->setCheckable(true);
781 dump_audio->setChecked(SConfig::GetInstance().m_DumpAudio);
782 connect(dump_audio, &QAction::toggled,
783 [](bool value) { SConfig::GetInstance().m_DumpAudio = value; });
784 }
785
AddJITMenu()786 void MenuBar::AddJITMenu()
787 {
788 m_jit = addMenu(tr("JIT"));
789
790 m_jit_interpreter_core = m_jit->addAction(tr("Interpreter Core"));
791 m_jit_interpreter_core->setCheckable(true);
792 m_jit_interpreter_core->setChecked(SConfig::GetInstance().cpu_core ==
793 PowerPC::CPUCore::Interpreter);
794
795 connect(m_jit_interpreter_core, &QAction::toggled, [](bool enabled) {
796 PowerPC::SetMode(enabled ? PowerPC::CoreMode::Interpreter : PowerPC::CoreMode::JIT);
797 });
798
799 m_jit->addSeparator();
800
801 m_jit_block_linking = m_jit->addAction(tr("JIT Block Linking Off"));
802 m_jit_block_linking->setCheckable(true);
803 m_jit_block_linking->setChecked(SConfig::GetInstance().bJITNoBlockLinking);
804 connect(m_jit_block_linking, &QAction::toggled, [this](bool enabled) {
805 SConfig::GetInstance().bJITNoBlockLinking = enabled;
806 ClearCache();
807 });
808
809 m_jit_disable_cache = m_jit->addAction(tr("Disable JIT Cache"));
810 m_jit_disable_cache->setCheckable(true);
811 m_jit_disable_cache->setChecked(SConfig::GetInstance().bJITNoBlockCache);
812 connect(m_jit_disable_cache, &QAction::toggled, [this](bool enabled) {
813 SConfig::GetInstance().bJITNoBlockCache = enabled;
814 ClearCache();
815 });
816
817 m_jit_disable_fastmem = m_jit->addAction(tr("Disable Fastmem"));
818 m_jit_disable_fastmem->setCheckable(true);
819 m_jit_disable_fastmem->setChecked(!SConfig::GetInstance().bFastmem);
820 connect(m_jit_disable_fastmem, &QAction::toggled, [this](bool enabled) {
821 SConfig::GetInstance().bFastmem = !enabled;
822 ClearCache();
823 });
824
825 m_jit_clear_cache = m_jit->addAction(tr("Clear Cache"), this, &MenuBar::ClearCache);
826
827 m_jit->addSeparator();
828
829 m_jit_log_coverage =
830 m_jit->addAction(tr("Log JIT Instruction Coverage"), this, &MenuBar::LogInstructions);
831 m_jit_search_instruction =
832 m_jit->addAction(tr("Search for an Instruction"), this, &MenuBar::SearchInstruction);
833
834 m_jit->addSeparator();
835
836 m_jit_off = m_jit->addAction(tr("JIT Off (JIT Core)"));
837 m_jit_off->setCheckable(true);
838 m_jit_off->setChecked(SConfig::GetInstance().bJITOff);
839 connect(m_jit_off, &QAction::toggled, [this](bool enabled) {
840 SConfig::GetInstance().bJITOff = enabled;
841 ClearCache();
842 });
843
844 m_jit_loadstore_off = m_jit->addAction(tr("JIT LoadStore Off"));
845 m_jit_loadstore_off->setCheckable(true);
846 m_jit_loadstore_off->setChecked(SConfig::GetInstance().bJITLoadStoreOff);
847 connect(m_jit_loadstore_off, &QAction::toggled, [this](bool enabled) {
848 SConfig::GetInstance().bJITLoadStoreOff = enabled;
849 ClearCache();
850 });
851
852 m_jit_loadstore_lbzx_off = m_jit->addAction(tr("JIT LoadStore lbzx Off"));
853 m_jit_loadstore_lbzx_off->setCheckable(true);
854 m_jit_loadstore_lbzx_off->setChecked(SConfig::GetInstance().bJITLoadStorelbzxOff);
855 connect(m_jit_loadstore_lbzx_off, &QAction::toggled, [this](bool enabled) {
856 SConfig::GetInstance().bJITLoadStorelbzxOff = enabled;
857 ClearCache();
858 });
859
860 m_jit_loadstore_lxz_off = m_jit->addAction(tr("JIT LoadStore lXz Off"));
861 m_jit_loadstore_lxz_off->setCheckable(true);
862 m_jit_loadstore_lxz_off->setChecked(SConfig::GetInstance().bJITLoadStorelXzOff);
863 connect(m_jit_loadstore_lxz_off, &QAction::toggled, [this](bool enabled) {
864 SConfig::GetInstance().bJITLoadStorelXzOff = enabled;
865 ClearCache();
866 });
867
868 m_jit_loadstore_lwz_off = m_jit->addAction(tr("JIT LoadStore lwz Off"));
869 m_jit_loadstore_lwz_off->setCheckable(true);
870 m_jit_loadstore_lwz_off->setChecked(SConfig::GetInstance().bJITLoadStorelwzOff);
871 connect(m_jit_loadstore_lwz_off, &QAction::toggled, [this](bool enabled) {
872 SConfig::GetInstance().bJITLoadStorelwzOff = enabled;
873 ClearCache();
874 });
875
876 m_jit_loadstore_floating_off = m_jit->addAction(tr("JIT LoadStore Floating Off"));
877 m_jit_loadstore_floating_off->setCheckable(true);
878 m_jit_loadstore_floating_off->setChecked(SConfig::GetInstance().bJITLoadStoreFloatingOff);
879 connect(m_jit_loadstore_floating_off, &QAction::toggled, [this](bool enabled) {
880 SConfig::GetInstance().bJITLoadStoreFloatingOff = enabled;
881 ClearCache();
882 });
883
884 m_jit_loadstore_paired_off = m_jit->addAction(tr("JIT LoadStore Paired Off"));
885 m_jit_loadstore_paired_off->setCheckable(true);
886 m_jit_loadstore_paired_off->setChecked(SConfig::GetInstance().bJITLoadStorePairedOff);
887 connect(m_jit_loadstore_paired_off, &QAction::toggled, [this](bool enabled) {
888 SConfig::GetInstance().bJITLoadStorePairedOff = enabled;
889 ClearCache();
890 });
891
892 m_jit_floatingpoint_off = m_jit->addAction(tr("JIT FloatingPoint Off"));
893 m_jit_floatingpoint_off->setCheckable(true);
894 m_jit_floatingpoint_off->setChecked(SConfig::GetInstance().bJITFloatingPointOff);
895 connect(m_jit_floatingpoint_off, &QAction::toggled, [this](bool enabled) {
896 SConfig::GetInstance().bJITFloatingPointOff = enabled;
897 ClearCache();
898 });
899
900 m_jit_integer_off = m_jit->addAction(tr("JIT Integer Off"));
901 m_jit_integer_off->setCheckable(true);
902 m_jit_integer_off->setChecked(SConfig::GetInstance().bJITIntegerOff);
903 connect(m_jit_integer_off, &QAction::toggled, [this](bool enabled) {
904 SConfig::GetInstance().bJITIntegerOff = enabled;
905 ClearCache();
906 });
907
908 m_jit_paired_off = m_jit->addAction(tr("JIT Paired Off"));
909 m_jit_paired_off->setCheckable(true);
910 m_jit_paired_off->setChecked(SConfig::GetInstance().bJITPairedOff);
911 connect(m_jit_paired_off, &QAction::toggled, [this](bool enabled) {
912 SConfig::GetInstance().bJITPairedOff = enabled;
913 ClearCache();
914 });
915
916 m_jit_systemregisters_off = m_jit->addAction(tr("JIT SystemRegisters Off"));
917 m_jit_systemregisters_off->setCheckable(true);
918 m_jit_systemregisters_off->setChecked(SConfig::GetInstance().bJITSystemRegistersOff);
919 connect(m_jit_systemregisters_off, &QAction::toggled, [this](bool enabled) {
920 SConfig::GetInstance().bJITSystemRegistersOff = enabled;
921 ClearCache();
922 });
923
924 m_jit_branch_off = m_jit->addAction(tr("JIT Branch Off"));
925 m_jit_branch_off->setCheckable(true);
926 m_jit_branch_off->setChecked(SConfig::GetInstance().bJITBranchOff);
927 connect(m_jit_branch_off, &QAction::toggled, [this](bool enabled) {
928 SConfig::GetInstance().bJITBranchOff = enabled;
929 ClearCache();
930 });
931
932 m_jit_register_cache_off = m_jit->addAction(tr("JIT Register Cache Off"));
933 m_jit_register_cache_off->setCheckable(true);
934 m_jit_register_cache_off->setChecked(SConfig::GetInstance().bJITRegisterCacheOff);
935 connect(m_jit_register_cache_off, &QAction::toggled, [this](bool enabled) {
936 SConfig::GetInstance().bJITRegisterCacheOff = enabled;
937 ClearCache();
938 });
939 }
940
AddSymbolsMenu()941 void MenuBar::AddSymbolsMenu()
942 {
943 m_symbols = addMenu(tr("Symbols"));
944
945 m_symbols->addAction(tr("&Clear Symbols"), this, &MenuBar::ClearSymbols);
946
947 auto* generate = m_symbols->addMenu(tr("&Generate Symbols From"));
948 generate->addAction(tr("Address"), this, &MenuBar::GenerateSymbolsFromAddress);
949 generate->addAction(tr("Signature Database"), this, &MenuBar::GenerateSymbolsFromSignatureDB);
950 generate->addAction(tr("RSO Modules"), this, &MenuBar::GenerateSymbolsFromRSO);
951 m_symbols->addSeparator();
952
953 m_symbols->addAction(tr("&Load Symbol Map"), this, &MenuBar::LoadSymbolMap);
954 m_symbols->addAction(tr("&Save Symbol Map"), this, &MenuBar::SaveSymbolMap);
955 m_symbols->addSeparator();
956
957 m_symbols->addAction(tr("Load &Other Map File..."), this, &MenuBar::LoadOtherSymbolMap);
958 m_symbols->addAction(tr("Load &Bad Map File..."), this, &MenuBar::LoadBadSymbolMap);
959 m_symbols->addAction(tr("Save Symbol Map &As..."), this, &MenuBar::SaveSymbolMapAs);
960 m_symbols->addSeparator();
961
962 m_symbols->addAction(tr("Sa&ve Code"), this, &MenuBar::SaveCode);
963 m_symbols->addSeparator();
964
965 m_symbols->addAction(tr("C&reate Signature File..."), this, &MenuBar::CreateSignatureFile);
966 m_symbols->addAction(tr("Append to &Existing Signature File..."), this,
967 &MenuBar::AppendSignatureFile);
968 m_symbols->addAction(tr("Combine &Two Signature Files..."), this,
969 &MenuBar::CombineSignatureFiles);
970 m_symbols->addAction(tr("Appl&y Signature File..."), this, &MenuBar::ApplySignatureFile);
971 m_symbols->addSeparator();
972
973 m_symbols->addAction(tr("&Patch HLE Functions"), this, &MenuBar::PatchHLEFunctions);
974 }
975
UpdateToolsMenu(bool emulation_started)976 void MenuBar::UpdateToolsMenu(bool emulation_started)
977 {
978 m_boot_sysmenu->setEnabled(!emulation_started);
979 m_perform_online_update_menu->setEnabled(!emulation_started);
980 m_ntscj_ipl->setEnabled(!emulation_started &&
981 File::Exists(SConfig::GetInstance().GetBootROMPath(JAP_DIR)));
982 m_ntscu_ipl->setEnabled(!emulation_started &&
983 File::Exists(SConfig::GetInstance().GetBootROMPath(USA_DIR)));
984 m_pal_ipl->setEnabled(!emulation_started &&
985 File::Exists(SConfig::GetInstance().GetBootROMPath(EUR_DIR)));
986 m_import_backup->setEnabled(!emulation_started);
987 m_check_nand->setEnabled(!emulation_started);
988
989 if (!emulation_started)
990 {
991 IOS::HLE::Kernel ios;
992 const auto tmd = ios.GetES()->FindInstalledTMD(Titles::SYSTEM_MENU);
993
994 const QString sysmenu_version =
995 tmd.IsValid() ?
996 QString::fromStdString(DiscIO::GetSysMenuVersionString(tmd.GetTitleVersion())) :
997 QString{};
998 m_boot_sysmenu->setText(tr("Load Wii System Menu %1").arg(sysmenu_version));
999
1000 m_boot_sysmenu->setEnabled(tmd.IsValid());
1001
1002 for (QAction* action : m_perform_online_update_menu->actions())
1003 action->setEnabled(!tmd.IsValid());
1004 m_perform_online_update_for_current_region->setEnabled(tmd.IsValid());
1005 }
1006
1007 const auto ios = IOS::HLE::GetIOS();
1008 const auto bt = ios ? std::static_pointer_cast<IOS::HLE::Device::BluetoothEmu>(
1009 ios->GetDeviceByName("/dev/usb/oh1/57e/305")) :
1010 nullptr;
1011 const bool enable_wiimotes =
1012 emulation_started && bt && !SConfig::GetInstance().m_bt_passthrough_enabled;
1013
1014 for (std::size_t i = 0; i < m_wii_remotes.size(); i++)
1015 {
1016 QAction* const wii_remote = m_wii_remotes[i];
1017
1018 wii_remote->setEnabled(enable_wiimotes);
1019 if (enable_wiimotes)
1020 wii_remote->setChecked(bt->AccessWiimoteByIndex(i)->IsConnected());
1021 }
1022 }
1023
InstallWAD()1024 void MenuBar::InstallWAD()
1025 {
1026 QString wad_file = QFileDialog::getOpenFileName(this, tr("Select a title to install to NAND"),
1027 QString(), tr("WAD files (*.wad)"));
1028
1029 if (wad_file.isEmpty())
1030 return;
1031
1032 if (WiiUtils::InstallWAD(wad_file.toStdString()))
1033 {
1034 Settings::Instance().NANDRefresh();
1035 ModalMessageBox::information(this, tr("Success"),
1036 tr("Successfully installed this title to the NAND."));
1037 }
1038 else
1039 {
1040 ModalMessageBox::critical(this, tr("Failure"), tr("Failed to install this title to the NAND."));
1041 }
1042 }
1043
ImportWiiSave()1044 void MenuBar::ImportWiiSave()
1045 {
1046 QString file = QFileDialog::getOpenFileName(this, tr("Select the save file"), QDir::currentPath(),
1047 tr("Wii save files (*.bin);;"
1048 "All Files (*)"));
1049
1050 if (file.isEmpty())
1051 return;
1052
1053 bool cancelled = false;
1054 auto can_overwrite = [&] {
1055 bool yes = ModalMessageBox::question(
1056 this, tr("Save Import"),
1057 tr("Save data for this title already exists in the NAND. Consider backing up "
1058 "the current data before overwriting.\nOverwrite now?")) == QMessageBox::Yes;
1059 cancelled = !yes;
1060 return yes;
1061 };
1062 if (WiiSave::Import(file.toStdString(), can_overwrite))
1063 ModalMessageBox::information(this, tr("Save Import"), tr("Successfully imported save files."));
1064 else if (!cancelled)
1065 ModalMessageBox::critical(this, tr("Save Import"), tr("Failed to import save files."));
1066 }
1067
ExportWiiSaves()1068 void MenuBar::ExportWiiSaves()
1069 {
1070 const QString export_dir = QFileDialog::getExistingDirectory(
1071 this, tr("Select Export Directory"), QString::fromStdString(File::GetUserPath(D_USER_IDX)),
1072 QFileDialog::ShowDirsOnly);
1073 if (export_dir.isEmpty())
1074 return;
1075
1076 const size_t count = WiiSave::ExportAll(export_dir.toStdString());
1077 ModalMessageBox::information(this, tr("Save Export"),
1078 tr("Exported %n save(s)", "", static_cast<int>(count)));
1079 }
1080
CheckNAND()1081 void MenuBar::CheckNAND()
1082 {
1083 IOS::HLE::Kernel ios;
1084 WiiUtils::NANDCheckResult result = WiiUtils::CheckNAND(ios);
1085 if (!result.bad)
1086 {
1087 ModalMessageBox::information(this, tr("NAND Check"), tr("No issues have been detected."));
1088 return;
1089 }
1090
1091 QString message = tr("The emulated NAND is damaged. System titles such as the Wii Menu and "
1092 "the Wii Shop Channel may not work correctly.\n\n"
1093 "Do you want to try to repair the NAND?");
1094 if (!result.titles_to_remove.empty())
1095 {
1096 std::string title_listings;
1097 Core::TitleDatabase title_db;
1098 const DiscIO::Language language = SConfig::GetInstance().GetCurrentLanguage(true);
1099 for (const u64 title_id : result.titles_to_remove)
1100 {
1101 title_listings += StringFromFormat("%016" PRIx64, title_id);
1102
1103 const std::string database_name = title_db.GetChannelName(title_id, language);
1104 if (!database_name.empty())
1105 {
1106 title_listings += " - " + database_name;
1107 }
1108 else
1109 {
1110 DiscIO::WiiSaveBanner banner(title_id);
1111 if (banner.IsValid())
1112 {
1113 title_listings += " - " + banner.GetName();
1114 const std::string description = banner.GetDescription();
1115 if (!StripSpaces(description).empty())
1116 title_listings += " - " + description;
1117 }
1118 }
1119
1120 title_listings += "\n";
1121 }
1122
1123 message += tr("\n\nWARNING: Fixing this NAND requires the deletion of titles that have "
1124 "incomplete data on the NAND, including all associated save data. "
1125 "By continuing, the following title(s) will be removed:\n\n"
1126 "%1"
1127 "\nLaunching these titles may also fix the issues.")
1128 .arg(QString::fromStdString(title_listings));
1129 }
1130
1131 if (ModalMessageBox::question(this, tr("NAND Check"), message) != QMessageBox::Yes)
1132 return;
1133
1134 if (WiiUtils::RepairNAND(ios))
1135 {
1136 ModalMessageBox::information(this, tr("NAND Check"), tr("The NAND has been repaired."));
1137 return;
1138 }
1139
1140 ModalMessageBox::critical(this, tr("NAND Check"),
1141 tr("The NAND could not be repaired. It is recommended to back up "
1142 "your current data and start over with a fresh NAND."));
1143 }
1144
NANDExtractCertificates()1145 void MenuBar::NANDExtractCertificates()
1146 {
1147 if (DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX)))
1148 {
1149 ModalMessageBox::information(this, tr("Success"),
1150 tr("Successfully extracted certificates from NAND"));
1151 }
1152 else
1153 {
1154 ModalMessageBox::critical(this, tr("Error"), tr("Failed to extract certificates from NAND"));
1155 }
1156 }
1157
OnSelectionChanged(std::shared_ptr<const UICommon::GameFile> game_file)1158 void MenuBar::OnSelectionChanged(std::shared_ptr<const UICommon::GameFile> game_file)
1159 {
1160 m_game_selected = !!game_file;
1161
1162 m_recording_play->setEnabled(m_game_selected && !Core::IsRunning());
1163 m_recording_start->setEnabled((m_game_selected || Core::IsRunning()) && !Movie::IsPlayingInput());
1164 }
1165
OnRecordingStatusChanged(bool recording)1166 void MenuBar::OnRecordingStatusChanged(bool recording)
1167 {
1168 m_recording_start->setEnabled(!recording && (m_game_selected || Core::IsRunning()));
1169 m_recording_stop->setEnabled(recording);
1170 m_recording_export->setEnabled(recording);
1171 }
1172
OnReadOnlyModeChanged(bool read_only)1173 void MenuBar::OnReadOnlyModeChanged(bool read_only)
1174 {
1175 m_recording_read_only->setChecked(read_only);
1176 }
1177
ChangeDebugFont()1178 void MenuBar::ChangeDebugFont()
1179 {
1180 bool okay;
1181 QFont font = QFontDialog::getFont(&okay, Settings::Instance().GetDebugFont(), this,
1182 tr("Pick a debug font"));
1183
1184 if (okay)
1185 Settings::Instance().SetDebugFont(font);
1186 }
1187
ClearSymbols()1188 void MenuBar::ClearSymbols()
1189 {
1190 auto result = ModalMessageBox::warning(this, tr("Confirmation"),
1191 tr("Do you want to clear the list of symbol names?"),
1192 QMessageBox::Yes | QMessageBox::Cancel);
1193
1194 if (result == QMessageBox::Cancel)
1195 return;
1196
1197 g_symbolDB.Clear();
1198 emit NotifySymbolsUpdated();
1199 }
1200
GenerateSymbolsFromAddress()1201 void MenuBar::GenerateSymbolsFromAddress()
1202 {
1203 PPCAnalyst::FindFunctions(Memory::MEM1_BASE_ADDR,
1204 Memory::MEM1_BASE_ADDR + Memory::GetRamSizeReal(), &g_symbolDB);
1205 emit NotifySymbolsUpdated();
1206 }
1207
GenerateSymbolsFromSignatureDB()1208 void MenuBar::GenerateSymbolsFromSignatureDB()
1209 {
1210 PPCAnalyst::FindFunctions(Memory::MEM1_BASE_ADDR,
1211 Memory::MEM1_BASE_ADDR + Memory::GetRamSizeReal(), &g_symbolDB);
1212 SignatureDB db(SignatureDB::HandlerType::DSY);
1213 if (db.Load(File::GetSysDirectory() + TOTALDB))
1214 {
1215 db.Apply(&g_symbolDB);
1216 ModalMessageBox::information(
1217 this, tr("Information"),
1218 tr("Generated symbol names from '%1'").arg(QString::fromStdString(TOTALDB)));
1219 db.List();
1220 }
1221 else
1222 {
1223 ModalMessageBox::critical(
1224 this, tr("Error"),
1225 tr("'%1' not found, no symbol names generated").arg(QString::fromStdString(TOTALDB)));
1226 }
1227
1228 emit NotifySymbolsUpdated();
1229 }
1230
GenerateSymbolsFromRSO()1231 void MenuBar::GenerateSymbolsFromRSO()
1232 {
1233 // i18n: RSO refers to a proprietary format for shared objects (like DLL files).
1234 const int ret =
1235 ModalMessageBox::question(this, tr("RSO auto-detection"), tr("Auto-detect RSO modules?"));
1236 if (ret == QMessageBox::Yes)
1237 return GenerateSymbolsFromRSOAuto();
1238
1239 QString text = QInputDialog::getText(this, tr("Input"), tr("Enter the RSO module address:"));
1240 bool good;
1241 uint address = text.toUInt(&good, 16);
1242
1243 if (!good)
1244 {
1245 ModalMessageBox::warning(this, tr("Error"), tr("Invalid RSO module address: %1").arg(text));
1246 return;
1247 }
1248
1249 RSOChainView rso_chain;
1250 if (rso_chain.Load(static_cast<u32>(address)))
1251 {
1252 rso_chain.Apply(&g_symbolDB);
1253 emit NotifySymbolsUpdated();
1254 }
1255 else
1256 {
1257 ModalMessageBox::warning(this, tr("Error"), tr("Failed to load RSO module at %1").arg(text));
1258 }
1259 }
1260
GenerateSymbolsFromRSOAuto()1261 void MenuBar::GenerateSymbolsFromRSOAuto()
1262 {
1263 constexpr std::array<std::string_view, 2> search_for = {".elf", ".plf"};
1264 const AddressSpace::Accessors* accessors =
1265 AddressSpace::GetAccessors(AddressSpace::Type::Effective);
1266 std::vector<std::pair<u32, std::string>> matches;
1267
1268 // Find filepath to elf/plf commonly used by RSO modules
1269 for (const auto& str : search_for)
1270 {
1271 u32 next = 0;
1272 while (true)
1273 {
1274 auto found_addr =
1275 accessors->Search(next, reinterpret_cast<const u8*>(str.data()), str.size() + 1, true);
1276
1277 if (!found_addr.has_value())
1278 break;
1279 next = *found_addr + 1;
1280
1281 // Get the beginning of the string
1282 found_addr = accessors->Search(*found_addr, reinterpret_cast<const u8*>(""), 1, false);
1283 if (!found_addr.has_value())
1284 continue;
1285
1286 // Get the string reference
1287 const u32 ref_addr = *found_addr + 1;
1288 const std::array<u8, 4> ref = {static_cast<u8>(ref_addr >> 24),
1289 static_cast<u8>(ref_addr >> 16),
1290 static_cast<u8>(ref_addr >> 8), static_cast<u8>(ref_addr)};
1291 found_addr = accessors->Search(ref_addr, ref.data(), ref.size(), false);
1292 if (!found_addr.has_value() || *found_addr < 16)
1293 continue;
1294
1295 // Go to the beginning of the RSO header
1296 matches.emplace_back(*found_addr - 16, PowerPC::HostGetString(ref_addr, 128));
1297 }
1298 }
1299
1300 QStringList items;
1301 for (const auto& match : matches)
1302 {
1303 const QString item = QLatin1String("%1 %2");
1304
1305 items << item.arg(QString::number(match.first, 16), QString::fromStdString(match.second));
1306 }
1307
1308 if (items.empty())
1309 {
1310 ModalMessageBox::warning(this, tr("Error"), tr("Unable to auto-detect RSO module"));
1311 return;
1312 }
1313
1314 bool ok;
1315 const QString item = QInputDialog::getItem(
1316 this, tr("Input"), tr("Select the RSO module address:"), items, 0, false, &ok);
1317
1318 if (!ok)
1319 return;
1320
1321 RSOChainView rso_chain;
1322 const u32 address = item.mid(0, item.indexOf(QLatin1Char(' '))).toUInt(nullptr, 16);
1323 if (rso_chain.Load(address))
1324 {
1325 rso_chain.Apply(&g_symbolDB);
1326 emit NotifySymbolsUpdated();
1327 }
1328 else
1329 {
1330 ModalMessageBox::warning(this, tr("Error"), tr("Failed to load RSO module at %1").arg(address));
1331 }
1332 }
1333
LoadSymbolMap()1334 void MenuBar::LoadSymbolMap()
1335 {
1336 std::string existing_map_file, writable_map_file;
1337 bool map_exists = CBoot::FindMapFile(&existing_map_file, &writable_map_file);
1338
1339 if (!map_exists)
1340 {
1341 g_symbolDB.Clear();
1342 PPCAnalyst::FindFunctions(Memory::MEM1_BASE_ADDR + 0x1300000,
1343 Memory::MEM1_BASE_ADDR + Memory::GetRamSizeReal(), &g_symbolDB);
1344 SignatureDB db(SignatureDB::HandlerType::DSY);
1345 if (db.Load(File::GetSysDirectory() + TOTALDB))
1346 db.Apply(&g_symbolDB);
1347
1348 ModalMessageBox::warning(this, tr("Warning"),
1349 tr("'%1' not found, scanning for common functions instead")
1350 .arg(QString::fromStdString(writable_map_file)));
1351 }
1352 else
1353 {
1354 const QString existing_map_file_path = QString::fromStdString(existing_map_file);
1355
1356 if (!TryLoadMapFile(existing_map_file_path))
1357 return;
1358
1359 ModalMessageBox::information(this, tr("Information"),
1360 tr("Loaded symbols from '%1'").arg(existing_map_file_path));
1361 }
1362
1363 HLE::PatchFunctions();
1364 emit NotifySymbolsUpdated();
1365 }
1366
SaveSymbolMap()1367 void MenuBar::SaveSymbolMap()
1368 {
1369 std::string existing_map_file, writable_map_file;
1370 CBoot::FindMapFile(&existing_map_file, &writable_map_file);
1371
1372 TrySaveSymbolMap(QString::fromStdString(writable_map_file));
1373 }
1374
LoadOtherSymbolMap()1375 void MenuBar::LoadOtherSymbolMap()
1376 {
1377 const QString file = QFileDialog::getOpenFileName(
1378 this, tr("Load map file"), QString::fromStdString(File::GetUserPath(D_MAPS_IDX)),
1379 tr("Dolphin Map File (*.map)"));
1380
1381 if (file.isEmpty())
1382 return;
1383
1384 if (!TryLoadMapFile(file))
1385 return;
1386
1387 HLE::PatchFunctions();
1388 emit NotifySymbolsUpdated();
1389 }
1390
LoadBadSymbolMap()1391 void MenuBar::LoadBadSymbolMap()
1392 {
1393 const QString file = QFileDialog::getOpenFileName(
1394 this, tr("Load map file"), QString::fromStdString(File::GetUserPath(D_MAPS_IDX)),
1395 tr("Dolphin Map File (*.map)"));
1396
1397 if (file.isEmpty())
1398 return;
1399
1400 if (!TryLoadMapFile(file, true))
1401 return;
1402
1403 HLE::PatchFunctions();
1404 emit NotifySymbolsUpdated();
1405 }
1406
SaveSymbolMapAs()1407 void MenuBar::SaveSymbolMapAs()
1408 {
1409 const std::string& title_id_str = SConfig::GetInstance().m_debugger_game_id;
1410 const QString file = QFileDialog::getSaveFileName(
1411 this, tr("Save map file"),
1412 QString::fromStdString(File::GetUserPath(D_MAPS_IDX) + "/" + title_id_str + ".map"),
1413 tr("Dolphin Map File (*.map)"));
1414
1415 if (file.isEmpty())
1416 return;
1417
1418 TrySaveSymbolMap(file);
1419 }
1420
SaveCode()1421 void MenuBar::SaveCode()
1422 {
1423 std::string existing_map_file, writable_map_file;
1424 CBoot::FindMapFile(&existing_map_file, &writable_map_file);
1425
1426 const std::string path =
1427 writable_map_file.substr(0, writable_map_file.find_last_of('.')) + "_code.map";
1428
1429 if (!g_symbolDB.SaveCodeMap(path))
1430 {
1431 ModalMessageBox::warning(
1432 this, tr("Error"),
1433 tr("Failed to save code map to path '%1'").arg(QString::fromStdString(path)));
1434 }
1435 }
1436
TryLoadMapFile(const QString & path,const bool bad)1437 bool MenuBar::TryLoadMapFile(const QString& path, const bool bad)
1438 {
1439 if (!g_symbolDB.LoadMap(path.toStdString(), bad))
1440 {
1441 ModalMessageBox::warning(this, tr("Error"), tr("Failed to load map file '%1'").arg(path));
1442 return false;
1443 }
1444
1445 return true;
1446 }
1447
TrySaveSymbolMap(const QString & path)1448 void MenuBar::TrySaveSymbolMap(const QString& path)
1449 {
1450 if (g_symbolDB.SaveSymbolMap(path.toStdString()))
1451 return;
1452
1453 ModalMessageBox::warning(this, tr("Error"),
1454 tr("Failed to save symbol map to path '%1'").arg(path));
1455 }
1456
CreateSignatureFile()1457 void MenuBar::CreateSignatureFile()
1458 {
1459 const QString text = QInputDialog::getText(
1460 this, tr("Input"), tr("Only export symbols with prefix:\n(Blank for all symbols)"));
1461
1462 const QString file = QFileDialog::getSaveFileName(this, tr("Save signature file"),
1463 QDir::homePath(), GetSignatureSelector());
1464 if (file.isEmpty())
1465 return;
1466
1467 const std::string prefix = text.toStdString();
1468 const std::string save_path = file.toStdString();
1469 SignatureDB db(save_path);
1470 db.Populate(&g_symbolDB, prefix);
1471
1472 if (!db.Save(save_path))
1473 {
1474 ModalMessageBox::warning(this, tr("Error"), tr("Failed to save signature file '%1'").arg(file));
1475 return;
1476 }
1477
1478 db.List();
1479 }
1480
AppendSignatureFile()1481 void MenuBar::AppendSignatureFile()
1482 {
1483 const QString text = QInputDialog::getText(
1484 this, tr("Input"), tr("Only append symbols with prefix:\n(Blank for all symbols)"));
1485
1486 const QString file = QFileDialog::getSaveFileName(this, tr("Append signature to"),
1487 QDir::homePath(), GetSignatureSelector());
1488 if (file.isEmpty())
1489 return;
1490
1491 const std::string prefix = text.toStdString();
1492 const std::string signature_path = file.toStdString();
1493 SignatureDB db(signature_path);
1494 db.Populate(&g_symbolDB, prefix);
1495 db.List();
1496 db.Load(signature_path);
1497 if (!db.Save(signature_path))
1498 {
1499 ModalMessageBox::warning(this, tr("Error"),
1500 tr("Failed to append to signature file '%1'").arg(file));
1501 return;
1502 }
1503
1504 db.List();
1505 }
1506
ApplySignatureFile()1507 void MenuBar::ApplySignatureFile()
1508 {
1509 const QString file = QFileDialog::getOpenFileName(this, tr("Apply signature file"),
1510 QDir::homePath(), GetSignatureSelector());
1511
1512 if (file.isEmpty())
1513 return;
1514
1515 const std::string load_path = file.toStdString();
1516 SignatureDB db(load_path);
1517 db.Load(load_path);
1518 db.Apply(&g_symbolDB);
1519 db.List();
1520 HLE::PatchFunctions();
1521 emit NotifySymbolsUpdated();
1522 }
1523
CombineSignatureFiles()1524 void MenuBar::CombineSignatureFiles()
1525 {
1526 const QString priorityFile = QFileDialog::getOpenFileName(
1527 this, tr("Choose priority input file"), QDir::homePath(), GetSignatureSelector());
1528 if (priorityFile.isEmpty())
1529 return;
1530
1531 const QString secondaryFile = QFileDialog::getOpenFileName(
1532 this, tr("Choose secondary input file"), QDir::homePath(), GetSignatureSelector());
1533 if (secondaryFile.isEmpty())
1534 return;
1535
1536 const QString saveFile = QFileDialog::getSaveFileName(this, tr("Save combined output file as"),
1537 QDir::homePath(), GetSignatureSelector());
1538 if (saveFile.isEmpty())
1539 return;
1540
1541 const std::string load_pathPriorityFile = priorityFile.toStdString();
1542 const std::string load_pathSecondaryFile = secondaryFile.toStdString();
1543 const std::string save_path = saveFile.toStdString();
1544 SignatureDB db(load_pathPriorityFile);
1545 db.Load(load_pathPriorityFile);
1546 db.Load(load_pathSecondaryFile);
1547 if (!db.Save(save_path))
1548 {
1549 ModalMessageBox::warning(this, tr("Error"),
1550 tr("Failed to save to signature file '%1'").arg(saveFile));
1551 return;
1552 }
1553
1554 db.List();
1555 }
1556
PatchHLEFunctions()1557 void MenuBar::PatchHLEFunctions()
1558 {
1559 HLE::PatchFunctions();
1560 }
1561
ClearCache()1562 void MenuBar::ClearCache()
1563 {
1564 Core::RunAsCPUThread(JitInterface::ClearCache);
1565 }
1566
LogInstructions()1567 void MenuBar::LogInstructions()
1568 {
1569 PPCTables::LogCompiledInstructions();
1570 }
1571
SearchInstruction()1572 void MenuBar::SearchInstruction()
1573 {
1574 bool good;
1575 const QString op = QInputDialog::getText(this, tr("Search instruction"), tr("Instruction:"),
1576 QLineEdit::Normal, QString{}, &good);
1577
1578 if (!good)
1579 return;
1580
1581 bool found = false;
1582 for (u32 addr = Memory::MEM1_BASE_ADDR; addr < Memory::MEM1_BASE_ADDR + Memory::GetRamSizeReal();
1583 addr += 4)
1584 {
1585 auto ins_name =
1586 QString::fromStdString(PPCTables::GetInstructionName(PowerPC::HostRead_U32(addr)));
1587 if (op == ins_name)
1588 {
1589 NOTICE_LOG(POWERPC, "Found %s at %08x", op.toStdString().c_str(), addr);
1590 found = true;
1591 }
1592 }
1593 if (!found)
1594 NOTICE_LOG(POWERPC, "Opcode %s not found", op.toStdString().c_str());
1595 }
1596