summaryrefslogtreecommitdiff
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) {
60 auto index = static_cast<size_t>(type); 60 auto index = static_cast<size_t>(type);
61 // If the index is after the jump in TitleType, subtract it out. 61 // If the index is after the jump in TitleType, subtract it out.
62 if (index >= static_cast<size_t>(TitleType::Application)) 62 if (index >= static_cast<size_t>(TitleType::Application))
63 index -= static_cast<size_t>(TitleType::Application); 63 index -= 0x7B;
64 return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id); 64 return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id);
65} 65}
66 66
@@ -343,7 +343,8 @@ static std::shared_ptr<NCA> GetNCAFromXCIForID(std::shared_ptr<XCI> xci, const N
343 return iter == xci->GetNCAs().end() ? nullptr : *iter; 343 return iter == xci->GetNCAs().end() ? nullptr : *iter;
344} 344}
345 345
346bool RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, const VfsCopyFunction& copy) { 346InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists,
347 const VfsCopyFunction& copy) {
347 const auto& ncas = xci->GetNCAs(); 348 const auto& ncas = xci->GetNCAs();
348 const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) { 349 const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) {
349 return nca->GetType() == NCAContentType::Meta; 350 return nca->GetType() == NCAContentType::Meta;
@@ -352,14 +353,16 @@ bool RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, const VfsCopyFuncti
352 if (meta_iter == ncas.end()) { 353 if (meta_iter == ncas.end()) {
353 LOG_ERROR(Loader, "The XCI you are attempting to install does not have a metadata NCA and " 354 LOG_ERROR(Loader, "The XCI you are attempting to install does not have a metadata NCA and "
354 "is therefore malformed. Double check your encryption keys."); 355 "is therefore malformed. Double check your encryption keys.");
355 return false; 356 return InstallResult::ErrorMetaFailed;
356 } 357 }
357 358
358 // Install Metadata File 359 // Install Metadata File
359 const auto meta_id_raw = (*meta_iter)->GetName().substr(0, 32); 360 const auto meta_id_raw = (*meta_iter)->GetName().substr(0, 32);
360 const auto meta_id = HexStringToArray<16>(meta_id_raw); 361 const auto meta_id = HexStringToArray<16>(meta_id_raw);
361 if (!RawInstallNCA(*meta_iter, copy, meta_id)) 362
362 return false; 363 const auto res = RawInstallNCA(*meta_iter, copy, overwrite_if_exists, meta_id);
364 if (res != InstallResult::Success)
365 return res;
363 366
364 // Install all the other NCAs 367 // Install all the other NCAs
365 const auto section0 = (*meta_iter)->GetSubdirectories()[0]; 368 const auto section0 = (*meta_iter)->GetSubdirectories()[0];
@@ -367,16 +370,19 @@ bool RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, const VfsCopyFuncti
367 const CNMT cnmt(cnmt_file); 370 const CNMT cnmt(cnmt_file);
368 for (const auto& record : cnmt.GetContentRecords()) { 371 for (const auto& record : cnmt.GetContentRecords()) {
369 const auto nca = GetNCAFromXCIForID(xci, record.nca_id); 372 const auto nca = GetNCAFromXCIForID(xci, record.nca_id);
370 if (nca == nullptr || !RawInstallNCA(nca, copy, record.nca_id)) 373 if (nca == nullptr)
371 return false; 374 return InstallResult::ErrorCopyFailed;
375 const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id);
376 if (res2 != InstallResult::Success)
377 return res2;
372 } 378 }
373 379
374 Refresh(); 380 Refresh();
375 return true; 381 return InstallResult::Success;
376} 382}
377 383
378bool RegisteredCache::InstallEntry(std::shared_ptr<NCA> nca, TitleType type, 384InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NCA> nca, TitleType type,
379 const VfsCopyFunction& copy) { 385 bool overwrite_if_exists, const VfsCopyFunction& copy) {
380 CNMTHeader header{ 386 CNMTHeader header{
381 nca->GetTitleId(), ///< Title ID 387 nca->GetTitleId(), ///< Title ID
382 0, ///< Ignore/Default title version 388 0, ///< Ignore/Default title version
@@ -393,11 +399,14 @@ bool RegisteredCache::InstallEntry(std::shared_ptr<NCA> nca, TitleType type,
393 mbedtls_sha256(data.data(), data.size(), c_rec.hash.data(), 0); 399 mbedtls_sha256(data.data(), data.size(), c_rec.hash.data(), 0);
394 memcpy(&c_rec.nca_id, &c_rec.hash, 16); 400 memcpy(&c_rec.nca_id, &c_rec.hash, 16);
395 const CNMT new_cnmt(header, opt_header, {c_rec}, {}); 401 const CNMT new_cnmt(header, opt_header, {c_rec}, {});
396 return RawInstallYuzuMeta(new_cnmt) && RawInstallNCA(nca, copy, c_rec.nca_id); 402 if (!RawInstallYuzuMeta(new_cnmt))
403 return InstallResult::ErrorMetaFailed;
404 return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
397} 405}
398 406
399bool RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy, 407InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
400 boost::optional<NcaID> override_id) { 408 bool overwrite_if_exists,
409 boost::optional<NcaID> override_id) {
401 const auto in = nca->GetBaseFile(); 410 const auto in = nca->GetBaseFile();
402 Core::Crypto::SHA256Hash hash{}; 411 Core::Crypto::SHA256Hash hash{};
403 412
@@ -416,15 +425,22 @@ bool RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunct
416 425
417 std::string path = GetRelativePathFromNcaID(id, false, true); 426 std::string path = GetRelativePathFromNcaID(id, false, true);
418 427
419 if (GetFileAtID(id) != nullptr) { 428 if (GetFileAtID(id) != nullptr && !overwrite_if_exists) {
420 LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping..."); 429 LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping...");
421 return false; 430 return InstallResult::ErrorAlreadyExists;
431 }
432
433 if (GetFileAtID(id) != nullptr) {
434 LOG_WARNING(Loader, "Overwriting existing NCA...");
435 VirtualDir c_dir;
436 { c_dir = dir->GetFileRelative(path)->GetContainingDirectory(); }
437 c_dir->DeleteFile(FileUtil::GetFilename(path));
422 } 438 }
423 439
424 auto out = dir->CreateFileRelative(path); 440 auto out = dir->CreateFileRelative(path);
425 if (out == nullptr) 441 if (out == nullptr)
426 return false; 442 return InstallResult::ErrorCopyFailed;
427 return copy(in, out); 443 return copy(in, out) ? InstallResult::Success : InstallResult::ErrorCopyFailed;
428} 444}
429 445
430bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) { 446bool 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>;
25using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; 25using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
26using VfsCopyFunction = std::function<bool(VirtualFile, VirtualFile)>; 26using VfsCopyFunction = std::function<bool(VirtualFile, VirtualFile)>;
27 27
28enum class InstallResult {
29 Success,
30 ErrorAlreadyExists,
31 ErrorCopyFailed,
32 ErrorMetaFailed,
33};
34
28struct RegisteredCacheEntry { 35struct RegisteredCacheEntry {
29 u64 title_id; 36 u64 title_id;
30 ContentRecordType type; 37 ContentRecordType type;
@@ -77,14 +84,16 @@ public:
77 84
78 // Raw copies all the ncas from the xci to the csache. Does some quick checks to make sure there 85 // Raw copies all the ncas from the xci to the csache. Does some quick checks to make sure there
79 // is a meta NCA and all of them are accessible. 86 // is a meta NCA and all of them are accessible.
80 bool InstallEntry(std::shared_ptr<XCI> xci, const VfsCopyFunction& copy = &VfsRawCopy); 87 InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false,
88 const VfsCopyFunction& copy = &VfsRawCopy);
81 89
82 // Due to the fact that we must use Meta-type NCAs to determine the existance of files, this 90 // Due to the fact that we must use Meta-type NCAs to determine the existance of files, this
83 // poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a 91 // poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a
84 // dir inside the NAND called 'yuzu_meta' and store the raw CNMT there. 92 // dir inside the NAND called 'yuzu_meta' and store the raw CNMT there.
85 // TODO(DarkLordZach): Author real meta-type NCAs and install those. 93 // TODO(DarkLordZach): Author real meta-type NCAs and install those.
86 bool InstallEntry(std::shared_ptr<NCA> nca, TitleType type, 94 InstallResult InstallEntry(std::shared_ptr<NCA> nca, TitleType type,
87 const VfsCopyFunction& copy = &VfsRawCopy); 95 bool overwrite_if_exists = false,
96 const VfsCopyFunction& copy = &VfsRawCopy);
88 97
89private: 98private:
90 template <typename T> 99 template <typename T>
@@ -97,8 +106,9 @@ private:
97 boost::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const; 106 boost::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const;
98 VirtualFile GetFileAtID(NcaID id) const; 107 VirtualFile GetFileAtID(NcaID id) const;
99 VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const; 108 VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const;
100 bool RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy, 109 InstallResult RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
101 boost::optional<NcaID> override_id = boost::none); 110 bool overwrite_if_exists,
111 boost::optional<NcaID> override_id = boost::none);
102 bool RawInstallYuzuMeta(const CNMT& cnmt); 112 bool RawInstallYuzuMeta(const CNMT& cnmt);
103 113
104 VirtualDir dir; 114 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() {
405 405
406static void GetMetadataFromControlNCA(const std::shared_ptr<FileSys::NCA>& nca, 406static void GetMetadataFromControlNCA(const std::shared_ptr<FileSys::NCA>& nca,
407 std::vector<u8>& icon, std::string& name) { 407 std::vector<u8>& icon, std::string& name) {
408
409 const auto control_dir = FileSys::ExtractRomFS(nca->GetRomFS()); 408 const auto control_dir = FileSys::ExtractRomFS(nca->GetRomFS());
410 if (control_dir == nullptr) 409 if (control_dir == nullptr)
411 return; 410 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() {
650 return true; 650 return true;
651 }; 651 };
652 652
653 const auto success = [this]() {
654 QMessageBox::information(this, tr("Successfully Installed"),
655 tr("The file was successfully installed."));
656 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
657 };
658
659 const auto failed = [this]() {
660 QMessageBox::warning(
661 this, tr("Failed to Install"),
662 tr("There was an error while attempting to install the provided file. It "
663 "could have an incorrect format or be missing metadata. Please "
664 "double-check your file and try again."));
665 };
666
667 const auto overwrite = [this]() {
668 return QMessageBox::question(this, "Failed to Install",
669 "The file you are attempting to install already exists "
670 "in the cache. Would you like to overwrite it?") ==
671 QMessageBox::Yes;
672 };
673
653 if (!filename.isEmpty()) { 674 if (!filename.isEmpty()) {
654 if (filename.endsWith("xci", Qt::CaseInsensitive)) { 675 if (filename.endsWith("xci", Qt::CaseInsensitive)) {
655 const auto xci = std::make_shared<FileSys::XCI>( 676 const auto xci = std::make_shared<FileSys::XCI>(
656 vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); 677 vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
657 if (xci->GetStatus() != Loader::ResultStatus::Success) { 678 if (xci->GetStatus() != Loader::ResultStatus::Success) {
658 QMessageBox::warning( 679 failed();
659 this, tr("Failed to Install XCI"),
660 tr("The XCI file you provided is invalid. Please double-check your encryption "
661 "keys and the file and try again."));
662 return; 680 return;
663 } 681 }
664 if (Service::FileSystem::GetUserNANDContents()->InstallEntry(xci, qt_raw_copy)) { 682 const auto res =
665 QMessageBox::information(this, tr("Successfully Installed XCI"), 683 Service::FileSystem::GetUserNANDContents()->InstallEntry(xci, false, qt_raw_copy);
666 tr("The file was successfully installed.")); 684 if (res == FileSys::InstallResult::Success) {
667 game_list->PopulateAsync(UISettings::values.gamedir, 685 success();
668 UISettings::values.gamedir_deepscan);
669 } else { 686 } else {
670 QMessageBox::warning( 687 if (res == FileSys::InstallResult::ErrorAlreadyExists) {
671 this, tr("Failed to Install XCI"), 688 if (overwrite()) {
672 tr("There was an error while attempting to install the provided XCI file. It " 689 const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry(
673 "could have an incorrect format or be missing a metadata entry. Please " 690 xci, true, qt_raw_copy);
674 "double-check your file and try again.")); 691 if (res2 == FileSys::InstallResult::Success) {
692 success();
693 } else {
694 failed();
695 }
696 }
697 } else {
698 failed();
699 }
675 } 700 }
676 } else { 701 } else {
677 const auto nca = std::make_shared<FileSys::NCA>( 702 const auto nca = std::make_shared<FileSys::NCA>(
678 vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); 703 vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
679 if (nca->GetStatus() != Loader::ResultStatus::Success) { 704 if (nca->GetStatus() != Loader::ResultStatus::Success) {
680 QMessageBox::warning( 705 failed();
681 this, tr("Failed to Install NCA"),
682 tr("The NCA file you provided is invalid. Please double-check your encryption "
683 "keys and the file and try again."));
684 return; 706 return;
685 } 707 }
686 708
@@ -702,7 +724,7 @@ void GMainWindow::OnMenuInstallToNAND() {
702 724
703 auto index = tt_options.indexOf(item); 725 auto index = tt_options.indexOf(item);
704 if (!ok || index == -1) { 726 if (!ok || index == -1) {
705 QMessageBox::warning(this, tr("Failed to Install NCA"), 727 QMessageBox::warning(this, tr("Failed to Install"),
706 tr("The title type you selected for the NCA is invalid.")); 728 tr("The title type you selected for the NCA is invalid."));
707 return; 729 return;
708 } 730 }
@@ -710,18 +732,24 @@ void GMainWindow::OnMenuInstallToNAND() {
710 if (index >= 5) 732 if (index >= 5)
711 index += 0x7B; 733 index += 0x7B;
712 734
713 if (Service::FileSystem::GetUserNANDContents()->InstallEntry( 735 const auto res = Service::FileSystem::GetUserNANDContents()->InstallEntry(
714 nca, static_cast<FileSys::TitleType>(index), qt_raw_copy)) { 736 nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy);
715 QMessageBox::information(this, tr("Successfully Installed NCA"), 737 if (res == FileSys::InstallResult::Success) {
716 tr("The file was successfully installed.")); 738 success();
717 game_list->PopulateAsync(UISettings::values.gamedir,
718 UISettings::values.gamedir_deepscan);
719 } else { 739 } else {
720 QMessageBox::warning(this, tr("Failed to Install NCA"), 740 if (res == FileSys::InstallResult::ErrorAlreadyExists) {
721 tr("There was an error while attempting to install the " 741 if (overwrite()) {
722 "provided NCA file. An error might have occured creating " 742 const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry(
723 "the metadata file or parsing the NCA. Please " 743 nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy);
724 "double-check your file and try again.")); 744 if (res2 == FileSys::InstallResult::Success) {
745 success();
746 } else {
747 failed();
748 }
749 }
750 } else {
751 failed();
752 }
725 } 753 }
726 } 754 }
727 } 755 }