From b25c40302fb2fdcf6ac714770d19104c429c2fd4 Mon Sep 17 00:00:00 2001 From: Pengfei Date: Mon, 9 Aug 2021 19:04:44 +0800 Subject: [PATCH] Remove context help from all dialogs --- src/frontend/cia_build_dialog.cpp | 189 ++++----- src/frontend/helpers/simple_job.cpp | 3 +- src/frontend/import_dialog.cpp | 7 +- src/frontend/main.cpp | 501 +++++++++++----------- src/frontend/select_files_dialog.cpp | 1 + src/frontend/title_info_dialog.cpp | 1 + src/frontend/utilities.cpp | 608 ++++++++++++++------------- 7 files changed, 661 insertions(+), 649 deletions(-) diff --git a/src/frontend/cia_build_dialog.cpp b/src/frontend/cia_build_dialog.cpp index 4561cbd..44f2a37 100644 --- a/src/frontend/cia_build_dialog.cpp +++ b/src/frontend/cia_build_dialog.cpp @@ -1,94 +1,95 @@ -// Copyright 2021 threeSD Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include -#include -#include -#include "common/assert.h" -#include "frontend/cia_build_dialog.h" -#include "ui_cia_build_dialog.h" - -CIABuildDialog::CIABuildDialog(QWidget* parent, bool is_dir_, bool is_nand, bool enable_legit, - const QString& default_path) - : QDialog(parent), ui(std::make_unique()), is_dir(is_dir_) { - ui->setupUi(this); - - const double scale = qApp->desktop()->logicalDpiX() / 96.0; - resize(static_cast(width() * scale), static_cast(height() * scale)); - - if (is_dir) { - setWindowTitle(tr("Batch Build CIA")); - } - if (is_nand) { - ui->pirateLegitButton->setVisible(false); - ui->pirateLegitLabel->setVisible(false); - - auto message = tr("Encrypted CIA with legit TMD, encrypted contents and legit ticket.
"); - if (is_dir) { - message.append(tr( - "Legit tickets for these titles do not include console-identifying information.")); - } else { - message.append(tr( - "Legit ticket for this title does not include console-identifying information.")); - } - ui->legitLabel->setText(message); - } - if (!enable_legit) { - const auto message = - is_dir ? tr("This option is not available as some of the titles are not legit.") - : tr("This option is not available as the title is not legit."); - ui->pirateLegitButton->setEnabled(false); - ui->pirateLegitLabel->setText(message); - ui->legitButton->setEnabled(false); - ui->legitLabel->setText(message); - } - - connect(ui->buttonBox, &QDialogButtonBox::accepted, [this] { - if (ui->destination->text().isEmpty()) { - const QString message = is_dir ? tr("Please specify destination folder.") - : tr("Please specify destination file."); - QMessageBox::warning(this, tr("threeSD"), message); - return; - } - accept(); - }); - connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &CIABuildDialog::reject); - - if (is_dir) { - ui->destination->setText(default_path); - } - connect(ui->destinationExplore, &QToolButton::clicked, [this, default_path] { - QString path; - if (is_dir) { - path = QFileDialog::getExistingDirectory(this, tr("Batch Build CIA"), - ui->destination->text()); - } else { - const auto cur = ui->destination->text().isEmpty() - ? default_path - : QFileInfo(ui->destination->text()).path(); - path = QFileDialog::getSaveFileName(this, tr("Build CIA"), cur, - tr("CTR Importable Archive (*.cia)")); - } - if (!path.isEmpty()) { - ui->destination->setText(path); - } - }); -} - -CIABuildDialog::~CIABuildDialog() = default; - -std::pair CIABuildDialog::GetResults() const { - Core::CIABuildType type; - if (ui->standardButton->isChecked()) { - type = Core::CIABuildType::Standard; - } else if (ui->pirateLegitButton->isChecked()) { - type = Core::CIABuildType::PirateLegit; - } else if (ui->legitButton->isChecked()) { - type = Core::CIABuildType::Legit; - } else { - UNREACHABLE(); - } - return {ui->destination->text(), type}; -} +// Copyright 2021 threeSD Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include "common/assert.h" +#include "frontend/cia_build_dialog.h" +#include "ui_cia_build_dialog.h" + +CIABuildDialog::CIABuildDialog(QWidget* parent, bool is_dir_, bool is_nand, bool enable_legit, + const QString& default_path) + : QDialog(parent), ui(std::make_unique()), is_dir(is_dir_) { + ui->setupUi(this); + + setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); + const double scale = qApp->desktop()->logicalDpiX() / 96.0; + resize(static_cast(width() * scale), static_cast(height() * scale)); + + if (is_dir) { + setWindowTitle(tr("Batch Build CIA")); + } + if (is_nand) { + ui->pirateLegitButton->setVisible(false); + ui->pirateLegitLabel->setVisible(false); + + auto message = tr("Encrypted CIA with legit TMD, encrypted contents and legit ticket.
"); + if (is_dir) { + message.append(tr( + "Legit tickets for these titles do not include console-identifying information.")); + } else { + message.append(tr( + "Legit ticket for this title does not include console-identifying information.")); + } + ui->legitLabel->setText(message); + } + if (!enable_legit) { + const auto message = + is_dir ? tr("This option is not available as some of the titles are not legit.") + : tr("This option is not available as the title is not legit."); + ui->pirateLegitButton->setEnabled(false); + ui->pirateLegitLabel->setText(message); + ui->legitButton->setEnabled(false); + ui->legitLabel->setText(message); + } + + connect(ui->buttonBox, &QDialogButtonBox::accepted, [this] { + if (ui->destination->text().isEmpty()) { + const QString message = is_dir ? tr("Please specify destination folder.") + : tr("Please specify destination file."); + QMessageBox::warning(this, tr("threeSD"), message); + return; + } + accept(); + }); + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &CIABuildDialog::reject); + + if (is_dir) { + ui->destination->setText(default_path); + } + connect(ui->destinationExplore, &QToolButton::clicked, [this, default_path] { + QString path; + if (is_dir) { + path = QFileDialog::getExistingDirectory(this, tr("Batch Build CIA"), + ui->destination->text()); + } else { + const auto cur = ui->destination->text().isEmpty() + ? default_path + : QFileInfo(ui->destination->text()).path(); + path = QFileDialog::getSaveFileName(this, tr("Build CIA"), cur, + tr("CTR Importable Archive (*.cia)")); + } + if (!path.isEmpty()) { + ui->destination->setText(path); + } + }); +} + +CIABuildDialog::~CIABuildDialog() = default; + +std::pair CIABuildDialog::GetResults() const { + Core::CIABuildType type; + if (ui->standardButton->isChecked()) { + type = Core::CIABuildType::Standard; + } else if (ui->pirateLegitButton->isChecked()) { + type = Core::CIABuildType::PirateLegit; + } else if (ui->legitButton->isChecked()) { + type = Core::CIABuildType::Legit; + } else { + UNREACHABLE(); + } + return {ui->destination->text(), type}; +} diff --git a/src/frontend/helpers/simple_job.cpp b/src/frontend/helpers/simple_job.cpp index e97a3f0..e33b132 100644 --- a/src/frontend/helpers/simple_job.cpp +++ b/src/frontend/helpers/simple_job.cpp @@ -37,8 +37,9 @@ void SimpleJob::StartWithProgressDialog(QWidget* widget) { bar->setValue(0); auto* dialog = new QProgressDialog(tr("Initializing..."), tr("Cancel"), 0, 0, widget); - dialog->setBar(bar); + dialog->setWindowFlags(dialog->windowFlags() & (~Qt::WindowContextHelpButtonHint)); dialog->setWindowModality(Qt::WindowModal); + dialog->setBar(bar); dialog->setMinimumDuration(0); connect(this, &SimpleJob::ProgressUpdated, this, [bar, dialog](u64 current, u64 total) { diff --git a/src/frontend/import_dialog.cpp b/src/frontend/import_dialog.cpp index 1eabb26..c671c12 100644 --- a/src/frontend/import_dialog.cpp +++ b/src/frontend/import_dialog.cpp @@ -90,6 +90,7 @@ ImportDialog::ImportDialog(QWidget* parent, const Core::Config& config_) ui->setupUi(this); + setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); const double scale = qApp->desktop()->logicalDpiX() / 96.0; resize(static_cast(width() * scale), static_cast(height() * scale)); @@ -126,6 +127,7 @@ ImportDialog::~ImportDialog() = default; void ImportDialog::RelistContent() { auto* dialog = new QProgressDialog(tr("Loading Contents..."), tr("Cancel"), 0, 0, this); + dialog->setWindowFlags(dialog->windowFlags() & (~Qt::WindowContextHelpButtonHint)); dialog->setWindowModality(Qt::WindowModal); dialog->setCancelButton(nullptr); dialog->setMinimumDuration(0); @@ -585,9 +587,10 @@ void ImportDialog::RunMultiJob(MultiJob* job, std::size_t total_count, u64 total bar->setValue(0); auto* dialog = new QProgressDialog(tr("Initializing..."), tr("Cancel"), 0, 0, this); + dialog->setWindowFlags(dialog->windowFlags() & (~Qt::WindowContextHelpButtonHint)); + dialog->setWindowModality(Qt::WindowModal); dialog->setBar(bar); dialog->setLabel(label); - dialog->setWindowModality(Qt::WindowModal); dialog->setMinimumDuration(0); connect(job, &MultiJob::NextContent, this, @@ -639,6 +642,8 @@ void ImportDialog::RunMultiJob(MultiJob* job, std::size_t total_count, u64 total connect(dialog, &QProgressDialog::canceled, this, [this, job] { // Add yet-another-ProgressDialog to indicate cancel progress auto* cancel_dialog = new QProgressDialog(tr("Canceling..."), tr("Cancel"), 0, 0, this); + cancel_dialog->setWindowFlags(cancel_dialog->windowFlags() & + (~Qt::WindowContextHelpButtonHint)); cancel_dialog->setWindowModality(Qt::WindowModal); cancel_dialog->setCancelButton(nullptr); cancel_dialog->setMinimumDuration(0); diff --git a/src/frontend/main.cpp b/src/frontend/main.cpp index 474b5f6..599f7d7 100644 --- a/src/frontend/main.cpp +++ b/src/frontend/main.cpp @@ -1,250 +1,251 @@ -// Copyright 2014 Citra Emulator Project / 2019 threeSD Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "common/assert.h" -#include "common/file_util.h" -#include "frontend/import_dialog.h" -#include "frontend/main.h" -#include "frontend/utilities.h" -#include "ui_main.h" - -#ifdef __APPLE__ -#include -#include "common/common_paths.h" -#endif - -#ifdef QT_STATICPLUGIN -#include - -Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) -#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) -Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin) -#endif -#endif - -bool IsConfigGood(const Core::Config& config) { - return !config.sdmc_path.empty() && !config.user_path.empty() && - !config.movable_sed_path.empty() && !config.bootrom_path.empty(); -} - -MainDialog::MainDialog(QWidget* parent) : QDialog(parent), ui(std::make_unique()) { - ui->setupUi(this); - - const double scale = qApp->desktop()->logicalDpiX() / 96.0; - resize(static_cast(width() * scale), static_cast(height() * scale)); - - ui->buttonBox->button(QDialogButtonBox::StandardButton::Ok)->setEnabled(false); - ui->buttonBox->button(QDialogButtonBox::StandardButton::Reset)->setText(tr("Refresh")); - - LoadPresetConfig(); - - connect(ui->utilitiesButton, &QPushButton::clicked, [this] { - UtilitiesDialog dialog(this); - dialog.exec(); - }); - - connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button) { - if (button == ui->buttonBox->button(QDialogButtonBox::StandardButton::Reset)) { - LoadPresetConfig(); - } else if (button == ui->buttonBox->button(QDialogButtonBox::StandardButton::Ok)) { - LaunchImportDialog(); - } - }); - - QString destination_text{}; - const auto destination = FileUtil::GetUserPathType(); - if (destination == FileUtil::UserPathType::Normal) { -#ifdef __linux__ - destination_text = tr("Non-Flatpak Citra Install"); -#else - destination_text = tr("User-wide Citra Install"); -#endif - } else if (destination == FileUtil::UserPathType::Portable) { - destination_text = tr("Portable Citra Install"); - } else if (destination == FileUtil::UserPathType::Flatpak) { - destination_text = tr("Flatpak Citra Install"); - } else { - UNREACHABLE(); - } - ui->importDestination->setText(tr("Import Destination: %1").arg(destination_text)); - - connect(ui->main, &QTreeWidget::itemSelectionChanged, [this] { - ui->buttonBox->button(QDialogButtonBox::StandardButton::Ok) - ->setEnabled(!ui->main->selectedItems().empty()); - }); - connect(ui->main, &QTreeWidget::itemDoubleClicked, [this] { - if (!ui->main->selectedItems().empty()) - LaunchImportDialog(); - }); - - ui->main->setIndentation(4); - ui->main->setColumnWidth(0, 0.3 * width()); - ui->main->setColumnWidth(1, 0.4 * width()); - ui->main->setColumnWidth(2, 0.2 * width()); - - // Set up device watcher - auto* device_watcher = new QDeviceWatcher(this); - device_watcher->start(); - connect(device_watcher, &QDeviceWatcher::deviceAdded, this, &MainDialog::LoadPresetConfig); - connect(device_watcher, &QDeviceWatcher::deviceChanged, this, &MainDialog::LoadPresetConfig); - connect(device_watcher, &QDeviceWatcher::deviceRemoved, this, &MainDialog::LoadPresetConfig); -} - -MainDialog::~MainDialog() = default; - -static const std::regex sdmc_path_regex{"(.+)([/\\\\])Nintendo 3DS/([0-9a-f]{32})/([0-9a-f]{32})/"}; - -void MainDialog::LoadPresetConfig() { - ui->main->clear(); - preset_config_list.clear(); - - for (const auto& storage : QStorageInfo::mountedVolumes()) { - if (!storage.isValid() || !storage.isReady()) { - continue; - } - - auto list = Core::LoadPresetConfig(storage.rootPath().toStdString()); - for (std::size_t i = 0; i < list.size(); ++i) { - preset_config_list.emplace_back(list[i]); - - QString path = storage.rootPath(); - if (path.endsWith(QLatin1Char{'/'}) || path.endsWith(QLatin1Char{'\\'})) { - path.remove(path.size() - 1, 1); - } - - // Get ID0 - QString id0 = tr("Unknown"); - std::smatch match; - if (std::regex_match(list[i].sdmc_path, match, sdmc_path_regex)) { - if (match.size() >= 5) { - id0 = QString::fromStdString(match[3].str()); - } - } - - // Get status - QString status = tr("Good"); - if (!IsConfigGood(list[i])) { - status = tr("No Configuration Found"); - } else if (list[i].version != Core::CurrentDumperVersion) { - status = tr("Version Dismatch"); - } else if (list[i].safe_mode_firm_path.empty() || - list[i].config_savegame_path.empty() || - list[i].system_archives_path.empty()) { - - status = tr("Missing System Files"); - } else if (list[i].seed_db_path.empty()) { - status = tr("Good, Missing Seeds"); - } - - auto* item = new QTreeWidgetItem{{path, id0, status}}; - ui->main->invisibleRootItem()->addChild(item); - } - } - - auto* item = new QTreeWidgetItem{{tr("Browse SD Card Root...")}}; - item->setFirstColumnSpanned(true); - ui->main->invisibleRootItem()->addChild(item); -} - -void MainDialog::LaunchImportDialog() { - auto* item = ui->main->currentItem(); - if (!item) { - return; - } - - Core::Config config; - const auto index = ui->main->invisibleRootItem()->indexOfChild(item); - if (index == ui->main->invisibleRootItem()->childCount() - 1) { - const QString path = QFileDialog::getExistingDirectory(this, tr("Select SD Card Root")); - if (path.isEmpty()) { - return; - } - - const auto& list = Core::LoadPresetConfig(path.toStdString()); - if (list.size() > 1) { - QMessageBox::information( - this, tr("threeSD"), - tr("You have more than one 3DS data on your SD Card.\nthreeSD will " - "select the first one for you.")); - } else if (list.empty() || !IsConfigGood(list[0])) { - QMessageBox::critical( - this, tr("Error"), - tr("Could not load configuration.
Please check if you have followed the " - "guide correctly.")); - return; - } - - config = list[0]; - } else { - config = preset_config_list.at(index); - } - - // Check config integrity - if (!IsConfigGood(config)) { - QMessageBox::critical( - this, tr("Error"), - tr("Could not load configuration from this SD card. You need to prepare your SD card " - "before using threeSD.
Please check if you have followed the " - "guide correctly.")); - return; - } - - if (config.version != Core::CurrentDumperVersion) { - QMessageBox::critical(this, tr("Version Dismatch"), - tr("You are using an unsupported version of threeSDumper.
Please " - "ensure that you are using the most recent version of both " - "threeSD and threeSDumper and try again.")); - return; - } - - if (config.safe_mode_firm_path.empty() || config.config_savegame_path.empty() || - config.system_archives_path.empty()) { - QMessageBox::warning( - this, tr("Warning"), - tr("Certain system files are missing from your configuration.
Some contents " - "may not be importable, or may not run.
Please check if you have followed the guide " - "correctly.")); - } else if (config.seed_db_path.empty()) { - QMessageBox::warning(this, tr("Warning"), - tr("Seed database is missing from your configuration.
Your system " - "likely does not have any seeds.
However, if it does have any, " - "imported games using seed encryption may not work.")); - } - - ImportDialog dialog(this, config); - dialog.exec(); -} - -int main(int argc, char* argv[]) { - // Init settings params - QCoreApplication::setOrganizationName(QStringLiteral("zhaowenlan1779")); - QCoreApplication::setApplicationName(QStringLiteral("threeSD")); - -#ifdef __APPLE__ - std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + ".."; - chdir(bin_path.c_str()); -#endif - - QApplication app(argc, argv); - - QIcon::setThemeSearchPaths(QStringList(QStringLiteral(":/icons/default"))); - QIcon::setThemeName(QStringLiteral(":/icons/default")); - - MainDialog main_dialog; - main_dialog.show(); - - return app.exec(); -} +// Copyright 2014 Citra Emulator Project / 2019 threeSD Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common/assert.h" +#include "common/file_util.h" +#include "frontend/import_dialog.h" +#include "frontend/main.h" +#include "frontend/utilities.h" +#include "ui_main.h" + +#ifdef __APPLE__ +#include +#include "common/common_paths.h" +#endif + +#ifdef QT_STATICPLUGIN +#include + +Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) +Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin) +#endif +#endif + +bool IsConfigGood(const Core::Config& config) { + return !config.sdmc_path.empty() && !config.user_path.empty() && + !config.movable_sed_path.empty() && !config.bootrom_path.empty(); +} + +MainDialog::MainDialog(QWidget* parent) : QDialog(parent), ui(std::make_unique()) { + ui->setupUi(this); + + setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); + const double scale = qApp->desktop()->logicalDpiX() / 96.0; + resize(static_cast(width() * scale), static_cast(height() * scale)); + + ui->buttonBox->button(QDialogButtonBox::StandardButton::Ok)->setEnabled(false); + ui->buttonBox->button(QDialogButtonBox::StandardButton::Reset)->setText(tr("Refresh")); + + LoadPresetConfig(); + + connect(ui->utilitiesButton, &QPushButton::clicked, [this] { + UtilitiesDialog dialog(this); + dialog.exec(); + }); + + connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button) { + if (button == ui->buttonBox->button(QDialogButtonBox::StandardButton::Reset)) { + LoadPresetConfig(); + } else if (button == ui->buttonBox->button(QDialogButtonBox::StandardButton::Ok)) { + LaunchImportDialog(); + } + }); + + QString destination_text{}; + const auto destination = FileUtil::GetUserPathType(); + if (destination == FileUtil::UserPathType::Normal) { +#ifdef __linux__ + destination_text = tr("Non-Flatpak Citra Install"); +#else + destination_text = tr("User-wide Citra Install"); +#endif + } else if (destination == FileUtil::UserPathType::Portable) { + destination_text = tr("Portable Citra Install"); + } else if (destination == FileUtil::UserPathType::Flatpak) { + destination_text = tr("Flatpak Citra Install"); + } else { + UNREACHABLE(); + } + ui->importDestination->setText(tr("Import Destination: %1").arg(destination_text)); + + connect(ui->main, &QTreeWidget::itemSelectionChanged, [this] { + ui->buttonBox->button(QDialogButtonBox::StandardButton::Ok) + ->setEnabled(!ui->main->selectedItems().empty()); + }); + connect(ui->main, &QTreeWidget::itemDoubleClicked, [this] { + if (!ui->main->selectedItems().empty()) + LaunchImportDialog(); + }); + + ui->main->setIndentation(4); + ui->main->setColumnWidth(0, 0.3 * width()); + ui->main->setColumnWidth(1, 0.4 * width()); + ui->main->setColumnWidth(2, 0.2 * width()); + + // Set up device watcher + auto* device_watcher = new QDeviceWatcher(this); + device_watcher->start(); + connect(device_watcher, &QDeviceWatcher::deviceAdded, this, &MainDialog::LoadPresetConfig); + connect(device_watcher, &QDeviceWatcher::deviceChanged, this, &MainDialog::LoadPresetConfig); + connect(device_watcher, &QDeviceWatcher::deviceRemoved, this, &MainDialog::LoadPresetConfig); +} + +MainDialog::~MainDialog() = default; + +static const std::regex sdmc_path_regex{"(.+)([/\\\\])Nintendo 3DS/([0-9a-f]{32})/([0-9a-f]{32})/"}; + +void MainDialog::LoadPresetConfig() { + ui->main->clear(); + preset_config_list.clear(); + + for (const auto& storage : QStorageInfo::mountedVolumes()) { + if (!storage.isValid() || !storage.isReady()) { + continue; + } + + auto list = Core::LoadPresetConfig(storage.rootPath().toStdString()); + for (std::size_t i = 0; i < list.size(); ++i) { + preset_config_list.emplace_back(list[i]); + + QString path = storage.rootPath(); + if (path.endsWith(QLatin1Char{'/'}) || path.endsWith(QLatin1Char{'\\'})) { + path.remove(path.size() - 1, 1); + } + + // Get ID0 + QString id0 = tr("Unknown"); + std::smatch match; + if (std::regex_match(list[i].sdmc_path, match, sdmc_path_regex)) { + if (match.size() >= 5) { + id0 = QString::fromStdString(match[3].str()); + } + } + + // Get status + QString status = tr("Good"); + if (!IsConfigGood(list[i])) { + status = tr("No Configuration Found"); + } else if (list[i].version != Core::CurrentDumperVersion) { + status = tr("Version Dismatch"); + } else if (list[i].safe_mode_firm_path.empty() || + list[i].config_savegame_path.empty() || + list[i].system_archives_path.empty()) { + + status = tr("Missing System Files"); + } else if (list[i].seed_db_path.empty()) { + status = tr("Good, Missing Seeds"); + } + + auto* item = new QTreeWidgetItem{{path, id0, status}}; + ui->main->invisibleRootItem()->addChild(item); + } + } + + auto* item = new QTreeWidgetItem{{tr("Browse SD Card Root...")}}; + item->setFirstColumnSpanned(true); + ui->main->invisibleRootItem()->addChild(item); +} + +void MainDialog::LaunchImportDialog() { + auto* item = ui->main->currentItem(); + if (!item) { + return; + } + + Core::Config config; + const auto index = ui->main->invisibleRootItem()->indexOfChild(item); + if (index == ui->main->invisibleRootItem()->childCount() - 1) { + const QString path = QFileDialog::getExistingDirectory(this, tr("Select SD Card Root")); + if (path.isEmpty()) { + return; + } + + const auto& list = Core::LoadPresetConfig(path.toStdString()); + if (list.size() > 1) { + QMessageBox::information( + this, tr("threeSD"), + tr("You have more than one 3DS data on your SD Card.\nthreeSD will " + "select the first one for you.")); + } else if (list.empty() || !IsConfigGood(list[0])) { + QMessageBox::critical( + this, tr("Error"), + tr("Could not load configuration.
Please check if you have followed the " + "guide correctly.")); + return; + } + + config = list[0]; + } else { + config = preset_config_list.at(index); + } + + // Check config integrity + if (!IsConfigGood(config)) { + QMessageBox::critical( + this, tr("Error"), + tr("Could not load configuration from this SD card. You need to prepare your SD card " + "before using threeSD.
Please check if you have followed the " + "guide correctly.")); + return; + } + + if (config.version != Core::CurrentDumperVersion) { + QMessageBox::critical(this, tr("Version Dismatch"), + tr("You are using an unsupported version of threeSDumper.
Please " + "ensure that you are using the most recent version of both " + "threeSD and threeSDumper and try again.")); + return; + } + + if (config.safe_mode_firm_path.empty() || config.config_savegame_path.empty() || + config.system_archives_path.empty()) { + QMessageBox::warning( + this, tr("Warning"), + tr("Certain system files are missing from your configuration.
Some contents " + "may not be importable, or may not run.
Please check if you have followed the guide " + "correctly.")); + } else if (config.seed_db_path.empty()) { + QMessageBox::warning(this, tr("Warning"), + tr("Seed database is missing from your configuration.
Your system " + "likely does not have any seeds.
However, if it does have any, " + "imported games using seed encryption may not work.")); + } + + ImportDialog dialog(this, config); + dialog.exec(); +} + +int main(int argc, char* argv[]) { + // Init settings params + QCoreApplication::setOrganizationName(QStringLiteral("zhaowenlan1779")); + QCoreApplication::setApplicationName(QStringLiteral("threeSD")); + +#ifdef __APPLE__ + std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + ".."; + chdir(bin_path.c_str()); +#endif + + QApplication app(argc, argv); + + QIcon::setThemeSearchPaths(QStringList(QStringLiteral(":/icons/default"))); + QIcon::setThemeName(QStringLiteral(":/icons/default")); + + MainDialog main_dialog; + main_dialog.show(); + + return app.exec(); +} diff --git a/src/frontend/select_files_dialog.cpp b/src/frontend/select_files_dialog.cpp index c232606..0bcc87b 100644 --- a/src/frontend/select_files_dialog.cpp +++ b/src/frontend/select_files_dialog.cpp @@ -14,6 +14,7 @@ SelectFilesDialog::SelectFilesDialog(QWidget* parent, bool source_is_dir_, bool ui->setupUi(this); + setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); const double scale = qApp->desktop()->logicalDpiX() / 96.0; resize(static_cast(width() * scale), static_cast(height() * scale)); diff --git a/src/frontend/title_info_dialog.cpp b/src/frontend/title_info_dialog.cpp index f36f600..19ee249 100644 --- a/src/frontend/title_info_dialog.cpp +++ b/src/frontend/title_info_dialog.cpp @@ -25,6 +25,7 @@ TitleInfoDialog::TitleInfoDialog(QWidget* parent, Core::SDMCImporter& importer_, ui->setupUi(this); + setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); const double scale = qApp->desktop()->logicalDpiX() / 96.0; resize(static_cast(width() * scale), static_cast(height() * scale)); diff --git a/src/frontend/utilities.cpp b/src/frontend/utilities.cpp index e0d0985..0a5055c 100644 --- a/src/frontend/utilities.cpp +++ b/src/frontend/utilities.cpp @@ -1,303 +1,305 @@ -// Copyright 2020 threeSD Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include -#include -#include -#include -#include -#include "core/file_sys/data/data_container.h" -#include "core/file_sys/data/extdata.h" -#include "core/file_sys/data/savegame.h" -#include "core/file_sys/ncch_container.h" -#include "core/key/key.h" -#include "core/sdmc_decryptor.h" -#include "frontend/select_files_dialog.h" -#include "frontend/utilities.h" -#include "ui_utilities.h" - -UtilitiesDialog::UtilitiesDialog(QWidget* parent) - : QDialog(parent), ui(std::make_unique()) { - - ui->setupUi(this); - - const double scale = qApp->desktop()->logicalDpiX() / 96.0; - resize(static_cast(width() * scale), static_cast(height() * scale)); - - connect(ui->useSdDecryption, &QCheckBox::clicked, [this] { - const bool checked = ui->useSdDecryption->isChecked(); - - ui->boot9Path->setEnabled(checked); - ui->boot9PathExplore->setEnabled(checked); - ui->movableSedPath->setEnabled(checked); - ui->movableSedPathExplore->setEnabled(checked); - ui->sdmcPath->setEnabled(checked); - ui->sdmcPathExplore->setEnabled(checked); - - // First hide both, to avoid resizing the dialog - ui->sdDecryptionLabel->setVisible(false); - ui->sdDecryptionDisabledLabel->setVisible(false); - ui->sdDecryptionLabel->setVisible(checked); - ui->sdDecryptionDisabledLabel->setVisible(!checked); - ui->sdDecryption->setEnabled(checked); - - ui->extdataExtractionLabel->setVisible(false); - ui->extdataExtractionDisabledLabel->setVisible(false); - ui->extdataExtractionLabel->setVisible(checked); - ui->extdataExtractionDisabledLabel->setVisible(!checked); - ui->extdataExtraction->setEnabled(checked); - - ui->romfsExtractionLabel->setVisible(false); - ui->romfsExtractionDisabledLabel->setVisible(false); - ui->romfsExtractionLabel->setVisible(!checked); - ui->romfsExtractionDisabledLabel->setVisible(checked); - ui->romfsExtraction->setEnabled(!checked); - }); - - connect(ui->boot9PathExplore, &QToolButton::clicked, [this] { - const QString path = QFileDialog::getOpenFileName(this, tr("Select File")); - if (!path.isEmpty()) { - ui->boot9Path->setText(path); - } - }); - connect(ui->movableSedPathExplore, &QToolButton::clicked, [this] { - const QString path = QFileDialog::getOpenFileName(this, tr("Select File")); - if (!path.isEmpty()) { - ui->movableSedPath->setText(path); - } - }); - connect(ui->sdmcPathExplore, &QToolButton::clicked, [this] { - const QString path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); - if (!path.isEmpty()) { - ui->sdmcPath->setText(path); - } - }); - - connect(ui->sdDecryption, &QPushButton::clicked, this, &UtilitiesDialog::SDDecryptionTool); - connect(ui->savedataExtraction, &QPushButton::clicked, this, - &UtilitiesDialog::SaveDataExtractionTool); - connect(ui->extdataExtraction, &QPushButton::clicked, this, - &UtilitiesDialog::ExtdataExtractionTool); - connect(ui->romfsExtraction, &QPushButton::clicked, this, - &UtilitiesDialog::RomFSExtractionTool); -} - -UtilitiesDialog::~UtilitiesDialog() = default; - -std::pair UtilitiesDialog::GetFilePaths(bool source_is_dir, - bool destination_is_dir) { - - SelectFilesDialog dialog(this, source_is_dir, destination_is_dir); - if (dialog.exec() == QDialog::Accepted) { - return dialog.GetResults(); - } else { - return {}; - } -} - -bool UtilitiesDialog::LoadSDKeys() { - if (ui->boot9Path->text().isEmpty() || ui->movableSedPath->text().isEmpty()) { - QMessageBox::critical(this, tr("Error"), - tr("Please select boot9.bin and movable.sed paths.")); - return false; - } - if (ui->sdmcPath->text().isEmpty()) { - QMessageBox::critical(this, tr("Error"), - tr("Please select SDMC root (\"Nintendo 3DS/<ID0>/<ID1>\").")); - return false; - } - - Core::Key::ClearKeys(); - Core::Key::LoadBootromKeys(ui->boot9Path->text().toStdString()); - Core::Key::LoadMovableSedKeys(ui->movableSedPath->text().toStdString()); - - if (!Core::Key::IsNormalKeyAvailable(Core::Key::SDKey)) { - LOG_ERROR(Core, "SDKey is not available"); - QMessageBox::critical(this, tr("Error"), - tr("Could not load SD Key. Please check your files.")); - return false; - } - return true; -} - -void UtilitiesDialog::ShowProgressDialog(std::function operation) { - auto* dialog = new QProgressDialog(tr("Processing..."), tr("Cancel"), 0, 0, this); - dialog->setWindowModality(Qt::WindowModal); - dialog->setCancelButton(nullptr); - dialog->setMinimumDuration(0); - dialog->setValue(0); - - using FutureWatcher = QFutureWatcher; - auto* future_watcher = new FutureWatcher(this); - connect(future_watcher, &FutureWatcher::finished, this, [this, dialog] { - dialog->hide(); - ShowResult(); - }); - - auto future = QtConcurrent::run([operation, this] { result = operation(); }); - future_watcher->setFuture(future); -} - -std::tuple UtilitiesDialog::GetSDMCRoot(const QString& source) { - QString sdmc_root = ui->sdmcPath->text().replace(QLatin1Char{'\\'}, QLatin1Char{'/'}); - if (!sdmc_root.endsWith(QLatin1Char{'/'})) { - sdmc_root.append(QLatin1Char{'/'}); - } - if (!source.startsWith(sdmc_root)) { - QMessageBox::critical(this, tr("Error"), tr("The file selected is not in SDMC root.")); - return {false, "", ""}; - } - const std::string relative_source = - source.toStdString().substr(sdmc_root.toStdString().size() - 1); - - return {true, sdmc_root.toStdString(), relative_source}; -} - -void UtilitiesDialog::SDDecryptionTool() { - if (!LoadSDKeys()) { - return; - } - const auto& [source, destination] = GetFilePaths(false, false); - if (source.isEmpty() || destination.isEmpty()) { - return; - } - - const auto& [success, sdmc_root, relative_source] = GetSDMCRoot(source); - if (!success) { - return; - } - // TODO: Add Progress reporting - ShowProgressDialog( - [sdmc_root = sdmc_root, relative_source = relative_source, destination = destination] { - Core::SDMCDecryptor decryptor(sdmc_root); - return decryptor.DecryptAndWriteFile(relative_source, destination.toStdString()); - }); -} - -void UtilitiesDialog::SaveDataExtractionTool() { - const bool decryption = ui->useSdDecryption->isChecked(); - if (decryption && !LoadSDKeys()) { - return; - } - const auto& [source, destination] = GetFilePaths(false, true); - if (source.isEmpty() || destination.isEmpty()) { - return; - } - - if (decryption) { - const auto& [success, sdmc_root, relative_source] = GetSDMCRoot(source); - if (!success) { - return; - } - - // TODO: Add Progress reporting - ShowProgressDialog([sdmc_root = sdmc_root, relative_source = relative_source, - source = source, destination = destination] { - const auto size = FileUtil::GetSize(source.toStdString()); - std::vector data(size); - Core::SDMCFile file(sdmc_root, relative_source, "rb"); - if (file.ReadBytes(data.data(), size) != size) { - return false; - } - - Core::DataContainer container(data); - if (!container.IsGood()) { - return false; - } - - std::vector> container_data; - if (!container.GetIVFCLevel4Data(container_data)) { - return false; - } - - Core::Savegame save(std::move(container_data)); - if (!save.IsGood()) { - return false; - } - - return save.Extract(destination.toStdString()); - }); - } else { - // TODO: Add Progress reporting - ShowProgressDialog([source = source, destination = destination] { - FileUtil::IOFile file(source.toStdString(), "rb"); - std::vector data = file.GetData(); - if (data.empty()) { - return false; - } - - Core::DataContainer container(data); - if (!container.IsGood()) { - return false; - } - - std::vector> container_data; - if (!container.GetIVFCLevel4Data(container_data)) { - return false; - } - - Core::Savegame save(std::move(container_data)); - if (!save.IsGood()) { - return false; - } - - return save.Extract(destination.toStdString()); - }); - } -} - -void UtilitiesDialog::ExtdataExtractionTool() { - if (!LoadSDKeys()) { - return; - } - const auto& [source, destination] = GetFilePaths(true, true); - if (source.isEmpty() || destination.isEmpty()) { - return; - } - - const auto& [success, sdmc_root, relative_source] = GetSDMCRoot(source); - if (!success) { - return; - } - // TODO: Add Progress reporting - ShowProgressDialog( - [sdmc_root = sdmc_root, relative_source = relative_source, destination = destination] { - Core::SDMCDecryptor decryptor(sdmc_root); - Core::Extdata extdata(relative_source, decryptor); - if (!extdata.IsGood()) { - return false; - } - - return extdata.Extract(destination.toStdString()); - }); -} - -void UtilitiesDialog::RomFSExtractionTool() { - const auto& [source, destination] = GetFilePaths(false, false); - if (source.isEmpty() || destination.isEmpty()) { - return; - } - - ShowProgressDialog([source = source, destination = destination] { - FileUtil::IOFile src_file(source.toStdString(), "rb"); - std::vector data = src_file.GetData(); - if (data.empty()) { - return false; - } - - const auto& shared_romfs = Core::LoadSharedRomFS(data); - return FileUtil::WriteBytesToFile(destination.toStdString(), shared_romfs.data(), - shared_romfs.size()); - }); -} - -void UtilitiesDialog::ShowResult() { - if (result) { - QMessageBox::information(this, tr("Success"), tr("Operation completed successfully.")); - } else { - QMessageBox::critical(this, tr("Error"), - tr("An error occured while performing the operation.")); - } -} +// Copyright 2020 threeSD Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include +#include "core/file_sys/data/data_container.h" +#include "core/file_sys/data/extdata.h" +#include "core/file_sys/data/savegame.h" +#include "core/file_sys/ncch_container.h" +#include "core/key/key.h" +#include "core/sdmc_decryptor.h" +#include "frontend/select_files_dialog.h" +#include "frontend/utilities.h" +#include "ui_utilities.h" + +UtilitiesDialog::UtilitiesDialog(QWidget* parent) + : QDialog(parent), ui(std::make_unique()) { + + ui->setupUi(this); + + setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); + const double scale = qApp->desktop()->logicalDpiX() / 96.0; + resize(static_cast(width() * scale), static_cast(height() * scale)); + + connect(ui->useSdDecryption, &QCheckBox::clicked, [this] { + const bool checked = ui->useSdDecryption->isChecked(); + + ui->boot9Path->setEnabled(checked); + ui->boot9PathExplore->setEnabled(checked); + ui->movableSedPath->setEnabled(checked); + ui->movableSedPathExplore->setEnabled(checked); + ui->sdmcPath->setEnabled(checked); + ui->sdmcPathExplore->setEnabled(checked); + + // First hide both, to avoid resizing the dialog + ui->sdDecryptionLabel->setVisible(false); + ui->sdDecryptionDisabledLabel->setVisible(false); + ui->sdDecryptionLabel->setVisible(checked); + ui->sdDecryptionDisabledLabel->setVisible(!checked); + ui->sdDecryption->setEnabled(checked); + + ui->extdataExtractionLabel->setVisible(false); + ui->extdataExtractionDisabledLabel->setVisible(false); + ui->extdataExtractionLabel->setVisible(checked); + ui->extdataExtractionDisabledLabel->setVisible(!checked); + ui->extdataExtraction->setEnabled(checked); + + ui->romfsExtractionLabel->setVisible(false); + ui->romfsExtractionDisabledLabel->setVisible(false); + ui->romfsExtractionLabel->setVisible(!checked); + ui->romfsExtractionDisabledLabel->setVisible(checked); + ui->romfsExtraction->setEnabled(!checked); + }); + + connect(ui->boot9PathExplore, &QToolButton::clicked, [this] { + const QString path = QFileDialog::getOpenFileName(this, tr("Select File")); + if (!path.isEmpty()) { + ui->boot9Path->setText(path); + } + }); + connect(ui->movableSedPathExplore, &QToolButton::clicked, [this] { + const QString path = QFileDialog::getOpenFileName(this, tr("Select File")); + if (!path.isEmpty()) { + ui->movableSedPath->setText(path); + } + }); + connect(ui->sdmcPathExplore, &QToolButton::clicked, [this] { + const QString path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); + if (!path.isEmpty()) { + ui->sdmcPath->setText(path); + } + }); + + connect(ui->sdDecryption, &QPushButton::clicked, this, &UtilitiesDialog::SDDecryptionTool); + connect(ui->savedataExtraction, &QPushButton::clicked, this, + &UtilitiesDialog::SaveDataExtractionTool); + connect(ui->extdataExtraction, &QPushButton::clicked, this, + &UtilitiesDialog::ExtdataExtractionTool); + connect(ui->romfsExtraction, &QPushButton::clicked, this, + &UtilitiesDialog::RomFSExtractionTool); +} + +UtilitiesDialog::~UtilitiesDialog() = default; + +std::pair UtilitiesDialog::GetFilePaths(bool source_is_dir, + bool destination_is_dir) { + + SelectFilesDialog dialog(this, source_is_dir, destination_is_dir); + if (dialog.exec() == QDialog::Accepted) { + return dialog.GetResults(); + } else { + return {}; + } +} + +bool UtilitiesDialog::LoadSDKeys() { + if (ui->boot9Path->text().isEmpty() || ui->movableSedPath->text().isEmpty()) { + QMessageBox::critical(this, tr("Error"), + tr("Please select boot9.bin and movable.sed paths.")); + return false; + } + if (ui->sdmcPath->text().isEmpty()) { + QMessageBox::critical(this, tr("Error"), + tr("Please select SDMC root (\"Nintendo 3DS/<ID0>/<ID1>\").")); + return false; + } + + Core::Key::ClearKeys(); + Core::Key::LoadBootromKeys(ui->boot9Path->text().toStdString()); + Core::Key::LoadMovableSedKeys(ui->movableSedPath->text().toStdString()); + + if (!Core::Key::IsNormalKeyAvailable(Core::Key::SDKey)) { + LOG_ERROR(Core, "SDKey is not available"); + QMessageBox::critical(this, tr("Error"), + tr("Could not load SD Key. Please check your files.")); + return false; + } + return true; +} + +void UtilitiesDialog::ShowProgressDialog(std::function operation) { + auto* dialog = new QProgressDialog(tr("Processing..."), tr("Cancel"), 0, 0, this); + dialog->setWindowFlags(dialog->windowFlags() & (~Qt::WindowContextHelpButtonHint)); + dialog->setWindowModality(Qt::WindowModal); + dialog->setCancelButton(nullptr); + dialog->setMinimumDuration(0); + dialog->setValue(0); + + using FutureWatcher = QFutureWatcher; + auto* future_watcher = new FutureWatcher(this); + connect(future_watcher, &FutureWatcher::finished, this, [this, dialog] { + dialog->hide(); + ShowResult(); + }); + + auto future = QtConcurrent::run([operation, this] { result = operation(); }); + future_watcher->setFuture(future); +} + +std::tuple UtilitiesDialog::GetSDMCRoot(const QString& source) { + QString sdmc_root = ui->sdmcPath->text().replace(QLatin1Char{'\\'}, QLatin1Char{'/'}); + if (!sdmc_root.endsWith(QLatin1Char{'/'})) { + sdmc_root.append(QLatin1Char{'/'}); + } + if (!source.startsWith(sdmc_root)) { + QMessageBox::critical(this, tr("Error"), tr("The file selected is not in SDMC root.")); + return {false, "", ""}; + } + const std::string relative_source = + source.toStdString().substr(sdmc_root.toStdString().size() - 1); + + return {true, sdmc_root.toStdString(), relative_source}; +} + +void UtilitiesDialog::SDDecryptionTool() { + if (!LoadSDKeys()) { + return; + } + const auto& [source, destination] = GetFilePaths(false, false); + if (source.isEmpty() || destination.isEmpty()) { + return; + } + + const auto& [success, sdmc_root, relative_source] = GetSDMCRoot(source); + if (!success) { + return; + } + // TODO: Add Progress reporting + ShowProgressDialog( + [sdmc_root = sdmc_root, relative_source = relative_source, destination = destination] { + Core::SDMCDecryptor decryptor(sdmc_root); + return decryptor.DecryptAndWriteFile(relative_source, destination.toStdString()); + }); +} + +void UtilitiesDialog::SaveDataExtractionTool() { + const bool decryption = ui->useSdDecryption->isChecked(); + if (decryption && !LoadSDKeys()) { + return; + } + const auto& [source, destination] = GetFilePaths(false, true); + if (source.isEmpty() || destination.isEmpty()) { + return; + } + + if (decryption) { + const auto& [success, sdmc_root, relative_source] = GetSDMCRoot(source); + if (!success) { + return; + } + + // TODO: Add Progress reporting + ShowProgressDialog([sdmc_root = sdmc_root, relative_source = relative_source, + source = source, destination = destination] { + const auto size = FileUtil::GetSize(source.toStdString()); + std::vector data(size); + Core::SDMCFile file(sdmc_root, relative_source, "rb"); + if (file.ReadBytes(data.data(), size) != size) { + return false; + } + + Core::DataContainer container(data); + if (!container.IsGood()) { + return false; + } + + std::vector> container_data; + if (!container.GetIVFCLevel4Data(container_data)) { + return false; + } + + Core::Savegame save(std::move(container_data)); + if (!save.IsGood()) { + return false; + } + + return save.Extract(destination.toStdString()); + }); + } else { + // TODO: Add Progress reporting + ShowProgressDialog([source = source, destination = destination] { + FileUtil::IOFile file(source.toStdString(), "rb"); + std::vector data = file.GetData(); + if (data.empty()) { + return false; + } + + Core::DataContainer container(data); + if (!container.IsGood()) { + return false; + } + + std::vector> container_data; + if (!container.GetIVFCLevel4Data(container_data)) { + return false; + } + + Core::Savegame save(std::move(container_data)); + if (!save.IsGood()) { + return false; + } + + return save.Extract(destination.toStdString()); + }); + } +} + +void UtilitiesDialog::ExtdataExtractionTool() { + if (!LoadSDKeys()) { + return; + } + const auto& [source, destination] = GetFilePaths(true, true); + if (source.isEmpty() || destination.isEmpty()) { + return; + } + + const auto& [success, sdmc_root, relative_source] = GetSDMCRoot(source); + if (!success) { + return; + } + // TODO: Add Progress reporting + ShowProgressDialog( + [sdmc_root = sdmc_root, relative_source = relative_source, destination = destination] { + Core::SDMCDecryptor decryptor(sdmc_root); + Core::Extdata extdata(relative_source, decryptor); + if (!extdata.IsGood()) { + return false; + } + + return extdata.Extract(destination.toStdString()); + }); +} + +void UtilitiesDialog::RomFSExtractionTool() { + const auto& [source, destination] = GetFilePaths(false, false); + if (source.isEmpty() || destination.isEmpty()) { + return; + } + + ShowProgressDialog([source = source, destination = destination] { + FileUtil::IOFile src_file(source.toStdString(), "rb"); + std::vector data = src_file.GetData(); + if (data.empty()) { + return false; + } + + const auto& shared_romfs = Core::LoadSharedRomFS(data); + return FileUtil::WriteBytesToFile(destination.toStdString(), shared_romfs.data(), + shared_romfs.size()); + }); +} + +void UtilitiesDialog::ShowResult() { + if (result) { + QMessageBox::information(this, tr("Success"), tr("Operation completed successfully.")); + } else { + QMessageBox::critical(this, tr("Error"), + tr("An error occured while performing the operation.")); + } +}