Add support for batch install to NAND
This adds support to batch install files to NAND
This commit is contained in:
		@@ -98,11 +98,13 @@ add_executable(yuzu
 | 
			
		||||
    game_list_p.h
 | 
			
		||||
    game_list_worker.cpp
 | 
			
		||||
    game_list_worker.h
 | 
			
		||||
    hotkeys.cpp
 | 
			
		||||
    hotkeys.h
 | 
			
		||||
    install_dialog.cpp
 | 
			
		||||
    install_dialog.h
 | 
			
		||||
    loading_screen.cpp
 | 
			
		||||
    loading_screen.h
 | 
			
		||||
    loading_screen.ui
 | 
			
		||||
    hotkeys.cpp
 | 
			
		||||
    hotkeys.h
 | 
			
		||||
    main.cpp
 | 
			
		||||
    main.h
 | 
			
		||||
    main.ui
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										72
									
								
								src/yuzu/install_dialog.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/yuzu/install_dialog.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
// Copyright 2020 yuzu Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <QCheckBox>
 | 
			
		||||
#include <QDialogButtonBox>
 | 
			
		||||
#include <QFileInfo>
 | 
			
		||||
#include <QHBoxLayout>
 | 
			
		||||
#include <QLabel>
 | 
			
		||||
#include <QListWidget>
 | 
			
		||||
#include <QVBoxLayout>
 | 
			
		||||
#include "yuzu/install_dialog.h"
 | 
			
		||||
#include "yuzu/uisettings.h"
 | 
			
		||||
 | 
			
		||||
InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialog(parent) {
 | 
			
		||||
    file_list = new QListWidget(this);
 | 
			
		||||
 | 
			
		||||
    for (const QString& file : files) {
 | 
			
		||||
        QListWidgetItem* item = new QListWidgetItem(QFileInfo(file).fileName(), file_list);
 | 
			
		||||
        item->setData(Qt::UserRole, file);
 | 
			
		||||
        item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
 | 
			
		||||
        item->setCheckState(Qt::Checked);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    file_list->setMinimumWidth((file_list->sizeHintForColumn(0) * 6) / 5);
 | 
			
		||||
 | 
			
		||||
    vbox_layout = new QVBoxLayout;
 | 
			
		||||
 | 
			
		||||
    hbox_layout = new QHBoxLayout;
 | 
			
		||||
 | 
			
		||||
    description = new QLabel(tr("Please confirm these are the files you wish to install."));
 | 
			
		||||
 | 
			
		||||
    overwrite_files = new QCheckBox(tr("Overwrite Existing Files"));
 | 
			
		||||
    overwrite_files->setCheckState(Qt::Unchecked);
 | 
			
		||||
 | 
			
		||||
    buttons = new QDialogButtonBox;
 | 
			
		||||
    buttons->addButton(QDialogButtonBox::Cancel);
 | 
			
		||||
    buttons->addButton(tr("Install"), QDialogButtonBox::AcceptRole);
 | 
			
		||||
 | 
			
		||||
    connect(buttons, &QDialogButtonBox::accepted, this, &InstallDialog::accept);
 | 
			
		||||
    connect(buttons, &QDialogButtonBox::rejected, this, &InstallDialog::reject);
 | 
			
		||||
 | 
			
		||||
    hbox_layout->addWidget(overwrite_files);
 | 
			
		||||
    hbox_layout->addWidget(buttons);
 | 
			
		||||
 | 
			
		||||
    vbox_layout->addWidget(description);
 | 
			
		||||
    vbox_layout->addWidget(file_list);
 | 
			
		||||
    vbox_layout->addLayout(hbox_layout);
 | 
			
		||||
 | 
			
		||||
    setLayout(vbox_layout);
 | 
			
		||||
    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
 | 
			
		||||
    setWindowTitle(tr("Install Files to NAND"));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
InstallDialog::~InstallDialog() = default;
 | 
			
		||||
 | 
			
		||||
QStringList InstallDialog::GetFilenames() const {
 | 
			
		||||
    QStringList filenames;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < file_list->count(); ++i) {
 | 
			
		||||
        const QListWidgetItem* item = file_list->item(i);
 | 
			
		||||
        if (item->checkState() == Qt::Checked) {
 | 
			
		||||
            filenames.append(item->data(Qt::UserRole).toString());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return filenames;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool InstallDialog::ShouldOverwriteFiles() const {
 | 
			
		||||
    return overwrite_files->isChecked();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								src/yuzu/install_dialog.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/yuzu/install_dialog.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
// Copyright 2020 yuzu Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <QDialog>
 | 
			
		||||
 | 
			
		||||
class QCheckBox;
 | 
			
		||||
class QDialogButtonBox;
 | 
			
		||||
class QHBoxLayout;
 | 
			
		||||
class QLabel;
 | 
			
		||||
class QListWidget;
 | 
			
		||||
class QVBoxLayout;
 | 
			
		||||
 | 
			
		||||
class InstallDialog : public QDialog {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    explicit InstallDialog(QWidget* parent, const QStringList& files);
 | 
			
		||||
    ~InstallDialog() override;
 | 
			
		||||
 | 
			
		||||
    QStringList GetFilenames() const;
 | 
			
		||||
    bool ShouldOverwriteFiles() const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    QListWidget* file_list;
 | 
			
		||||
 | 
			
		||||
    QVBoxLayout* vbox_layout;
 | 
			
		||||
    QHBoxLayout* hbox_layout;
 | 
			
		||||
 | 
			
		||||
    QLabel* description;
 | 
			
		||||
    QCheckBox* overwrite_files;
 | 
			
		||||
    QDialogButtonBox* buttons;
 | 
			
		||||
};
 | 
			
		||||
@@ -107,6 +107,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
 | 
			
		||||
#include "yuzu/game_list.h"
 | 
			
		||||
#include "yuzu/game_list_p.h"
 | 
			
		||||
#include "yuzu/hotkeys.h"
 | 
			
		||||
#include "yuzu/install_dialog.h"
 | 
			
		||||
#include "yuzu/loading_screen.h"
 | 
			
		||||
#include "yuzu/main.h"
 | 
			
		||||
#include "yuzu/uisettings.h"
 | 
			
		||||
@@ -1596,38 +1597,67 @@ void GMainWindow::OnMenuLoadFolder() {
 | 
			
		||||
void GMainWindow::OnMenuInstallToNAND() {
 | 
			
		||||
    const QString file_filter =
 | 
			
		||||
        tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive "
 | 
			
		||||
           "(*.nca);;Nintendo Submissions Package (*.nsp);;NX Cartridge "
 | 
			
		||||
           "(*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge "
 | 
			
		||||
           "Image (*.xci)");
 | 
			
		||||
    QString filename = QFileDialog::getOpenFileName(this, tr("Install File"),
 | 
			
		||||
                                                    UISettings::values.roms_path, file_filter);
 | 
			
		||||
    QStringList files = QFileDialog::getOpenFileNames(this, tr("Install Files"),
 | 
			
		||||
                                                      UISettings::values.roms_path, file_filter);
 | 
			
		||||
 | 
			
		||||
    if (filename.isEmpty()) {
 | 
			
		||||
    if (files.isEmpty()) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto qt_raw_copy = [this](const FileSys::VirtualFile& src,
 | 
			
		||||
                                    const FileSys::VirtualFile& dest, std::size_t block_size) {
 | 
			
		||||
        if (src == nullptr || dest == nullptr)
 | 
			
		||||
    InstallDialog installDialog(this, files);
 | 
			
		||||
    if (installDialog.exec() == QDialog::Rejected) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const QStringList filenames = installDialog.GetFilenames();
 | 
			
		||||
    const bool overwrite_files = installDialog.ShouldOverwriteFiles();
 | 
			
		||||
 | 
			
		||||
    int count = 0;
 | 
			
		||||
    int total_count = filenames.size();
 | 
			
		||||
    bool is_progressdialog_created = false;
 | 
			
		||||
 | 
			
		||||
    const auto qt_raw_copy = [this, &count, &total_count, &is_progressdialog_created](
 | 
			
		||||
                                 const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
 | 
			
		||||
                                 std::size_t block_size) {
 | 
			
		||||
        if (src == nullptr || dest == nullptr) {
 | 
			
		||||
            return false;
 | 
			
		||||
        if (!dest->Resize(src->GetSize()))
 | 
			
		||||
        }
 | 
			
		||||
        if (!dest->Resize(src->GetSize())) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::array<u8, 0x1000> buffer{};
 | 
			
		||||
        const int progress_maximum = static_cast<int>(src->GetSize() / buffer.size());
 | 
			
		||||
 | 
			
		||||
        QProgressDialog progress(
 | 
			
		||||
            tr("Installing file \"%1\"...").arg(QString::fromStdString(src->GetName())),
 | 
			
		||||
            tr("Cancel"), 0, progress_maximum, this);
 | 
			
		||||
        progress.setWindowModality(Qt::WindowModal);
 | 
			
		||||
        if (!is_progressdialog_created) {
 | 
			
		||||
            ui.action_Install_File_NAND->setEnabled(false);
 | 
			
		||||
            install_progress = new QProgressDialog(
 | 
			
		||||
                tr("Installing file \"%1\"...").arg(QString::fromStdString(src->GetName())),
 | 
			
		||||
                tr("Cancel"), 0, progress_maximum, this);
 | 
			
		||||
            install_progress->setWindowTitle(
 | 
			
		||||
                tr("%n file(s) remaining", "", total_count - count - 1));
 | 
			
		||||
            install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint &
 | 
			
		||||
                                             ~Qt::WindowMaximizeButtonHint);
 | 
			
		||||
            install_progress->setAutoClose(false);
 | 
			
		||||
            is_progressdialog_created = true;
 | 
			
		||||
        } else {
 | 
			
		||||
            install_progress->setWindowTitle(
 | 
			
		||||
                tr("%n file(s) remaining", "", total_count - count - 1));
 | 
			
		||||
            install_progress->setLabelText(
 | 
			
		||||
                tr("Installing file \"%1\"...").arg(QString::fromStdString(src->GetName())));
 | 
			
		||||
            install_progress->setMaximum(progress_maximum);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
 | 
			
		||||
            if (progress.wasCanceled()) {
 | 
			
		||||
            if (install_progress->wasCanceled()) {
 | 
			
		||||
                dest->Resize(0);
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const int progress_value = static_cast<int>(i / buffer.size());
 | 
			
		||||
            progress.setValue(progress_value);
 | 
			
		||||
            install_progress->setValue(progress_value);
 | 
			
		||||
 | 
			
		||||
            const auto read = src->Read(buffer.data(), buffer.size(), i);
 | 
			
		||||
            dest->Write(buffer.data(), read, i);
 | 
			
		||||
@@ -1636,143 +1666,166 @@ void GMainWindow::OnMenuInstallToNAND() {
 | 
			
		||||
        return true;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const auto success = [this]() {
 | 
			
		||||
    const auto success = [this, &count, &is_progressdialog_created]() {
 | 
			
		||||
        if (is_progressdialog_created) {
 | 
			
		||||
            install_progress->close();
 | 
			
		||||
        }
 | 
			
		||||
        QMessageBox::information(this, tr("Successfully Installed"),
 | 
			
		||||
                                 tr("The file was successfully installed."));
 | 
			
		||||
                                 tr("%n file(s) successfully installed", "", count));
 | 
			
		||||
        game_list->PopulateAsync(UISettings::values.game_dirs);
 | 
			
		||||
        FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) +
 | 
			
		||||
                                       DIR_SEP + "game_list");
 | 
			
		||||
        ui.action_Install_File_NAND->setEnabled(true);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const auto failed = [this]() {
 | 
			
		||||
    const auto failed = [this, &is_progressdialog_created](const QString& file) {
 | 
			
		||||
        if (is_progressdialog_created) {
 | 
			
		||||
            install_progress->close();
 | 
			
		||||
        }
 | 
			
		||||
        QMessageBox::warning(
 | 
			
		||||
            this, tr("Failed to Install"),
 | 
			
		||||
            this, tr("Failed to Install %1").arg(QFileInfo(file).fileName()),
 | 
			
		||||
            tr("There was an error while attempting to install the provided file. It "
 | 
			
		||||
               "could have an incorrect format or be missing metadata. Please "
 | 
			
		||||
               "double-check your file and try again."));
 | 
			
		||||
        game_list->PopulateAsync(UISettings::values.game_dirs);
 | 
			
		||||
        ui.action_Install_File_NAND->setEnabled(true);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const auto overwrite = [this]() {
 | 
			
		||||
        return QMessageBox::question(this, tr("Failed to Install"),
 | 
			
		||||
                                     tr("The file you are attempting to install already exists "
 | 
			
		||||
                                        "in the cache. Would you like to overwrite it?")) ==
 | 
			
		||||
               QMessageBox::Yes;
 | 
			
		||||
    const auto overwrite = [this](const QString& file) {
 | 
			
		||||
        return QMessageBox::question(
 | 
			
		||||
                   this, tr("Failed to Install %1").arg(QFileInfo(file).fileName()),
 | 
			
		||||
                   tr("The file you are attempting to install already exists "
 | 
			
		||||
                      "in the cache. Would you like to overwrite it?")) == QMessageBox::Yes;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (filename.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) ||
 | 
			
		||||
        filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) {
 | 
			
		||||
        std::shared_ptr<FileSys::NSP> nsp;
 | 
			
		||||
        if (filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) {
 | 
			
		||||
            nsp = std::make_shared<FileSys::NSP>(
 | 
			
		||||
                vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
 | 
			
		||||
            if (nsp->IsExtractedType())
 | 
			
		||||
                failed();
 | 
			
		||||
        } else {
 | 
			
		||||
            const auto xci = std::make_shared<FileSys::XCI>(
 | 
			
		||||
                vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
 | 
			
		||||
            nsp = xci->GetSecurePartitionNSP();
 | 
			
		||||
        }
 | 
			
		||||
    for (const QString& filename : filenames) {
 | 
			
		||||
        if (filename.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) ||
 | 
			
		||||
            filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) {
 | 
			
		||||
            std::shared_ptr<FileSys::NSP> nsp;
 | 
			
		||||
            if (filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) {
 | 
			
		||||
                nsp = std::make_shared<FileSys::NSP>(
 | 
			
		||||
                    vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
 | 
			
		||||
                if (nsp->IsExtractedType()) {
 | 
			
		||||
                    failed(filename);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                const auto xci = std::make_shared<FileSys::XCI>(
 | 
			
		||||
                    vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
 | 
			
		||||
                nsp = xci->GetSecurePartitionNSP();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        if (nsp->GetStatus() != Loader::ResultStatus::Success) {
 | 
			
		||||
            failed();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        const auto res = Core::System::GetInstance()
 | 
			
		||||
                             .GetFileSystemController()
 | 
			
		||||
                             .GetUserNANDContents()
 | 
			
		||||
                             ->InstallEntry(*nsp, false, qt_raw_copy);
 | 
			
		||||
        if (res == FileSys::InstallResult::Success) {
 | 
			
		||||
            success();
 | 
			
		||||
        } else {
 | 
			
		||||
            if (res == FileSys::InstallResult::ErrorAlreadyExists) {
 | 
			
		||||
                if (overwrite()) {
 | 
			
		||||
            if (nsp->GetStatus() != Loader::ResultStatus::Success) {
 | 
			
		||||
                failed(filename);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            const auto res = Core::System::GetInstance()
 | 
			
		||||
                                 .GetFileSystemController()
 | 
			
		||||
                                 .GetUserNANDContents()
 | 
			
		||||
                                 ->InstallEntry(*nsp, false, qt_raw_copy);
 | 
			
		||||
            if (res == FileSys::InstallResult::Success) {
 | 
			
		||||
                ++count;
 | 
			
		||||
            } else if (res == FileSys::InstallResult::ErrorAlreadyExists) {
 | 
			
		||||
                if (overwrite_files && overwrite(filename)) {
 | 
			
		||||
                    const auto res2 = Core::System::GetInstance()
 | 
			
		||||
                                          .GetFileSystemController()
 | 
			
		||||
                                          .GetUserNANDContents()
 | 
			
		||||
                                          ->InstallEntry(*nsp, true, qt_raw_copy);
 | 
			
		||||
                    if (res2 == FileSys::InstallResult::Success) {
 | 
			
		||||
                        success();
 | 
			
		||||
                    } else {
 | 
			
		||||
                        failed();
 | 
			
		||||
                    if (res2 != FileSys::InstallResult::Success) {
 | 
			
		||||
                        failed(filename);
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    ++count;
 | 
			
		||||
                } else {
 | 
			
		||||
                    --total_count;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                failed();
 | 
			
		||||
                failed(filename);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        const auto nca = std::make_shared<FileSys::NCA>(
 | 
			
		||||
            vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
 | 
			
		||||
        const auto id = nca->GetStatus();
 | 
			
		||||
 | 
			
		||||
        // Game updates necessary are missing base RomFS
 | 
			
		||||
        if (id != Loader::ResultStatus::Success &&
 | 
			
		||||
            id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
 | 
			
		||||
            failed();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const QStringList tt_options{tr("System Application"),
 | 
			
		||||
                                     tr("System Archive"),
 | 
			
		||||
                                     tr("System Application Update"),
 | 
			
		||||
                                     tr("Firmware Package (Type A)"),
 | 
			
		||||
                                     tr("Firmware Package (Type B)"),
 | 
			
		||||
                                     tr("Game"),
 | 
			
		||||
                                     tr("Game Update"),
 | 
			
		||||
                                     tr("Game DLC"),
 | 
			
		||||
                                     tr("Delta Title")};
 | 
			
		||||
        bool ok;
 | 
			
		||||
        const auto item = QInputDialog::getItem(
 | 
			
		||||
            this, tr("Select NCA Install Type..."),
 | 
			
		||||
            tr("Please select the type of title you would like to install this NCA as:\n(In "
 | 
			
		||||
               "most instances, the default 'Game' is fine.)"),
 | 
			
		||||
            tt_options, 5, false, &ok);
 | 
			
		||||
 | 
			
		||||
        auto index = tt_options.indexOf(item);
 | 
			
		||||
        if (!ok || index == -1) {
 | 
			
		||||
            QMessageBox::warning(this, tr("Failed to Install"),
 | 
			
		||||
                                 tr("The title type you selected for the NCA is invalid."));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If index is equal to or past Game, add the jump in TitleType.
 | 
			
		||||
        if (index >= 5) {
 | 
			
		||||
            index += static_cast<size_t>(FileSys::TitleType::Application) -
 | 
			
		||||
                     static_cast<size_t>(FileSys::TitleType::FirmwarePackageB);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        FileSys::InstallResult res;
 | 
			
		||||
        if (index >= static_cast<s32>(FileSys::TitleType::Application)) {
 | 
			
		||||
            res = Core::System::GetInstance()
 | 
			
		||||
                      .GetFileSystemController()
 | 
			
		||||
                      .GetUserNANDContents()
 | 
			
		||||
                      ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false,
 | 
			
		||||
                                     qt_raw_copy);
 | 
			
		||||
        } else {
 | 
			
		||||
            res = Core::System::GetInstance()
 | 
			
		||||
                      .GetFileSystemController()
 | 
			
		||||
                      .GetSystemNANDContents()
 | 
			
		||||
                      ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false,
 | 
			
		||||
                                     qt_raw_copy);
 | 
			
		||||
        }
 | 
			
		||||
            const auto nca = std::make_shared<FileSys::NCA>(
 | 
			
		||||
                vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
 | 
			
		||||
            const auto id = nca->GetStatus();
 | 
			
		||||
 | 
			
		||||
        if (res == FileSys::InstallResult::Success) {
 | 
			
		||||
            success();
 | 
			
		||||
        } else if (res == FileSys::InstallResult::ErrorAlreadyExists) {
 | 
			
		||||
            if (overwrite()) {
 | 
			
		||||
                const auto res2 = Core::System::GetInstance()
 | 
			
		||||
                                      .GetFileSystemController()
 | 
			
		||||
                                      .GetUserNANDContents()
 | 
			
		||||
                                      ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index),
 | 
			
		||||
                                                     true, qt_raw_copy);
 | 
			
		||||
                if (res2 == FileSys::InstallResult::Success) {
 | 
			
		||||
                    success();
 | 
			
		||||
            // Game updates necessary are missing base RomFS
 | 
			
		||||
            if (id != Loader::ResultStatus::Success &&
 | 
			
		||||
                id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
 | 
			
		||||
                failed(filename);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const QStringList tt_options{tr("System Application"),
 | 
			
		||||
                                         tr("System Archive"),
 | 
			
		||||
                                         tr("System Application Update"),
 | 
			
		||||
                                         tr("Firmware Package (Type A)"),
 | 
			
		||||
                                         tr("Firmware Package (Type B)"),
 | 
			
		||||
                                         tr("Game"),
 | 
			
		||||
                                         tr("Game Update"),
 | 
			
		||||
                                         tr("Game DLC"),
 | 
			
		||||
                                         tr("Delta Title")};
 | 
			
		||||
            bool ok;
 | 
			
		||||
            const auto item = QInputDialog::getItem(
 | 
			
		||||
                this, tr("Select NCA Install Type..."),
 | 
			
		||||
                tr("Please select the type of title you would like to install this NCA as:\n(In "
 | 
			
		||||
                   "most instances, the default 'Game' is fine.)"),
 | 
			
		||||
                tt_options, 5, false, &ok);
 | 
			
		||||
 | 
			
		||||
            auto index = tt_options.indexOf(item);
 | 
			
		||||
            if (!ok || index == -1) {
 | 
			
		||||
                QMessageBox::warning(this, tr("Failed to Install"),
 | 
			
		||||
                                     tr("The title type you selected for the NCA is invalid."));
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // If index is equal to or past Game, add the jump in TitleType.
 | 
			
		||||
            if (index >= 5) {
 | 
			
		||||
                index += static_cast<size_t>(FileSys::TitleType::Application) -
 | 
			
		||||
                         static_cast<size_t>(FileSys::TitleType::FirmwarePackageB);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            FileSys::InstallResult res;
 | 
			
		||||
            if (index >= static_cast<s32>(FileSys::TitleType::Application)) {
 | 
			
		||||
                res = Core::System::GetInstance()
 | 
			
		||||
                          .GetFileSystemController()
 | 
			
		||||
                          .GetUserNANDContents()
 | 
			
		||||
                          ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false,
 | 
			
		||||
                                         qt_raw_copy);
 | 
			
		||||
            } else {
 | 
			
		||||
                res = Core::System::GetInstance()
 | 
			
		||||
                          .GetFileSystemController()
 | 
			
		||||
                          .GetSystemNANDContents()
 | 
			
		||||
                          ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false,
 | 
			
		||||
                                         qt_raw_copy);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (res == FileSys::InstallResult::Success) {
 | 
			
		||||
                ++count;
 | 
			
		||||
            } else if (res == FileSys::InstallResult::ErrorAlreadyExists) {
 | 
			
		||||
                if (overwrite_files && overwrite(filename)) {
 | 
			
		||||
                    const auto res2 =
 | 
			
		||||
                        Core::System::GetInstance()
 | 
			
		||||
                            .GetFileSystemController()
 | 
			
		||||
                            .GetUserNANDContents()
 | 
			
		||||
                            ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), true,
 | 
			
		||||
                                           qt_raw_copy);
 | 
			
		||||
                    if (res2 != FileSys::InstallResult::Success) {
 | 
			
		||||
                        failed(filename);
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    ++count;
 | 
			
		||||
                } else {
 | 
			
		||||
                    failed();
 | 
			
		||||
                    --total_count;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                failed(filename);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            failed();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Return success only on the last file
 | 
			
		||||
        if (filename == filenames.last()) {
 | 
			
		||||
            success();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@ class MicroProfileDialog;
 | 
			
		||||
class ProfilerWidget;
 | 
			
		||||
class QLabel;
 | 
			
		||||
class QPushButton;
 | 
			
		||||
class QProgressDialog;
 | 
			
		||||
class WaitTreeWidget;
 | 
			
		||||
enum class GameListOpenTarget;
 | 
			
		||||
class GameListPlaceholder;
 | 
			
		||||
@@ -272,6 +273,9 @@ private:
 | 
			
		||||
 | 
			
		||||
    HotkeyRegistry hotkey_registry;
 | 
			
		||||
 | 
			
		||||
    // Install to NAND progress dialog
 | 
			
		||||
    QProgressDialog* install_progress;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    void dropEvent(QDropEvent* event) override;
 | 
			
		||||
    void dragEnterEvent(QDragEnterEvent* event) override;
 | 
			
		||||
 
 | 
			
		||||
@@ -130,7 +130,7 @@
 | 
			
		||||
    <bool>true</bool>
 | 
			
		||||
   </property>
 | 
			
		||||
   <property name="text">
 | 
			
		||||
    <string>Install File to NAND...</string>
 | 
			
		||||
    <string>Install Files to NAND...</string>
 | 
			
		||||
   </property>
 | 
			
		||||
  </action>
 | 
			
		||||
  <action name="action_Load_File">
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user