diff --git a/src/core/core.cpp b/src/core/core.cpp index 05af68f22..28038ff6f 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -5,6 +5,7 @@ #include #include #include "common/logging/log.h" +#include "common/string_util.h" #include "core/core.h" #include "core/core_timing.h" #include "core/gdbstub/gdbstub.h" diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 5440cdefb..766fef254 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -427,7 +427,7 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) { } Refresh(); return std::find_if(yuzu_meta.begin(), yuzu_meta.end(), - [&cnmt](const std::pair& kv) { + [&cnmt](const std::pair& kv) { return kv.second.GetType() == cnmt.GetType() && kv.second.GetTitleID() == cnmt.GetTitleID(); }) != yuzu_meta.end(); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 94fb8ae6a..c48191486 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -24,6 +24,7 @@ #include "common/string_util.h" #include "core/core.h" #include "core/crypto/key_manager.h" +#include "core/file_sys/card_image.h" #include "core/file_sys/vfs_real.h" #include "core/gdbstub/gdbstub.h" #include "core/loader/loader.h" @@ -114,6 +115,9 @@ GMainWindow::GMainWindow() .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); show(); + // Necessary to load titles from nand in gamelist. + Service::FileSystem::RegisterBIS(std::make_unique(vfs->OpenDirectory( + FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::ReadWrite))); game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); // Show one-time "callout" messages to the user @@ -309,6 +313,8 @@ void GMainWindow::ConnectMenuEvents() { // File connect(ui.action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile); connect(ui.action_Load_Folder, &QAction::triggered, this, &GMainWindow::OnMenuLoadFolder); + connect(ui.action_Install_File_NAND, &QAction::triggered, this, + &GMainWindow::OnMenuInstallToNAND); connect(ui.action_Select_Game_List_Root, &QAction::triggered, this, &GMainWindow::OnMenuSelectGameListRoot); connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); @@ -612,6 +618,89 @@ void GMainWindow::OnMenuLoadFolder() { } } +void GMainWindow::OnMenuInstallToNAND() { + const static QString file_filter = + tr("Installable Switch File (*.nca *.xci);;Nintendo Content Archive (*.nca);;NX Cartridge " + "Image (*.xci)"); + QString filename = QFileDialog::getOpenFileName(this, tr("Install File"), + UISettings::values.roms_path, file_filter); + if (!filename.isEmpty()) { + if (filename.endsWith("xci", Qt::CaseInsensitive)) { + const auto xci = std::make_shared( + vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); + if (xci->GetStatus() != Loader::ResultStatus::Success) { + QMessageBox::critical( + this, tr("Failed to Install XCI"), + tr("The XCI file you provided is invalid. Please double-check your encryption " + "keys and the file and try again.")); + return; + } + if (!Service::FileSystem::GetUserNANDContents()->InstallEntry(xci)) { + QMessageBox::critical( + this, tr("Failed to Install XCI"), + tr("There was an error while attempting to install the provided XCI file. It " + "could have an incorrect format or be missing a metadata entry. Please " + "double-check your file and try again.")); + } else { + QMessageBox::information(this, tr("Successfully Installed XCI"), + tr("The file was successfully installed.")); + game_list->PopulateAsync(UISettings::values.gamedir, + UISettings::values.gamedir_deepscan); + } + } else { + const auto nca = std::make_shared( + vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); + if (nca->GetStatus() != Loader::ResultStatus::Success) { + QMessageBox::critical( + this, tr("Failed to Install NCA"), + tr("The NCA file you provided is invalid. Please double-check your encryption " + "keys and the file and try again.")); + return; + } + + const static QStringList tt_options{"System Application", + "System Archive", + "System Application Update", + "Firmware Package (Type A)", + "Firmware Package (Type B)", + "Game", + "Game Update", + "Game DLC", + "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::critical(this, tr("Failed to Install NCA"), + tr("The title type you selected for the NCA is invalid.")); + return; + } + + if (index >= 5) + index += 0x80; + + if (!Service::FileSystem::GetUserNANDContents()->InstallEntry( + nca, static_cast(index))) { + QMessageBox::critical(this, tr("Failed to Install NCA"), + tr("There was an error while attempting to install the " + "provided NCA file. An error might have occured creating " + "the metadata file or parsing the NCA. Please " + "double-check your file and try again.")); + } else { + QMessageBox::information(this, tr("Successfully Installed NCA"), + tr("The file was successfully installed.")); + game_list->PopulateAsync(UISettings::values.gamedir, + UISettings::values.gamedir_deepscan); + } + } + } +} + void GMainWindow::OnMenuSelectGameListRoot() { QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); if (!dir_path.isEmpty()) { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 74487c58c..5f4d2ab9a 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -125,6 +125,7 @@ private slots: void OnGameListOpenSaveFolder(u64 program_id); void OnMenuLoadFile(); void OnMenuLoadFolder(); + void OnMenuInstallToNAND(); /// Called whenever a user selects the "File->Select Game List Root" menu item void OnMenuSelectGameListRoot(); void OnMenuRecentFile(); diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 22c4cad08..a3bfb2af3 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -57,6 +57,8 @@ Recent Files + + @@ -102,6 +104,11 @@ + + + Install File to NAND... + + Load File...