Просмотр исходного кода

registration: Add support for force overwrite of installed

Zach Hilman 8 лет назад
Родитель
Сommit
6b76b77400
4 измененных файлов с 106 добавлено и 53 удалено
  1. 33 17
      src/core/file_sys/registered_cache.cpp
  2. 15 5
      src/core/file_sys/registered_cache.h
  3. 0 1
      src/yuzu/game_list.cpp
  4. 58 30
      src/yuzu/main.cpp

+ 33 - 17
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) {

+ 15 - 5
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;

+ 0 - 1
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;

+ 58 - 30
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();
+                }
             }
         }
     }