From 6b76b774007020befdaa8d7475a9a4edd6d0a0a4 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 11 Aug 2018 23:01:38 -0400 Subject: [PATCH] registration: Add support for force overwrite of installed --- src/core/file_sys/registered_cache.cpp | 50 ++++++++++----- src/core/file_sys/registered_cache.h | 20 ++++-- src/yuzu/game_list.cpp | 1 - src/yuzu/main.cpp | 88 +++++++++++++++++--------- 4 files changed, 106 insertions(+), 53 deletions(-) diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 20fec2391..e916d5610 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -60,7 +60,7 @@ static std::string GetCNMTName(TitleType type, u64 title_id) { auto index = static_cast(type); // If the index is after the jump in TitleType, subtract it out. if (index >= static_cast(TitleType::Application)) - index -= static_cast(TitleType::Application); + index -= 0x7B; return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id); } @@ -343,7 +343,8 @@ static std::shared_ptr GetNCAFromXCIForID(std::shared_ptr xci, const N return iter == xci->GetNCAs().end() ? nullptr : *iter; } -bool RegisteredCache::InstallEntry(std::shared_ptr xci, const VfsCopyFunction& copy) { +InstallResult RegisteredCache::InstallEntry(std::shared_ptr xci, bool overwrite_if_exists, + const VfsCopyFunction& copy) { const auto& ncas = xci->GetNCAs(); const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr nca) { return nca->GetType() == NCAContentType::Meta; @@ -352,14 +353,16 @@ bool RegisteredCache::InstallEntry(std::shared_ptr xci, const VfsCopyFuncti if (meta_iter == ncas.end()) { LOG_ERROR(Loader, "The XCI you are attempting to install does not have a metadata NCA and " "is therefore malformed. Double check your encryption keys."); - return false; + return InstallResult::ErrorMetaFailed; } // Install Metadata File const auto meta_id_raw = (*meta_iter)->GetName().substr(0, 32); const auto meta_id = HexStringToArray<16>(meta_id_raw); - if (!RawInstallNCA(*meta_iter, copy, meta_id)) - return false; + + const auto res = RawInstallNCA(*meta_iter, copy, overwrite_if_exists, meta_id); + if (res != InstallResult::Success) + return res; // Install all the other NCAs const auto section0 = (*meta_iter)->GetSubdirectories()[0]; @@ -367,16 +370,19 @@ bool RegisteredCache::InstallEntry(std::shared_ptr xci, const VfsCopyFuncti const CNMT cnmt(cnmt_file); for (const auto& record : cnmt.GetContentRecords()) { const auto nca = GetNCAFromXCIForID(xci, record.nca_id); - if (nca == nullptr || !RawInstallNCA(nca, copy, record.nca_id)) - return false; + if (nca == nullptr) + return InstallResult::ErrorCopyFailed; + const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id); + if (res2 != InstallResult::Success) + return res2; } Refresh(); - return true; + return InstallResult::Success; } -bool RegisteredCache::InstallEntry(std::shared_ptr nca, TitleType type, - const VfsCopyFunction& copy) { +InstallResult RegisteredCache::InstallEntry(std::shared_ptr nca, TitleType type, + bool overwrite_if_exists, const VfsCopyFunction& copy) { CNMTHeader header{ nca->GetTitleId(), ///< Title ID 0, ///< Ignore/Default title version @@ -393,11 +399,14 @@ bool RegisteredCache::InstallEntry(std::shared_ptr nca, TitleType type, mbedtls_sha256(data.data(), data.size(), c_rec.hash.data(), 0); memcpy(&c_rec.nca_id, &c_rec.hash, 16); const CNMT new_cnmt(header, opt_header, {c_rec}, {}); - return RawInstallYuzuMeta(new_cnmt) && RawInstallNCA(nca, copy, c_rec.nca_id); + if (!RawInstallYuzuMeta(new_cnmt)) + return InstallResult::ErrorMetaFailed; + return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); } -bool RegisteredCache::RawInstallNCA(std::shared_ptr nca, const VfsCopyFunction& copy, - boost::optional override_id) { +InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr nca, const VfsCopyFunction& copy, + bool overwrite_if_exists, + boost::optional override_id) { const auto in = nca->GetBaseFile(); Core::Crypto::SHA256Hash hash{}; @@ -416,15 +425,22 @@ bool RegisteredCache::RawInstallNCA(std::shared_ptr nca, const VfsCopyFunct std::string path = GetRelativePathFromNcaID(id, false, true); - if (GetFileAtID(id) != nullptr) { + if (GetFileAtID(id) != nullptr && !overwrite_if_exists) { LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping..."); - return false; + return InstallResult::ErrorAlreadyExists; + } + + if (GetFileAtID(id) != nullptr) { + LOG_WARNING(Loader, "Overwriting existing NCA..."); + VirtualDir c_dir; + { c_dir = dir->GetFileRelative(path)->GetContainingDirectory(); } + c_dir->DeleteFile(FileUtil::GetFilename(path)); } auto out = dir->CreateFileRelative(path); if (out == nullptr) - return false; - return copy(in, out); + return InstallResult::ErrorCopyFailed; + return copy(in, out) ? InstallResult::Success : InstallResult::ErrorCopyFailed; } bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) { diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index f2b07eec8..a7c51a59c 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -25,6 +25,13 @@ using NcaID = std::array; using RegisteredCacheParsingFunction = std::function; using VfsCopyFunction = std::function; +enum class InstallResult { + Success, + ErrorAlreadyExists, + ErrorCopyFailed, + ErrorMetaFailed, +}; + struct RegisteredCacheEntry { u64 title_id; ContentRecordType type; @@ -77,14 +84,16 @@ public: // Raw copies all the ncas from the xci to the csache. Does some quick checks to make sure there // is a meta NCA and all of them are accessible. - bool InstallEntry(std::shared_ptr xci, const VfsCopyFunction& copy = &VfsRawCopy); + InstallResult InstallEntry(std::shared_ptr xci, bool overwrite_if_exists = false, + const VfsCopyFunction& copy = &VfsRawCopy); // Due to the fact that we must use Meta-type NCAs to determine the existance of files, this // poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a // dir inside the NAND called 'yuzu_meta' and store the raw CNMT there. // TODO(DarkLordZach): Author real meta-type NCAs and install those. - bool InstallEntry(std::shared_ptr nca, TitleType type, - const VfsCopyFunction& copy = &VfsRawCopy); + InstallResult InstallEntry(std::shared_ptr nca, TitleType type, + bool overwrite_if_exists = false, + const VfsCopyFunction& copy = &VfsRawCopy); private: template @@ -97,8 +106,9 @@ private: boost::optional GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const; VirtualFile GetFileAtID(NcaID id) const; VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const; - bool RawInstallNCA(std::shared_ptr nca, const VfsCopyFunction& copy, - boost::optional override_id = boost::none); + InstallResult RawInstallNCA(std::shared_ptr nca, const VfsCopyFunction& copy, + bool overwrite_if_exists, + boost::optional override_id = boost::none); bool RawInstallYuzuMeta(const CNMT& cnmt); VirtualDir dir; diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index faaeda63d..f867118d9 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -405,7 +405,6 @@ void GameList::RefreshGameDirectory() { static void GetMetadataFromControlNCA(const std::shared_ptr& nca, std::vector& icon, std::string& name) { - const auto control_dir = FileSys::ExtractRomFS(nca->GetRomFS()); if (control_dir == nullptr) return; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index e8254c30f..b5f97f332 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -650,37 +650,59 @@ void GMainWindow::OnMenuInstallToNAND() { return true; }; + const auto success = [this]() { + QMessageBox::information(this, tr("Successfully Installed"), + tr("The file was successfully installed.")); + game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); + }; + + const auto failed = [this]() { + QMessageBox::warning( + this, tr("Failed to Install"), + 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.")); + }; + + const auto overwrite = [this]() { + return QMessageBox::question(this, "Failed to Install", + "The file you are attempting to install already exists " + "in the cache. Would you like to overwrite it?") == + QMessageBox::Yes; + }; + 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::warning( - 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.")); + failed(); return; } - if (Service::FileSystem::GetUserNANDContents()->InstallEntry(xci, qt_raw_copy)) { - QMessageBox::information(this, tr("Successfully Installed XCI"), - tr("The file was successfully installed.")); - game_list->PopulateAsync(UISettings::values.gamedir, - UISettings::values.gamedir_deepscan); + const auto res = + Service::FileSystem::GetUserNANDContents()->InstallEntry(xci, false, qt_raw_copy); + if (res == FileSys::InstallResult::Success) { + success(); } else { - QMessageBox::warning( - 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.")); + if (res == FileSys::InstallResult::ErrorAlreadyExists) { + if (overwrite()) { + const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry( + xci, true, qt_raw_copy); + if (res2 == FileSys::InstallResult::Success) { + success(); + } else { + failed(); + } + } + } else { + failed(); + } } } else { const auto nca = std::make_shared( vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); if (nca->GetStatus() != Loader::ResultStatus::Success) { - QMessageBox::warning( - 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.")); + failed(); return; } @@ -702,7 +724,7 @@ void GMainWindow::OnMenuInstallToNAND() { auto index = tt_options.indexOf(item); if (!ok || index == -1) { - QMessageBox::warning(this, tr("Failed to Install NCA"), + QMessageBox::warning(this, tr("Failed to Install"), tr("The title type you selected for the NCA is invalid.")); return; } @@ -710,18 +732,24 @@ void GMainWindow::OnMenuInstallToNAND() { if (index >= 5) index += 0x7B; - if (Service::FileSystem::GetUserNANDContents()->InstallEntry( - nca, static_cast(index), qt_raw_copy)) { - QMessageBox::information(this, tr("Successfully Installed NCA"), - tr("The file was successfully installed.")); - game_list->PopulateAsync(UISettings::values.gamedir, - UISettings::values.gamedir_deepscan); + const auto res = Service::FileSystem::GetUserNANDContents()->InstallEntry( + nca, static_cast(index), false, qt_raw_copy); + if (res == FileSys::InstallResult::Success) { + success(); } else { - QMessageBox::warning(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.")); + if (res == FileSys::InstallResult::ErrorAlreadyExists) { + if (overwrite()) { + const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry( + nca, static_cast(index), true, qt_raw_copy); + if (res2 == FileSys::InstallResult::Success) { + success(); + } else { + failed(); + } + } + } else { + failed(); + } } } }