Remove context help from all dialogs

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