diff options
author | Morph <39850852+Morph1984@users.noreply.github.com> | 2020-04-16 23:27:38 -0400 |
---|---|---|
committer | Morph <39850852+Morph1984@users.noreply.github.com> | 2020-07-10 00:38:28 -0400 |
commit | 4c269e5ced47765f35dbb58afa9fcac8138a1f7c (patch) | |
tree | fdcb76c40ac436c0a01e4501a428c41a02212179 /src/yuzu/main.cpp | |
parent | 0974533c966e58f36312ac79fc958ab0c5cc080b (diff) |
Add support for batch install to NAND
This adds support to batch install files to NAND
Diffstat (limited to 'src/yuzu/main.cpp')
-rw-r--r-- | src/yuzu/main.cpp | 295 |
1 files changed, 174 insertions, 121 deletions
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4d501a8f9a..45ddc3baf3 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -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; + } + + InstallDialog installDialog(this, files); + if (installDialog.exec() == QDialog::Rejected) { 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) + 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; - } + } else { + const auto nca = std::make_shared<FileSys::NCA>( + vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); + const auto id = nca->GetStatus(); - 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; - } + // Game updates necessary are missing base RomFS + if (id != Loader::ResultStatus::Success && + id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { + failed(filename); + 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); - } + 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; + } - 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 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); + } - 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(); + 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(); } } } |