From df9275819e1a42ccd039d538c02805be1a5bf383 Mon Sep 17 00:00:00 2001 From: Antonino Di Guardo <64427768+digant73@users.noreply.github.com> Date: Fri, 20 Sep 2024 08:46:51 +0200 Subject: [PATCH] Added reconciliation functions for game list file (games.yml) (#16061) --- rpcs3/Emu/System.cpp | 4 +- rpcs3/Emu/System.h | 2 +- rpcs3/rpcs3qt/game_list_frame.cpp | 28 +++++-- rpcs3/rpcs3qt/main_window.cpp | 120 ++++++++++++++++++++++++++---- rpcs3/rpcs3qt/main_window.h | 6 +- rpcs3/rpcs3qt/main_window.ui | 34 +++++++-- 6 files changed, 160 insertions(+), 34 deletions(-) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 2ac1622a1f..bd13f437db 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -4082,7 +4082,7 @@ game_boot_result Emulator::AddGameToYml(const std::string& path) return game_boot_result::invalid_file_or_folder; } -u32 Emulator::RemoveGames(const std::vector& title_id_list) +u32 Emulator::RemoveGames(const std::vector& title_id_list, bool save_on_disk) { if (title_id_list.empty()) { @@ -4103,7 +4103,7 @@ u32 Emulator::RemoveGames(const std::vector& title_id_list) m_games_config.set_save_on_dirty(true); - if (m_games_config.is_dirty() && !m_games_config.save()) + if (save_on_disk && m_games_config.is_dirty() && !m_games_config.save()) { sys_log.error("Failed to save games.yml after removing games"); } diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 8babaf9844..51e1e96654 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -379,7 +379,7 @@ public: u32 AddGamesFromDir(const std::string& path); game_boot_result AddGame(const std::string& path); game_boot_result AddGameToYml(const std::string& path); - u32 RemoveGames(const std::vector& title_id_list); + u32 RemoveGames(const std::vector& title_id_list, bool save_on_disk = true); game_boot_result RemoveGameFromYml(const std::string& title_id); // Check if path is inside the specified directory diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 473f9b1463..609871926d 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -425,13 +425,27 @@ void game_list_frame::Refresh(const bool from_drive, const std::vectorSetValue(0); } - // Remove the specified serials (title id) in "games.yml" file (if any) - Emu.RemoveGames(serials_to_remove_from_yml); - const std::string games_dir = rpcs3::utils::get_games_dir(); - const u32 games_added = Emu.AddGamesFromDir(games_dir); - if (games_added) + // List of serials (title id) to remove in "games.yml" file (if any) + std::vector serials_to_remove = serials_to_remove_from_yml; // Initialize the list with the specified serials (if any) + + // Scan game list to detect the titles belonging to auto-detection "games" folder + for (const auto& [serial, path] : Emu.GetGamesConfig().get_games()) // Loop on game list file + { + // NOTE: Used starts_with(games_dir) instead of Emu.IsPathInsideDir(path, games_dir) due the latter would check also the existence of the paths + // + if (path.starts_with(games_dir)) // If game path belongs to auto-detection "games" folder, add the serial to the removal list + { + serials_to_remove.push_back(serial); + } + } + + // Remove the specified and detected serials (title id) only from the game list in memory (not yet in "games.yml" file) + Emu.RemoveGames(serials_to_remove, false); + + // Scan auto-detection "games" folder adding the detected titles to the game list plus flushing also all the other changes in "games.yml" file + if (const u32 games_added = Emu.AddGamesFromDir(games_dir); games_added != 0) { game_list_log.notice("Refresh added %d new entries found in %s", games_added, games_dir); } @@ -1721,7 +1735,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) { u64 total_data_size = 0; - text += tr("\nData Info:\n"); + text += tr("\n%0 Info:\n").arg(is_disc_game ? tr("Game Data") : gameinfo->localized_category); for (const std::string& data_dir : data_dir_list) { @@ -1740,7 +1754,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) } } - if (fs::device_stat stat{}; fs::statfs(rpcs3::utils::get_hdd0_dir(), stat)) // retrieve disk space info on data path's drive + if (fs::device_stat stat{}; fs::statfs(rpcs3::utils::get_hdd0_dir(), stat)) // Retrieve disk space info on data path's drive { text += tr("\nCurrent free disk space: %0\n").arg(gui::utils::format_byte_size(stat.avail_free)); } diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index c6c2b9be47..47ab0d069a 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -1980,10 +1980,13 @@ void main_window::OnEmuStop() #endif } + ui->batchRemoveShaderCachesAct->setEnabled(true); ui->batchRemovePPUCachesAct->setEnabled(true); ui->batchRemoveSPUCachesAct->setEnabled(true); - ui->batchRemoveShaderCachesAct->setEnabled(true); - ui->removeDiskCacheAct->setEnabled(true); + ui->removeHDD1CachesAct->setEnabled(true); + ui->removeAllCachesAct->setEnabled(true); + ui->removeSavestatesAct->setEnabled(true); + ui->cleanupGameListAct->setEnabled(true); ui->actionManage_Users->setEnabled(true); ui->confCamerasAct->setEnabled(true); @@ -2030,10 +2033,13 @@ void main_window::OnEmuReady() const ui->actionManage_Users->setEnabled(false); ui->confCamerasAct->setEnabled(false); + ui->batchRemoveShaderCachesAct->setEnabled(false); ui->batchRemovePPUCachesAct->setEnabled(false); ui->batchRemoveSPUCachesAct->setEnabled(false); - ui->batchRemoveShaderCachesAct->setEnabled(false); - ui->removeDiskCacheAct->setEnabled(false); + ui->removeHDD1CachesAct->setEnabled(false); + ui->removeAllCachesAct->setEnabled(false); + ui->removeSavestatesAct->setEnabled(false); + ui->cleanupGameListAct->setEnabled(false); } void main_window::EnableMenus(bool enabled) const @@ -2680,13 +2686,15 @@ void main_window::CreateConnects() connect(ui->exitAct, &QAction::triggered, this, &QWidget::close); connect(ui->batchCreateCPUCachesAct, &QAction::triggered, m_game_list_frame, [list = m_game_list_frame]() { list->BatchCreateCPUCaches(); }); - connect(ui->batchRemovePPUCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchRemovePPUCaches); - connect(ui->batchRemoveSPUCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchRemoveSPUCaches); - connect(ui->batchRemoveShaderCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchRemoveShaderCaches); connect(ui->batchRemoveCustomConfigurationsAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchRemoveCustomConfigurations); connect(ui->batchRemoveCustomPadConfigurationsAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchRemoveCustomPadConfigurations); - - connect(ui->removeDiskCacheAct, &QAction::triggered, this, &main_window::RemoveDiskCache); + connect(ui->batchRemoveShaderCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchRemoveShaderCaches); + connect(ui->batchRemovePPUCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchRemovePPUCaches); + connect(ui->batchRemoveSPUCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchRemoveSPUCaches); + connect(ui->removeHDD1CachesAct, &QAction::triggered, this, &main_window::RemoveHDD1Caches); + connect(ui->removeAllCachesAct, &QAction::triggered, this, &main_window::RemoveAllCaches); + connect(ui->removeSavestatesAct, &QAction::triggered, this, &main_window::RemoveSavestates); + connect(ui->cleanupGameListAct, &QAction::triggered, this, &main_window::CleanupGameList); connect(ui->removeFirmwareCacheAct, &QAction::triggered, this, &main_window::RemoveFirmwareCache); connect(ui->createFirmwareCacheAct, &QAction::triggered, this, &main_window::CreateFirmwareCache); @@ -3531,20 +3539,102 @@ void main_window::SetIconSizeActions(int idx) const ui->setIconSizeLargeAct->setChecked(true); } -void main_window::RemoveDiskCache() +void main_window::RemoveHDD1Caches() { - const std::string cache_dir = rpcs3::utils::get_hdd1_dir() + "/caches"; - - if (fs::remove_all(cache_dir, false)) + if (fs::remove_all(rpcs3::utils::get_hdd1_dir() + "caches", false)) { - QMessageBox::information(this, tr("Cache Cleared"), tr("Disk cache was cleared successfully")); + QMessageBox::information(this, tr("HDD1 Caches Removed"), tr("HDD1 caches successfully removed")); } else { - QMessageBox::warning(this, tr("Error"), tr("Could not remove disk cache")); + QMessageBox::warning(this, tr("Error"), tr("Could not remove HDD1 caches")); } } +void main_window::RemoveAllCaches() +{ + if (QMessageBox::question(this, tr("Confirm Removal"), tr("Remove all caches?")) != QMessageBox::Yes) + return; + + const std::string cache_base_dir = rpcs3::utils::get_cache_dir(); + u64 caches_count = 0; + u64 caches_removed = 0; + + for (const game_info& game : m_game_list_frame->GetGameInfo()) // Loop on detected games + { + if (game && qstr(game->info.category) != cat::cat_ps3_os && fs::exists(cache_base_dir + game->info.serial)) // If not OS category and cache exists + { + caches_count++; + + if (fs::remove_all(cache_base_dir + game->info.serial)) + { + caches_removed++; + } + } + } + + if (caches_count == caches_removed) + { + QMessageBox::information(this, tr("Caches Removed"), tr("%0 cache(s) successfully removed").arg(caches_removed)); + } + else + { + QMessageBox::warning(this, tr("Error"), tr("Could not remove %0 of %1 cache(s)").arg(caches_count - caches_removed).arg(caches_count)); + } + + RemoveHDD1Caches(); +} + +void main_window::RemoveSavestates() +{ + if (QMessageBox::question(this, tr("Confirm Removal"), tr("Remove savestates?")) != QMessageBox::Yes) + return; + + if (fs::remove_all(fs::get_config_dir() + "savestates", false)) + { + QMessageBox::information(this, tr("Savestates Removed"), tr("Savestates successfully removed")); + } + else + { + QMessageBox::warning(this, tr("Error"), tr("Could not remove savestates")); + } +} + +void main_window::CleanupGameList() +{ + if (QMessageBox::question(this, tr("Confirm Removal"), tr("Remove invalid game paths from game list?\n" + "Undetectable games (zombies) as well as corrupted games will be removed from the game list file (games.yml)")) != QMessageBox::Yes) + { + return; + } + + // List of serials (title id) to remove in "games.yml" file (if any) + std::vector serials_to_remove_from_yml{}; + + for (const auto& [serial, path] : Emu.GetGamesConfig().get_games()) // Loop on game list file + { + bool found = false; + + for (const game_info& game : m_game_list_frame->GetGameInfo()) // Loop on detected games + { + // If Disc Game and its serial is found in game list file + if (game && qstr(game->info.category) == cat::cat_disc_game && game->info.serial == serial) + { + found = true; + break; + } + } + + if (!found) // If serial not found, add it to the removal list + { + serials_to_remove_from_yml.push_back(serial); + } + } + + // Remove the found serials (title id) in "games.yml" file (if any) + QMessageBox::information(this, tr("Summary"), tr("%0 game(s) removed from game list").arg(Emu.RemoveGames(serials_to_remove_from_yml))); +} + void main_window::RemoveFirmwareCache() { const std::string cache_dir = rpcs3::utils::get_cache_dir(); diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index d68e746b77..7bad86b653 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -128,7 +128,11 @@ private Q_SLOTS: void SetIconSizeActions(int idx) const; void ResizeIcons(int index); - void RemoveDiskCache(); + void RemoveHDD1Caches(); + void RemoveAllCaches(); + void RemoveSavestates(); + void CleanupGameList(); + void RemoveFirmwareCache(); void CreateFirmwareCache(); diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index 739e2e87a8..daa567540b 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -186,11 +186,14 @@ + - + + + - + @@ -1128,6 +1131,16 @@ Remove Custom Configurations + + + Remove Custom Pad Configurations + + + + + Remove Shader Caches + + Remove PPU Caches @@ -1138,19 +1151,24 @@ Remove SPU Caches - + - Remove Shader Caches + Remove HDD1 Caches - + - Remove Custom Pad Configurations + Remove All Caches - + - Remove Disk Cache + Remove Savestates + + + + + Clean up Game List