diff --git a/Source/Core/DolphinQt2/MainWindow.cpp b/Source/Core/DolphinQt2/MainWindow.cpp index 6dc3ff4ec9..e5004f4eb2 100644 --- a/Source/Core/DolphinQt2/MainWindow.cpp +++ b/Source/Core/DolphinQt2/MainWindow.cpp @@ -12,6 +12,9 @@ #include #include #include +#include + +#include #include "Common/Common.h" @@ -33,6 +36,8 @@ #include "Core/NetPlayServer.h" #include "Core/State.h" +#include "DiscIO/NANDImporter.h" + #include "DolphinQt2/AboutDialog.h" #include "DolphinQt2/Config/ControllersWindow.h" #include "DolphinQt2/Config/Graphics/GraphicsWindow.h" @@ -44,6 +49,7 @@ #include "DolphinQt2/MainWindow.h" #include "DolphinQt2/NetPlay/NetPlayDialog.h" #include "DolphinQt2/NetPlay/NetPlaySetupDialog.h" +#include "DolphinQt2/QtUtils/QueueOnObject.h" #include "DolphinQt2/QtUtils/WindowActivationEventFilter.h" #include "DolphinQt2/Resources.h" #include "DolphinQt2/Settings.h" @@ -199,6 +205,8 @@ void MainWindow::ConnectMenuBar() connect(m_menu_bar, &MenuBar::ConfigureHotkeys, this, &MainWindow::ShowHotkeyDialog); // Tools + connect(m_menu_bar, &MenuBar::BootGameCubeIPL, this, &MainWindow::OnBootGameCubeIPL); + connect(m_menu_bar, &MenuBar::ImportNANDBackup, this, &MainWindow::OnImportNANDBackup); connect(m_menu_bar, &MenuBar::PerformOnlineUpdate, this, &MainWindow::PerformOnlineUpdate); connect(m_menu_bar, &MenuBar::BootWiiSystemMenu, this, &MainWindow::BootWiiSystemMenu); connect(m_menu_bar, &MenuBar::StartNetPlay, this, &MainWindow::ShowNetPlaySetupDialog); @@ -345,10 +353,10 @@ void MainWindow::OnStopComplete() QGuiApplication::instance()->quit(); // If the current emulation prevented the booting of another, do that now - if (!m_pending_boot.isEmpty()) + if (m_pending_boot != nullptr) { - StartGame(m_pending_boot); - m_pending_boot.clear(); + StartGame(std::move(m_pending_boot)); + m_pending_boot.reset(); } } @@ -446,6 +454,11 @@ void MainWindow::ScreenShot() } void MainWindow::StartGame(const QString& path) +{ + StartGame(BootParameters::GenerateFromFile(path.toStdString())); +} + +void MainWindow::StartGame(std::unique_ptr&& parameters) { // If we're running, only start a new game once we've stopped the last. if (Core::GetState() != Core::State::Uninitialized) @@ -454,11 +467,11 @@ void MainWindow::StartGame(const QString& path) return; // As long as the shutdown isn't complete, we can't boot, so let's boot later - m_pending_boot = path; + m_pending_boot = std::move(parameters); return; } // Boot up, show an error if it fails to load the game. - if (!BootManager::BootCore(BootParameters::GenerateFromFile(path.toStdString()))) + if (!BootManager::BootCore(std::move(parameters))) { QMessageBox::critical(this, tr("Error"), tr("Failed to init core"), QMessageBox::Ok); return; @@ -637,7 +650,8 @@ void MainWindow::NetPlayInit() m_netplay_setup_dialog = new NetPlaySetupDialog(this); m_netplay_dialog = new NetPlayDialog(this); - connect(m_netplay_dialog, &NetPlayDialog::Boot, this, &MainWindow::StartGame); + connect(m_netplay_dialog, &NetPlayDialog::Boot, this, + static_cast(&MainWindow::StartGame)); connect(m_netplay_dialog, &NetPlayDialog::Stop, this, &MainWindow::RequestStop); connect(m_netplay_dialog, &NetPlayDialog::rejected, this, &MainWindow::NetPlayQuit); connect(this, &MainWindow::EmulationStopped, m_netplay_dialog, &NetPlayDialog::EmulationStopped); @@ -819,3 +833,52 @@ QSize MainWindow::sizeHint() const { return QSize(800, 600); } + +void MainWindow::OnBootGameCubeIPL(DiscIO::Region region) +{ + StartGame(std::make_unique(BootParameters::IPL{region})); +} + +void MainWindow::OnImportNANDBackup() +{ + auto response = QMessageBox::question( + this, tr("Question"), + tr("Merging a new NAND over your currently selected NAND will overwrite any channels " + "and savegames that already exist. This process is not reversible, so it is " + "recommended that you keep backups of both NANDs. Are you sure you want to " + "continue?")); + + if (response == QMessageBox::No) + return; + + QString file = QFileDialog::getOpenFileName(this, tr("Select the save file"), QDir::currentPath(), + tr("BootMii NAND backup file (*.bin);;" + "All Files (*)")); + + if (file.isEmpty()) + return; + + QProgressDialog* dialog = new QProgressDialog(this); + dialog->setMinimum(0); + dialog->setMaximum(0); + dialog->setLabelText(tr("Importing NAND backup")); + dialog->setCancelButton(nullptr); + + auto beginning = QDateTime::currentDateTime().toSecsSinceEpoch(); + + auto result = std::async(std::launch::async, [&] { + DiscIO::NANDImporter().ImportNANDBin(file.toStdString(), [&dialog, beginning] { + QueueOnObject(dialog, [&dialog, beginning] { + dialog->setLabelText(tr("Importing NAND backup\n Time elapsed: %1s") + .arg(QDateTime::currentDateTime().toSecsSinceEpoch() - beginning)); + }); + }); + QueueOnObject(dialog, [dialog] { dialog->close(); }); + }); + + dialog->exec(); + + result.wait(); + + m_menu_bar->UpdateToolsMenu(Core::IsRunning()); +} diff --git a/Source/Core/DolphinQt2/MainWindow.h b/Source/Core/DolphinQt2/MainWindow.h index c9804d940d..96efeef45b 100644 --- a/Source/Core/DolphinQt2/MainWindow.h +++ b/Source/Core/DolphinQt2/MainWindow.h @@ -9,11 +9,14 @@ #include #include +#include + #include "DolphinQt2/GameList/GameList.h" #include "DolphinQt2/MenuBar.h" #include "DolphinQt2/RenderWidget.h" #include "DolphinQt2/ToolBar.h" +struct BootParameters; class HotkeyScheduler; class LoggerWidget; class MappingWindow; @@ -83,6 +86,7 @@ private: void InitCoreCallbacks(); void StartGame(const QString& path); + void StartGame(std::unique_ptr&& parameters); void ShowRenderWidget(); void HideRenderWidget(); @@ -99,6 +103,8 @@ private: bool NetPlayHost(const QString& game_id); void NetPlayQuit(); + void OnBootGameCubeIPL(DiscIO::Region region); + void OnImportNANDBackup(); void OnStopComplete(); void dragEnterEvent(QDragEnterEvent* event) override; void dropEvent(QDropEvent* event) override; @@ -113,7 +119,7 @@ private: bool m_stop_requested = false; bool m_exit_requested = false; int m_state_slot = 1; - QString m_pending_boot; + std::unique_ptr m_pending_boot; HotkeyScheduler* m_hotkey_scheduler; ControllersWindow* m_controllers_window; diff --git a/Source/Core/DolphinQt2/MenuBar.cpp b/Source/Core/DolphinQt2/MenuBar.cpp index b599c99f53..8a1c6ec509 100644 --- a/Source/Core/DolphinQt2/MenuBar.cpp +++ b/Source/Core/DolphinQt2/MenuBar.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include "DolphinQt2/MenuBar.h" + #include #include #include @@ -9,14 +11,17 @@ #include #include +#include "Common/CommonPaths.h" +#include "Common/FileUtil.h" #include "Core/CommonTitles.h" #include "Core/ConfigManager.h" +#include "Core/HW/WiiSaveCrypted.h" #include "Core/IOS/ES/ES.h" #include "Core/IOS/IOS.h" #include "Core/State.h" +#include "DiscIO/NANDImporter.h" #include "DolphinQt2/AboutDialog.h" #include "DolphinQt2/GameList/GameFile.h" -#include "DolphinQt2/MenuBar.h" #include "DolphinQt2/Settings.h" MenuBar::MenuBar(QWidget* parent) : QMenuBar(parent) @@ -86,13 +91,35 @@ void MenuBar::AddFileMenu() void MenuBar::AddToolsMenu() { QMenu* tools_menu = addMenu(tr("&Tools")); + + tools_menu->addAction(tr("Import Wii Save..."), this, &MenuBar::ImportWiiSave); + tools_menu->addAction(tr("Export All Wii Saves"), this, &MenuBar::ExportWiiSaves); + + tools_menu->addSeparator(); + m_wad_install_action = tools_menu->addAction(tr("Install WAD..."), this, &MenuBar::InstallWAD); + tools_menu->addSeparator(); + QMenu* gc_ipl = tools_menu->addMenu(tr("Load GameCube Main Menu")); + + 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 = + gc_ipl->addAction(tr("PAL"), this, [this] { emit BootGameCubeIPL(DiscIO::Region::PAL); }); + tools_menu->addAction(tr("Start &NetPlay..."), this, &MenuBar::StartNetPlay); tools_menu->addSeparator(); // Label will be set by a NANDRefresh later m_boot_sysmenu = tools_menu->addAction(QStringLiteral(""), [this] { emit BootWiiSystemMenu(); }); + m_import_backup = tools_menu->addAction(tr("Import BootMii NAND Backup..."), + [this] { emit ImportNANDBackup(); }); + + m_extract_certificates = tools_menu->addAction(tr("Extract Certificates from NAND"), this, + &MenuBar::NANDExtractCertificates); + m_boot_sysmenu->setEnabled(false); connect(&Settings::Instance(), &Settings::NANDRefresh, [this] { UpdateToolsMenu(false); }); @@ -368,6 +395,13 @@ void MenuBar::UpdateToolsMenu(bool emulation_started) { m_boot_sysmenu->setEnabled(!emulation_started); m_perform_online_update_menu->setEnabled(!emulation_started); + m_ntscj_ipl->setEnabled(!emulation_started && + File::Exists(SConfig::GetInstance().GetBootROMPath(JAP_DIR))); + m_ntscu_ipl->setEnabled(!emulation_started && + File::Exists(SConfig::GetInstance().GetBootROMPath(USA_DIR))); + m_pal_ipl->setEnabled(!emulation_started && + File::Exists(SConfig::GetInstance().GetBootROMPath(EUR_DIR))); + m_import_backup->setEnabled(!emulation_started); if (!emulation_started) { @@ -411,3 +445,31 @@ void MenuBar::InstallWAD() result_dialog.exec(); } + +void MenuBar::ImportWiiSave() +{ + QString file = QFileDialog::getOpenFileName(this, tr("Select the save file"), QDir::currentPath(), + tr("Wii save files (*.bin);;" + "All Files (*)")); + + if (!file.isEmpty()) + CWiiSaveCrypted::ImportWiiSave(file.toStdString()); +} + +void MenuBar::ExportWiiSaves() +{ + CWiiSaveCrypted::ExportAllSaves(); +} + +void MenuBar::NANDExtractCertificates() +{ + if (DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX))) + { + QMessageBox::information(this, tr("Success"), + tr("Successfully extracted certificates from NAND")); + } + else + { + QMessageBox::critical(this, tr("Error"), tr("Failed to extract certificates from NAND")); + } +} diff --git a/Source/Core/DolphinQt2/MenuBar.h b/Source/Core/DolphinQt2/MenuBar.h index db490271ea..a170441d19 100644 --- a/Source/Core/DolphinQt2/MenuBar.h +++ b/Source/Core/DolphinQt2/MenuBar.h @@ -9,6 +9,11 @@ #include #include +namespace DiscIO +{ +enum class Region; +}; + class MenuBar final : public QMenuBar { Q_OBJECT @@ -22,9 +27,6 @@ public: void UpdateStateSlotMenu(); void UpdateToolsMenu(bool emulation_started); - // Tools - void InstallWAD(); - signals: // File void Open(); @@ -50,9 +52,13 @@ signals: void StateSaveOldest(); void SetStateSlot(int slot); void BootWiiSystemMenu(); + void ImportNANDBackup(); void PerformOnlineUpdate(const std::string& region); + // Tools + void BootGameCubeIPL(DiscIO::Region region); + // Options void Configure(); void ConfigureGraphics(); @@ -87,6 +93,11 @@ private: void AddToolsMenu(); void AddHelpMenu(); + void InstallWAD(); + void ImportWiiSave(); + void ExportWiiSaves(); + void NANDExtractCertificates(); + // File QAction* m_open_action; QAction* m_exit_action; @@ -95,6 +106,11 @@ private: QAction* m_wad_install_action; QMenu* m_perform_online_update_menu; QAction* m_perform_online_update_for_current_region; + QAction* m_ntscj_ipl; + QAction* m_ntscu_ipl; + QAction* m_pal_ipl; + QAction* m_import_backup; + QAction* m_extract_certificates; // Emulation QAction* m_play_action;