Don't show "extra" files from DirectoryBlobs in game list

For instance, we don't want to show TGC files that might be
inside the /files/ directory of a GameCube DirectoryBlob,
and we don't want to show the /sys/main.dol files for extra
partitions of Wii DirectoryBlobs.
This commit is contained in:
JosJuice 2017-06-11 14:45:42 +02:00
parent 1ea44f425d
commit 5fe3745750
5 changed files with 96 additions and 12 deletions

View File

@ -150,15 +150,20 @@ static std::optional<PartitionType> ParsePartitionDirectoryName(const std::strin
return {}; return {};
} }
static bool PathCharactersEqual(char a, char b) static bool IsDirectorySeparator(char c)
{ {
return a == b return c == '/'
#ifdef _WIN32 #ifdef _WIN32
|| (a == '/' && b == '\\') || (a == '\\' && b == '/') || c == '\\'
#endif #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) static bool PathEndsWith(const std::string& path, const std::string& suffix)
{ {
if (suffix.size() > path.size()) 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, 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")) if (!PathEndsWith(dol_path, "/sys/main.dol"))
return false; return false;
@ -194,12 +199,75 @@ static bool IsValidDirectoryBlob(const std::string& dol_path, std::string* parti
#else #else
constexpr char dir_separator = '/'; constexpr char dir_separator = '/';
#endif #endif
const size_t true_root_end = dol_path.find_last_of(dir_separator, partition_root->size() - 2) + 1; if (true_root)
*true_root = dol_path.substr(0, true_root_end); {
*true_root =
dol_path.substr(0, dol_path.find_last_of(dir_separator, partition_root->size() - 2) + 1);
}
return true; 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<PartitionType> 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> DirectoryBlobReader::Create(const std::string& dol_path) std::unique_ptr<DirectoryBlobReader> DirectoryBlobReader::Create(const std::string& dol_path)
{ {
std::string partition_root, true_root; std::string partition_root, true_root;

View File

@ -27,6 +27,9 @@ namespace DiscIO
{ {
enum class PartitionType : u32; 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 class DiscContent
{ {
public: public:

View File

@ -6,6 +6,7 @@
#include <QDirIterator> #include <QDirIterator>
#include <QFile> #include <QFile>
#include "DiscIO/DirectoryBlob.h"
#include "DolphinQt2/GameList/GameTracker.h" #include "DolphinQt2/GameList/GameTracker.h"
#include "DolphinQt2/Settings.h" #include "DolphinQt2/Settings.h"
@ -137,3 +138,13 @@ void GameTracker::UpdateFile(const QString& file)
emit GameRemoved(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<GameFile>(game));
}
}

View File

@ -54,12 +54,7 @@ class GameLoader final : public QObject
Q_OBJECT Q_OBJECT
public: public:
void LoadGame(const QString& path) void LoadGame(const QString& path);
{
GameFile* game = new GameFile(path);
if (game->IsValid())
emit GameLoaded(QSharedPointer<GameFile>(game));
}
signals: signals:
void GameLoaded(QSharedPointer<GameFile> game); void GameLoaded(QSharedPointer<GameFile> game);

View File

@ -53,6 +53,7 @@
#include "Core/Movie.h" #include "Core/Movie.h"
#include "Core/TitleDatabase.h" #include "Core/TitleDatabase.h"
#include "DiscIO/Blob.h" #include "DiscIO/Blob.h"
#include "DiscIO/DirectoryBlob.h"
#include "DiscIO/Enums.h" #include "DiscIO/Enums.h"
#include "DiscIO/Volume.h" #include "DiscIO/Volume.h"
#include "DolphinWX/Frame.h" #include "DolphinWX/Frame.h"
@ -761,6 +762,12 @@ void GameListCtrl::RescanList()
auto search_results = Common::DoFileSearch(SConfig::GetInstance().m_ISOFolder, search_extensions, auto search_results = Common::DoFileSearch(SConfig::GetInstance().m_ISOFolder, search_extensions,
SConfig::GetInstance().m_RecursiveISOFolder); 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<std::string> cached_paths; std::vector<std::string> cached_paths;
for (const auto& file : m_cached_files) for (const auto& file : m_cached_files)
cached_paths.emplace_back(file->GetFileName()); cached_paths.emplace_back(file->GetFileName());