From 8a4778ba8c2d7037d6ad67bfe969d8ec24df1d73 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sun, 6 Jan 2019 22:32:23 +0100 Subject: [PATCH] Qt: add some batch operations --- rpcs3/rpcs3qt/game_list_frame.cpp | 320 ++++++++++++++++++++++++++++-- rpcs3/rpcs3qt/game_list_frame.h | 17 +- rpcs3/rpcs3qt/main_window.cpp | 11 +- rpcs3/rpcs3qt/main_window.ui | 46 ++++- rpcs3/rpcs3qt/progress_dialog.cpp | 6 + 5 files changed, 363 insertions(+), 37 deletions(-) diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 2956f299a2..04cd8a55c3 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -355,6 +355,16 @@ void game_list_frame::SortGameList() m_gameList->resizeColumnToContents(gui::column_count - 1); } +std::string game_list_frame::GetCacheDirBySerial(const std::string& serial) +{ + return fs::get_config_dir() + "cache/" + serial; +} + +std::string game_list_frame::GetDataDirBySerial(const std::string& serial) +{ + return fs::get_config_dir() + "data/" + serial; +} + void game_list_frame::Refresh(const bool fromDrive, const bool scrollAfter) { if (fromDrive) @@ -462,8 +472,8 @@ void game_list_frame::Refresh(const bool fromDrive, const bool scrollAfter) const auto compat = m_game_compat->GetCompatibility(game.serial); const bool hasCustomConfig = fs::is_file(Emu.GetCustomConfigPath(game.serial)) || fs::is_file(Emu.GetCustomConfigPath(game.serial, true)); - const QColor color = getGridCompatibilityColor(compat.color); + const QColor color = getGridCompatibilityColor(compat.color); const QPixmap pxmap = PaintedPixmap(img, hasCustomConfig, color); m_game_data.push_back(game_info(new gui_game_info{ game, compat, img, pxmap, hasCustomConfig })); @@ -655,8 +665,8 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) const QString serial = qstr(currGame.serial); const QString name = qstr(currGame.name).simplified(); - const std::string cache_base_dir = fs::get_cache_dir() + "cache/" + currGame.serial; - const std::string data_base_dir = fs::get_config_dir() + "data/" + currGame.serial; + const std::string cache_base_dir = GetCacheDirBySerial(currGame.serial); + const std::string data_base_dir = GetDataDirBySerial(currGame.serial); // Make Actions QMenu myMenu; @@ -774,10 +784,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) }); connect(createPPUCache, &QAction::triggered, [=] { - Emu.SetForceBoot(true); - Emu.Stop(); - Emu.SetForceBoot(true); - Emu.BootGame(currGame.path, true); + CreatePPUCache(currGame.path); }); connect(removeGame, &QAction::triggered, [=] { @@ -902,16 +909,34 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) myMenu.exec(globalPos); } +bool game_list_frame::CreatePPUCache(const std::string& path) +{ + Emu.SetForceBoot(true); + Emu.Stop(); + Emu.SetForceBoot(true); + const bool success = Emu.BootGame(path, true); + + if (success) + { + LOG_WARNING(GENERAL, "Creating PPU Cache for %s", path); + } + else + { + LOG_ERROR(GENERAL, "Could not create PPU Cache for %s", path); + } + return success; +} + bool game_list_frame::RemoveCustomConfiguration(const std::string& title_id, bool is_interactive) { const std::string config_path_new = Emu.GetCustomConfigPath(title_id); const std::string config_path_old = Emu.GetCustomConfigPath(title_id, true); if (!fs::is_file(config_path_new) && !fs::is_file(config_path_old)) - return false; + return true; if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), tr("Remove custom game configuration?")) != QMessageBox::Yes) - return false; + return true; bool result = true; @@ -943,10 +968,10 @@ bool game_list_frame::RemoveCustomConfiguration(const std::string& title_id, boo bool game_list_frame::RemoveShadersCache(const std::string& base_dir, bool is_interactive) { if (!fs::is_dir(base_dir)) - return false; + return true; if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), tr("Remove shaders cache?")) != QMessageBox::Yes) - return false; + return true; QDirIterator dir_iter(qstr(base_dir), QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); while (dir_iter.hasNext()) @@ -956,23 +981,31 @@ bool game_list_frame::RemoveShadersCache(const std::string& base_dir, bool is_in if (dir_iter.fileName() == "shaders_cache") { if (QDir(filepath).removeRecursively()) + { LOG_NOTICE(GENERAL, "Removed shaders cache dir: %s", sstr(filepath)); + } else - LOG_WARNING(GENERAL, "Could not remove shaders cache file: %s", sstr(filepath)); + { + LOG_FATAL(GENERAL, "Could not completely remove shaders cache file: %s", sstr(filepath)); + return false; + } + break; } } - LOG_SUCCESS(GENERAL, "Removed shaders cache in %s", base_dir); return true; } bool game_list_frame::RemovePPUCache(const std::string& base_dir, bool is_interactive) { if (!fs::is_dir(base_dir)) - return false; + return true; if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), tr("Remove PPU cache?")) != QMessageBox::Yes) - return false; + return true; + + u32 files_removed = 0; + u32 files_total = 0; QDirIterator dir_iter(qstr(base_dir), QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); while (dir_iter.hasNext()) @@ -982,23 +1015,38 @@ bool game_list_frame::RemovePPUCache(const std::string& base_dir, bool is_intera if (dir_iter.fileInfo().absoluteFilePath().endsWith(".obj", Qt::CaseInsensitive)) { if (QFile::remove(filepath)) + { + ++files_removed; LOG_NOTICE(GENERAL, "Removed PPU cache file: %s", sstr(filepath)); + } else + { LOG_WARNING(GENERAL, "Could not remove PPU cache file: %s", sstr(filepath)); + } + ++files_total; } } - LOG_SUCCESS(GENERAL, "Removed PPU cache in %s", base_dir); - return true; + const bool success = files_total == files_removed; + + if (success) + LOG_SUCCESS(GENERAL, "Removed PPU cache in %s", base_dir); + else + LOG_FATAL(GENERAL, "Only %d/%d PPU cache files could be removed in %s", files_removed, files_total, base_dir); + + return success; } bool game_list_frame::RemoveSPUCache(const std::string& base_dir, bool is_interactive) { if (!fs::is_dir(base_dir)) - return false; + return true; if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), tr("Remove SPU cache?")) != QMessageBox::Yes) - return false; + return true; + + u32 files_removed = 0; + u32 files_total = 0; QDirIterator dir_iter(qstr(base_dir), QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); while (dir_iter.hasNext()) @@ -1008,14 +1056,244 @@ bool game_list_frame::RemoveSPUCache(const std::string& base_dir, bool is_intera if (dir_iter.fileInfo().absoluteFilePath().endsWith(".dat", Qt::CaseInsensitive)) { if (QFile::remove(filepath)) + { + ++files_removed; LOG_NOTICE(GENERAL, "Removed SPU cache file: %s", sstr(filepath)); + } else + { LOG_WARNING(GENERAL, "Could not remove SPU cache file: %s", sstr(filepath)); + } + ++files_total; } } - LOG_SUCCESS(GENERAL, "Removed SPU cache in %s", base_dir); - return true; + const bool success = files_total == files_removed; + + if (success) + LOG_SUCCESS(GENERAL, "Removed SPU cache in %s", base_dir); + else + LOG_FATAL(GENERAL, "Only %d/%d SPU cache files could be removed in %s", files_removed, files_total, base_dir); + + return success; +} + +void game_list_frame::BatchCreatePPUCaches() +{ + std::set paths; + for (const auto& game : m_game_data) + { + paths.emplace(game->info.path); + } + const u32 total = paths.size(); + + if (total == 0) + { + QMessageBox::information(this, tr("PPU Cache Batch Creation"), tr("No titles found"), QMessageBox::Ok); + return; + } + + progress_dialog* pdlg = new progress_dialog(tr("Creating all PPU caches"), tr("Cancel"), 0, m_game_data.size(), this); + pdlg->setWindowTitle(tr("PPU Cache Batch Creation")); + pdlg->setAutoClose(false); + pdlg->setAutoReset(false); + pdlg->show(); + + u32 created = 0; + for (const auto& path : paths) + { + if (pdlg->wasCanceled()) + { + LOG_NOTICE(GENERAL, "PPU Cache Batch Creation was canceled"); + break; + } + QApplication::processEvents(); + + if (CreatePPUCache(path)) + { + while (!Emu.IsStopped()) + { + QApplication::processEvents(); + } + pdlg->SetValue(++created); + } + } + + pdlg->setLabelText(tr("Created PPU Caches for %0 titles").arg(created)); + pdlg->setCancelButtonText(tr("OK")); + QApplication::beep(); +} + +void game_list_frame::BatchRemovePPUCaches() +{ + std::set serials; + for (const auto& game : m_game_data) + { + serials.emplace(game->info.serial); + } + const u32 total = serials.size(); + + if (total == 0) + { + QMessageBox::information(this, tr("PPU Cache Batch Removal"), tr("No files found"), QMessageBox::Ok); + return; + } + + progress_dialog* pdlg = new progress_dialog(tr("Removing all PPU caches"), tr("Cancel"), 0, total, this); + pdlg->setWindowTitle(tr("PPU Cache Batch Removal")); + pdlg->setAutoClose(false); + pdlg->setAutoReset(false); + pdlg->show(); + + u32 removed = 0; + for (const auto& serial : serials) + { + if (pdlg->wasCanceled()) + { + LOG_NOTICE(GENERAL, "PPU Cache Batch Removal was canceled"); + break; + } + QApplication::processEvents(); + + if (RemovePPUCache(GetCacheDirBySerial(serial))) + { + pdlg->SetValue(++removed); + } + } + + pdlg->setLabelText(tr("%0/%1 caches cleared").arg(removed).arg(total)); + pdlg->setCancelButtonText(tr("OK")); + QApplication::beep(); +} + +void game_list_frame::BatchRemoveSPUCaches() +{ + std::set serials; + for (const auto& game : m_game_data) + { + serials.emplace(game->info.serial); + } + const u32 total = serials.size(); + + if (total == 0) + { + QMessageBox::information(this, tr("SPU Cache Batch Removal"), tr("No files found"), QMessageBox::Ok); + return; + } + + progress_dialog* pdlg = new progress_dialog(tr("Removing all SPU caches"), tr("Cancel"), 0, total, this); + pdlg->setWindowTitle(tr("SPU Cache Batch Removal")); + pdlg->setAutoClose(false); + pdlg->setAutoReset(false); + pdlg->show(); + + u32 removed = 0; + for (const auto& serial : serials) + { + if (pdlg->wasCanceled()) + { + LOG_NOTICE(GENERAL, "SPU Cache Batch Removal was canceled. %d/%d folders cleared", removed, total); + break; + } + QApplication::processEvents(); + + if (RemoveSPUCache(GetCacheDirBySerial(serial))) + { + pdlg->SetValue(++removed); + } + } + + pdlg->setLabelText(tr("%0/%1 caches cleared").arg(removed).arg(total)); + pdlg->setCancelButtonText(tr("OK")); + QApplication::beep(); +} + +void game_list_frame::BatchRemoveCustomConfigurations() +{ + std::set serials; + for (const auto& game : m_game_data) + { + if (game->hasCustomConfig && !serials.count(game->info.serial)) + { + serials.emplace(game->info.serial); + } + } + const u32 total = serials.size(); + + if (total == 0) + { + QMessageBox::information(this, tr("Custom Configuration Batch Removal"), tr("No files found"), QMessageBox::Ok); + return; + } + + progress_dialog* pdlg = new progress_dialog(tr("Removing all custom configurations"), tr("Cancel"), 0, total, this); + pdlg->setWindowTitle(tr("Custom Configuration Batch Removal")); + pdlg->setAutoClose(false); + pdlg->setAutoReset(false); + pdlg->show(); + + u32 removed = 0; + for (const auto& serial : serials) + { + if (pdlg->wasCanceled()) + { + LOG_NOTICE(GENERAL, "Custom Configuration Batch Removal was canceled. %d/%d custom configurations cleared", removed, total); + break; + } + QApplication::processEvents(); + + if (RemoveCustomConfiguration(serial)) + { + pdlg->SetValue(++removed); + } + } + + pdlg->setLabelText(tr("%0/%1 custom configurations cleared").arg(removed).arg(total)); + pdlg->setCancelButtonText(tr("OK")); + QApplication::beep(); + Refresh(true); +} + +void game_list_frame::BatchRemoveShaderCaches() +{ + std::set serials; + for (const auto& game : m_game_data) + { + serials.emplace(game->info.serial); + } + const u32 total = serials.size(); + + if (total == 0) + { + QMessageBox::information(this, tr("Shader Cache Batch Removal"), tr("No files found"), QMessageBox::Ok); + return; + } + + progress_dialog* pdlg = new progress_dialog(tr("Removing all shader caches"), tr("Cancel"), 0, total, this); + pdlg->setWindowTitle(tr("Shader Cache Batch Removal")); + pdlg->setAutoClose(false); + pdlg->setAutoReset(false); + pdlg->show(); + + u32 removed = 0; + for (const auto& serial : serials) + { + if (pdlg->wasCanceled()) + { + LOG_NOTICE(GENERAL, "Shader Cache Batch Removal was canceled"); + break; + } + QApplication::processEvents(); + + if (RemoveShadersCache(GetCacheDirBySerial(serial))) + { + pdlg->SetValue(++removed); + } + } + + pdlg->setLabelText(tr("%0/%1 shader caches cleared").arg(removed).arg(total)); + pdlg->setCancelButtonText(tr("OK")); + QApplication::beep(); } QPixmap game_list_frame::PaintedPixmap(const QImage& img, bool paint_config_icon, const QColor& compatibility_color) diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h index ee99d6825b..6d415eb5dc 100644 --- a/rpcs3/rpcs3qt/game_list_frame.h +++ b/rpcs3/rpcs3qt/game_list_frame.h @@ -211,15 +211,16 @@ public: void SetShowHidden(bool show); public Q_SLOTS: + void BatchCreatePPUCaches(); + void BatchRemovePPUCaches(); + void BatchRemoveSPUCaches(); + void BatchRemoveCustomConfigurations(); + void BatchRemoveShaderCaches(); void SetListMode(const bool& isList); void SetSearchText(const QString& text); void SetShowCompatibilityInGrid(bool show); private Q_SLOTS: - bool RemoveCustomConfiguration(const std::string& base_dir, bool is_interactive = false); - bool RemoveShadersCache(const std::string& base_dir, bool is_interactive = false); - bool RemovePPUCache(const std::string& base_dir, bool is_interactive = false); - bool RemoveSPUCache(const std::string& base_dir, bool is_interactive = false); void OnColClicked(int col); void ShowContextMenu(const QPoint &pos); void doubleClickedSlot(QTableWidgetItem *item); @@ -243,6 +244,14 @@ private: int PopulateGameList(); bool SearchMatchesApp(const std::string& name, const std::string& serial); + bool RemoveCustomConfiguration(const std::string& base_dir, bool is_interactive = false); + bool RemoveShadersCache(const std::string& base_dir, bool is_interactive = false); + bool RemovePPUCache(const std::string& base_dir, bool is_interactive = false); + bool RemoveSPUCache(const std::string& base_dir, bool is_interactive = false); + bool CreatePPUCache(const std::string& path); + + std::string GetCacheDirBySerial(const std::string& serial); + std::string GetDataDirBySerial(const std::string& serial); std::string CurrentSelectionIconPath(); std::string GetStringFromU32(const u32& key, const std::map& map, bool combined = false); diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index b3a4627fff..f75d645a9e 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -465,8 +465,6 @@ void main_window::InstallPkg(const QString& dropPath, bool is_bulk) progress_dialog pdlg(tr("Installing package ... please wait ..."), tr("Cancel"), 0, 1000, this); pdlg.setWindowTitle(tr("RPCS3 Package Installer")); - pdlg.setWindowModality(Qt::WindowModal); - pdlg.setFixedWidth(QLabel("This is the very length of the progressdialog due to hidpi reasons.").sizeHint().width()); pdlg.show(); // Synchronization variable @@ -582,8 +580,6 @@ void main_window::InstallPup(const QString& dropPath) progress_dialog pdlg(tr("Installing firmware version %1\nPlease wait...").arg(qstr(version_string)), tr("Cancel"), 0, static_cast(updatefilenames.size()), this); pdlg.setWindowTitle(tr("RPCS3 Firmware Installer")); - pdlg.setWindowModality(Qt::WindowModal); - pdlg.setFixedWidth(QLabel("This is the very length of the progressdialog due to hidpi reasons.").sizeHint().width()); pdlg.show(); // Synchronization variable @@ -1242,6 +1238,13 @@ void main_window::CreateConnects() connect(ui->bootInstallPkgAct, &QAction::triggered, [this] {InstallPkg(); }); connect(ui->bootInstallPupAct, &QAction::triggered, [this] {InstallPup(); }); connect(ui->exitAct, &QAction::triggered, this, &QWidget::close); + + connect(ui->batchCreatePPUCachesAct, &QAction::triggered, m_gameListFrame, &game_list_frame::BatchCreatePPUCaches); + connect(ui->batchRemovePPUCachesAct, &QAction::triggered, m_gameListFrame, &game_list_frame::BatchRemovePPUCaches); + connect(ui->batchRemoveSPUCachesAct, &QAction::triggered, m_gameListFrame, &game_list_frame::BatchRemoveSPUCaches); + connect(ui->batchRemoveShaderCachesAct, &QAction::triggered, m_gameListFrame, &game_list_frame::BatchRemoveShaderCaches); + connect(ui->batchRemoveCustomConfigurationsAct, &QAction::triggered, m_gameListFrame, &game_list_frame::BatchRemoveCustomConfigurations); + connect(ui->sysPauseAct, &QAction::triggered, this, &main_window::OnPlayOrPause); connect(ui->sysStopAct, &QAction::triggered, [=]() { Emu.Stop(); }); connect(ui->sysRebootAct, &QAction::triggered, [=]() { Emu.Restart(); }); diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index ea74891e42..c3e6967705 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -165,6 +165,17 @@ + + + All Titles + + + + + + + + @@ -174,6 +185,8 @@ + + @@ -931,14 +944,6 @@ Other - - - true - - - Show Game Tool Bar - - true @@ -965,6 +970,31 @@ Show Title Bars + + + Create PPU Caches + + + + + Remove Custom Configurations + + + + + Remove PPU Caches + + + + + Remove SPU Caches + + + + + Remove Shader Caches + + diff --git a/rpcs3/rpcs3qt/progress_dialog.cpp b/rpcs3/rpcs3qt/progress_dialog.cpp index 02cde1715d..1e1b2c8974 100644 --- a/rpcs3/rpcs3qt/progress_dialog.cpp +++ b/rpcs3/rpcs3qt/progress_dialog.cpp @@ -1,8 +1,14 @@ #include "progress_dialog.h" +#include + progress_dialog::progress_dialog(const QString &labelText, const QString &cancelButtonText, int minimum, int maximum, QWidget *parent, Qt::WindowFlags flags) : QProgressDialog(labelText, cancelButtonText, minimum, maximum, parent, flags) { + setFixedWidth(QLabel("This is the very length of the progressdialog due to hidpi reasons.").sizeHint().width()); + setWindowModality(Qt::WindowModal); + connect(this, &QProgressDialog::canceled, this, &QProgressDialog::deleteLater); + #ifdef _WIN32 m_tb_button = std::make_unique(); m_tb_button->setWindow(parent ? parent->windowHandle() : windowHandle());