dolphin/Source/Core/DolphinQt/MenuBar.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1798 lines
66 KiB
C++
Raw Normal View History

2015-12-20 05:24:48 +00:00
// Copyright 2015 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
2015-12-20 05:24:48 +00:00
2018-07-06 22:40:15 +00:00
#include "DolphinQt/MenuBar.h"
#include <cinttypes>
2021-02-27 20:04:45 +00:00
#include <future>
2015-12-20 05:24:48 +00:00
#include <QAction>
#include <QActionGroup>
#include <QDesktopServices>
#include <QFileDialog>
2018-02-14 22:25:01 +00:00
#include <QFontDialog>
#include <QInputDialog>
2017-05-08 17:03:59 +00:00
#include <QMap>
#include <QUrl>
2015-12-20 05:24:48 +00:00
2023-07-14 03:10:18 +00:00
#include "Common/Align.h"
#include "Common/CommonPaths.h"
#include "Common/FileUtil.h"
#include "Common/StringUtil.h"
#include "Core/AchievementManager.h"
2018-02-14 22:25:01 +00:00
#include "Core/Boot/Boot.h"
#include "Core/CommonTitles.h"
#include "Core/Config/AchievementSettings.h"
#include "Core/Config/MainSettings.h"
#include "Core/ConfigManager.h"
2017-08-27 11:55:05 +00:00
#include "Core/Core.h"
2018-02-14 22:25:01 +00:00
#include "Core/Debugger/RSO.h"
#include "Core/HLE/HLE.h"
#include "Core/HW/AddressSpace.h"
Configurable MEM1 and MEM2 sizes at runtime via Dolphin.ini Changed several enums from Memmap.h to be static vars and implemented Get functions to query them. This seems to have boosted speed a bit in some titles? The new variables and some previously statically initialized items are now initialized via Memory::Init() and the new AddressSpace::Init(). s_ram_size_real and the new s_exram_size_real in particular are initialized from new OnionConfig values "MAIN_MEM1_SIZE" and "MAIN_MEM2_SIZE", only if "MAIN_RAM_OVERRIDE_ENABLE" is true. GUI features have been added to Config > Advanced to adjust the new OnionConfig values. A check has been added to State::doState to ensure savestates with memory configurations different from the current settings aren't loaded. The STATE_VERSION is now 115. FIFO Files have been updated from version 4 to version 5, now including the MEM1 and MEM2 sizes from the time of DFF creation. FIFO Logs not using the new features (OnionConfig MAIN_RAM_OVERRIDE_ENABLE is false) are still backwards compatible. FIFO Logs that do use the new features have a MIN_LOADER_VERSION of 5. Thanks to the order of function calls, FIFO logs are able to automatically configure the new OnionConfig settings to match what is needed. This is a bit hacky, though, so I also threw in a failsafe for if the conditions that allow this to work ever go away. I took the liberty of adding a log message to explain why the core fails to initialize if the MIN_LOADER_VERSION is too great. Some IOS code has had the function "RAMOverrideForIOSMemoryValues" appended to it to recalculate IOS Memory Values from retail IOSes/apploaders to fit the extended memory sizes. Worry not, if MAIN_RAM_OVERRIDE_ENABLE is false, this function does absolutely nothing. A hotfix in DolphinQt/MenuBar.cpp has been implemented for RAM Override.
2020-04-28 17:10:50 +00:00
#include "Core/HW/Memmap.h"
2018-05-12 20:39:14 +00:00
#include "Core/HW/WiiSave.h"
2017-08-27 11:55:05 +00:00
#include "Core/HW/Wiimote.h"
#include "Core/IOS/ES/ES.h"
2023-07-14 03:10:18 +00:00
#include "Core/IOS/FS/FileSystem.h"
#include "Core/IOS/IOS.h"
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
2017-08-27 11:55:05 +00:00
#include "Core/Movie.h"
#include "Core/NetPlayProto.h"
2018-04-09 13:31:20 +00:00
#include "Core/PowerPC/JitInterface.h"
#include "Core/PowerPC/MMU.h"
2018-02-14 22:25:01 +00:00
#include "Core/PowerPC/PPCAnalyst.h"
#include "Core/PowerPC/PPCSymbolDB.h"
2018-04-09 13:31:20 +00:00
#include "Core/PowerPC/PowerPC.h"
2018-02-14 22:25:01 +00:00
#include "Core/PowerPC/SignatureDB/SignatureDB.h"
#include "Core/State.h"
#include "Core/System.h"
#include "Core/TitleDatabase.h"
#include "Core/WiiUtils.h"
#include "DiscIO/Enums.h"
#include "DiscIO/NANDImporter.h"
#include "DiscIO/WiiSaveBanner.h"
2018-07-06 22:40:15 +00:00
#include "DolphinQt/AboutDialog.h"
#include "DolphinQt/Host.h"
#include "DolphinQt/NANDRepairDialog.h"
#include "DolphinQt/QtUtils/DolphinFileDialog.h"
2019-03-04 19:49:00 +00:00
#include "DolphinQt/QtUtils/ModalMessageBox.h"
2021-02-27 20:04:45 +00:00
#include "DolphinQt/QtUtils/ParallelProgressDialog.h"
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
2018-07-06 22:40:15 +00:00
#include "DolphinQt/Settings.h"
#include "DolphinQt/Updater.h"
2015-12-20 05:24:48 +00:00
#include "UICommon/AutoUpdate.h"
#include "UICommon/GameFile.h"
QPointer<MenuBar> MenuBar::s_menu_bar;
2019-10-12 16:04:51 +00:00
QString MenuBar::GetSignatureSelector() const
{
return QStringLiteral("%1 (*.dsy);; %2 (*.csv);; %3 (*.mega)")
.arg(tr("Dolphin Signature File"), tr("Dolphin Signature CSV File"),
tr("WiiTools Signature MEGA File"));
}
2015-12-20 05:24:48 +00:00
MenuBar::MenuBar(QWidget* parent) : QMenuBar(parent)
{
s_menu_bar = this;
2015-12-20 05:24:48 +00:00
AddFileMenu();
AddEmulationMenu();
2017-08-27 11:55:05 +00:00
AddMovieMenu();
AddOptionsMenu();
AddToolsMenu();
2015-12-20 05:24:48 +00:00
AddViewMenu();
2018-04-09 13:31:20 +00:00
AddJITMenu();
2018-02-14 22:25:01 +00:00
AddSymbolsMenu();
AddHelpMenu();
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
[=, this](Core::State state) { OnEmulationStateChanged(state); });
connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this,
[this] { OnEmulationStateChanged(Core::GetState()); });
OnEmulationStateChanged(Core::GetState());
connect(&Settings::Instance(), &Settings::DebugModeToggled, this, &MenuBar::OnDebugModeToggled);
2017-08-27 11:55:05 +00:00
connect(this, &MenuBar::SelectionChanged, this, &MenuBar::OnSelectionChanged);
connect(this, &MenuBar::RecordingStatusChanged, this, &MenuBar::OnRecordingStatusChanged);
connect(this, &MenuBar::ReadOnlyModeChanged, this, &MenuBar::OnReadOnlyModeChanged);
}
void MenuBar::OnEmulationStateChanged(Core::State state)
{
bool running = state != Core::State::Uninitialized;
bool playing = running && state != Core::State::Paused;
2017-08-27 11:55:05 +00:00
// File
m_eject_disc->setEnabled(running);
m_change_disc->setEnabled(running);
// Emulation
m_play_action->setEnabled(!playing);
m_play_action->setVisible(!playing);
m_pause_action->setEnabled(playing);
m_pause_action->setVisible(playing);
m_stop_action->setEnabled(running);
m_stop_action->setVisible(running);
m_reset_action->setEnabled(running);
m_fullscreen_action->setEnabled(running);
m_screenshot_action->setEnabled(running);
m_state_save_menu->setEnabled(running);
2017-08-27 11:55:05 +00:00
#ifdef USE_RETRO_ACHIEVEMENTS
const bool hardcore = AchievementManager::GetInstance().IsHardcoreModeActive();
m_state_load_menu->setEnabled(running && !hardcore);
m_frame_advance_action->setEnabled(running && !hardcore);
#else // USE_RETRO_ACHIEVEMENTS
m_state_load_menu->setEnabled(running);
m_frame_advance_action->setEnabled(running);
#endif // USE_RETRO_ACHIEVEMENTS
2017-08-27 11:55:05 +00:00
// Movie
m_recording_read_only->setEnabled(running);
if (!running)
{
m_recording_stop->setEnabled(false);
m_recording_export->setEnabled(false);
}
m_recording_play->setEnabled(m_game_selected && !running);
#ifdef USE_RETRO_ACHIEVEMENTS
m_recording_play->setEnabled(m_game_selected && !running && !hardcore);
#else // USE_RETRO_ACHIEVEMENTS
m_recording_play->setEnabled(m_game_selected && !running);
#endif // USE_RETRO_ACHIEVEMENTS
m_recording_start->setEnabled((m_game_selected || running) && !Movie::IsPlayingInput());
2017-08-27 11:55:05 +00:00
2018-04-09 13:31:20 +00:00
// JIT
m_jit_interpreter_core->setEnabled(running);
m_jit_block_linking->setEnabled(!running);
m_jit_disable_cache->setEnabled(!running);
m_jit_disable_fastmem_arena->setEnabled(!running);
m_jit_disable_large_entry_points_map->setEnabled(!running);
2018-04-09 13:31:20 +00:00
m_jit_clear_cache->setEnabled(running);
m_jit_log_coverage->setEnabled(!running);
m_jit_search_instruction->setEnabled(running);
2018-02-14 22:25:01 +00:00
// Symbols
m_symbols->setEnabled(running);
UpdateStateSlotMenu();
UpdateToolsMenu(running);
OnDebugModeToggled(Settings::Instance().IsDebugModeEnabled());
}
void MenuBar::OnDebugModeToggled(bool enabled)
{
2018-02-14 22:25:01 +00:00
// Options
m_boot_to_pause->setVisible(enabled);
m_automatic_start->setVisible(enabled);
m_reset_ignore_panic_handler->setVisible(enabled);
2018-02-14 22:25:01 +00:00
m_change_font->setVisible(enabled);
// View
m_show_code->setVisible(enabled);
m_show_registers->setVisible(enabled);
m_show_threads->setVisible(enabled);
2017-09-27 06:53:05 +00:00
m_show_watch->setVisible(enabled);
m_show_breakpoints->setVisible(enabled);
2018-03-16 11:39:53 +00:00
m_show_memory->setVisible(enabled);
m_show_network->setVisible(enabled);
2018-04-09 13:31:20 +00:00
m_show_jit->setVisible(enabled);
2018-02-14 22:25:01 +00:00
if (enabled)
2018-04-09 13:31:20 +00:00
{
addMenu(m_jit);
2018-02-14 22:25:01 +00:00
addMenu(m_symbols);
2018-04-09 13:31:20 +00:00
}
2018-02-14 22:25:01 +00:00
else
2018-04-09 13:31:20 +00:00
{
removeAction(m_jit->menuAction());
2018-02-14 22:25:01 +00:00
removeAction(m_symbols->menuAction());
2018-04-09 13:31:20 +00:00
}
2015-12-20 05:24:48 +00:00
}
void MenuBar::AddFileMenu()
{
QMenu* file_menu = addMenu(tr("&File"));
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
m_open_action = file_menu->addAction(tr("&Open..."), QKeySequence::Open, this, &MenuBar::Open);
#else
m_open_action = file_menu->addAction(tr("&Open..."), this, &MenuBar::Open, QKeySequence::Open);
#endif
file_menu->addSeparator();
2018-07-09 08:02:10 +00:00
m_change_disc = file_menu->addAction(tr("Change &Disc..."), this, &MenuBar::ChangeDisc);
m_eject_disc = file_menu->addAction(tr("&Eject Disc"), this, &MenuBar::EjectDisc);
file_menu->addSeparator();
m_open_user_folder =
file_menu->addAction(tr("Open &User Folder"), this, &MenuBar::OpenUserFolder);
file_menu->addSeparator();
m_exit_action = file_menu->addAction(tr("E&xit"), this, &MenuBar::Exit);
2021-04-25 00:18:28 +00:00
m_exit_action->setShortcuts({QKeySequence::Quit, QKeySequence(Qt::ALT | Qt::Key_F4)});
}
void MenuBar::AddToolsMenu()
{
QMenu* tools_menu = addMenu(tr("&Tools"));
2017-08-24 13:11:04 +00:00
2019-05-18 22:24:25 +00:00
tools_menu->addAction(tr("&Resource Pack Manager"), this,
[this] { emit ShowResourcePackManager(); });
2018-01-25 18:54:50 +00:00
tools_menu->addAction(tr("&Cheats Manager"), this, [this] { emit ShowCheatsManager(); });
2018-03-26 02:17:47 +00:00
2019-05-18 22:24:25 +00:00
tools_menu->addAction(tr("FIFO Player"), this, &MenuBar::ShowFIFOPlayer);
2017-08-24 13:11:04 +00:00
auto* usb_device_menu = new QMenu(tr("Emulated USB Devices"), tools_menu);
usb_device_menu->addAction(tr("&Skylanders Portal"), this, &MenuBar::ShowSkylanderPortal);
usb_device_menu->addAction(tr("&Infinity Base"), this, &MenuBar::ShowInfinityBase);
tools_menu->addMenu(usb_device_menu);
2017-08-24 13:11:04 +00:00
tools_menu->addSeparator();
2019-05-18 22:24:25 +00:00
tools_menu->addAction(tr("Start &NetPlay..."), this, &MenuBar::StartNetPlay);
tools_menu->addAction(tr("Browse &NetPlay Sessions...."), this, &MenuBar::BrowseNetPlay);
tools_menu->addSeparator();
2019-05-18 22:24:25 +00:00
#ifdef USE_RETRO_ACHIEVEMENTS
if (Config::Get(Config::RA_ENABLED))
{
tools_menu->addAction(tr("Achievements"), this, [this] { emit ShowAchievementsWindow(); });
tools_menu->addSeparator();
}
#endif // USE_RETRO_ACHIEVEMENTS
QMenu* gc_ipl = tools_menu->addMenu(tr("Load GameCube Main Menu"));
2018-07-09 08:02:10 +00:00
m_ntscj_ipl = gc_ipl->addAction(tr("NTSC-J"), this,
[this] { emit BootGameCubeIPL(DiscIO::Region::NTSC_J); });
m_ntscu_ipl = gc_ipl->addAction(tr("NTSC-U"), this,
[this] { emit BootGameCubeIPL(DiscIO::Region::NTSC_U); });
m_pal_ipl =
2018-07-09 08:02:10 +00:00
gc_ipl->addAction(tr("PAL"), this, [this] { emit BootGameCubeIPL(DiscIO::Region::PAL); });
2019-05-18 22:24:25 +00:00
tools_menu->addAction(tr("Memory Card Manager"), this, [this] { emit ShowMemcardManager(); });
2017-08-30 14:44:28 +00:00
tools_menu->addSeparator();
// Label will be set by a NANDRefresh later
m_boot_sysmenu = tools_menu->addAction(QString{}, this, [this] { emit BootWiiSystemMenu(); });
2019-05-18 22:24:25 +00:00
m_wad_install_action = tools_menu->addAction(tr("Install WAD..."), this, &MenuBar::InstallWAD);
m_manage_nand_menu = tools_menu->addMenu(tr("Manage NAND"));
m_import_backup = m_manage_nand_menu->addAction(tr("Import BootMii NAND Backup..."), this,
[this] { emit ImportNANDBackup(); });
m_check_nand = m_manage_nand_menu->addAction(tr("Check NAND..."), this, &MenuBar::CheckNAND);
m_extract_certificates = m_manage_nand_menu->addAction(tr("Extract Certificates from NAND"), this,
&MenuBar::NANDExtractCertificates);
m_boot_sysmenu->setEnabled(false);
connect(&Settings::Instance(), &Settings::NANDRefresh, this, [this] { UpdateToolsMenu(false); });
m_perform_online_update_menu = tools_menu->addMenu(tr("Perform Online System Update"));
2018-07-09 08:02:10 +00:00
m_perform_online_update_for_current_region = m_perform_online_update_menu->addAction(
tr("Current Region"), this, [this] { emit PerformOnlineUpdate(""); });
m_perform_online_update_menu->addSeparator();
2018-07-09 08:02:10 +00:00
m_perform_online_update_menu->addAction(tr("Europe"), this,
[this] { emit PerformOnlineUpdate("EUR"); });
m_perform_online_update_menu->addAction(tr("Japan"), this,
[this] { emit PerformOnlineUpdate("JPN"); });
m_perform_online_update_menu->addAction(tr("Korea"), this,
[this] { emit PerformOnlineUpdate("KOR"); });
m_perform_online_update_menu->addAction(tr("United States"), this,
[this] { emit PerformOnlineUpdate("USA"); });
2019-05-18 22:24:25 +00:00
tools_menu->addSeparator();
m_import_wii_save =
tools_menu->addAction(tr("Import Wii Save..."), this, &MenuBar::ImportWiiSave);
m_export_wii_saves =
tools_menu->addAction(tr("Export All Wii Saves"), this, &MenuBar::ExportWiiSaves);
2019-05-18 22:24:25 +00:00
QMenu* menu = new QMenu(tr("Connect Wii Remotes"), tools_menu);
tools_menu->addSeparator();
tools_menu->addMenu(menu);
for (int i = 0; i < 4; i++)
{
2018-07-09 08:02:10 +00:00
m_wii_remotes[i] = menu->addAction(tr("Connect Wii Remote %1").arg(i + 1), this,
[this, i] { emit ConnectWiiRemote(i); });
m_wii_remotes[i]->setCheckable(true);
}
menu->addSeparator();
m_wii_remotes[4] =
2018-07-09 08:02:10 +00:00
menu->addAction(tr("Connect Balance Board"), this, [this] { emit ConnectWiiRemote(4); });
m_wii_remotes[4]->setCheckable(true);
}
void MenuBar::AddEmulationMenu()
{
QMenu* emu_menu = addMenu(tr("&Emulation"));
2018-07-09 08:02:10 +00:00
m_play_action = emu_menu->addAction(tr("&Play"), this, &MenuBar::Play);
m_pause_action = emu_menu->addAction(tr("&Pause"), this, &MenuBar::Pause);
m_stop_action = emu_menu->addAction(tr("&Stop"), this, &MenuBar::Stop);
m_reset_action = emu_menu->addAction(tr("&Reset"), this, &MenuBar::Reset);
m_fullscreen_action = emu_menu->addAction(tr("Toggle &Fullscreen"), this, &MenuBar::Fullscreen);
m_frame_advance_action = emu_menu->addAction(tr("&Frame Advance"), this, &MenuBar::FrameAdvance);
2017-07-16 00:36:37 +00:00
2018-07-09 08:02:10 +00:00
m_screenshot_action = emu_menu->addAction(tr("Take Screenshot"), this, &MenuBar::Screenshot);
2017-07-16 00:36:37 +00:00
emu_menu->addSeparator();
AddStateLoadMenu(emu_menu);
AddStateSaveMenu(emu_menu);
AddStateSlotMenu(emu_menu);
UpdateStateSlotMenu();
for (QMenu* menu : {m_state_load_menu, m_state_save_menu, m_state_slot_menu})
connect(menu, &QMenu::aboutToShow, this, &MenuBar::UpdateStateSlotMenu);
}
void MenuBar::AddStateLoadMenu(QMenu* emu_menu)
{
m_state_load_menu = emu_menu->addMenu(tr("&Load State"));
2018-07-09 08:02:10 +00:00
m_state_load_menu->addAction(tr("Load State from File"), this, &MenuBar::StateLoad);
m_state_load_menu->addAction(tr("Load State from Selected Slot"), this, &MenuBar::StateLoadSlot);
m_state_load_slots_menu = m_state_load_menu->addMenu(tr("Load State from Slot"));
2018-07-09 08:02:10 +00:00
m_state_load_menu->addAction(tr("Undo Load State"), this, &MenuBar::StateLoadUndo);
for (int i = 1; i <= 10; i++)
{
QAction* action = m_state_load_slots_menu->addAction(QString{});
connect(action, &QAction::triggered, this, [=, this]() { emit StateLoadSlotAt(i); });
}
}
void MenuBar::AddStateSaveMenu(QMenu* emu_menu)
{
m_state_save_menu = emu_menu->addMenu(tr("Sa&ve State"));
2018-07-09 08:02:10 +00:00
m_state_save_menu->addAction(tr("Save State to File"), this, &MenuBar::StateSave);
m_state_save_menu->addAction(tr("Save State to Selected Slot"), this, &MenuBar::StateSaveSlot);
m_state_save_menu->addAction(tr("Save State to Oldest Slot"), this, &MenuBar::StateSaveOldest);
m_state_save_slots_menu = m_state_save_menu->addMenu(tr("Save State to Slot"));
2018-07-09 08:02:10 +00:00
m_state_save_menu->addAction(tr("Undo Save State"), this, &MenuBar::StateSaveUndo);
for (int i = 1; i <= 10; i++)
{
QAction* action = m_state_save_slots_menu->addAction(QString{});
connect(action, &QAction::triggered, this, [=, this]() { emit StateSaveSlotAt(i); });
}
}
void MenuBar::AddStateSlotMenu(QMenu* emu_menu)
{
m_state_slot_menu = emu_menu->addMenu(tr("Select State Slot"));
m_state_slots = new QActionGroup(this);
for (int i = 1; i <= 10; i++)
{
QAction* action = m_state_slot_menu->addAction(QString{});
action->setCheckable(true);
action->setActionGroup(m_state_slots);
if (Settings::Instance().GetStateSlot() == i)
action->setChecked(true);
connect(action, &QAction::triggered, this, [=, this]() { emit SetStateSlot(i); });
}
}
void MenuBar::UpdateStateSlotMenu()
{
QList<QAction*> actions_slot = m_state_slots->actions();
QList<QAction*> actions_load = m_state_load_slots_menu->actions();
QList<QAction*> actions_save = m_state_save_slots_menu->actions();
for (int i = 0; i < actions_slot.length(); i++)
{
int slot = i + 1;
QString info = QString::fromStdString(State::GetInfoStringOfSlot(slot));
2017-07-23 08:50:20 +00:00
actions_load.at(i)->setText(tr("Load from Slot %1 - %2").arg(slot).arg(info));
actions_save.at(i)->setText(tr("Save to Slot %1 - %2").arg(slot).arg(info));
actions_slot.at(i)->setText(tr("Select Slot %1 - %2").arg(slot).arg(info));
}
2015-12-20 05:24:48 +00:00
}
void MenuBar::AddViewMenu()
{
QMenu* view_menu = addMenu(tr("&View"));
QAction* show_log = view_menu->addAction(tr("Show &Log"));
2017-06-29 10:46:20 +00:00
show_log->setCheckable(true);
show_log->setChecked(Settings::Instance().IsLogVisible());
connect(show_log, &QAction::toggled, &Settings::Instance(), &Settings::SetLogVisible);
QAction* show_log_config = view_menu->addAction(tr("Show Log &Configuration"));
2017-06-29 10:46:20 +00:00
show_log_config->setCheckable(true);
show_log_config->setChecked(Settings::Instance().IsLogConfigVisible());
connect(show_log_config, &QAction::toggled, &Settings::Instance(),
&Settings::SetLogConfigVisible);
2018-04-19 09:32:00 +00:00
QAction* show_toolbar = view_menu->addAction(tr("Show &Toolbar"));
show_toolbar->setCheckable(true);
show_toolbar->setChecked(Settings::Instance().IsToolBarVisible());
connect(show_toolbar, &QAction::toggled, &Settings::Instance(), &Settings::SetToolBarVisible);
2017-06-29 10:46:20 +00:00
connect(&Settings::Instance(), &Settings::LogVisibilityChanged, show_log, &QAction::setChecked);
connect(&Settings::Instance(), &Settings::LogConfigVisibilityChanged, show_log_config,
&QAction::setChecked);
2018-04-19 09:32:00 +00:00
connect(&Settings::Instance(), &Settings::ToolBarVisibilityChanged, show_toolbar,
&QAction::setChecked);
QAction* lock_widgets = view_menu->addAction(tr("&Lock Widgets In Place"));
lock_widgets->setCheckable(true);
lock_widgets->setChecked(Settings::Instance().AreWidgetsLocked());
connect(lock_widgets, &QAction::toggled, &Settings::Instance(), &Settings::SetWidgetsLocked);
2017-06-29 10:46:20 +00:00
view_menu->addSeparator();
2018-02-14 22:25:01 +00:00
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());
connect(m_show_registers, &QAction::toggled, &Settings::Instance(),
&Settings::SetRegistersVisible);
connect(&Settings::Instance(), &Settings::RegistersVisibilityChanged, m_show_registers,
&QAction::setChecked);
m_show_threads = view_menu->addAction(tr("&Threads"));
m_show_threads->setCheckable(true);
m_show_threads->setChecked(Settings::Instance().IsThreadsVisible());
connect(m_show_threads, &QAction::toggled, &Settings::Instance(), &Settings::SetThreadsVisible);
connect(&Settings::Instance(), &Settings::ThreadsVisibilityChanged, m_show_threads,
&QAction::setChecked);
// i18n: This kind of "watch" is used for watching emulated memory.
// It's not related to timekeeping devices.
2017-09-27 06:53:05 +00:00
m_show_watch = view_menu->addAction(tr("&Watch"));
m_show_watch->setCheckable(true);
m_show_watch->setChecked(Settings::Instance().IsWatchVisible());
connect(m_show_watch, &QAction::toggled, &Settings::Instance(), &Settings::SetWatchVisible);
connect(&Settings::Instance(), &Settings::WatchVisibilityChanged, m_show_watch,
&QAction::setChecked);
m_show_breakpoints = view_menu->addAction(tr("&Breakpoints"));
m_show_breakpoints->setCheckable(true);
m_show_breakpoints->setChecked(Settings::Instance().IsBreakpointsVisible());
connect(m_show_breakpoints, &QAction::toggled, &Settings::Instance(),
&Settings::SetBreakpointsVisible);
connect(&Settings::Instance(), &Settings::BreakpointsVisibilityChanged, m_show_breakpoints,
&QAction::setChecked);
2018-03-16 11:39:53 +00:00
m_show_memory = view_menu->addAction(tr("&Memory"));
m_show_memory->setCheckable(true);
m_show_memory->setChecked(Settings::Instance().IsMemoryVisible());
connect(m_show_memory, &QAction::toggled, &Settings::Instance(), &Settings::SetMemoryVisible);
connect(&Settings::Instance(), &Settings::MemoryVisibilityChanged, m_show_memory,
&QAction::setChecked);
m_show_network = view_menu->addAction(tr("&Network"));
m_show_network->setCheckable(true);
m_show_network->setChecked(Settings::Instance().IsNetworkVisible());
connect(m_show_network, &QAction::toggled, &Settings::Instance(), &Settings::SetNetworkVisible);
connect(&Settings::Instance(), &Settings::NetworkVisibilityChanged, m_show_network,
&QAction::setChecked);
2018-04-09 13:31:20 +00:00
m_show_jit = view_menu->addAction(tr("&JIT"));
m_show_jit->setCheckable(true);
m_show_jit->setChecked(Settings::Instance().IsJITVisible());
connect(m_show_jit, &QAction::toggled, &Settings::Instance(), &Settings::SetJITVisible);
connect(&Settings::Instance(), &Settings::JITVisibilityChanged, m_show_jit, &QAction::setChecked);
2022-12-18 08:43:28 +00:00
m_show_assembler = view_menu->addAction(tr("&Assembler"));
m_show_assembler->setCheckable(true);
m_show_assembler->setChecked(Settings::Instance().IsAssemblerVisible());
connect(m_show_assembler, &QAction::toggled, &Settings::Instance(),
&Settings::SetAssemblerVisible);
connect(&Settings::Instance(), &Settings::AssemblerVisibilityChanged, m_show_assembler,
&QAction::setChecked);
view_menu->addSeparator();
2015-12-20 05:24:48 +00:00
AddGameListTypeSection(view_menu);
view_menu->addSeparator();
AddListColumnsMenu(view_menu);
view_menu->addSeparator();
AddShowPlatformsMenu(view_menu);
AddShowRegionsMenu(view_menu);
2018-03-21 10:13:53 +00:00
view_menu->addSeparator();
QAction* const purge_action =
view_menu->addAction(tr("Purge Game List Cache"), this, &MenuBar::PurgeGameListCache);
purge_action->setEnabled(false);
connect(&Settings::Instance(), &Settings::GameListRefreshRequested, purge_action,
[purge_action] { purge_action->setEnabled(false); });
connect(&Settings::Instance(), &Settings::GameListRefreshStarted, purge_action,
[purge_action] { purge_action->setEnabled(true); });
2018-03-21 10:13:53 +00:00
view_menu->addSeparator();
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
view_menu->addAction(tr("Search"), QKeySequence::Find, this, &MenuBar::ShowSearch);
#else
view_menu->addAction(tr("Search"), this, &MenuBar::ShowSearch, QKeySequence::Find);
#endif
2015-12-20 05:24:48 +00:00
}
void MenuBar::AddOptionsMenu()
{
QMenu* options_menu = addMenu(tr("&Options"));
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
options_menu->addAction(tr("Co&nfiguration"), QKeySequence::Preferences, this,
&MenuBar::Configure);
#else
2020-02-26 19:37:02 +00:00
options_menu->addAction(tr("Co&nfiguration"), this, &MenuBar::Configure,
QKeySequence::Preferences);
#endif
options_menu->addSeparator();
2018-07-09 08:02:10 +00:00
options_menu->addAction(tr("&Graphics Settings"), this, &MenuBar::ConfigureGraphics);
options_menu->addAction(tr("&Audio Settings"), this, &MenuBar::ConfigureAudio);
m_controllers_action =
options_menu->addAction(tr("&Controller Settings"), this, &MenuBar::ConfigureControllers);
2018-07-09 08:02:10 +00:00
options_menu->addAction(tr("&Hotkey Settings"), this, &MenuBar::ConfigureHotkeys);
options_menu->addAction(tr("&Free Look Settings"), this, &MenuBar::ConfigureFreelook);
2018-02-14 22:25:01 +00:00
options_menu->addSeparator();
// Debugging mode only
m_boot_to_pause = options_menu->addAction(tr("Boot to Pause"));
2018-02-14 22:25:01 +00:00
m_boot_to_pause->setCheckable(true);
m_boot_to_pause->setChecked(SConfig::GetInstance().bBootToPause);
connect(m_boot_to_pause, &QAction::toggled, this,
[](bool enable) { SConfig::GetInstance().bBootToPause = enable; });
2018-02-14 22:25:01 +00:00
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,
[](bool enable) { SConfig::GetInstance().bAutomaticStart = enable; });
2018-02-14 22:25:01 +00:00
m_reset_ignore_panic_handler = options_menu->addAction(tr("Reset Ignore Panic Handler"));
connect(m_reset_ignore_panic_handler, &QAction::triggered, this, []() {
Config::DeleteKey(Config::LayerType::CurrentRun, Config::MAIN_USE_PANIC_HANDLERS);
});
2018-07-09 08:02:10 +00:00
m_change_font = options_menu->addAction(tr("&Font..."), this, &MenuBar::ChangeDebugFont);
}
void MenuBar::InstallUpdateManually()
{
const std::string autoupdate_track = Config::Get(Config::MAIN_AUTOUPDATE_UPDATE_TRACK);
const std::string manual_track = autoupdate_track.empty() ? "dev" : autoupdate_track;
auto* const updater = new Updater(this->parentWidget(), manual_track,
Config::Get(Config::MAIN_AUTOUPDATE_HASH_OVERRIDE));
2022-11-01 06:08:35 +00:00
updater->CheckForUpdate();
}
void MenuBar::AddHelpMenu()
{
QMenu* help_menu = addMenu(tr("&Help"));
QAction* website = help_menu->addAction(tr("&Website"));
2017-05-05 08:08:01 +00:00
connect(website, &QAction::triggered, this,
[]() { QDesktopServices::openUrl(QUrl(QStringLiteral("https://dolphin-emu.org/"))); });
QAction* documentation = help_menu->addAction(tr("Online &Documentation"));
2017-05-05 08:08:01 +00:00
connect(documentation, &QAction::triggered, this, []() {
QDesktopServices::openUrl(QUrl(QStringLiteral("https://dolphin-emu.org/docs/guides")));
});
QAction* github = help_menu->addAction(tr("&GitHub Repository"));
2017-05-05 08:08:01 +00:00
connect(github, &QAction::triggered, this, []() {
QDesktopServices::openUrl(QUrl(QStringLiteral("https://github.com/dolphin-emu/dolphin")));
});
2019-08-05 14:51:02 +00:00
QAction* bugtracker = help_menu->addAction(tr("&Bug Tracker"));
connect(bugtracker, &QAction::triggered, this, []() {
QDesktopServices::openUrl(
QUrl(QStringLiteral("https://bugs.dolphin-emu.org/projects/emulator")));
});
2017-05-05 08:08:01 +00:00
if (AutoUpdateChecker::SystemSupportsAutoUpdates())
{
help_menu->addSeparator();
help_menu->addAction(tr("&Check for Updates..."), this, &MenuBar::InstallUpdateManually);
}
#ifndef __APPLE__
2017-05-05 08:08:01 +00:00
help_menu->addSeparator();
#endif
2018-07-09 08:02:10 +00:00
help_menu->addAction(tr("&About"), this, &MenuBar::ShowAboutDialog);
}
2015-12-20 05:24:48 +00:00
void MenuBar::AddGameListTypeSection(QMenu* view_menu)
{
QAction* list_view = view_menu->addAction(tr("List View"));
2015-12-20 05:24:48 +00:00
list_view->setCheckable(true);
QAction* grid_view = view_menu->addAction(tr("Grid View"));
grid_view->setCheckable(true);
2015-12-20 05:24:48 +00:00
QActionGroup* list_group = new QActionGroup(this);
list_group->addAction(list_view);
list_group->addAction(grid_view);
2015-12-20 05:24:48 +00:00
bool prefer_list = Settings::Instance().GetPreferredView();
list_view->setChecked(prefer_list);
grid_view->setChecked(!prefer_list);
2015-12-20 05:24:48 +00:00
connect(list_view, &QAction::triggered, this, &MenuBar::ShowList);
connect(grid_view, &QAction::triggered, this, &MenuBar::ShowGrid);
2015-12-20 05:24:48 +00:00
}
void MenuBar::AddListColumnsMenu(QMenu* view_menu)
2015-12-20 05:24:48 +00:00
{
static const QMap<QString, const Config::Info<bool>*> columns{
{tr("Platform"), &Config::MAIN_GAMELIST_COLUMN_PLATFORM},
{tr("Banner"), &Config::MAIN_GAMELIST_COLUMN_BANNER},
{tr("Title"), &Config::MAIN_GAMELIST_COLUMN_TITLE},
{tr("Description"), &Config::MAIN_GAMELIST_COLUMN_DESCRIPTION},
{tr("Maker"), &Config::MAIN_GAMELIST_COLUMN_MAKER},
{tr("File Name"), &Config::MAIN_GAMELIST_COLUMN_FILE_NAME},
{tr("File Path"), &Config::MAIN_GAMELIST_COLUMN_FILE_PATH},
{tr("Game ID"), &Config::MAIN_GAMELIST_COLUMN_GAME_ID},
{tr("Region"), &Config::MAIN_GAMELIST_COLUMN_REGION},
{tr("File Size"), &Config::MAIN_GAMELIST_COLUMN_FILE_SIZE},
{tr("File Format"), &Config::MAIN_GAMELIST_COLUMN_FILE_FORMAT},
{tr("Block Size"), &Config::MAIN_GAMELIST_COLUMN_BLOCK_SIZE},
{tr("Compression"), &Config::MAIN_GAMELIST_COLUMN_COMPRESSION},
{tr("Tags"), &Config::MAIN_GAMELIST_COLUMN_TAGS}};
2017-05-08 17:03:59 +00:00
2015-12-20 05:24:48 +00:00
QActionGroup* column_group = new QActionGroup(this);
m_cols_menu = view_menu->addMenu(tr("List Columns"));
2015-12-20 05:24:48 +00:00
column_group->setExclusive(false);
2017-05-08 17:03:59 +00:00
for (const auto& key : columns.keys())
2015-12-20 05:24:48 +00:00
{
const Config::Info<bool>* const config = columns[key];
QAction* action = column_group->addAction(m_cols_menu->addAction(key));
2015-12-20 05:24:48 +00:00
action->setCheckable(true);
action->setChecked(Config::Get(*config));
2017-05-08 17:03:59 +00:00
connect(action, &QAction::toggled, [this, config, key](bool value) {
Config::SetBase(*config, value);
2017-05-08 17:03:59 +00:00
emit ColumnVisibilityToggled(key, value);
});
2015-12-20 05:24:48 +00:00
}
}
void MenuBar::AddShowPlatformsMenu(QMenu* view_menu)
{
static const QMap<QString, const Config::Info<bool>*> platform_map{
{tr("Show Wii"), &Config::MAIN_GAMELIST_LIST_WII},
{tr("Show GameCube"), &Config::MAIN_GAMELIST_LIST_GC},
{tr("Show WAD"), &Config::MAIN_GAMELIST_LIST_WAD},
{tr("Show ELF/DOL"), &Config::MAIN_GAMELIST_LIST_ELF_DOL}};
QActionGroup* platform_group = new QActionGroup(this);
QMenu* plat_menu = view_menu->addMenu(tr("Show Platforms"));
platform_group->setExclusive(false);
for (const auto& key : platform_map.keys())
{
const Config::Info<bool>* const config = platform_map[key];
QAction* action = platform_group->addAction(plat_menu->addAction(key));
action->setCheckable(true);
action->setChecked(Config::Get(*config));
connect(action, &QAction::toggled, [this, config, key](bool value) {
Config::SetBase(*config, value);
emit GameListPlatformVisibilityToggled(key, value);
});
}
}
void MenuBar::AddShowRegionsMenu(QMenu* view_menu)
{
static const QMap<QString, const Config::Info<bool>*> region_map{
{tr("Show JPN"), &Config::MAIN_GAMELIST_LIST_JPN},
{tr("Show PAL"), &Config::MAIN_GAMELIST_LIST_PAL},
{tr("Show USA"), &Config::MAIN_GAMELIST_LIST_USA},
{tr("Show Australia"), &Config::MAIN_GAMELIST_LIST_AUSTRALIA},
{tr("Show France"), &Config::MAIN_GAMELIST_LIST_FRANCE},
{tr("Show Germany"), &Config::MAIN_GAMELIST_LIST_GERMANY},
{tr("Show Italy"), &Config::MAIN_GAMELIST_LIST_ITALY},
{tr("Show Korea"), &Config::MAIN_GAMELIST_LIST_KOREA},
{tr("Show Netherlands"), &Config::MAIN_GAMELIST_LIST_NETHERLANDS},
{tr("Show Russia"), &Config::MAIN_GAMELIST_LIST_RUSSIA},
{tr("Show Spain"), &Config::MAIN_GAMELIST_LIST_SPAIN},
{tr("Show Taiwan"), &Config::MAIN_GAMELIST_LIST_TAIWAN},
{tr("Show World"), &Config::MAIN_GAMELIST_LIST_WORLD},
{tr("Show Unknown"), &Config::MAIN_GAMELIST_LIST_UNKNOWN}};
QMenu* const region_menu = view_menu->addMenu(tr("Show Regions"));
const QAction* const show_all_regions = region_menu->addAction(tr("Show All"));
const QAction* const hide_all_regions = region_menu->addAction(tr("Hide All"));
region_menu->addSeparator();
for (const auto& key : region_map.keys())
{
const Config::Info<bool>* const config = region_map[key];
QAction* const menu_item = region_menu->addAction(key);
menu_item->setCheckable(true);
menu_item->setChecked(Config::Get(*config));
const auto set_visibility = [this, config, key, menu_item](bool visibility) {
menu_item->setChecked(visibility);
Config::SetBase(*config, visibility);
emit GameListRegionVisibilityToggled(key, visibility);
};
const auto set_visible = std::bind(set_visibility, true);
const auto set_hidden = std::bind(set_visibility, false);
connect(menu_item, &QAction::toggled, set_visibility);
connect(show_all_regions, &QAction::triggered, menu_item, set_visible);
connect(hide_all_regions, &QAction::triggered, menu_item, set_hidden);
}
}
2017-08-27 11:55:05 +00:00
void MenuBar::AddMovieMenu()
{
auto* movie_menu = addMenu(tr("&Movie"));
m_recording_start =
2018-07-09 08:02:10 +00:00
movie_menu->addAction(tr("Start Re&cording Input"), this, [this] { emit StartRecording(); });
2017-08-27 11:55:05 +00:00
m_recording_play =
2018-07-09 08:02:10 +00:00
movie_menu->addAction(tr("P&lay Input Recording..."), this, [this] { emit PlayRecording(); });
m_recording_stop = movie_menu->addAction(tr("Stop Playing/Recording Input"), this,
[this] { emit StopRecording(); });
2017-08-27 11:55:05 +00:00
m_recording_export =
2018-07-09 08:02:10 +00:00
movie_menu->addAction(tr("Export Recording..."), this, [this] { emit ExportRecording(); });
2017-08-27 11:55:05 +00:00
m_recording_start->setEnabled(false);
m_recording_play->setEnabled(false);
m_recording_stop->setEnabled(false);
m_recording_export->setEnabled(false);
m_recording_read_only = movie_menu->addAction(tr("&Read-Only Mode"));
2017-08-27 11:55:05 +00:00
m_recording_read_only->setCheckable(true);
m_recording_read_only->setChecked(Movie::IsReadOnly());
connect(m_recording_read_only, &QAction::toggled, [](bool value) { Movie::SetReadOnly(value); });
2018-07-09 08:02:10 +00:00
movie_menu->addAction(tr("TAS Input"), this, [this] { emit ShowTASInput(); });
2018-01-27 13:35:02 +00:00
2017-08-27 11:55:05 +00:00
movie_menu->addSeparator();
auto* pause_at_end = movie_menu->addAction(tr("Pause at End of Movie"));
pause_at_end->setCheckable(true);
pause_at_end->setChecked(Config::Get(Config::MAIN_MOVIE_PAUSE_MOVIE));
2017-08-27 11:55:05 +00:00
connect(pause_at_end, &QAction::toggled,
[](bool value) { Config::SetBaseOrCurrent(Config::MAIN_MOVIE_PAUSE_MOVIE, value); });
2017-08-27 11:55:05 +00:00
auto* rerecord_counter = movie_menu->addAction(tr("Show Rerecord Counter"));
rerecord_counter->setCheckable(true);
rerecord_counter->setChecked(Config::Get(Config::MAIN_MOVIE_SHOW_RERECORD));
connect(rerecord_counter, &QAction::toggled,
[](bool value) { Config::SetBaseOrCurrent(Config::MAIN_MOVIE_SHOW_RERECORD, value); });
2017-08-27 11:55:05 +00:00
auto* lag_counter = movie_menu->addAction(tr("Show Lag Counter"));
lag_counter->setCheckable(true);
lag_counter->setChecked(Config::Get(Config::MAIN_SHOW_LAG));
2017-08-27 11:55:05 +00:00
connect(lag_counter, &QAction::toggled,
[](bool value) { Config::SetBaseOrCurrent(Config::MAIN_SHOW_LAG, value); });
2017-08-27 11:55:05 +00:00
auto* frame_counter = movie_menu->addAction(tr("Show Frame Counter"));
frame_counter->setCheckable(true);
frame_counter->setChecked(Config::Get(Config::MAIN_SHOW_FRAME_COUNT));
2017-08-27 11:55:05 +00:00
connect(frame_counter, &QAction::toggled,
[](bool value) { Config::SetBaseOrCurrent(Config::MAIN_SHOW_FRAME_COUNT, value); });
2017-08-27 11:55:05 +00:00
auto* input_display = movie_menu->addAction(tr("Show Input Display"));
input_display->setCheckable(true);
input_display->setChecked(Config::Get(Config::MAIN_MOVIE_SHOW_INPUT_DISPLAY));
connect(input_display, &QAction::toggled, [](bool value) {
Config::SetBaseOrCurrent(Config::MAIN_MOVIE_SHOW_INPUT_DISPLAY, value);
});
2017-08-27 11:55:05 +00:00
auto* system_clock = movie_menu->addAction(tr("Show System Clock"));
system_clock->setCheckable(true);
system_clock->setChecked(Config::Get(Config::MAIN_MOVIE_SHOW_RTC));
2017-08-27 11:55:05 +00:00
connect(system_clock, &QAction::toggled,
[](bool value) { Config::SetBaseOrCurrent(Config::MAIN_MOVIE_SHOW_RTC, value); });
2017-08-27 11:55:05 +00:00
movie_menu->addSeparator();
auto* dump_frames = movie_menu->addAction(tr("Dump Frames"));
dump_frames->setCheckable(true);
dump_frames->setChecked(Config::Get(Config::MAIN_MOVIE_DUMP_FRAMES));
2017-08-27 11:55:05 +00:00
connect(dump_frames, &QAction::toggled,
[](bool value) { Config::SetBaseOrCurrent(Config::MAIN_MOVIE_DUMP_FRAMES, value); });
2017-08-27 11:55:05 +00:00
auto* dump_audio = movie_menu->addAction(tr("Dump Audio"));
dump_audio->setCheckable(true);
dump_audio->setChecked(Config::Get(Config::MAIN_DUMP_AUDIO));
2017-08-27 11:55:05 +00:00
connect(dump_audio, &QAction::toggled,
[](bool value) { Config::SetBaseOrCurrent(Config::MAIN_DUMP_AUDIO, value); });
2017-08-27 11:55:05 +00:00
}
2018-04-09 13:31:20 +00:00
void MenuBar::AddJITMenu()
{
m_jit = addMenu(tr("JIT"));
m_jit_interpreter_core = m_jit->addAction(tr("Interpreter Core"));
m_jit_interpreter_core->setCheckable(true);
m_jit_interpreter_core->setChecked(Config::Get(Config::MAIN_CPU_CORE) ==
PowerPC::CPUCore::Interpreter);
2018-04-09 13:31:20 +00:00
connect(m_jit_interpreter_core, &QAction::toggled, [](bool enabled) {
Core::System::GetInstance().GetPowerPC().SetMode(enabled ? PowerPC::CoreMode::Interpreter :
PowerPC::CoreMode::JIT);
});
2018-04-09 13:31:20 +00:00
m_jit->addSeparator();
m_jit_block_linking = m_jit->addAction(tr("JIT Block Linking Off"));
m_jit_block_linking->setCheckable(true);
m_jit_block_linking->setChecked(SConfig::GetInstance().bJITNoBlockLinking);
connect(m_jit_block_linking, &QAction::toggled, [this](bool enabled) {
SConfig::GetInstance().bJITNoBlockLinking = enabled;
ClearCache();
});
2018-04-09 13:31:20 +00:00
m_jit_disable_cache = m_jit->addAction(tr("Disable JIT Cache"));
m_jit_disable_cache->setCheckable(true);
m_jit_disable_cache->setChecked(SConfig::GetInstance().bJITNoBlockCache);
connect(m_jit_disable_cache, &QAction::toggled, [this](bool enabled) {
SConfig::GetInstance().bJITNoBlockCache = enabled;
ClearCache();
});
2018-04-09 13:31:20 +00:00
m_jit_disable_fastmem = m_jit->addAction(tr("Disable Fastmem"));
m_jit_disable_fastmem->setCheckable(true);
m_jit_disable_fastmem->setChecked(!Config::Get(Config::MAIN_FASTMEM));
connect(m_jit_disable_fastmem, &QAction::toggled,
[](bool enabled) { Config::SetBaseOrCurrent(Config::MAIN_FASTMEM, !enabled); });
m_jit_disable_fastmem_arena = m_jit->addAction(tr("Disable Fastmem Arena"));
m_jit_disable_fastmem_arena->setCheckable(true);
m_jit_disable_fastmem_arena->setChecked(!Config::Get(Config::MAIN_FASTMEM_ARENA));
connect(m_jit_disable_fastmem_arena, &QAction::toggled,
[](bool enabled) { Config::SetBaseOrCurrent(Config::MAIN_FASTMEM_ARENA, !enabled); });
m_jit_disable_large_entry_points_map = m_jit->addAction(tr("Disable Large Entry Points Map"));
m_jit_disable_large_entry_points_map->setCheckable(true);
m_jit_disable_large_entry_points_map->setChecked(
!Config::Get(Config::MAIN_LARGE_ENTRY_POINTS_MAP));
connect(m_jit_disable_large_entry_points_map, &QAction::toggled, [](bool enabled) {
Config::SetBaseOrCurrent(Config::MAIN_LARGE_ENTRY_POINTS_MAP, !enabled);
});
2018-07-09 08:02:10 +00:00
m_jit_clear_cache = m_jit->addAction(tr("Clear Cache"), this, &MenuBar::ClearCache);
2018-04-09 13:31:20 +00:00
m_jit->addSeparator();
m_jit_log_coverage =
2018-07-09 08:02:10 +00:00
m_jit->addAction(tr("Log JIT Instruction Coverage"), this, &MenuBar::LogInstructions);
2018-04-09 13:31:20 +00:00
m_jit_search_instruction =
2018-07-09 08:02:10 +00:00
m_jit->addAction(tr("Search for an Instruction"), this, &MenuBar::SearchInstruction);
2018-04-09 13:31:20 +00:00
m_jit->addSeparator();
m_jit_off = m_jit->addAction(tr("JIT Off (JIT Core)"));
m_jit_off->setCheckable(true);
m_jit_off->setChecked(Config::Get(Config::MAIN_DEBUG_JIT_OFF));
connect(m_jit_off, &QAction::toggled,
[](bool enabled) { Config::SetBaseOrCurrent(Config::MAIN_DEBUG_JIT_OFF, enabled); });
2018-04-09 13:31:20 +00:00
m_jit_loadstore_off = m_jit->addAction(tr("JIT LoadStore Off"));
m_jit_loadstore_off->setCheckable(true);
m_jit_loadstore_off->setChecked(Config::Get(Config::MAIN_DEBUG_JIT_LOAD_STORE_OFF));
connect(m_jit_loadstore_off, &QAction::toggled, [](bool enabled) {
Config::SetBaseOrCurrent(Config::MAIN_DEBUG_JIT_LOAD_STORE_OFF, enabled);
});
2018-04-09 13:31:20 +00:00
m_jit_loadstore_lbzx_off = m_jit->addAction(tr("JIT LoadStore lbzx Off"));
m_jit_loadstore_lbzx_off->setCheckable(true);
m_jit_loadstore_lbzx_off->setChecked(Config::Get(Config::MAIN_DEBUG_JIT_LOAD_STORE_LBZX_OFF));
connect(m_jit_loadstore_lbzx_off, &QAction::toggled, [](bool enabled) {
Config::SetBaseOrCurrent(Config::MAIN_DEBUG_JIT_LOAD_STORE_LBZX_OFF, enabled);
});
2018-04-09 13:31:20 +00:00
m_jit_loadstore_lxz_off = m_jit->addAction(tr("JIT LoadStore lXz Off"));
m_jit_loadstore_lxz_off->setCheckable(true);
m_jit_loadstore_lxz_off->setChecked(Config::Get(Config::MAIN_DEBUG_JIT_LOAD_STORE_LXZ_OFF));
connect(m_jit_loadstore_lxz_off, &QAction::toggled, [](bool enabled) {
Config::SetBaseOrCurrent(Config::MAIN_DEBUG_JIT_LOAD_STORE_LXZ_OFF, enabled);
});
2018-04-09 13:31:20 +00:00
m_jit_loadstore_lwz_off = m_jit->addAction(tr("JIT LoadStore lwz Off"));
m_jit_loadstore_lwz_off->setCheckable(true);
m_jit_loadstore_lwz_off->setChecked(Config::Get(Config::MAIN_DEBUG_JIT_LOAD_STORE_LWZ_OFF));
connect(m_jit_loadstore_lwz_off, &QAction::toggled, [](bool enabled) {
Config::SetBaseOrCurrent(Config::MAIN_DEBUG_JIT_LOAD_STORE_LWZ_OFF, enabled);
});
2018-04-09 13:31:20 +00:00
m_jit_loadstore_floating_off = m_jit->addAction(tr("JIT LoadStore Floating Off"));
m_jit_loadstore_floating_off->setCheckable(true);
m_jit_loadstore_floating_off->setChecked(
Config::Get(Config::MAIN_DEBUG_JIT_LOAD_STORE_FLOATING_OFF));
connect(m_jit_loadstore_floating_off, &QAction::toggled, [](bool enabled) {
Config::SetBaseOrCurrent(Config::MAIN_DEBUG_JIT_LOAD_STORE_FLOATING_OFF, enabled);
});
2018-04-09 13:31:20 +00:00
m_jit_loadstore_paired_off = m_jit->addAction(tr("JIT LoadStore Paired Off"));
m_jit_loadstore_paired_off->setCheckable(true);
m_jit_loadstore_paired_off->setChecked(Config::Get(Config::MAIN_DEBUG_JIT_LOAD_STORE_PAIRED_OFF));
connect(m_jit_loadstore_paired_off, &QAction::toggled, [](bool enabled) {
Config::SetBaseOrCurrent(Config::MAIN_DEBUG_JIT_LOAD_STORE_PAIRED_OFF, enabled);
});
2018-04-09 13:31:20 +00:00
m_jit_floatingpoint_off = m_jit->addAction(tr("JIT FloatingPoint Off"));
m_jit_floatingpoint_off->setCheckable(true);
m_jit_floatingpoint_off->setChecked(Config::Get(Config::MAIN_DEBUG_JIT_FLOATING_POINT_OFF));
connect(m_jit_floatingpoint_off, &QAction::toggled, [](bool enabled) {
Config::SetBaseOrCurrent(Config::MAIN_DEBUG_JIT_FLOATING_POINT_OFF, enabled);
});
2018-04-09 13:31:20 +00:00
m_jit_integer_off = m_jit->addAction(tr("JIT Integer Off"));
m_jit_integer_off->setCheckable(true);
m_jit_integer_off->setChecked(Config::Get(Config::MAIN_DEBUG_JIT_INTEGER_OFF));
connect(m_jit_integer_off, &QAction::toggled, [](bool enabled) {
Config::SetBaseOrCurrent(Config::MAIN_DEBUG_JIT_INTEGER_OFF, enabled);
});
2018-04-09 13:31:20 +00:00
m_jit_paired_off = m_jit->addAction(tr("JIT Paired Off"));
m_jit_paired_off->setCheckable(true);
m_jit_paired_off->setChecked(Config::Get(Config::MAIN_DEBUG_JIT_PAIRED_OFF));
connect(m_jit_paired_off, &QAction::toggled, [](bool enabled) {
Config::SetBaseOrCurrent(Config::MAIN_DEBUG_JIT_PAIRED_OFF, enabled);
});
2018-04-09 13:31:20 +00:00
m_jit_systemregisters_off = m_jit->addAction(tr("JIT SystemRegisters Off"));
m_jit_systemregisters_off->setCheckable(true);
m_jit_systemregisters_off->setChecked(Config::Get(Config::MAIN_DEBUG_JIT_SYSTEM_REGISTERS_OFF));
connect(m_jit_systemregisters_off, &QAction::toggled, [](bool enabled) {
Config::SetBaseOrCurrent(Config::MAIN_DEBUG_JIT_SYSTEM_REGISTERS_OFF, enabled);
});
m_jit_branch_off = m_jit->addAction(tr("JIT Branch Off"));
m_jit_branch_off->setCheckable(true);
m_jit_branch_off->setChecked(Config::Get(Config::MAIN_DEBUG_JIT_BRANCH_OFF));
connect(m_jit_branch_off, &QAction::toggled, [](bool enabled) {
Config::SetBaseOrCurrent(Config::MAIN_DEBUG_JIT_BRANCH_OFF, enabled);
});
m_jit_register_cache_off = m_jit->addAction(tr("JIT Register Cache Off"));
m_jit_register_cache_off->setCheckable(true);
m_jit_register_cache_off->setChecked(Config::Get(Config::MAIN_DEBUG_JIT_REGISTER_CACHE_OFF));
connect(m_jit_register_cache_off, &QAction::toggled, [](bool enabled) {
Config::SetBaseOrCurrent(Config::MAIN_DEBUG_JIT_REGISTER_CACHE_OFF, enabled);
});
2018-04-09 13:31:20 +00:00
}
2018-02-14 22:25:01 +00:00
void MenuBar::AddSymbolsMenu()
{
m_symbols = addMenu(tr("Symbols"));
2018-07-09 08:02:10 +00:00
m_symbols->addAction(tr("&Clear Symbols"), this, &MenuBar::ClearSymbols);
2018-02-14 22:25:01 +00:00
auto* generate = m_symbols->addMenu(tr("&Generate Symbols From"));
2018-07-09 08:02:10 +00:00
generate->addAction(tr("Address"), this, &MenuBar::GenerateSymbolsFromAddress);
generate->addAction(tr("Signature Database"), this, &MenuBar::GenerateSymbolsFromSignatureDB);
generate->addAction(tr("RSO Modules"), this, &MenuBar::GenerateSymbolsFromRSO);
2018-02-14 22:25:01 +00:00
m_symbols->addSeparator();
2018-07-09 08:02:10 +00:00
m_symbols->addAction(tr("&Load Symbol Map"), this, &MenuBar::LoadSymbolMap);
m_symbols->addAction(tr("&Save Symbol Map"), this, &MenuBar::SaveSymbolMap);
2018-02-14 22:25:01 +00:00
m_symbols->addSeparator();
2018-07-09 08:02:10 +00:00
m_symbols->addAction(tr("Load &Other Map File..."), this, &MenuBar::LoadOtherSymbolMap);
m_symbols->addAction(tr("Load &Bad Map File..."), this, &MenuBar::LoadBadSymbolMap);
2018-07-09 08:02:10 +00:00
m_symbols->addAction(tr("Save Symbol Map &As..."), this, &MenuBar::SaveSymbolMapAs);
2018-02-14 22:25:01 +00:00
m_symbols->addSeparator();
m_symbols->addAction(tr("Sa&ve Code"), this, &MenuBar::SaveCode);
2018-02-14 22:25:01 +00:00
m_symbols->addSeparator();
m_symbols->addAction(tr("C&reate Signature File..."), this, &MenuBar::CreateSignatureFile);
m_symbols->addAction(tr("Append to &Existing Signature File..."), this,
&MenuBar::AppendSignatureFile);
m_symbols->addAction(tr("Combine &Two Signature Files..."), this,
&MenuBar::CombineSignatureFiles);
m_symbols->addAction(tr("Appl&y Signature File..."), this, &MenuBar::ApplySignatureFile);
2018-02-14 22:25:01 +00:00
m_symbols->addSeparator();
2018-07-09 08:02:10 +00:00
m_symbols->addAction(tr("&Patch HLE Functions"), this, &MenuBar::PatchHLEFunctions);
2018-02-14 22:25:01 +00:00
}
void MenuBar::UpdateToolsMenu(bool emulation_started)
{
m_boot_sysmenu->setEnabled(!emulation_started);
2017-07-06 09:01:32 +00:00
m_perform_online_update_menu->setEnabled(!emulation_started);
m_ntscj_ipl->setEnabled(!emulation_started && File::Exists(Config::GetBootROMPath(JAP_DIR)));
m_ntscu_ipl->setEnabled(!emulation_started && File::Exists(Config::GetBootROMPath(USA_DIR)));
m_pal_ipl->setEnabled(!emulation_started && File::Exists(Config::GetBootROMPath(EUR_DIR)));
m_wad_install_action->setEnabled(!emulation_started);
m_import_backup->setEnabled(!emulation_started);
m_check_nand->setEnabled(!emulation_started);
m_import_wii_save->setEnabled(!emulation_started);
m_export_wii_saves->setEnabled(!emulation_started);
2017-07-06 09:01:32 +00:00
if (!emulation_started)
{
IOS::HLE::Kernel ios;
const auto tmd = ios.GetESCore().FindInstalledTMD(Titles::SYSTEM_MENU);
const QString sysmenu_version =
tmd.IsValid() ? QString::fromStdString(
DiscIO::GetSysMenuVersionString(tmd.GetTitleVersion(), tmd.IsvWii())) :
QString{};
const QString sysmenu_text = (tmd.IsValid() && tmd.IsvWii()) ? tr("Load vWii System Menu %1") :
tr("Load Wii System Menu %1");
m_boot_sysmenu->setText(sysmenu_text.arg(sysmenu_version));
2017-07-06 09:01:32 +00:00
m_boot_sysmenu->setEnabled(tmd.IsValid());
for (QAction* action : m_perform_online_update_menu->actions())
action->setEnabled(!tmd.IsValid());
m_perform_online_update_for_current_region->setEnabled(tmd.IsValid());
}
const auto bt = WiiUtils::GetBluetoothEmuDevice();
const bool enable_wiimotes = emulation_started && bt != nullptr;
for (std::size_t i = 0; i < m_wii_remotes.size(); i++)
{
QAction* const wii_remote = m_wii_remotes[i];
wii_remote->setEnabled(enable_wiimotes);
if (enable_wiimotes)
wii_remote->setChecked(bt->AccessWiimoteByIndex(i)->IsConnected());
}
}
void MenuBar::InstallWAD()
{
QString wad_file = DolphinFileDialog::getOpenFileName(
this, tr("Select a title to install to NAND"), QString(), tr("WAD files (*.wad)"));
if (wad_file.isEmpty())
return;
if (WiiUtils::InstallWAD(wad_file.toStdString()))
{
Settings::Instance().NANDRefresh();
2019-03-04 19:49:00 +00:00
ModalMessageBox::information(this, tr("Success"),
tr("Successfully installed this title to the NAND."));
}
else
{
2019-03-04 19:49:00 +00:00
ModalMessageBox::critical(this, tr("Failure"), tr("Failed to install this title to the NAND."));
}
}
2017-08-24 13:11:04 +00:00
void MenuBar::ImportWiiSave()
{
QString file =
DolphinFileDialog::getOpenFileName(this, tr("Select the save file"), QDir::currentPath(),
tr("Wii save files (*.bin);;"
"All Files (*)"));
2017-08-24 13:11:04 +00:00
if (file.isEmpty())
return;
auto can_overwrite = [&] {
return ModalMessageBox::question(
this, tr("Save Import"),
tr("Save data for this title already exists in the NAND. Consider backing up "
"the current data before overwriting.\nOverwrite now?")) == QMessageBox::Yes;
};
const auto result = WiiSave::Import(file.toStdString(), can_overwrite);
switch (result)
{
case WiiSave::CopyResult::Success:
ModalMessageBox::information(this, tr("Save Import"), tr("Successfully imported save file."));
break;
case WiiSave::CopyResult::CorruptedSource:
ModalMessageBox::critical(this, tr("Save Import"),
tr("Failed to import save file. The given file appears to be "
"corrupted or is not a valid Wii save."));
break;
case WiiSave::CopyResult::TitleMissing:
ModalMessageBox::critical(
this, tr("Save Import"),
tr("Failed to import save file. Please launch the game once, then try again."));
break;
case WiiSave::CopyResult::Cancelled:
break;
default:
ModalMessageBox::critical(
this, tr("Save Import"),
tr("Failed to import save file. Your NAND may be corrupt, or something is preventing "
"access to files within it. Try repairing your NAND (Tools -> Manage NAND -> Check "
"NAND...), then import the save again."));
break;
}
2017-08-24 13:11:04 +00:00
}
2017-08-24 13:21:21 +00:00
void MenuBar::ExportWiiSaves()
{
const QString export_dir = DolphinFileDialog::getExistingDirectory(
this, tr("Select Export Directory"), QString::fromStdString(File::GetUserPath(D_USER_IDX)),
QFileDialog::ShowDirsOnly);
if (export_dir.isEmpty())
return;
const size_t count = WiiSave::ExportAll(export_dir.toStdString());
2019-03-04 19:49:00 +00:00
ModalMessageBox::information(this, tr("Save Export"),
tr("Exported %n save(s)", "", static_cast<int>(count)));
2017-08-24 13:21:21 +00:00
}
void MenuBar::CheckNAND()
{
IOS::HLE::Kernel ios;
WiiUtils::NANDCheckResult result = WiiUtils::CheckNAND(ios);
if (!result.bad)
{
2023-07-14 03:10:18 +00:00
const bool overfull = result.used_clusters_user > IOS::HLE::FS::USER_CLUSTERS ||
result.used_clusters_system > IOS::HLE::FS::SYSTEM_CLUSTERS;
const QString user_cluster_message =
tr("The user-accessible part of your NAND contains %1 blocks (%2 KiB) "
"of data, out of an allowed maximum of %3 blocks (%4 KiB).")
.arg(Common::AlignUp(result.used_clusters_user, IOS::HLE::FS::CLUSTERS_PER_BLOCK) /
IOS::HLE::FS::CLUSTERS_PER_BLOCK)
.arg((result.used_clusters_user * IOS::HLE::FS::CLUSTER_SIZE) / 1024)
.arg(IOS::HLE::FS::USER_CLUSTERS / IOS::HLE::FS::CLUSTERS_PER_BLOCK)
.arg((IOS::HLE::FS::USER_CLUSTERS * IOS::HLE::FS::CLUSTER_SIZE) / 1024);
const QString system_cluster_message =
tr("The system-reserved part of your NAND contains %1 blocks (%2 KiB) "
"of data, out of an allowed maximum of %3 blocks (%4 KiB).")
.arg(Common::AlignUp(result.used_clusters_system, IOS::HLE::FS::CLUSTERS_PER_BLOCK) /
IOS::HLE::FS::CLUSTERS_PER_BLOCK)
.arg((result.used_clusters_system * IOS::HLE::FS::CLUSTER_SIZE) / 1024)
.arg(IOS::HLE::FS::SYSTEM_CLUSTERS / IOS::HLE::FS::CLUSTERS_PER_BLOCK)
.arg((IOS::HLE::FS::SYSTEM_CLUSTERS * IOS::HLE::FS::CLUSTER_SIZE) / 1024);
if (overfull)
{
ModalMessageBox::warning(this, tr("NAND Check"),
QStringLiteral("<b>%1</b><br/><br/>%2<br/><br/>%3")
.arg(tr("Your NAND contains more data than allowed. Wii "
"software may behave incorrectly or not allow saving."))
.arg(user_cluster_message)
.arg(system_cluster_message));
}
else
{
ModalMessageBox::information(this, tr("NAND Check"),
QStringLiteral("<b>%1</b><br/><br/>%2<br/><br/>%3")
.arg(tr("No issues have been detected."))
.arg(user_cluster_message)
.arg(system_cluster_message));
}
return;
}
{
NANDRepairDialog dialog(result, this);
SetQWidgetWindowDecorations(&dialog);
if (dialog.exec() != QDialog::Accepted)
return;
}
if (WiiUtils::RepairNAND(ios))
{
2019-03-04 19:49:00 +00:00
ModalMessageBox::information(this, tr("NAND Check"), tr("The NAND has been repaired."));
return;
}
2019-03-04 19:49:00 +00:00
ModalMessageBox::critical(this, tr("NAND Check"),
tr("The NAND could not be repaired. It is recommended to back up "
"your current data and start over with a fresh NAND."));
}
void MenuBar::NANDExtractCertificates()
{
if (DiscIO::NANDImporter().ExtractCertificates())
{
2019-03-04 19:49:00 +00:00
ModalMessageBox::information(this, tr("Success"),
tr("Successfully extracted certificates from NAND"));
}
else
{
2019-03-04 19:49:00 +00:00
ModalMessageBox::critical(this, tr("Error"), tr("Failed to extract certificates from NAND"));
}
}
2017-08-27 11:55:05 +00:00
void MenuBar::OnSelectionChanged(std::shared_ptr<const UICommon::GameFile> game_file)
2017-08-27 11:55:05 +00:00
{
m_game_selected = !!game_file;
2017-08-27 11:55:05 +00:00
m_recording_play->setEnabled(m_game_selected && !Core::IsRunning());
m_recording_start->setEnabled((m_game_selected || Core::IsRunning()) && !Movie::IsPlayingInput());
2017-08-27 11:55:05 +00:00
}
void MenuBar::OnRecordingStatusChanged(bool recording)
{
m_recording_start->setEnabled(!recording && (m_game_selected || Core::IsRunning()));
2017-08-27 11:55:05 +00:00
m_recording_stop->setEnabled(recording);
m_recording_export->setEnabled(recording);
2017-08-27 11:55:05 +00:00
}
void MenuBar::OnReadOnlyModeChanged(bool read_only)
{
m_recording_read_only->setChecked(read_only);
}
2018-02-14 22:25:01 +00:00
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()
{
2019-03-04 19:49:00 +00:00
auto result = ModalMessageBox::warning(this, tr("Confirmation"),
tr("Do you want to clear the list of symbol names?"),
QMessageBox::Yes | QMessageBox::Cancel);
2018-02-14 22:25:01 +00:00
if (result == QMessageBox::Cancel)
return;
g_symbolDB.Clear();
emit NotifySymbolsUpdated();
2018-02-14 22:25:01 +00:00
}
void MenuBar::GenerateSymbolsFromAddress()
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
PPCAnalyst::FindFunctions(guard, Memory::MEM1_BASE_ADDR,
Memory::MEM1_BASE_ADDR + memory.GetRamSizeReal(), &g_symbolDB);
emit NotifySymbolsUpdated();
2018-02-14 22:25:01 +00:00
}
void MenuBar::GenerateSymbolsFromSignatureDB()
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
PPCAnalyst::FindFunctions(guard, Memory::MEM1_BASE_ADDR,
Memory::MEM1_BASE_ADDR + memory.GetRamSizeReal(), &g_symbolDB);
2018-02-14 22:25:01 +00:00
SignatureDB db(SignatureDB::HandlerType::DSY);
if (db.Load(File::GetSysDirectory() + TOTALDB))
{
db.Apply(guard, &g_symbolDB);
2019-03-04 19:49:00 +00:00
ModalMessageBox::information(
2018-02-14 22:25:01 +00:00
this, tr("Information"),
tr("Generated symbol names from '%1'").arg(QString::fromStdString(TOTALDB)));
db.List();
}
else
{
2019-03-04 19:49:00 +00:00
ModalMessageBox::critical(
2018-02-14 22:25:01 +00:00
this, tr("Error"),
tr("'%1' not found, no symbol names generated").arg(QString::fromStdString(TOTALDB)));
}
emit NotifySymbolsUpdated();
2018-02-14 22:25:01 +00:00
}
void MenuBar::GenerateSymbolsFromRSO()
{
// i18n: RSO refers to a proprietary format for shared objects (like DLL files).
const int ret =
ModalMessageBox::question(this, tr("RSO auto-detection"), tr("Auto-detect RSO modules?"));
if (ret == QMessageBox::Yes)
return GenerateSymbolsFromRSOAuto();
const QString text =
QInputDialog::getText(this, tr("Input"), tr("Enter the RSO module address:"),
QLineEdit::Normal, QString{}, nullptr, Qt::WindowCloseButtonHint);
2018-02-14 22:25:01 +00:00
bool good;
const uint address = text.toUInt(&good, 16);
2018-02-14 22:25:01 +00:00
if (!good)
{
2019-03-04 19:49:00 +00:00
ModalMessageBox::warning(this, tr("Error"), tr("Invalid RSO module address: %1").arg(text));
2018-02-14 22:25:01 +00:00
return;
}
Core::CPUThreadGuard guard(Core::System::GetInstance());
2018-02-14 22:25:01 +00:00
RSOChainView rso_chain;
if (rso_chain.Load(guard, static_cast<u32>(address)))
2018-02-14 22:25:01 +00:00
{
rso_chain.Apply(guard, &g_symbolDB);
emit NotifySymbolsUpdated();
2018-02-14 22:25:01 +00:00
}
else
{
2019-03-04 19:49:00 +00:00
ModalMessageBox::warning(this, tr("Error"), tr("Failed to load RSO module at %1").arg(text));
2018-02-14 22:25:01 +00:00
}
}
void MenuBar::GenerateSymbolsFromRSOAuto()
{
2021-02-27 20:04:45 +00:00
ParallelProgressDialog progress(tr("Modules found: %1").arg(0), tr("Cancel"), 0, 0, this);
progress.GetRaw()->setWindowTitle(tr("Detecting RSO Modules"));
progress.GetRaw()->setMinimumDuration(1000 * 10);
progress.GetRaw()->setWindowModality(Qt::WindowModal);
2021-02-27 20:04:45 +00:00
auto future = std::async(std::launch::async, [&progress, this]() -> RSOVector {
progress.SetValue(0);
auto matches = DetectRSOModules(progress);
progress.Reset();
2021-02-27 20:04:45 +00:00
return matches;
});
SetQWidgetWindowDecorations(progress.GetRaw());
2021-02-27 20:04:45 +00:00
progress.GetRaw()->exec();
const auto matches = future.get();
QStringList items;
for (const auto& match : matches)
{
const QString item = QLatin1String("%1 %2");
items << item.arg(QString::number(match.first, 16), QString::fromStdString(match.second));
}
if (items.empty())
{
ModalMessageBox::warning(this, tr("Error"), tr("Unable to auto-detect RSO module"));
return;
}
bool ok;
const QString item =
QInputDialog::getItem(this, tr("Input"), tr("Select the RSO module address:"), items, 0,
false, &ok, Qt::WindowCloseButtonHint);
if (!ok)
return;
RSOChainView rso_chain;
const u32 address = item.mid(0, item.indexOf(QLatin1Char(' '))).toUInt(nullptr, 16);
Core::CPUThreadGuard guard(Core::System::GetInstance());
if (rso_chain.Load(guard, address))
{
rso_chain.Apply(guard, &g_symbolDB);
emit NotifySymbolsUpdated();
}
else
{
ModalMessageBox::warning(this, tr("Error"), tr("Failed to load RSO module at %1").arg(address));
}
}
2021-02-27 20:04:45 +00:00
RSOVector MenuBar::DetectRSOModules(ParallelProgressDialog& progress)
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
2021-02-27 20:04:45 +00:00
constexpr std::array<std::string_view, 2> search_for = {".elf", ".plf"};
const AddressSpace::Accessors* accessors =
AddressSpace::GetAccessors(AddressSpace::Type::Effective);
RSOVector matches;
// Find filepath to elf/plf commonly used by RSO modules
for (const auto& str : search_for)
{
u32 next = 0;
while (true)
{
if (progress.WasCanceled())
{
return matches;
}
auto found_addr = accessors->Search(guard, next, reinterpret_cast<const u8*>(str.data()),
str.size() + 1, true);
2021-02-27 20:04:45 +00:00
if (!found_addr.has_value())
break;
next = *found_addr + 1;
// Non-null data can precede the module name.
// Get the maximum name length that a module could have.
auto get_max_module_name_len = [&guard, found_addr] {
2021-02-27 20:04:45 +00:00
constexpr u32 MODULE_NAME_MAX_LENGTH = 260;
u32 len = 0;
for (; len < MODULE_NAME_MAX_LENGTH; ++len)
{
const auto res = PowerPC::MMU::HostRead_U8(guard, *found_addr - (len + 1));
2021-02-27 20:04:45 +00:00
if (!std::isprint(res))
{
break;
}
}
return len;
};
if (progress.WasCanceled())
{
return matches;
}
const auto max_name_length = get_max_module_name_len();
auto found = false;
u32 module_name_length = 0;
// Look for the Module Name Offset Field based on each possible length
for (u32 i = 0; i < max_name_length; ++i)
{
if (progress.WasCanceled())
{
return matches;
}
const auto lookup_addr = (*found_addr - max_name_length) + i;
const std::array<u8, 4> ref = {
static_cast<u8>(lookup_addr >> 24), static_cast<u8>(lookup_addr >> 16),
static_cast<u8>(lookup_addr >> 8), static_cast<u8>(lookup_addr)};
// Get the field (Module Name Offset) that point to the string
const auto module_name_offset_addr =
accessors->Search(guard, lookup_addr, ref.data(), ref.size(), false);
2021-02-27 20:04:45 +00:00
if (!module_name_offset_addr.has_value())
continue;
// The next 4 bytes should be the module name length
module_name_length = accessors->ReadU32(guard, *module_name_offset_addr + 4);
2021-02-27 20:04:45 +00:00
if (module_name_length == max_name_length - i + str.length())
{
found_addr = module_name_offset_addr;
found = true;
break;
}
}
if (!found)
continue;
const auto module_name_offset = accessors->ReadU32(guard, *found_addr);
2021-02-27 20:04:45 +00:00
// Go to the beginning of the RSO header
matches.emplace_back(*found_addr - 16, PowerPC::MMU::HostGetString(guard, module_name_offset,
module_name_length));
2021-02-27 20:04:45 +00:00
progress.SetLabelText(tr("Modules found: %1").arg(matches.size()));
}
}
return matches;
}
2018-02-14 22:25:01 +00:00
void MenuBar::LoadSymbolMap()
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
2018-02-14 22:25:01 +00:00
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();
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
PPCAnalyst::FindFunctions(guard, Memory::MEM1_BASE_ADDR + 0x1300000,
Memory::MEM1_BASE_ADDR + memory.GetRamSizeReal(), &g_symbolDB);
SignatureDB db(SignatureDB::HandlerType::DSY);
if (db.Load(File::GetSysDirectory() + TOTALDB))
db.Apply(guard, &g_symbolDB);
}
2018-02-14 22:25:01 +00:00
2019-03-04 19:49:00 +00:00
ModalMessageBox::warning(this, tr("Warning"),
tr("'%1' not found, scanning for common functions instead")
.arg(QString::fromStdString(writable_map_file)));
2018-02-14 22:25:01 +00:00
}
else
{
const QString existing_map_file_path = QString::fromStdString(existing_map_file);
if (!TryLoadMapFile(existing_map_file_path))
return;
2019-03-04 19:49:00 +00:00
ModalMessageBox::information(this, tr("Information"),
tr("Loaded symbols from '%1'").arg(existing_map_file_path));
2018-02-14 22:25:01 +00:00
}
HLE::PatchFunctions(system);
emit NotifySymbolsUpdated();
2018-02-14 22:25:01 +00:00
}
void MenuBar::SaveSymbolMap()
{
std::string existing_map_file, writable_map_file;
CBoot::FindMapFile(&existing_map_file, &writable_map_file);
TrySaveSymbolMap(QString::fromStdString(writable_map_file));
2018-02-14 22:25:01 +00:00
}
void MenuBar::LoadOtherSymbolMap()
{
const QString file = DolphinFileDialog::getOpenFileName(
this, tr("Load map file"), QString::fromStdString(File::GetUserPath(D_MAPS_IDX)),
tr("Dolphin Map File (*.map)"));
2018-02-14 22:25:01 +00:00
if (file.isEmpty())
return;
if (!TryLoadMapFile(file))
return;
auto& system = Core::System::GetInstance();
HLE::PatchFunctions(system);
emit NotifySymbolsUpdated();
2018-02-14 22:25:01 +00:00
}
void MenuBar::LoadBadSymbolMap()
{
const QString file = DolphinFileDialog::getOpenFileName(
this, tr("Load map file"), QString::fromStdString(File::GetUserPath(D_MAPS_IDX)),
tr("Dolphin Map File (*.map)"));
if (file.isEmpty())
return;
if (!TryLoadMapFile(file, true))
return;
auto& system = Core::System::GetInstance();
HLE::PatchFunctions(system);
emit NotifySymbolsUpdated();
}
2018-02-14 22:25:01 +00:00
void MenuBar::SaveSymbolMapAs()
{
const std::string& title_id_str = SConfig::GetInstance().m_debugger_game_id;
const QString file = DolphinFileDialog::getSaveFileName(
2018-02-14 22:25:01 +00:00
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;
TrySaveSymbolMap(file);
2018-02-14 22:25:01 +00:00
}
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";
2018-02-14 22:25:01 +00:00
bool success;
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
success = g_symbolDB.SaveCodeMap(guard, path);
}
if (!success)
{
2019-03-04 19:49:00 +00:00
ModalMessageBox::warning(
this, tr("Error"),
tr("Failed to save code map to path '%1'").arg(QString::fromStdString(path)));
}
2018-02-14 22:25:01 +00:00
}
bool MenuBar::TryLoadMapFile(const QString& path, const bool bad)
2018-02-14 22:25:01 +00:00
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
if (!g_symbolDB.LoadMap(guard, path.toStdString(), bad))
{
2019-03-04 19:49:00 +00:00
ModalMessageBox::warning(this, tr("Error"), tr("Failed to load map file '%1'").arg(path));
return false;
}
2018-02-14 22:25:01 +00:00
return true;
}
void MenuBar::TrySaveSymbolMap(const QString& path)
{
if (g_symbolDB.SaveSymbolMap(path.toStdString()))
2018-02-14 22:25:01 +00:00
return;
2019-03-04 19:49:00 +00:00
ModalMessageBox::warning(this, tr("Error"),
tr("Failed to save symbol map to path '%1'").arg(path));
}
2018-02-14 22:25:01 +00:00
void MenuBar::CreateSignatureFile()
{
const QString text = QInputDialog::getText(
this, tr("Input"), tr("Only export symbols with prefix:\n(Blank for all symbols)"),
QLineEdit::Normal, QString{}, nullptr, Qt::WindowCloseButtonHint);
2018-02-14 22:25:01 +00:00
const QString file = DolphinFileDialog::getSaveFileName(this, tr("Save signature file"),
QDir::homePath(), GetSignatureSelector());
2018-02-14 22:25:01 +00:00
if (file.isEmpty())
return;
const std::string prefix = text.toStdString();
const std::string save_path = file.toStdString();
2018-02-14 22:25:01 +00:00
SignatureDB db(save_path);
db.Populate(&g_symbolDB, prefix);
if (!db.Save(save_path))
{
2019-03-04 19:49:00 +00:00
ModalMessageBox::warning(this, tr("Error"), tr("Failed to save signature file '%1'").arg(file));
return;
}
2018-02-14 22:25:01 +00:00
db.List();
}
void MenuBar::AppendSignatureFile()
{
const QString text = QInputDialog::getText(
this, tr("Input"), tr("Only append symbols with prefix:\n(Blank for all symbols)"),
QLineEdit::Normal, QString{}, nullptr, Qt::WindowCloseButtonHint);
const QString file = DolphinFileDialog::getSaveFileName(this, tr("Append signature to"),
QDir::homePath(), GetSignatureSelector());
if (file.isEmpty())
return;
const std::string prefix = text.toStdString();
const std::string signature_path = file.toStdString();
SignatureDB db(signature_path);
db.Populate(&g_symbolDB, prefix);
db.List();
db.Load(signature_path);
if (!db.Save(signature_path))
{
2019-03-04 19:49:00 +00:00
ModalMessageBox::warning(this, tr("Error"),
tr("Failed to append to signature file '%1'").arg(file));
return;
}
db.List();
}
void MenuBar::ApplySignatureFile()
{
const QString file = DolphinFileDialog::getOpenFileName(this, tr("Apply signature file"),
QDir::homePath(), GetSignatureSelector());
if (file.isEmpty())
return;
const std::string load_path = file.toStdString();
SignatureDB db(load_path);
db.Load(load_path);
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
db.Apply(guard, &g_symbolDB);
}
db.List();
auto& system = Core::System::GetInstance();
HLE::PatchFunctions(system);
emit NotifySymbolsUpdated();
}
void MenuBar::CombineSignatureFiles()
{
const QString priorityFile = DolphinFileDialog::getOpenFileName(
2019-10-12 16:04:51 +00:00
this, tr("Choose priority input file"), QDir::homePath(), GetSignatureSelector());
if (priorityFile.isEmpty())
return;
const QString secondaryFile = DolphinFileDialog::getOpenFileName(
2019-10-12 16:04:51 +00:00
this, tr("Choose secondary input file"), QDir::homePath(), GetSignatureSelector());
if (secondaryFile.isEmpty())
return;
const QString saveFile = DolphinFileDialog::getSaveFileName(
this, tr("Save combined output file as"), QDir::homePath(), GetSignatureSelector());
if (saveFile.isEmpty())
return;
const std::string load_pathPriorityFile = priorityFile.toStdString();
const std::string load_pathSecondaryFile = secondaryFile.toStdString();
const std::string save_path = saveFile.toStdString();
SignatureDB db(load_pathPriorityFile);
db.Load(load_pathPriorityFile);
db.Load(load_pathSecondaryFile);
if (!db.Save(save_path))
{
2019-03-04 19:49:00 +00:00
ModalMessageBox::warning(this, tr("Error"),
tr("Failed to save to signature file '%1'").arg(saveFile));
return;
}
db.List();
}
2018-02-14 22:25:01 +00:00
void MenuBar::PatchHLEFunctions()
{
auto& system = Core::System::GetInstance();
HLE::PatchFunctions(system);
2018-02-14 22:25:01 +00:00
}
2018-04-09 13:31:20 +00:00
void MenuBar::ClearCache()
{
Core::RunAsCPUThread([] { Core::System::GetInstance().GetJitInterface().ClearCache(); });
2018-04-09 13:31:20 +00:00
}
void MenuBar::LogInstructions()
{
PPCTables::LogCompiledInstructions();
}
void MenuBar::SearchInstruction()
{
bool good;
const QString op =
QInputDialog::getText(this, tr("Search instruction"), tr("Instruction:"), QLineEdit::Normal,
QString{}, &good, Qt::WindowCloseButtonHint);
2018-04-09 13:31:20 +00:00
if (!good)
return;
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
Core::CPUThreadGuard guard(system);
2018-04-09 13:31:20 +00:00
bool found = false;
for (u32 addr = Memory::MEM1_BASE_ADDR; addr < Memory::MEM1_BASE_ADDR + memory.GetRamSizeReal();
Configurable MEM1 and MEM2 sizes at runtime via Dolphin.ini Changed several enums from Memmap.h to be static vars and implemented Get functions to query them. This seems to have boosted speed a bit in some titles? The new variables and some previously statically initialized items are now initialized via Memory::Init() and the new AddressSpace::Init(). s_ram_size_real and the new s_exram_size_real in particular are initialized from new OnionConfig values "MAIN_MEM1_SIZE" and "MAIN_MEM2_SIZE", only if "MAIN_RAM_OVERRIDE_ENABLE" is true. GUI features have been added to Config > Advanced to adjust the new OnionConfig values. A check has been added to State::doState to ensure savestates with memory configurations different from the current settings aren't loaded. The STATE_VERSION is now 115. FIFO Files have been updated from version 4 to version 5, now including the MEM1 and MEM2 sizes from the time of DFF creation. FIFO Logs not using the new features (OnionConfig MAIN_RAM_OVERRIDE_ENABLE is false) are still backwards compatible. FIFO Logs that do use the new features have a MIN_LOADER_VERSION of 5. Thanks to the order of function calls, FIFO logs are able to automatically configure the new OnionConfig settings to match what is needed. This is a bit hacky, though, so I also threw in a failsafe for if the conditions that allow this to work ever go away. I took the liberty of adding a log message to explain why the core fails to initialize if the MIN_LOADER_VERSION is too great. Some IOS code has had the function "RAMOverrideForIOSMemoryValues" appended to it to recalculate IOS Memory Values from retail IOSes/apploaders to fit the extended memory sizes. Worry not, if MAIN_RAM_OVERRIDE_ENABLE is false, this function does absolutely nothing. A hotfix in DolphinQt/MenuBar.cpp has been implemented for RAM Override.
2020-04-28 17:10:50 +00:00
addr += 4)
2018-04-09 13:31:20 +00:00
{
const auto ins_name = QString::fromStdString(
PPCTables::GetInstructionName(PowerPC::MMU::HostRead_U32(guard, addr), addr));
2018-04-09 13:31:20 +00:00
if (op == ins_name)
{
2020-11-26 02:13:50 +00:00
NOTICE_LOG_FMT(POWERPC, "Found {} at {:08x}", op.toStdString(), addr);
2018-04-09 13:31:20 +00:00
found = true;
}
}
if (!found)
2020-11-26 02:13:50 +00:00
NOTICE_LOG_FMT(POWERPC, "Opcode {} not found", op.toStdString());
2018-04-09 13:31:20 +00:00
}