summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/frontend_common/content_manager.h137
-rw-r--r--src/yuzu/main.cpp138
2 files changed, 161 insertions, 114 deletions
diff --git a/src/frontend_common/content_manager.h b/src/frontend_common/content_manager.h
index 23f2979db..92276700b 100644
--- a/src/frontend_common/content_manager.h
+++ b/src/frontend_common/content_manager.h
@@ -11,10 +11,12 @@
11#include "core/file_sys/content_archive.h" 11#include "core/file_sys/content_archive.h"
12#include "core/file_sys/mode.h" 12#include "core/file_sys/mode.h"
13#include "core/file_sys/nca_metadata.h" 13#include "core/file_sys/nca_metadata.h"
14#include "core/file_sys/patch_manager.h"
14#include "core/file_sys/registered_cache.h" 15#include "core/file_sys/registered_cache.h"
15#include "core/file_sys/submission_package.h" 16#include "core/file_sys/submission_package.h"
16#include "core/hle/service/filesystem/filesystem.h" 17#include "core/hle/service/filesystem/filesystem.h"
17#include "core/loader/loader.h" 18#include "core/loader/loader.h"
19#include "core/loader/nca.h"
18 20
19namespace ContentManager { 21namespace ContentManager {
20 22
@@ -25,6 +27,12 @@ enum class InstallResult {
25 BaseInstallAttempted, 27 BaseInstallAttempted,
26}; 28};
27 29
30enum class GameVerificationResult {
31 Success,
32 Failed,
33 NotImplemented,
34};
35
28/** 36/**
29 * \brief Removes a single installed DLC 37 * \brief Removes a single installed DLC
30 * \param fs_controller [FileSystemController] reference from the Core::System instance 38 * \param fs_controller [FileSystemController] reference from the Core::System instance
@@ -121,7 +129,7 @@ inline bool RemoveMod(const Service::FileSystem::FileSystemController& fs_contro
121 * \param filename Path to the NSP file 129 * \param filename Path to the NSP file
122 * \param callback Optional callback to report the progress of the installation. The first size_t 130 * \param callback Optional callback to report the progress of the installation. The first size_t
123 * parameter is the total size of the virtual file and the second is the current progress. If you 131 * parameter is the total size of the virtual file and the second is the current progress. If you
124 * return false to the callback, it will cancel the installation as soon as possible. 132 * return true to the callback, it will cancel the installation as soon as possible.
125 * \return [InstallResult] representing how the installation finished 133 * \return [InstallResult] representing how the installation finished
126 */ 134 */
127inline InstallResult InstallNSP( 135inline InstallResult InstallNSP(
@@ -186,7 +194,7 @@ inline InstallResult InstallNSP(
186 * \param title_type Type of NCA package to install 194 * \param title_type Type of NCA package to install
187 * \param callback Optional callback to report the progress of the installation. The first size_t 195 * \param callback Optional callback to report the progress of the installation. The first size_t
188 * parameter is the total size of the virtual file and the second is the current progress. If you 196 * parameter is the total size of the virtual file and the second is the current progress. If you
189 * return false to the callback, it will cancel the installation as soon as possible. 197 * return true to the callback, it will cancel the installation as soon as possible.
190 * \return [InstallResult] representing how the installation finished 198 * \return [InstallResult] representing how the installation finished
191 */ 199 */
192inline InstallResult InstallNCA( 200inline InstallResult InstallNCA(
@@ -235,4 +243,129 @@ inline InstallResult InstallNCA(
235 } 243 }
236} 244}
237 245
246/**
247 * \brief Verifies the installed contents for a given ManualContentProvider
248 * \param system Raw pointer to the system instance
249 * \param provider Raw pointer to the content provider that's tracking indexed games
250 * \param callback Optional callback to report the progress of the installation. The first size_t
251 * parameter is the total size of the installed contents and the second is the current progress. If
252 * you return true to the callback, it will cancel the installation as soon as possible.
253 * \return A list of entries that failed to install. Returns an empty vector if successful.
254 */
255inline std::vector<std::string> VerifyInstalledContents(
256 Core::System* system, FileSys::ManualContentProvider* provider,
257 const std::function<bool(size_t, size_t)>& callback = std::function<bool(size_t, size_t)>()) {
258 // Get content registries.
259 auto bis_contents = system->GetFileSystemController().GetSystemNANDContents();
260 auto user_contents = system->GetFileSystemController().GetUserNANDContents();
261
262 std::vector<FileSys::RegisteredCache*> content_providers;
263 if (bis_contents) {
264 content_providers.push_back(bis_contents);
265 }
266 if (user_contents) {
267 content_providers.push_back(user_contents);
268 }
269
270 // Get associated NCA files.
271 std::vector<FileSys::VirtualFile> nca_files;
272
273 // Get all installed IDs.
274 size_t total_size = 0;
275 for (auto nca_provider : content_providers) {
276 const auto entries = nca_provider->ListEntriesFilter();
277
278 for (const auto& entry : entries) {
279 auto nca_file = nca_provider->GetEntryRaw(entry.title_id, entry.type);
280 if (!nca_file) {
281 continue;
282 }
283
284 total_size += nca_file->GetSize();
285 nca_files.push_back(std::move(nca_file));
286 }
287 }
288
289 // Declare a list of file names which failed to verify.
290 std::vector<std::string> failed;
291
292 size_t processed_size = 0;
293 bool cancelled = false;
294 auto nca_callback = [&](size_t nca_processed, size_t nca_total) {
295 cancelled = callback(total_size, processed_size + nca_processed);
296 return !cancelled;
297 };
298
299 // Using the NCA loader, determine if all NCAs are valid.
300 for (auto& nca_file : nca_files) {
301 Loader::AppLoader_NCA nca_loader(nca_file);
302
303 auto status = nca_loader.VerifyIntegrity(nca_callback);
304 if (cancelled) {
305 break;
306 }
307 if (status != Loader::ResultStatus::Success) {
308 FileSys::NCA nca(nca_file);
309 const auto title_id = nca.GetTitleId();
310 std::string title_name = "unknown";
311
312 const auto control = provider->GetEntry(FileSys::GetBaseTitleID(title_id),
313 FileSys::ContentRecordType::Control);
314 if (control && control->GetStatus() == Loader::ResultStatus::Success) {
315 const FileSys::PatchManager pm{title_id, system->GetFileSystemController(),
316 *provider};
317 const auto [nacp, logo] = pm.ParseControlNCA(*control);
318 if (nacp) {
319 title_name = nacp->GetApplicationName();
320 }
321 }
322
323 if (title_id > 0) {
324 failed.push_back(
325 fmt::format("{} ({:016X}) ({})", nca_file->GetName(), title_id, title_name));
326 } else {
327 failed.push_back(fmt::format("{} (unknown)", nca_file->GetName()));
328 }
329 }
330
331 processed_size += nca_file->GetSize();
332 }
333 return failed;
334}
335
336/**
337 * \brief Verifies the contents of a given game
338 * \param system Raw pointer to the system instance
339 * \param game_path Patch to the game file
340 * \param callback Optional callback to report the progress of the installation. The first size_t
341 * parameter is the total size of the installed contents and the second is the current progress. If
342 * you return true to the callback, it will cancel the installation as soon as possible.
343 * \return GameVerificationResult representing how the verification process finished
344 */
345inline GameVerificationResult VerifyGameContents(
346 Core::System* system, const std::string& game_path,
347 const std::function<bool(size_t, size_t)>& callback = std::function<bool(size_t, size_t)>()) {
348 const auto loader = Loader::GetLoader(
349 *system, system->GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read));
350 if (loader == nullptr) {
351 return GameVerificationResult::NotImplemented;
352 }
353
354 bool cancelled = false;
355 auto loader_callback = [&](size_t processed, size_t total) {
356 cancelled = callback(total, processed);
357 return !cancelled;
358 };
359
360 const auto status = loader->VerifyIntegrity(loader_callback);
361 if (cancelled || status == Loader::ResultStatus::ErrorIntegrityVerificationNotImplemented) {
362 return GameVerificationResult::NotImplemented;
363 }
364
365 if (status == Loader::ResultStatus::ErrorIntegrityVerificationFailed) {
366 return GameVerificationResult::Failed;
367 }
368 return GameVerificationResult::Success;
369}
370
238} // namespace ContentManager 371} // namespace ContentManager
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 05bd4174c..d8b0beadf 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2786,16 +2786,6 @@ void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
2786 QMessageBox::warning(this, tr("Integrity verification couldn't be performed!"), 2786 QMessageBox::warning(this, tr("Integrity verification couldn't be performed!"),
2787 tr("File contents were not checked for validity.")); 2787 tr("File contents were not checked for validity."));
2788 }; 2788 };
2789 const auto Failed = [this] {
2790 QMessageBox::critical(this, tr("Integrity verification failed!"),
2791 tr("File contents may be corrupt."));
2792 };
2793
2794 const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read));
2795 if (loader == nullptr) {
2796 NotImplemented();
2797 return;
2798 }
2799 2789
2800 QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this); 2790 QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this);
2801 progress.setWindowModality(Qt::WindowModal); 2791 progress.setWindowModality(Qt::WindowModal);
@@ -2803,30 +2793,26 @@ void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
2803 progress.setAutoClose(false); 2793 progress.setAutoClose(false);
2804 progress.setAutoReset(false); 2794 progress.setAutoReset(false);
2805 2795
2806 const auto QtProgressCallback = [&](size_t processed_size, size_t total_size) { 2796 const auto QtProgressCallback = [&](size_t total_size, size_t processed_size) {
2807 if (progress.wasCanceled()) {
2808 return false;
2809 }
2810
2811 progress.setValue(static_cast<int>((processed_size * 100) / total_size)); 2797 progress.setValue(static_cast<int>((processed_size * 100) / total_size));
2812 return true; 2798 return progress.wasCanceled();
2813 }; 2799 };
2814 2800
2815 const auto status = loader->VerifyIntegrity(QtProgressCallback); 2801 const auto result =
2816 if (progress.wasCanceled() || 2802 ContentManager::VerifyGameContents(system.get(), game_path, QtProgressCallback);
2817 status == Loader::ResultStatus::ErrorIntegrityVerificationNotImplemented) { 2803 progress.close();
2804 switch (result) {
2805 case ContentManager::GameVerificationResult::Success:
2806 QMessageBox::information(this, tr("Integrity verification succeeded!"),
2807 tr("The operation completed successfully."));
2808 break;
2809 case ContentManager::GameVerificationResult::Failed:
2810 QMessageBox::critical(this, tr("Integrity verification failed!"),
2811 tr("File contents may be corrupt."));
2812 break;
2813 case ContentManager::GameVerificationResult::NotImplemented:
2818 NotImplemented(); 2814 NotImplemented();
2819 return;
2820 }
2821
2822 if (status == Loader::ResultStatus::ErrorIntegrityVerificationFailed) {
2823 Failed();
2824 return;
2825 } 2815 }
2826
2827 progress.close();
2828 QMessageBox::information(this, tr("Integrity verification succeeded!"),
2829 tr("The operation completed successfully."));
2830} 2816}
2831 2817
2832void GMainWindow::OnGameListCopyTID(u64 program_id) { 2818void GMainWindow::OnGameListCopyTID(u64 program_id) {
@@ -4121,10 +4107,6 @@ void GMainWindow::OnOpenYuzuFolder() {
4121} 4107}
4122 4108
4123void GMainWindow::OnVerifyInstalledContents() { 4109void GMainWindow::OnVerifyInstalledContents() {
4124 // Declare sizes.
4125 size_t total_size = 0;
4126 size_t processed_size = 0;
4127
4128 // Initialize a progress dialog. 4110 // Initialize a progress dialog.
4129 QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this); 4111 QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this);
4130 progress.setWindowModality(Qt::WindowModal); 4112 progress.setWindowModality(Qt::WindowModal);
@@ -4132,93 +4114,25 @@ void GMainWindow::OnVerifyInstalledContents() {
4132 progress.setAutoClose(false); 4114 progress.setAutoClose(false);
4133 progress.setAutoReset(false); 4115 progress.setAutoReset(false);
4134 4116
4135 // Declare a list of file names which failed to verify.
4136 std::vector<std::string> failed;
4137
4138 // Declare progress callback. 4117 // Declare progress callback.
4139 auto QtProgressCallback = [&](size_t nca_processed, size_t nca_total) { 4118 auto QtProgressCallback = [&](size_t total_size, size_t processed_size) {
4140 if (progress.wasCanceled()) { 4119 progress.setValue(static_cast<int>((processed_size * 100) / total_size));
4141 return false; 4120 return progress.wasCanceled();
4142 }
4143 progress.setValue(static_cast<int>(((processed_size + nca_processed) * 100) / total_size));
4144 return true;
4145 }; 4121 };
4146 4122
4147 // Get content registries. 4123 const std::vector<std::string> result =
4148 auto bis_contents = system->GetFileSystemController().GetSystemNANDContents(); 4124 ContentManager::VerifyInstalledContents(system.get(), provider.get(), QtProgressCallback);
4149 auto user_contents = system->GetFileSystemController().GetUserNANDContents();
4150
4151 std::vector<FileSys::RegisteredCache*> content_providers;
4152 if (bis_contents) {
4153 content_providers.push_back(bis_contents);
4154 }
4155 if (user_contents) {
4156 content_providers.push_back(user_contents);
4157 }
4158
4159 // Get associated NCA files.
4160 std::vector<FileSys::VirtualFile> nca_files;
4161
4162 // Get all installed IDs.
4163 for (auto nca_provider : content_providers) {
4164 const auto entries = nca_provider->ListEntriesFilter();
4165
4166 for (const auto& entry : entries) {
4167 auto nca_file = nca_provider->GetEntryRaw(entry.title_id, entry.type);
4168 if (!nca_file) {
4169 continue;
4170 }
4171
4172 total_size += nca_file->GetSize();
4173 nca_files.push_back(std::move(nca_file));
4174 }
4175 }
4176
4177 // Using the NCA loader, determine if all NCAs are valid.
4178 for (auto& nca_file : nca_files) {
4179 Loader::AppLoader_NCA nca_loader(nca_file);
4180
4181 auto status = nca_loader.VerifyIntegrity(QtProgressCallback);
4182 if (progress.wasCanceled()) {
4183 break;
4184 }
4185 if (status != Loader::ResultStatus::Success) {
4186 FileSys::NCA nca(nca_file);
4187 const auto title_id = nca.GetTitleId();
4188 std::string title_name = "unknown";
4189
4190 const auto control = provider->GetEntry(FileSys::GetBaseTitleID(title_id),
4191 FileSys::ContentRecordType::Control);
4192 if (control && control->GetStatus() == Loader::ResultStatus::Success) {
4193 const FileSys::PatchManager pm{title_id, system->GetFileSystemController(),
4194 *provider};
4195 const auto [nacp, logo] = pm.ParseControlNCA(*control);
4196 if (nacp) {
4197 title_name = nacp->GetApplicationName();
4198 }
4199 }
4200
4201 if (title_id > 0) {
4202 failed.push_back(
4203 fmt::format("{} ({:016X}) ({})", nca_file->GetName(), title_id, title_name));
4204 } else {
4205 failed.push_back(fmt::format("{} (unknown)", nca_file->GetName()));
4206 }
4207 }
4208
4209 processed_size += nca_file->GetSize();
4210 }
4211
4212 progress.close(); 4125 progress.close();
4213 4126
4214 if (failed.size() > 0) { 4127 if (result.empty()) {
4215 auto failed_names = QString::fromStdString(fmt::format("{}", fmt::join(failed, "\n"))); 4128 QMessageBox::information(this, tr("Integrity verification succeeded!"),
4129 tr("The operation completed successfully."));
4130 } else {
4131 const auto failed_names =
4132 QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
4216 QMessageBox::critical( 4133 QMessageBox::critical(
4217 this, tr("Integrity verification failed!"), 4134 this, tr("Integrity verification failed!"),
4218 tr("Verification failed for the following files:\n\n%1").arg(failed_names)); 4135 tr("Verification failed for the following files:\n\n%1").arg(failed_names));
4219 } else {
4220 QMessageBox::information(this, tr("Integrity verification succeeded!"),
4221 tr("The operation completed successfully."));
4222 } 4136 }
4223} 4137}
4224 4138