summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar liamwhite2023-09-13 09:39:27 -0400
committerGravatar GitHub2023-09-13 09:39:27 -0400
commit3f52b5167b65f91f484d4431c02a80749a8a2b03 (patch)
tree1819a109ebde820d9fb54c56a41278e8f0d15f82
parentMerge pull request #11480 from german77/mii_service (diff)
parentqt: add verification for installed contents (diff)
downloadyuzu-3f52b5167b65f91f484d4431c02a80749a8a2b03.tar.gz
yuzu-3f52b5167b65f91f484d4431c02a80749a8a2b03.tar.xz
yuzu-3f52b5167b65f91f484d4431c02a80749a8a2b03.zip
Merge pull request #11486 from liamwhite/system-verification
qt: add verification for installed contents
Diffstat (limited to '')
-rw-r--r--src/core/file_sys/nca_metadata.cpp4
-rw-r--r--src/core/file_sys/nca_metadata.h1
-rw-r--r--src/core/file_sys/registered_cache.cpp28
-rw-r--r--src/core/file_sys/registered_cache.h5
-rw-r--r--src/yuzu/main.cpp104
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu/main.ui6
7 files changed, 148 insertions, 1 deletions
diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp
index 52c78020c..f4a774675 100644
--- a/src/core/file_sys/nca_metadata.cpp
+++ b/src/core/file_sys/nca_metadata.cpp
@@ -45,6 +45,10 @@ CNMT::CNMT(CNMTHeader header_, OptionalHeader opt_header_,
45 45
46CNMT::~CNMT() = default; 46CNMT::~CNMT() = default;
47 47
48const CNMTHeader& CNMT::GetHeader() const {
49 return header;
50}
51
48u64 CNMT::GetTitleID() const { 52u64 CNMT::GetTitleID() const {
49 return header.title_id; 53 return header.title_id;
50} 54}
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h
index c59ece010..68e463b5f 100644
--- a/src/core/file_sys/nca_metadata.h
+++ b/src/core/file_sys/nca_metadata.h
@@ -89,6 +89,7 @@ public:
89 std::vector<ContentRecord> content_records_, std::vector<MetaRecord> meta_records_); 89 std::vector<ContentRecord> content_records_, std::vector<MetaRecord> meta_records_);
90 ~CNMT(); 90 ~CNMT();
91 91
92 const CNMTHeader& GetHeader() const;
92 u64 GetTitleID() const; 93 u64 GetTitleID() const;
93 u32 GetTitleVersion() const; 94 u32 GetTitleVersion() const;
94 TitleType GetType() const; 95 TitleType GetType() const;
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index f70adab82..e33b00d89 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -9,6 +9,7 @@
9#include "common/fs/path_util.h" 9#include "common/fs/path_util.h"
10#include "common/hex_util.h" 10#include "common/hex_util.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/scope_exit.h"
12#include "core/crypto/key_manager.h" 13#include "core/crypto/key_manager.h"
13#include "core/file_sys/card_image.h" 14#include "core/file_sys/card_image.h"
14#include "core/file_sys/common_funcs.h" 15#include "core/file_sys/common_funcs.h"
@@ -625,7 +626,7 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
625 nca->GetTitleId() != title_id) { 626 nca->GetTitleId() != title_id) {
626 // Create fake cnmt for patch to multiprogram application 627 // Create fake cnmt for patch to multiprogram application
627 const auto sub_nca_result = 628 const auto sub_nca_result =
628 InstallEntry(*nca, TitleType::Update, overwrite_if_exists, copy); 629 InstallEntry(*nca, cnmt.GetHeader(), record, overwrite_if_exists, copy);
629 if (sub_nca_result != InstallResult::Success) { 630 if (sub_nca_result != InstallResult::Success) {
630 return sub_nca_result; 631 return sub_nca_result;
631 } 632 }
@@ -672,6 +673,31 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
672 return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); 673 return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
673} 674}
674 675
676InstallResult RegisteredCache::InstallEntry(const NCA& nca, const CNMTHeader& base_header,
677 const ContentRecord& base_record,
678 bool overwrite_if_exists, const VfsCopyFunction& copy) {
679 const CNMTHeader header{
680 .title_id = nca.GetTitleId(),
681 .title_version = base_header.title_version,
682 .type = base_header.type,
683 .reserved = {},
684 .table_offset = 0x10,
685 .number_content_entries = 1,
686 .number_meta_entries = 0,
687 .attributes = 0,
688 .reserved2 = {},
689 .is_committed = 0,
690 .required_download_system_version = 0,
691 .reserved3 = {},
692 };
693 const OptionalHeader opt_header{0, 0};
694 const CNMT new_cnmt(header, opt_header, {base_record}, {});
695 if (!RawInstallYuzuMeta(new_cnmt)) {
696 return InstallResult::ErrorMetaFailed;
697 }
698 return RawInstallNCA(nca, copy, overwrite_if_exists, base_record.nca_id);
699}
700
675bool RegisteredCache::RemoveExistingEntry(u64 title_id) const { 701bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
676 bool removed_data = false; 702 bool removed_data = false;
677 703
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index bd7f53eaf..64815a845 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -24,6 +24,7 @@ enum class NCAContentType : u8;
24enum class TitleType : u8; 24enum class TitleType : u8;
25 25
26struct ContentRecord; 26struct ContentRecord;
27struct CNMTHeader;
27struct MetaRecord; 28struct MetaRecord;
28class RegisteredCache; 29class RegisteredCache;
29 30
@@ -169,6 +170,10 @@ public:
169 InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false, 170 InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false,
170 const VfsCopyFunction& copy = &VfsRawCopy); 171 const VfsCopyFunction& copy = &VfsRawCopy);
171 172
173 InstallResult InstallEntry(const NCA& nca, const CNMTHeader& base_header,
174 const ContentRecord& base_record, bool overwrite_if_exists = false,
175 const VfsCopyFunction& copy = &VfsRawCopy);
176
172 // Removes an existing entry based on title id 177 // Removes an existing entry based on title id
173 bool RemoveExistingEntry(u64 title_id) const; 178 bool RemoveExistingEntry(u64 title_id) const;
174 179
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 1540fe1c1..97d216638 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -8,6 +8,7 @@
8#include <iostream> 8#include <iostream>
9#include <memory> 9#include <memory>
10#include <thread> 10#include <thread>
11#include "core/loader/nca.h"
11#ifdef __APPLE__ 12#ifdef __APPLE__
12#include <unistd.h> // for chdir 13#include <unistd.h> // for chdir
13#endif 14#endif
@@ -1554,6 +1555,7 @@ void GMainWindow::ConnectMenuEvents() {
1554 1555
1555 // Help 1556 // Help
1556 connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder); 1557 connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder);
1558 connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents);
1557 connect_menu(ui->action_About, &GMainWindow::OnAbout); 1559 connect_menu(ui->action_About, &GMainWindow::OnAbout);
1558} 1560}
1559 1561
@@ -4006,6 +4008,108 @@ void GMainWindow::OnOpenYuzuFolder() {
4006 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::YuzuDir)))); 4008 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::YuzuDir))));
4007} 4009}
4008 4010
4011void GMainWindow::OnVerifyInstalledContents() {
4012 // Declare sizes.
4013 size_t total_size = 0;
4014 size_t processed_size = 0;
4015
4016 // Initialize a progress dialog.
4017 QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this);
4018 progress.setWindowModality(Qt::WindowModal);
4019 progress.setMinimumDuration(100);
4020 progress.setAutoClose(false);
4021 progress.setAutoReset(false);
4022
4023 // Declare a list of file names which failed to verify.
4024 std::vector<std::string> failed;
4025
4026 // Declare progress callback.
4027 auto QtProgressCallback = [&](size_t nca_processed, size_t nca_total) {
4028 if (progress.wasCanceled()) {
4029 return false;
4030 }
4031 progress.setValue(static_cast<int>(((processed_size + nca_processed) * 100) / total_size));
4032 return true;
4033 };
4034
4035 // Get content registries.
4036 auto bis_contents = system->GetFileSystemController().GetSystemNANDContents();
4037 auto user_contents = system->GetFileSystemController().GetUserNANDContents();
4038
4039 std::vector<FileSys::RegisteredCache*> content_providers;
4040 if (bis_contents) {
4041 content_providers.push_back(bis_contents);
4042 }
4043 if (user_contents) {
4044 content_providers.push_back(user_contents);
4045 }
4046
4047 // Get associated NCA files.
4048 std::vector<FileSys::VirtualFile> nca_files;
4049
4050 // Get all installed IDs.
4051 for (auto nca_provider : content_providers) {
4052 const auto entries = nca_provider->ListEntriesFilter();
4053
4054 for (const auto& entry : entries) {
4055 auto nca_file = nca_provider->GetEntryRaw(entry.title_id, entry.type);
4056 if (!nca_file) {
4057 continue;
4058 }
4059
4060 total_size += nca_file->GetSize();
4061 nca_files.push_back(std::move(nca_file));
4062 }
4063 }
4064
4065 // Using the NCA loader, determine if all NCAs are valid.
4066 for (auto& nca_file : nca_files) {
4067 Loader::AppLoader_NCA nca_loader(nca_file);
4068
4069 auto status = nca_loader.VerifyIntegrity(QtProgressCallback);
4070 if (progress.wasCanceled()) {
4071 break;
4072 }
4073 if (status != Loader::ResultStatus::Success) {
4074 FileSys::NCA nca(nca_file);
4075 const auto title_id = nca.GetTitleId();
4076 std::string title_name = "unknown";
4077
4078 const auto control = provider->GetEntry(FileSys::GetBaseTitleID(title_id),
4079 FileSys::ContentRecordType::Control);
4080 if (control && control->GetStatus() == Loader::ResultStatus::Success) {
4081 const FileSys::PatchManager pm{title_id, system->GetFileSystemController(),
4082 *provider};
4083 const auto [nacp, logo] = pm.ParseControlNCA(*control);
4084 if (nacp) {
4085 title_name = nacp->GetApplicationName();
4086 }
4087 }
4088
4089 if (title_id > 0) {
4090 failed.push_back(
4091 fmt::format("{} ({:016X}) ({})", nca_file->GetName(), title_id, title_name));
4092 } else {
4093 failed.push_back(fmt::format("{} (unknown)", nca_file->GetName()));
4094 }
4095 }
4096
4097 processed_size += nca_file->GetSize();
4098 }
4099
4100 progress.close();
4101
4102 if (failed.size() > 0) {
4103 auto failed_names = QString::fromStdString(fmt::format("{}", fmt::join(failed, "\n")));
4104 QMessageBox::critical(
4105 this, tr("Integrity verification failed!"),
4106 tr("Verification failed for the following files:\n\n%1").arg(failed_names));
4107 } else {
4108 QMessageBox::information(this, tr("Integrity verification succeeded!"),
4109 tr("The operation completed successfully."));
4110 }
4111}
4112
4009void GMainWindow::OnAbout() { 4113void GMainWindow::OnAbout() {
4010 AboutDialog aboutDialog(this); 4114 AboutDialog aboutDialog(this);
4011 aboutDialog.exec(); 4115 aboutDialog.exec();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 3b0e7f2fe..cf191f698 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -350,6 +350,7 @@ private slots:
350 void OnConfigurePerGame(); 350 void OnConfigurePerGame();
351 void OnLoadAmiibo(); 351 void OnLoadAmiibo();
352 void OnOpenYuzuFolder(); 352 void OnOpenYuzuFolder();
353 void OnVerifyInstalledContents();
353 void OnAbout(); 354 void OnAbout();
354 void OnToggleFilterBar(); 355 void OnToggleFilterBar();
355 void OnToggleStatusBar(); 356 void OnToggleStatusBar();
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 013ba0ceb..e54d7d75d 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -148,6 +148,7 @@
148 <addaction name="action_Configure_Tas"/> 148 <addaction name="action_Configure_Tas"/>
149 </widget> 149 </widget>
150 <addaction name="action_Rederive"/> 150 <addaction name="action_Rederive"/>
151 <addaction name="action_Verify_installed_contents"/>
151 <addaction name="separator"/> 152 <addaction name="separator"/>
152 <addaction name="action_Capture_Screenshot"/> 153 <addaction name="action_Capture_Screenshot"/>
153 <addaction name="menuTAS"/> 154 <addaction name="menuTAS"/>
@@ -214,6 +215,11 @@
214 <string>&amp;Reinitialize keys...</string> 215 <string>&amp;Reinitialize keys...</string>
215 </property> 216 </property>
216 </action> 217 </action>
218 <action name="action_Verify_installed_contents">
219 <property name="text">
220 <string>Verify installed contents</string>
221 </property>
222 </action>
217 <action name="action_About"> 223 <action name="action_About">
218 <property name="text"> 224 <property name="text">
219 <string>&amp;About yuzu</string> 225 <string>&amp;About yuzu</string>