From f3c33b29dd249aaf0c78985ff920762289a838ed Mon Sep 17 00:00:00 2001 From: pdaloxd <31321612+pablodrake@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:50:02 +0100 Subject: [PATCH] Fix issue #1684 (#2277) * Added recursive game scan and only using game directories * Added recursion depth limit to scan directories * Added recursive search by ID in cli mode * Added recursive search to pkg installing --- src/common/path_util.cpp | 28 +++++++++++++++++++++++++ src/common/path_util.h | 15 +++++++++++++ src/main.cpp | 8 +++---- src/qt_gui/game_info.cpp | 35 ++++++++++++++++++++++++------- src/qt_gui/main.cpp | 8 +++---- src/qt_gui/main_window.cpp | 43 +++++++++++++++++++++++++++++++++----- 6 files changed, 117 insertions(+), 20 deletions(-) diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index 53eb123dc..a4312fada 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -176,6 +176,34 @@ void SetUserPath(PathType shad_path, const fs::path& new_path) { UserPaths.insert_or_assign(shad_path, new_path); } +std::optional FindGameByID(const fs::path& dir, const std::string& game_id, + int max_depth) { + if (max_depth < 0) { + return std::nullopt; + } + + // Check if this is the game we're looking for + if (dir.filename() == game_id && fs::exists(dir / "sce_sys" / "param.sfo")) { + auto eboot_path = dir / "eboot.bin"; + if (fs::exists(eboot_path)) { + return eboot_path; + } + } + + // Recursively search subdirectories + std::error_code ec; + for (const auto& entry : fs::directory_iterator(dir, ec)) { + if (!entry.is_directory()) { + continue; + } + if (auto found = FindGameByID(entry.path(), game_id, max_depth - 1)) { + return found; + } + } + + return std::nullopt; +} + #ifdef ENABLE_QT_GUI void PathToQString(QString& result, const std::filesystem::path& path) { #ifdef _WIN32 diff --git a/src/common/path_util.h b/src/common/path_util.h index 09b7a3337..7190378d6 100644 --- a/src/common/path_util.h +++ b/src/common/path_util.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #ifdef ENABLE_QT_GUI @@ -115,4 +116,18 @@ void PathToQString(QString& result, const std::filesystem::path& path); [[nodiscard]] std::filesystem::path PathFromQString(const QString& path); #endif +/** + * Recursively searches for a game directory by its ID. + * Limits search depth to prevent excessive filesystem traversal. + * + * @param dir Base directory to start the search from + * @param game_id The game ID to search for + * @param max_depth Maximum directory depth to search + * + * @returns Path to eboot.bin if found, std::nullopt otherwise + */ +[[nodiscard]] std::optional FindGameByID(const std::filesystem::path& dir, + const std::string& game_id, + int max_depth); + } // namespace Common::FS diff --git a/src/main.cpp b/src/main.cpp index fad3b1f53..6b334e446 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -167,12 +167,12 @@ int main(int argc, char* argv[]) { // Check if the provided path is a valid file if (!std::filesystem::exists(eboot_path)) { - // If not a file, treat it as a game ID and search in install directories + // If not a file, treat it as a game ID and search in install directories recursively bool game_found = false; + const int max_depth = 5; for (const auto& install_dir : Config::getGameInstallDirs()) { - const auto candidate_path = install_dir / game_path / "eboot.bin"; - if (std::filesystem::exists(candidate_path)) { - eboot_path = candidate_path; + if (auto found_path = Common::FS::FindGameByID(install_dir, game_path, max_depth)) { + eboot_path = *found_path; game_found = true; break; } diff --git a/src/qt_gui/game_info.cpp b/src/qt_gui/game_info.cpp index e4750fa1d..adbf392ed 100644 --- a/src/qt_gui/game_info.cpp +++ b/src/qt_gui/game_info.cpp @@ -7,6 +7,33 @@ #include "compatibility_info.h" #include "game_info.h" +// Maximum depth to search for games in subdirectories +const int max_recursion_depth = 5; + +void ScanDirectoryRecursively(const QString& dir, QStringList& filePaths, int current_depth = 0) { + // Stop recursion if we've reached the maximum depth + if (current_depth >= max_recursion_depth) { + return; + } + + QDir directory(dir); + QFileInfoList entries = directory.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + + for (const auto& entry : entries) { + if (entry.fileName().endsWith("-UPDATE")) { + continue; + } + + // Check if this directory contains a PS4 game (has sce_sys/param.sfo) + if (QFile::exists(entry.filePath() + "/sce_sys/param.sfo")) { + filePaths.append(entry.absoluteFilePath()); + } else { + // If not a game directory, recursively scan it with increased depth + ScanDirectoryRecursively(entry.absoluteFilePath(), filePaths, current_depth + 1); + } + } +} + GameInfoClass::GameInfoClass() = default; GameInfoClass::~GameInfoClass() = default; @@ -15,13 +42,7 @@ void GameInfoClass::GetGameInfo(QWidget* parent) { for (const auto& installLoc : Config::getGameInstallDirs()) { QString installDir; Common::FS::PathToQString(installDir, installLoc); - QDir parentFolder(installDir); - QFileInfoList fileList = parentFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); - for (const auto& fileInfo : fileList) { - if (fileInfo.isDir() && !fileInfo.filePath().endsWith("-UPDATE")) { - filePaths.append(fileInfo.absoluteFilePath()); - } - } + ScanDirectoryRecursively(installDir, filePaths, 0); } m_games = QtConcurrent::mapped(filePaths, [&](const QString& path) { diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index 8babadc35..052f73f25 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -181,12 +181,12 @@ int main(int argc, char* argv[]) { // Check if the provided path is a valid file if (!std::filesystem::exists(game_file_path)) { - // If not a file, treat it as a game ID and search in install directories + // If not a file, treat it as a game ID and search in install directories recursively bool game_found = false; + const int max_depth = 5; for (const auto& install_dir : Config::getGameInstallDirs()) { - auto potential_game_path = install_dir / game_path / "eboot.bin"; - if (std::filesystem::exists(potential_game_path)) { - game_file_path = potential_game_path; + if (auto found_path = Common::FS::FindGameByID(install_dir, game_path, max_depth)) { + game_file_path = *found_path; game_found = true; break; } diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 945210776..aa02d1237 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -747,12 +747,42 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int } std::filesystem::path game_install_dir = last_install_dir; - auto game_folder_path = game_install_dir / pkg.GetTitleID(); QString pkgType = QString::fromStdString(pkg.GetPkgFlags()); bool use_game_update = pkgType.contains("PATCH") && Config::getSeparateUpdateEnabled(); - auto game_update_path = use_game_update - ? game_install_dir / (std::string(pkg.GetTitleID()) + "-UPDATE") - : game_folder_path; + + // Default paths + auto game_folder_path = game_install_dir / pkg.GetTitleID(); + auto game_update_path = use_game_update ? game_folder_path.parent_path() / + (std::string{pkg.GetTitleID()} + "-UPDATE") + : game_folder_path; + const int max_depth = 5; + + if (pkgType.contains("PATCH")) { + // For patches, try to find the game recursively + auto found_game = Common::FS::FindGameByID(game_install_dir, + std::string{pkg.GetTitleID()}, max_depth); + if (found_game.has_value()) { + game_folder_path = found_game.value().parent_path(); + game_update_path = use_game_update ? game_folder_path.parent_path() / + (std::string{pkg.GetTitleID()} + "-UPDATE") + : game_folder_path; + } + } else { + // For base games, we check if the game is already installed + auto found_game = Common::FS::FindGameByID(game_install_dir, + std::string{pkg.GetTitleID()}, max_depth); + if (found_game.has_value()) { + game_folder_path = found_game.value().parent_path(); + } + // If the game is not found, we install it in the game install directory + else { + game_folder_path = game_install_dir / pkg.GetTitleID(); + } + game_update_path = use_game_update ? game_folder_path.parent_path() / + (std::string{pkg.GetTitleID()} + "-UPDATE") + : game_folder_path; + } + QString gameDirPath; Common::FS::PathToQString(gameDirPath, game_folder_path); QDir game_dir(gameDirPath); @@ -897,10 +927,13 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int connect(&futureWatcher, &QFutureWatcher::finished, this, [=, this]() { if (pkgNum == nPkg) { QString path; - Common::FS::PathToQString(path, game_install_dir); + + // We want to show the parent path instead of the full path + Common::FS::PathToQString(path, game_folder_path.parent_path()); QIcon windowIcon( Common::FS::PathToUTF8String(game_folder_path / "sce_sys/icon0.png") .c_str()); + QMessageBox extractMsgBox(this); extractMsgBox.setWindowTitle(tr("Extraction Finished")); if (!windowIcon.isNull()) {