mirror of
https://github.com/Dark98/threeSD.git
synced 2026-07-03 00:38:58 +00:00
frontend: Add utilities
This commit is contained in:
@@ -7,6 +7,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "common/file_util.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
#include "core/quick_decryptor.h"
|
#include "core/quick_decryptor.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|||||||
@@ -17,6 +17,12 @@ add_executable(threeSD
|
|||||||
main.cpp
|
main.cpp
|
||||||
main.h
|
main.h
|
||||||
main.ui
|
main.ui
|
||||||
|
select_files_dialog.cpp
|
||||||
|
select_files_dialog.h
|
||||||
|
select_files_dialog.ui
|
||||||
|
utilities.cpp
|
||||||
|
utilities.h
|
||||||
|
utilities.ui
|
||||||
${THEMES}
|
${THEMES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#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 "ui_main.h"
|
#include "ui_main.h"
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
@@ -47,6 +48,10 @@ MainDialog::MainDialog(QWidget* parent) : QDialog(parent), ui(std::make_unique<U
|
|||||||
ShowAdvanced();
|
ShowAdvanced();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
connect(ui->utilitiesButton, &QPushButton::clicked, [this] {
|
||||||
|
UtilitiesDialog dialog(this);
|
||||||
|
dialog.exec();
|
||||||
|
});
|
||||||
|
|
||||||
connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button) {
|
connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button) {
|
||||||
if (button == ui->buttonBox->button(QDialogButtonBox::StandardButton::Reset)) {
|
if (button == ui->buttonBox->button(QDialogButtonBox::StandardButton::Reset)) {
|
||||||
|
|||||||
@@ -51,6 +51,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="utilitiesButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Utilities...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2020 threeSD Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include "frontend/select_files_dialog.h"
|
||||||
|
#include "ui_select_files_dialog.h"
|
||||||
|
|
||||||
|
SelectFilesDialog::SelectFilesDialog(QWidget* parent, bool source_is_dir_, bool destination_is_dir_)
|
||||||
|
: QDialog(parent), ui(std::make_unique<Ui::SelectFilesDialog>()), source_is_dir(source_is_dir_),
|
||||||
|
destination_is_dir(destination_is_dir_) {
|
||||||
|
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
connect(ui->buttonBox, &QDialogButtonBox::accepted, [this] {
|
||||||
|
if (ui->source->text().isEmpty() || ui->destination->text().isEmpty()) {
|
||||||
|
QMessageBox::warning(this, tr("Warning"), tr("Please fill in all the fields."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
accept();
|
||||||
|
});
|
||||||
|
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &SelectFilesDialog::reject);
|
||||||
|
|
||||||
|
connect(ui->sourceExplore, &QToolButton::clicked, [this] {
|
||||||
|
QString path;
|
||||||
|
if (source_is_dir) {
|
||||||
|
path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
|
||||||
|
} else {
|
||||||
|
path = QFileDialog::getOpenFileName(this, tr("Select File"));
|
||||||
|
}
|
||||||
|
if (!path.isEmpty()) {
|
||||||
|
ui->source->setText(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(ui->destinationExplore, &QToolButton::clicked, [this] {
|
||||||
|
QString path;
|
||||||
|
if (destination_is_dir) {
|
||||||
|
path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
|
||||||
|
} else {
|
||||||
|
path = QFileDialog::getSaveFileName(this, tr("Select File"));
|
||||||
|
}
|
||||||
|
if (!path.isEmpty()) {
|
||||||
|
ui->destination->setText(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectFilesDialog::~SelectFilesDialog() = default;
|
||||||
|
|
||||||
|
std::pair<QString, QString> SelectFilesDialog::GetResults() const {
|
||||||
|
return {ui->source->text(), ui->destination->text()};
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2020 threeSD Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
class QWidget;
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class SelectFilesDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SelectFilesDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SelectFilesDialog(QWidget* parent, bool source_is_dir, bool destination_is_dir);
|
||||||
|
~SelectFilesDialog() override;
|
||||||
|
|
||||||
|
std::pair<QString, QString> GetResults() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Ui::SelectFilesDialog> ui;
|
||||||
|
bool source_is_dir; // Whether Source should be a directory
|
||||||
|
bool destination_is_dir; // Whether Destination should be a directory
|
||||||
|
};
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>SelectFilesDialog</class>
|
||||||
|
<widget class="QDialog" name="SelectFilesDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>600</width>
|
||||||
|
<height>120</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Select Files</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QGridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Source:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="source"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QToolButton" name="sourceExplore">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Destination:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="destination"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QToolButton" name="destinationExplore">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Ok|QDialogButtonBox::Cancel</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</ui>
|
||||||
@@ -0,0 +1,297 @@
|
|||||||
|
// Copyright 2020 threeSD Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QFutureWatcher>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QProgressDialog>
|
||||||
|
#include <QtConcurrent/QtConcurrentRun>
|
||||||
|
#include "core/data_container.h"
|
||||||
|
#include "core/decryptor.h"
|
||||||
|
#include "core/inner_fat.h"
|
||||||
|
#include "core/key/key.h"
|
||||||
|
#include "core/ncch/ncch_container.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::UtilitiesDialog>()) {
|
||||||
|
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
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->savedataExtractionLabel->setVisible(false);
|
||||||
|
ui->savedataExtractionDisabledLabel->setVisible(false);
|
||||||
|
ui->savedataExtractionLabel->setVisible(checked);
|
||||||
|
ui->savedataExtractionDisabledLabel->setVisible(!checked);
|
||||||
|
ui->savedataExtraction->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<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) {
|
||||||
|
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<bool()> 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<void>;
|
||||||
|
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<bool, std::string, std::string> 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, relative_source, 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, relative_source, source, destination] {
|
||||||
|
const auto size = FileUtil::GetSize(source.toStdString());
|
||||||
|
std::vector<u8> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::SDSavegame save(std::move(container.GetIVFCLevel4Data()));
|
||||||
|
if (!save.IsGood()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return save.Extract(destination.toStdString());
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// TODO: Add Progress reporting
|
||||||
|
ShowProgressDialog([source, destination] {
|
||||||
|
const auto size = FileUtil::GetSize(source.toStdString());
|
||||||
|
std::vector<u8> data(size);
|
||||||
|
FileUtil::IOFile file(source.toStdString(), "rb");
|
||||||
|
if (file.ReadBytes(data.data(), size) != size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::DataContainer container(data);
|
||||||
|
if (!container.IsGood()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::SDSavegame save(std::move(container.GetIVFCLevel4Data()));
|
||||||
|
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, relative_source, destination] {
|
||||||
|
Core::SDMCDecryptor decryptor(sdmc_root);
|
||||||
|
Core::SDExtdata 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, destination] {
|
||||||
|
const auto size = FileUtil::GetSize(source.toStdString());
|
||||||
|
std::vector<u8> data(size);
|
||||||
|
FileUtil::IOFile src_file(source.toStdString(), "rb");
|
||||||
|
if (src_file.ReadBytes(data.data(), size) != size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& shared_romfs = Core::LoadSharedRomFS(data);
|
||||||
|
FileUtil::IOFile dest_file(destination.toStdString(), "wb");
|
||||||
|
if (dest_file.WriteBytes(shared_romfs.data(), shared_romfs.size()) != shared_romfs.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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."));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2020 threeSD Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
class QWidget;
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class UtilitiesDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class UtilitiesDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit UtilitiesDialog(QWidget* parent);
|
||||||
|
~UtilitiesDialog() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Open a dialog to ask the user for source and destination paths.
|
||||||
|
* @return {source, destination}
|
||||||
|
*/
|
||||||
|
std::pair<QString, QString> GetFilePaths(bool source_is_dir, bool destination_is_dir);
|
||||||
|
|
||||||
|
bool LoadSDKeys();
|
||||||
|
|
||||||
|
void ShowProgressDialog(std::function<bool()> operation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets SDMC root, and relative source path.
|
||||||
|
* @return {success, sdmc root, relative source path}
|
||||||
|
*/
|
||||||
|
std::tuple<bool, std::string, std::string> GetSDMCRoot(const QString& source);
|
||||||
|
|
||||||
|
void SDDecryptionTool();
|
||||||
|
void SaveDataExtractionTool();
|
||||||
|
void ExtdataExtractionTool();
|
||||||
|
void RomFSExtractionTool();
|
||||||
|
void ShowResult();
|
||||||
|
|
||||||
|
bool result = false; /// Result of the last operation.
|
||||||
|
std::unique_ptr<Ui::UtilitiesDialog> ui;
|
||||||
|
};
|
||||||
@@ -0,0 +1,277 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>UtilitiesDialog</class>
|
||||||
|
<widget class="QDialog" name="UtilitiesDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>800</width>
|
||||||
|
<height>480</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>threeSD Utilities</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Encryption</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="useSdDecryption">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use SD Decryption (check this when your files are directly from SD Card)</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QGridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>boot9.bin</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="boot9Path"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QToolButton" name="boot9PathExplore">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>movable.sed</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="movableSedPath"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QToolButton" name="movableSedPathExplore">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>SDMC Root</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Path to "Nintendo 3DS/<ID0>/<ID1>" folder.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="sdmcPath"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="2">
|
||||||
|
<widget class="QToolButton" name="sdmcPathExplore">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>SD Decryption</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="sdDecryptionLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Decrypt files from your SD Card.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="sdDecryptionDisabledLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>SD Decryption must be enabled to use this tool.</string>
|
||||||
|
</property>
|
||||||
|
<property name="visible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="sdDecryption">
|
||||||
|
<property name="text">
|
||||||
|
<string>Open...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Save Data Extraction</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="savedataExtractionLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Extract 3DS SD Savegames.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="savedataExtractionDisabledLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>SD Decryption must be enabled to use this tool.</string>
|
||||||
|
</property>
|
||||||
|
<property name="visible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="savedataExtraction">
|
||||||
|
<property name="text">
|
||||||
|
<string>Open...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Extdata Extraction</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="extdataExtractionLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Extract 3DS Extra Data.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="extdataExtractionDisabledLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>SD Decryption must be enabled to use this tool.</string>
|
||||||
|
</property>
|
||||||
|
<property name="visible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="extdataExtraction">
|
||||||
|
<property name="text">
|
||||||
|
<string>Open...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>RomFS Extraction</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="romfsExtractionLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Extract shared RomFS from NCCH. Useful for System Archives.</string>
|
||||||
|
</property>
|
||||||
|
<property name="visible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="romfsExtractionDisabledLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>SD Decryption must be disabled to use this tool.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="romfsExtraction">
|
||||||
|
<property name="text">
|
||||||
|
<string>Open...</string>
|
||||||
|
</property>
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</ui>
|
||||||
Reference in New Issue
Block a user