summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/file_sys/registered_cache.cpp101
-rw-r--r--src/core/file_sys/registered_cache.h6
-rw-r--r--src/core/file_sys/xts_archive.cpp14
-rw-r--r--src/yuzu/game_list.cpp34
-rw-r--r--src/yuzu/game_list.h15
-rw-r--r--src/yuzu/main.cpp182
-rw-r--r--src/yuzu/main.h9
7 files changed, 291 insertions, 70 deletions
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 37351c561..e94eed3b6 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -547,56 +547,6 @@ InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_ex
547 return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy); 547 return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy);
548} 548}
549 549
550bool RegisteredCache::RemoveExistingEntry(u64 title_id) {
551 const auto delete_nca = [this](const NcaID& id) {
552 const auto path = GetRelativePathFromNcaID(id, false, true, false);
553
554 if (dir->GetFileRelative(path) == nullptr) {
555 return false;
556 }
557
558 Core::Crypto::SHA256Hash hash{};
559 mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0);
560 const auto dirname = fmt::format("000000{:02X}", hash[0]);
561
562 const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
563
564 const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false)));
565
566 return res;
567 };
568
569 // If an entry exists in the registered cache, remove it
570 if (HasEntry(title_id, ContentRecordType::Meta)) {
571 LOG_INFO(Loader,
572 "Previously installed entry (v{}) for title_id={:016X} detected! "
573 "Attempting to remove...",
574 GetEntryVersion(title_id).value_or(0), title_id);
575 // Get all the ncas associated with the current CNMT and delete them
576 const auto meta_old_id =
577 GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{});
578 const auto program_id =
579 GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{});
580 const auto data_id =
581 GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{});
582 const auto control_id =
583 GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{});
584 const auto html_id =
585 GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{});
586 const auto legal_id =
587 GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{});
588
589 delete_nca(meta_old_id);
590 delete_nca(program_id);
591 delete_nca(data_id);
592 delete_nca(control_id);
593 delete_nca(html_id);
594 delete_nca(legal_id);
595 return true;
596 }
597 return false;
598}
599
600InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists, 550InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists,
601 const VfsCopyFunction& copy) { 551 const VfsCopyFunction& copy) {
602 const auto ncas = nsp.GetNCAsCollapsed(); 552 const auto ncas = nsp.GetNCAsCollapsed();
@@ -692,6 +642,57 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
692 return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); 642 return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
693} 643}
694 644
645bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
646 const auto delete_nca = [this](const NcaID& id) {
647 const auto path = GetRelativePathFromNcaID(id, false, true, false);
648
649 const bool isFile = dir->GetFileRelative(path) != nullptr;
650 const bool isDir = dir->GetDirectoryRelative(path) != nullptr;
651
652 if (isFile) {
653 return dir->DeleteFile(path);
654 } else if (isDir) {
655 return dir->DeleteSubdirectoryRecursive(path);
656 }
657
658 return false;
659 };
660
661 // If an entry exists in the registered cache, remove it
662 if (HasEntry(title_id, ContentRecordType::Meta)) {
663 LOG_INFO(Loader,
664 "Previously installed entry (v{}) for title_id={:016X} detected! "
665 "Attempting to remove...",
666 GetEntryVersion(title_id).value_or(0), title_id);
667
668 // Get all the ncas associated with the current CNMT and delete them
669 const auto meta_old_id =
670 GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{});
671 const auto program_id =
672 GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{});
673 const auto data_id =
674 GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{});
675 const auto control_id =
676 GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{});
677 const auto html_id =
678 GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{});
679 const auto legal_id =
680 GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{});
681
682 const auto deleted_meta = delete_nca(meta_old_id);
683 const auto deleted_program = delete_nca(program_id);
684 const auto deleted_data = delete_nca(data_id);
685 const auto deleted_control = delete_nca(control_id);
686 const auto deleted_html = delete_nca(html_id);
687 const auto deleted_legal = delete_nca(legal_id);
688
689 return deleted_meta && (deleted_meta || deleted_program || deleted_data ||
690 deleted_control || deleted_html || deleted_legal);
691 }
692
693 return false;
694}
695
695InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy, 696InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
696 bool overwrite_if_exists, 697 bool overwrite_if_exists,
697 std::optional<NcaID> override_id) { 698 std::optional<NcaID> override_id) {
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 29cf0d40c..ec1d54f27 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -155,9 +155,6 @@ public:
155 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, 155 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
156 std::optional<u64> title_id = {}) const override; 156 std::optional<u64> title_id = {}) const override;
157 157
158 // Removes an existing entry based on title id
159 bool RemoveExistingEntry(u64 title_id);
160
161 // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure 158 // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
162 // there is a meta NCA and all of them are accessible. 159 // there is a meta NCA and all of them are accessible.
163 InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false, 160 InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false,
@@ -172,6 +169,9 @@ public:
172 InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false, 169 InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false,
173 const VfsCopyFunction& copy = &VfsRawCopy); 170 const VfsCopyFunction& copy = &VfsRawCopy);
174 171
172 // Removes an existing entry based on title id
173 bool RemoveExistingEntry(u64 title_id) const;
174
175private: 175private:
176 template <typename T> 176 template <typename T>
177 void IterateAllMetadata(std::vector<T>& out, 177 void IterateAllMetadata(std::vector<T>& out,
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp
index 86e06ccb9..81413c684 100644
--- a/src/core/file_sys/xts_archive.cpp
+++ b/src/core/file_sys/xts_archive.cpp
@@ -70,14 +70,18 @@ NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
70NAX::~NAX() = default; 70NAX::~NAX() = default;
71 71
72Loader::ResultStatus NAX::Parse(std::string_view path) { 72Loader::ResultStatus NAX::Parse(std::string_view path) {
73 if (file->ReadObject(header.get()) != sizeof(NAXHeader)) 73 if (file == nullptr) {
74 return Loader::ResultStatus::ErrorNullFile;
75 }
76 if (file->ReadObject(header.get()) != sizeof(NAXHeader)) {
74 return Loader::ResultStatus::ErrorBadNAXHeader; 77 return Loader::ResultStatus::ErrorBadNAXHeader;
75 78 }
76 if (header->magic != Common::MakeMagic('N', 'A', 'X', '0')) 79 if (header->magic != Common::MakeMagic('N', 'A', 'X', '0')) {
77 return Loader::ResultStatus::ErrorBadNAXHeader; 80 return Loader::ResultStatus::ErrorBadNAXHeader;
78 81 }
79 if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size) 82 if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size) {
80 return Loader::ResultStatus::ErrorIncorrectNAXFileSize; 83 return Loader::ResultStatus::ErrorIncorrectNAXFileSize;
84 }
81 85
82 keys.DeriveSDSeedLazy(); 86 keys.DeriveSDSeedLazy();
83 std::array<Core::Crypto::Key256, 2> sd_keys{}; 87 std::array<Core::Crypto::Key256, 2> sd_keys{};
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index ab7fc7a24..62acc3720 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -474,28 +474,56 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
474 474
475void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string path) { 475void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string path) {
476 QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); 476 QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
477 QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location")); 477 QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location"));
478 QAction* open_transferable_shader_cache = 478 QAction* open_transferable_shader_cache =
479 context_menu.addAction(tr("Open Transferable Shader Cache")); 479 context_menu.addAction(tr("Open Transferable Shader Cache"));
480 context_menu.addSeparator(); 480 context_menu.addSeparator();
481 QMenu* remove_menu = context_menu.addMenu(tr("Remove"));
482 QAction* remove_update = remove_menu->addAction(tr("Remove Installed Update"));
483 QAction* remove_dlc = remove_menu->addAction(tr("Remove All Installed DLC"));
484 QAction* remove_shader_cache = remove_menu->addAction(tr("Remove Shader Cache"));
485 QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration"));
486 remove_menu->addSeparator();
487 QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents"));
481 QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); 488 QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS"));
482 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); 489 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
483 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); 490 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
484 context_menu.addSeparator(); 491 context_menu.addSeparator();
485 QAction* properties = context_menu.addAction(tr("Properties")); 492 QAction* properties = context_menu.addAction(tr("Properties"));
486 493
487 open_save_location->setEnabled(program_id != 0); 494 open_save_location->setVisible(program_id != 0);
495 open_mod_location->setVisible(program_id != 0);
496 open_transferable_shader_cache->setVisible(program_id != 0);
497 remove_update->setVisible(program_id != 0);
498 remove_dlc->setVisible(program_id != 0);
499 remove_shader_cache->setVisible(program_id != 0);
500 remove_all_content->setVisible(program_id != 0);
488 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); 501 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
489 navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0); 502 navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
490 503
491 connect(open_save_location, &QAction::triggered, [this, program_id, path]() { 504 connect(open_save_location, &QAction::triggered, [this, program_id, path]() {
492 emit OpenFolderRequested(GameListOpenTarget::SaveData, path); 505 emit OpenFolderRequested(GameListOpenTarget::SaveData, path);
493 }); 506 });
494 connect(open_lfs_location, &QAction::triggered, [this, program_id, path]() { 507 connect(open_mod_location, &QAction::triggered, [this, program_id, path]() {
495 emit OpenFolderRequested(GameListOpenTarget::ModData, path); 508 emit OpenFolderRequested(GameListOpenTarget::ModData, path);
496 }); 509 });
497 connect(open_transferable_shader_cache, &QAction::triggered, 510 connect(open_transferable_shader_cache, &QAction::triggered,
498 [this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); }); 511 [this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); });
512 connect(remove_all_content, &QAction::triggered, [this, program_id]() {
513 emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::Game);
514 });
515 connect(remove_update, &QAction::triggered, [this, program_id]() {
516 emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::Update);
517 });
518 connect(remove_dlc, &QAction::triggered, [this, program_id]() {
519 emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::AddOnContent);
520 });
521 connect(remove_shader_cache, &QAction::triggered, [this, program_id]() {
522 emit RemoveFileRequested(program_id, GameListRemoveTarget::ShaderCache);
523 });
524 connect(remove_custom_config, &QAction::triggered, [this, program_id]() {
525 emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration);
526 });
499 connect(dump_romfs, &QAction::triggered, 527 connect(dump_romfs, &QAction::triggered,
500 [this, program_id, path]() { emit DumpRomFSRequested(program_id, path); }); 528 [this, program_id, path]() { emit DumpRomFSRequested(program_id, path); });
501 connect(copy_tid, &QAction::triggered, 529 connect(copy_tid, &QAction::triggered,
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index a38cb2fc3..483835cce 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -39,6 +39,17 @@ enum class GameListOpenTarget {
39 ModData, 39 ModData,
40}; 40};
41 41
42enum class GameListRemoveTarget {
43 ShaderCache,
44 CustomConfiguration,
45};
46
47enum class InstalledEntryType {
48 Game,
49 Update,
50 AddOnContent,
51};
52
42class GameList : public QWidget { 53class GameList : public QWidget {
43 Q_OBJECT 54 Q_OBJECT
44 55
@@ -75,6 +86,8 @@ signals:
75 void ShouldCancelWorker(); 86 void ShouldCancelWorker();
76 void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path); 87 void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path);
77 void OpenTransferableShaderCacheRequested(u64 program_id); 88 void OpenTransferableShaderCacheRequested(u64 program_id);
89 void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type);
90 void RemoveFileRequested(u64 program_id, GameListRemoveTarget target);
78 void DumpRomFSRequested(u64 program_id, const std::string& game_path); 91 void DumpRomFSRequested(u64 program_id, const std::string& game_path);
79 void CopyTIDRequested(u64 program_id); 92 void CopyTIDRequested(u64 program_id);
80 void NavigateToGamedbEntryRequested(u64 program_id, 93 void NavigateToGamedbEntryRequested(u64 program_id,
@@ -117,8 +130,6 @@ private:
117 friend class GameListSearchField; 130 friend class GameListSearchField;
118}; 131};
119 132
120Q_DECLARE_METATYPE(GameListOpenTarget);
121
122class GameListPlaceholder : public QWidget { 133class GameListPlaceholder : public QWidget {
123 Q_OBJECT 134 Q_OBJECT
124public: 135public:
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 31a635176..276658c9e 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -847,6 +847,9 @@ void GMainWindow::ConnectWidgetEvents() {
847 connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); 847 connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder);
848 connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this, 848 connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this,
849 &GMainWindow::OnTransferableShaderCacheOpenFile); 849 &GMainWindow::OnTransferableShaderCacheOpenFile);
850 connect(game_list, &GameList::RemoveInstalledEntryRequested, this,
851 &GMainWindow::OnGameListRemoveInstalledEntry);
852 connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile);
850 connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); 853 connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);
851 connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); 854 connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
852 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, 855 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
@@ -1257,7 +1260,6 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str
1257 case GameListOpenTarget::SaveData: { 1260 case GameListOpenTarget::SaveData: {
1258 open_target = tr("Save Data"); 1261 open_target = tr("Save Data");
1259 const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); 1262 const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
1260 ASSERT(program_id != 0);
1261 1263
1262 if (has_user_save) { 1264 if (has_user_save) {
1263 // User save data 1265 // User save data
@@ -1322,14 +1324,12 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str
1322} 1324}
1323 1325
1324void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { 1326void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
1325 ASSERT(program_id != 0);
1326
1327 const QString shader_dir = 1327 const QString shader_dir =
1328 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)); 1328 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir));
1329 const QString tranferable_shader_cache_folder_path = 1329 const QString transferable_shader_cache_folder_path =
1330 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); 1330 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");
1331 const QString transferable_shader_cache_file_path = 1331 const QString transferable_shader_cache_file_path =
1332 tranferable_shader_cache_folder_path + QDir::separator() + 1332 transferable_shader_cache_folder_path + QDir::separator() +
1333 QString::fromStdString(fmt::format("{:016X}.bin", program_id)); 1333 QString::fromStdString(fmt::format("{:016X}.bin", program_id));
1334 1334
1335 if (!QFile::exists(transferable_shader_cache_file_path)) { 1335 if (!QFile::exists(transferable_shader_cache_file_path)) {
@@ -1350,7 +1350,7 @@ void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
1350 param << QDir::toNativeSeparators(transferable_shader_cache_file_path); 1350 param << QDir::toNativeSeparators(transferable_shader_cache_file_path);
1351 QProcess::startDetached(explorer, param); 1351 QProcess::startDetached(explorer, param);
1352#else 1352#else
1353 QDesktopServices::openUrl(QUrl::fromLocalFile(tranferable_shader_cache_folder_path)); 1353 QDesktopServices::openUrl(QUrl::fromLocalFile(transferable_shader_cache_folder_path));
1354#endif 1354#endif
1355} 1355}
1356 1356
@@ -1394,6 +1394,174 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src
1394 return true; 1394 return true;
1395} 1395}
1396 1396
1397void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) {
1398 const QString entry_type = [this, type] {
1399 switch (type) {
1400 case InstalledEntryType::Game:
1401 return tr("Contents");
1402 case InstalledEntryType::Update:
1403 return tr("Update");
1404 case InstalledEntryType::AddOnContent:
1405 return tr("DLC");
1406 default:
1407 return QString{};
1408 }
1409 }();
1410
1411 if (QMessageBox::question(
1412 this, tr("Remove Entry"), tr("Remove Installed Game %1?").arg(entry_type),
1413 QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) {
1414 return;
1415 }
1416
1417 switch (type) {
1418 case InstalledEntryType::Game:
1419 RemoveBaseContent(program_id, entry_type);
1420 [[fallthrough]];
1421 case InstalledEntryType::Update:
1422 RemoveUpdateContent(program_id, entry_type);
1423 if (type != InstalledEntryType::Game) {
1424 break;
1425 }
1426 [[fallthrough]];
1427 case InstalledEntryType::AddOnContent:
1428 RemoveAddOnContent(program_id, entry_type);
1429 break;
1430 }
1431 FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
1432 "game_list");
1433 game_list->PopulateAsync(UISettings::values.game_dirs);
1434}
1435
1436void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) {
1437 const auto& fs_controller = Core::System::GetInstance().GetFileSystemController();
1438 const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) ||
1439 fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id);
1440
1441 if (res) {
1442 QMessageBox::information(this, tr("Successfully Removed"),
1443 tr("Successfully removed the installed base game."));
1444 } else {
1445 QMessageBox::warning(
1446 this, tr("Error Removing %1").arg(entry_type),
1447 tr("The base game is not installed in the NAND and cannot be removed."));
1448 }
1449}
1450
1451void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type) {
1452 const auto update_id = program_id | 0x800;
1453 const auto& fs_controller = Core::System::GetInstance().GetFileSystemController();
1454 const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) ||
1455 fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id);
1456
1457 if (res) {
1458 QMessageBox::information(this, tr("Successfully Removed"),
1459 tr("Successfully removed the installed update."));
1460 } else {
1461 QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type),
1462 tr("There is no update installed for this title."));
1463 }
1464}
1465
1466void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) {
1467 u32 count{};
1468 const auto& fs_controller = Core::System::GetInstance().GetFileSystemController();
1469 const auto dlc_entries = Core::System::GetInstance().GetContentProvider().ListEntriesFilter(
1470 FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
1471
1472 for (const auto& entry : dlc_entries) {
1473 if ((entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id) {
1474 const auto res =
1475 fs_controller.GetUserNANDContents()->RemoveExistingEntry(entry.title_id) ||
1476 fs_controller.GetSDMCContents()->RemoveExistingEntry(entry.title_id);
1477 if (res) {
1478 ++count;
1479 }
1480 }
1481 }
1482
1483 if (count == 0) {
1484 QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type),
1485 tr("There are no DLC installed for this title."));
1486 return;
1487 }
1488
1489 QMessageBox::information(this, tr("Successfully Removed"),
1490 tr("Successfully removed %1 installed DLC.").arg(count));
1491}
1492
1493void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target) {
1494 const QString question = [this, target] {
1495 switch (target) {
1496 case GameListRemoveTarget::ShaderCache:
1497 return tr("Delete Transferable Shader Cache?");
1498 case GameListRemoveTarget::CustomConfiguration:
1499 return tr("Remove Custom Game Configuration?");
1500 default:
1501 return QString{};
1502 }
1503 }();
1504
1505 if (QMessageBox::question(this, tr("Remove File"), question, QMessageBox::Yes | QMessageBox::No,
1506 QMessageBox::No) != QMessageBox::Yes) {
1507 return;
1508 }
1509
1510 switch (target) {
1511 case GameListRemoveTarget::ShaderCache:
1512 RemoveTransferableShaderCache(program_id);
1513 break;
1514 case GameListRemoveTarget::CustomConfiguration:
1515 RemoveCustomConfiguration(program_id);
1516 break;
1517 }
1518}
1519
1520void GMainWindow::RemoveTransferableShaderCache(u64 program_id) {
1521 const QString shader_dir =
1522 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir));
1523 const QString transferable_shader_cache_folder_path =
1524 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");
1525 const QString transferable_shader_cache_file_path =
1526 transferable_shader_cache_folder_path + QDir::separator() +
1527 QString::fromStdString(fmt::format("{:016X}.bin", program_id));
1528
1529 if (!QFile::exists(transferable_shader_cache_file_path)) {
1530 QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"),
1531 tr("A shader cache for this title does not exist."));
1532 return;
1533 }
1534
1535 if (QFile::remove(transferable_shader_cache_file_path)) {
1536 QMessageBox::information(this, tr("Successfully Removed"),
1537 tr("Successfully removed the transferable shader cache."));
1538 } else {
1539 QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"),
1540 tr("Failed to remove the transferable shader cache."));
1541 }
1542}
1543
1544void GMainWindow::RemoveCustomConfiguration(u64 program_id) {
1545 const QString config_dir =
1546 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir));
1547 const QString custom_config_file_path =
1548 config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id));
1549
1550 if (!QFile::exists(custom_config_file_path)) {
1551 QMessageBox::warning(this, tr("Error Removing Custom Configuration"),
1552 tr("A custom configuration for this title does not exist."));
1553 return;
1554 }
1555
1556 if (QFile::remove(custom_config_file_path)) {
1557 QMessageBox::information(this, tr("Successfully Removed"),
1558 tr("Successfully removed the custom game configuration."));
1559 } else {
1560 QMessageBox::warning(this, tr("Error Removing Custom Configuration"),
1561 tr("Failed to remove the custom game configuration."));
1562 }
1563}
1564
1397void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { 1565void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {
1398 const auto failed = [this] { 1566 const auto failed = [this] {
1399 QMessageBox::warning(this, tr("RomFS Extraction Failed!"), 1567 QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
@@ -1714,9 +1882,9 @@ void GMainWindow::OnMenuInstallToNAND() {
1714 : tr("%n file(s) failed to install\n", "", failed_files.size())); 1882 : tr("%n file(s) failed to install\n", "", failed_files.size()));
1715 1883
1716 QMessageBox::information(this, tr("Install Results"), install_results); 1884 QMessageBox::information(this, tr("Install Results"), install_results);
1717 game_list->PopulateAsync(UISettings::values.game_dirs);
1718 FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + 1885 FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
1719 "game_list"); 1886 "game_list");
1887 game_list->PopulateAsync(UISettings::values.game_dirs);
1720 ui.action_Install_File_NAND->setEnabled(true); 1888 ui.action_Install_File_NAND->setEnabled(true);
1721} 1889}
1722 1890
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index db573d606..73a44a3bf 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -32,6 +32,8 @@ class QPushButton;
32class QProgressDialog; 32class QProgressDialog;
33class WaitTreeWidget; 33class WaitTreeWidget;
34enum class GameListOpenTarget; 34enum class GameListOpenTarget;
35enum class GameListRemoveTarget;
36enum class InstalledEntryType;
35class GameListPlaceholder; 37class GameListPlaceholder;
36 38
37namespace Core::Frontend { 39namespace Core::Frontend {
@@ -198,6 +200,8 @@ private slots:
198 void OnGameListLoadFile(QString game_path); 200 void OnGameListLoadFile(QString game_path);
199 void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); 201 void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path);
200 void OnTransferableShaderCacheOpenFile(u64 program_id); 202 void OnTransferableShaderCacheOpenFile(u64 program_id);
203 void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type);
204 void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target);
201 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); 205 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path);
202 void OnGameListCopyTID(u64 program_id); 206 void OnGameListCopyTID(u64 program_id);
203 void OnGameListNavigateToGamedbEntry(u64 program_id, 207 void OnGameListNavigateToGamedbEntry(u64 program_id,
@@ -229,6 +233,11 @@ private slots:
229 void OnLanguageChanged(const QString& locale); 233 void OnLanguageChanged(const QString& locale);
230 234
231private: 235private:
236 void RemoveBaseContent(u64 program_id, const QString& entry_type);
237 void RemoveUpdateContent(u64 program_id, const QString& entry_type);
238 void RemoveAddOnContent(u64 program_id, const QString& entry_type);
239 void RemoveTransferableShaderCache(u64 program_id);
240 void RemoveCustomConfiguration(u64 program_id);
232 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); 241 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
233 InstallResult InstallNSPXCI(const QString& filename); 242 InstallResult InstallNSPXCI(const QString& filename);
234 InstallResult InstallNCA(const QString& filename); 243 InstallResult InstallNCA(const QString& filename);