summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/file_sys/registered_cache.cpp50
-rw-r--r--src/core/file_sys/registered_cache.h20
-rw-r--r--src/yuzu/game_list.cpp1
-rw-r--r--src/yuzu/main.cpp88
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<size_t>(type);
// If the index is after the jump in TitleType, subtract it out.
if (index >= static_cast<size_t>(TitleType::Application))
- index -= static_cast<size_t>(TitleType::Application);
+ index -= 0x7B;
return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id);
}
@@ -343,7 +343,8 @@ static std::shared_ptr<NCA> GetNCAFromXCIForID(std::shared_ptr<XCI> xci, const N
return iter == xci->GetNCAs().end() ? nullptr : *iter;
}
-bool RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, const VfsCopyFunction& copy) {
+InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> 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> nca) {
return nca->GetType() == NCAContentType::Meta;
@@ -352,14 +353,16 @@ bool RegisteredCache::InstallEntry(std::shared_ptr<XCI> 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> 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> nca, TitleType type,
- const VfsCopyFunction& copy) {
+InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NCA> 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> 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> nca, const VfsCopyFunction& copy,
- boost::optional<NcaID> override_id) {
+InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
+ bool overwrite_if_exists,
+ boost::optional<NcaID> override_id) {
const auto in = nca->GetBaseFile();
Core::Crypto::SHA256Hash hash{};
@@ -416,15 +425,22 @@ bool RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> 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<u8, 0x10>;
using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
using VfsCopyFunction = std::function<bool(VirtualFile, VirtualFile)>;
+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> xci, const VfsCopyFunction& copy = &VfsRawCopy);
+ InstallResult InstallEntry(std::shared_ptr<XCI> 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> nca, TitleType type,
- const VfsCopyFunction& copy = &VfsRawCopy);
+ InstallResult InstallEntry(std::shared_ptr<NCA> nca, TitleType type,
+ bool overwrite_if_exists = false,
+ const VfsCopyFunction& copy = &VfsRawCopy);
private:
template <typename T>
@@ -97,8 +106,9 @@ private:
boost::optional<NcaID> 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> nca, const VfsCopyFunction& copy,
- boost::optional<NcaID> override_id = boost::none);
+ InstallResult RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
+ bool overwrite_if_exists,
+ boost::optional<NcaID> 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<FileSys::NCA>& nca,
std::vector<u8>& 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<FileSys::XCI>(
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<FileSys::NCA>(
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<FileSys::TitleType>(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<FileSys::TitleType>(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<FileSys::TitleType>(index), true, qt_raw_copy);
+ if (res2 == FileSys::InstallResult::Success) {
+ success();
+ } else {
+ failed();
+ }
+ }
+ } else {
+ failed();
+ }
}
}
}