diff --git a/Source/Core/DiscIO/DirectoryBlob.cpp b/Source/Core/DiscIO/DirectoryBlob.cpp index b9cf27123b..5a92d6c88c 100644 --- a/Source/Core/DiscIO/DirectoryBlob.cpp +++ b/Source/Core/DiscIO/DirectoryBlob.cpp @@ -150,15 +150,20 @@ static std::optional ParsePartitionDirectoryName(const std::strin return {}; } -static bool PathCharactersEqual(char a, char b) +static bool IsDirectorySeparator(char c) { - return a == b + return c == '/' #ifdef _WIN32 - || (a == '/' && b == '\\') || (a == '\\' && b == '/') + || c == '\\' #endif ; } +static bool PathCharactersEqual(char a, char b) +{ + return a == b || (IsDirectorySeparator(a) && IsDirectorySeparator(b)); +} + static bool PathEndsWith(const std::string& path, const std::string& suffix) { if (suffix.size() > path.size()) @@ -178,7 +183,7 @@ static bool PathEndsWith(const std::string& path, const std::string& suffix) } static bool IsValidDirectoryBlob(const std::string& dol_path, std::string* partition_root, - std::string* true_root) + std::string* true_root = nullptr) { if (!PathEndsWith(dol_path, "/sys/main.dol")) return false; @@ -194,12 +199,75 @@ static bool IsValidDirectoryBlob(const std::string& dol_path, std::string* parti #else constexpr char dir_separator = '/'; #endif - const size_t true_root_end = dol_path.find_last_of(dir_separator, partition_root->size() - 2) + 1; - *true_root = dol_path.substr(0, true_root_end); + if (true_root) + { + *true_root = + dol_path.substr(0, dol_path.find_last_of(dir_separator, partition_root->size() - 2) + 1); + } return true; } +static bool ExistsAndIsValidDirectoryBlob(const std::string& dol_path) +{ + std::string partition_root; + return File::Exists(dol_path) && IsValidDirectoryBlob(dol_path, &partition_root); +} + +static bool IsInFilesDirectory(const std::string& path) +{ + size_t files_pos = std::string::npos; + while (true) + { + files_pos = path.rfind("files", files_pos); + if (files_pos == std::string::npos) + return false; + + const size_t slash_before_pos = files_pos - 1; + const size_t slash_after_pos = files_pos + 5; + if ((files_pos == 0 || IsDirectorySeparator(path[slash_before_pos])) && + (slash_after_pos == path.size() || (IsDirectorySeparator(path[slash_after_pos]))) && + ExistsAndIsValidDirectoryBlob(path.substr(0, files_pos) + "sys/main.dol")) + { + return true; + } + + --files_pos; + } +} + +static bool IsMainDolForNonGamePartition(const std::string& path) +{ + std::string partition_root, true_root; + if (!IsValidDirectoryBlob(path, &partition_root, &true_root)) + return false; // This is not a /sys/main.dol + + std::string partition_directory_name = partition_root.substr(true_root.size()); + partition_directory_name.pop_back(); // Remove trailing slash + const std::optional partition_type = + ParsePartitionDirectoryName(partition_directory_name); + if (!partition_type || *partition_type == PartitionType::Game) + return false; // volume_path is the game partition's /sys/main.dol + + const File::FSTEntry true_root_entry = File::ScanDirectoryTree(true_root, false); + for (const File::FSTEntry& entry : true_root_entry.children) + { + if (entry.isDirectory && + ParsePartitionDirectoryName(entry.virtualName) == PartitionType::Game && + ExistsAndIsValidDirectoryBlob(entry.physicalName + "/sys/main.dol")) + { + return true; // volume_path is the /sys/main.dol for a non-game partition + } + } + + return false; // volume_path is the game partition's /sys/main.dol +} + +bool ShouldHideFromGameList(const std::string& volume_path) +{ + return IsInFilesDirectory(volume_path) || IsMainDolForNonGamePartition(volume_path); +} + std::unique_ptr DirectoryBlobReader::Create(const std::string& dol_path) { std::string partition_root, true_root; diff --git a/Source/Core/DiscIO/DirectoryBlob.h b/Source/Core/DiscIO/DirectoryBlob.h index dc7ffd8138..18fca46726 100644 --- a/Source/Core/DiscIO/DirectoryBlob.h +++ b/Source/Core/DiscIO/DirectoryBlob.h @@ -27,6 +27,9 @@ namespace DiscIO { enum class PartitionType : u32; +// Returns true if the path is inside a DirectoryBlob and doesn't represent the DirectoryBlob itself +bool ShouldHideFromGameList(const std::string& volume_path); + class DiscContent { public: diff --git a/Source/Core/DolphinQt2/GameList/GameTracker.cpp b/Source/Core/DolphinQt2/GameList/GameTracker.cpp index be66ab406e..3d541e6559 100644 --- a/Source/Core/DolphinQt2/GameList/GameTracker.cpp +++ b/Source/Core/DolphinQt2/GameList/GameTracker.cpp @@ -6,6 +6,7 @@ #include #include +#include "DiscIO/DirectoryBlob.h" #include "DolphinQt2/GameList/GameTracker.h" #include "DolphinQt2/Settings.h" @@ -137,3 +138,13 @@ void GameTracker::UpdateFile(const QString& file) emit GameRemoved(file); } } + +void GameLoader::LoadGame(const QString& path) +{ + if (!DiscIO::ShouldHideFromGameList(path.toStdString())) + { + GameFile* game = new GameFile(path); + if (game->IsValid()) + emit GameLoaded(QSharedPointer(game)); + } +} diff --git a/Source/Core/DolphinQt2/GameList/GameTracker.h b/Source/Core/DolphinQt2/GameList/GameTracker.h index 5867278b19..926e2a9ae2 100644 --- a/Source/Core/DolphinQt2/GameList/GameTracker.h +++ b/Source/Core/DolphinQt2/GameList/GameTracker.h @@ -54,12 +54,7 @@ class GameLoader final : public QObject Q_OBJECT public: - void LoadGame(const QString& path) - { - GameFile* game = new GameFile(path); - if (game->IsValid()) - emit GameLoaded(QSharedPointer(game)); - } + void LoadGame(const QString& path); signals: void GameLoaded(QSharedPointer game); diff --git a/Source/Core/DolphinWX/GameListCtrl.cpp b/Source/Core/DolphinWX/GameListCtrl.cpp index 2395028790..b0e23a9469 100644 --- a/Source/Core/DolphinWX/GameListCtrl.cpp +++ b/Source/Core/DolphinWX/GameListCtrl.cpp @@ -53,6 +53,7 @@ #include "Core/Movie.h" #include "Core/TitleDatabase.h" #include "DiscIO/Blob.h" +#include "DiscIO/DirectoryBlob.h" #include "DiscIO/Enums.h" #include "DiscIO/Volume.h" #include "DolphinWX/Frame.h" @@ -761,6 +762,12 @@ void GameListCtrl::RescanList() auto search_results = Common::DoFileSearch(SConfig::GetInstance().m_ISOFolder, search_extensions, SConfig::GetInstance().m_RecursiveISOFolder); + // TODO Prevent DoFileSearch from looking inside /files/ directories of DirectoryBlobs at all? + // TODO Make DoFileSearch support filter predicates so we don't have remove things afterwards? + search_results.erase( + std::remove_if(search_results.begin(), search_results.end(), DiscIO::ShouldHideFromGameList), + search_results.end()); + std::vector cached_paths; for (const auto& file : m_cached_files) cached_paths.emplace_back(file->GetFileName());