mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 23:55:40 +00:00
GameList: Reduce number of system calls when scanning
This commit is contained in:
parent
e1578be20f
commit
03f3f0369c
|
@ -322,15 +322,6 @@ ConsoleRegion GetConsoleRegionForDiscRegion(DiscRegion region)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view GetTitleForPath(const char* path)
|
|
||||||
{
|
|
||||||
std::string_view path_view = path;
|
|
||||||
std::size_t title_start = path_view.find_last_of("/\\");
|
|
||||||
if (title_start != std::string_view::npos)
|
|
||||||
path_view.remove_prefix(title_start + 1);
|
|
||||||
return path_view.substr(0, path_view.find_last_of('.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string GetGameCodeForPath(const char* image_path, bool fallback_to_hash)
|
std::string GetGameCodeForPath(const char* image_path, bool fallback_to_hash)
|
||||||
{
|
{
|
||||||
std::unique_ptr<CDImage> cdi = CDImage::Open(image_path, nullptr);
|
std::unique_ptr<CDImage> cdi = CDImage::Open(image_path, nullptr);
|
||||||
|
|
|
@ -81,7 +81,6 @@ DiscRegion GetRegionForImage(CDImage* cdi);
|
||||||
DiscRegion GetRegionForExe(const char* path);
|
DiscRegion GetRegionForExe(const char* path);
|
||||||
DiscRegion GetRegionForPsf(const char* path);
|
DiscRegion GetRegionForPsf(const char* path);
|
||||||
std::optional<DiscRegion> GetRegionForPath(const char* image_path);
|
std::optional<DiscRegion> GetRegionForPath(const char* image_path);
|
||||||
std::string_view GetTitleForPath(const char* path);
|
|
||||||
|
|
||||||
State GetState();
|
State GetState();
|
||||||
void SetState(State new_state);
|
void SetState(State new_state);
|
||||||
|
|
|
@ -2870,7 +2870,7 @@ void CommonHostInterface::GetGameInfo(const char* path, CDImage* image, std::str
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
*title = System::GetTitleForPath(path);
|
*title = FileSystem::GetFileTitleFromPath(path);
|
||||||
if (image)
|
if (image)
|
||||||
*code = System::GetGameCodeForImage(image, true);
|
*code = System::GetGameCodeForImage(image, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,13 +64,9 @@ bool GameList::IsScannableFilename(const std::string& path)
|
||||||
return System::IsLoadableFilename(path.c_str());
|
return System::IsLoadableFilename(path.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameList::GetExeListEntry(const char* path, GameListEntry* entry)
|
bool GameList::GetExeListEntry(const std::string& path, GameListEntry* entry)
|
||||||
{
|
{
|
||||||
FILESYSTEM_STAT_DATA ffd;
|
std::FILE* fp = FileSystem::OpenCFile(path.c_str(), "rb");
|
||||||
if (!FileSystem::StatFile(path, &ffd))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::FILE* fp = FileSystem::OpenCFile(path, "rb");
|
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -89,41 +85,30 @@ bool GameList::GetExeListEntry(const char* path, GameListEntry* entry)
|
||||||
|
|
||||||
if (!BIOS::IsValidPSExeHeader(header, file_size))
|
if (!BIOS::IsValidPSExeHeader(header, file_size))
|
||||||
{
|
{
|
||||||
Log_DebugPrintf("%s is not a valid PS-EXE", path);
|
Log_DebugPrintf("%s is not a valid PS-EXE", path.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* extension = std::strrchr(path, '.');
|
|
||||||
if (!extension)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
entry->code.clear();
|
entry->code.clear();
|
||||||
entry->title = FileSystem::GetFileTitleFromPath(path);
|
entry->title = FileSystem::GetFileTitleFromPath(path);
|
||||||
entry->path = path;
|
|
||||||
entry->region = BIOS::GetPSExeDiscRegion(header);
|
entry->region = BIOS::GetPSExeDiscRegion(header);
|
||||||
entry->total_size = ZeroExtend64(file_size);
|
entry->total_size = ZeroExtend64(file_size);
|
||||||
entry->last_modified_time = ffd.ModificationTime.AsUnixTimestamp();
|
|
||||||
entry->type = GameListEntryType::PSExe;
|
entry->type = GameListEntryType::PSExe;
|
||||||
entry->compatibility_rating = GameListCompatibilityRating::Unknown;
|
entry->compatibility_rating = GameListCompatibilityRating::Unknown;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameList::GetPsfListEntry(const char* path, GameListEntry* entry)
|
bool GameList::GetPsfListEntry(const std::string& path, GameListEntry* entry)
|
||||||
{
|
{
|
||||||
FILESYSTEM_STAT_DATA ffd;
|
// we don't need to walk the library chain here - the top file is enough
|
||||||
if (!FileSystem::StatFile(path, &ffd))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
PSFLoader::File file;
|
PSFLoader::File file;
|
||||||
if (!file.Load(path))
|
if (!file.Load(path.c_str()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
entry->code.clear();
|
entry->code.clear();
|
||||||
entry->path = path;
|
|
||||||
entry->region = file.GetRegion();
|
entry->region = file.GetRegion();
|
||||||
entry->total_size = static_cast<u32>(file.GetProgramData().size());
|
entry->total_size = static_cast<u32>(file.GetProgramData().size());
|
||||||
entry->last_modified_time = ffd.ModificationTime.AsUnixTimestamp();
|
|
||||||
entry->type = GameListEntryType::PSF;
|
entry->type = GameListEntryType::PSF;
|
||||||
entry->compatibility_rating = GameListCompatibilityRating::Unknown;
|
entry->compatibility_rating = GameListCompatibilityRating::Unknown;
|
||||||
|
|
||||||
|
@ -171,7 +156,7 @@ bool GameList::GetGameListEntry(const std::string& path, GameListEntry* entry)
|
||||||
{
|
{
|
||||||
// no game code, so use the filename title
|
// no game code, so use the filename title
|
||||||
entry->code = System::GetGameCodeForImage(cdi.get(), true);
|
entry->code = System::GetGameCodeForImage(cdi.get(), true);
|
||||||
entry->title = System::GetTitleForPath(path.c_str());
|
entry->title = FileSystem::GetFileTitleFromPath(path);
|
||||||
entry->compatibility_rating = GameListCompatibilityRating::Unknown;
|
entry->compatibility_rating = GameListCompatibilityRating::Unknown;
|
||||||
entry->release_date = 0;
|
entry->release_date = 0;
|
||||||
entry->min_players = 0;
|
entry->min_players = 0;
|
||||||
|
@ -238,11 +223,6 @@ bool GameList::GetGameListEntry(const std::string& path, GameListEntry* entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FILESYSTEM_STAT_DATA ffd;
|
|
||||||
if (!FileSystem::StatFile(path.c_str(), &ffd))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
entry->last_modified_time = ffd.ModificationTime.AsUnixTimestamp();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -493,55 +473,70 @@ void GameList::ScanDirectory(const char* path, bool recursive, ProgressCallback*
|
||||||
FileSystem::FindResultsArray files;
|
FileSystem::FindResultsArray files;
|
||||||
FileSystem::FindFiles(path, "*", FILESYSTEM_FIND_FILES | (recursive ? FILESYSTEM_FIND_RECURSIVE : 0), &files);
|
FileSystem::FindFiles(path, "*", FILESYSTEM_FIND_FILES | (recursive ? FILESYSTEM_FIND_RECURSIVE : 0), &files);
|
||||||
|
|
||||||
GameListEntry entry;
|
|
||||||
progress->SetProgressRange(static_cast<u32>(files.size()));
|
progress->SetProgressRange(static_cast<u32>(files.size()));
|
||||||
progress->SetProgressValue(0);
|
progress->SetProgressValue(0);
|
||||||
|
|
||||||
for (const FILESYSTEM_FIND_DATA& ffd : files)
|
for (FILESYSTEM_FIND_DATA& ffd : files)
|
||||||
{
|
{
|
||||||
progress->IncrementProgressValue();
|
progress->IncrementProgressValue();
|
||||||
if (!IsScannableFilename(ffd.FileName))
|
|
||||||
|
if (!IsScannableFilename(ffd.FileName) || GetEntryForPath(ffd.FileName.c_str()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::string entry_path(ffd.FileName);
|
const u64 modified_time = ffd.ModificationTime.AsUnixTimestamp();
|
||||||
if (std::any_of(m_entries.begin(), m_entries.end(),
|
if (AddFileFromCache(ffd.FileName, modified_time))
|
||||||
[&entry_path](const GameListEntry& other) { return other.path == entry_path; }))
|
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
Log_DebugPrintf("Trying '%s'...", entry_path.c_str());
|
|
||||||
|
|
||||||
// try opening the image
|
const std::string_view file_part(FileSystem::GetFileNameFromPath(ffd.FileName));
|
||||||
if (!GetGameListEntryFromCache(entry_path, &entry) ||
|
if (!file_part.empty())
|
||||||
entry.last_modified_time != ffd.ModificationTime.AsUnixTimestamp())
|
progress->SetFormattedStatusText("Scanning '%*s'...", static_cast<int>(file_part.size()), file_part.data());
|
||||||
{
|
|
||||||
const char* file_part_slash =
|
|
||||||
std::max(std::strrchr(entry_path.c_str(), '/'), std::strrchr(entry_path.c_str(), '\\'));
|
|
||||||
progress->SetFormattedStatusText("Scanning '%s'...",
|
|
||||||
file_part_slash ? (file_part_slash + 1) : entry_path.c_str());
|
|
||||||
|
|
||||||
if (GetGameListEntry(entry_path, &entry))
|
// ownership of fp is transferred
|
||||||
{
|
ScanFile(std::move(ffd.FileName), modified_time);
|
||||||
if (m_cache_write_stream || OpenCacheForWriting())
|
|
||||||
{
|
|
||||||
if (!WriteEntryToCache(&entry, m_cache_write_stream.get()))
|
|
||||||
Log_WarningPrintf("Failed to write entry '%s' to cache", entry.path.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_entries.push_back(std::move(entry));
|
|
||||||
entry = {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
progress->SetProgressValue(static_cast<u32>(files.size()));
|
progress->SetProgressValue(static_cast<u32>(files.size()));
|
||||||
progress->PopState();
|
progress->PopState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GameList::AddFileFromCache(const std::string& path, u64 timestamp)
|
||||||
|
{
|
||||||
|
if (std::any_of(m_entries.begin(), m_entries.end(),
|
||||||
|
[&path](const GameListEntry& other) { return other.path == path; }))
|
||||||
|
{
|
||||||
|
// already exists
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameListEntry entry;
|
||||||
|
if (!GetGameListEntryFromCache(path, &entry) || entry.last_modified_time != timestamp)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_entries.push_back(std::move(entry));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GameList::ScanFile(std::string path, u64 timestamp)
|
||||||
|
{
|
||||||
|
Log_DevPrintf("Scanning '%s'...", path.c_str());
|
||||||
|
|
||||||
|
GameListEntry entry;
|
||||||
|
if (!GetGameListEntry(path, &entry))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
entry.path = std::move(path);
|
||||||
|
entry.last_modified_time = timestamp;
|
||||||
|
|
||||||
|
if (m_cache_write_stream || OpenCacheForWriting())
|
||||||
|
{
|
||||||
|
if (!WriteEntryToCache(&entry, m_cache_write_stream.get()))
|
||||||
|
Log_WarningPrintf("Failed to write entry '%s' to cache", entry.path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_entries.push_back(std::move(entry));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void GameList::AddDirectory(std::string path, bool recursive)
|
void GameList::AddDirectory(std::string path, bool recursive)
|
||||||
{
|
{
|
||||||
auto iter = std::find_if(m_search_directories.begin(), m_search_directories.end(),
|
auto iter = std::find_if(m_search_directories.begin(), m_search_directories.end(),
|
||||||
|
@ -632,6 +627,7 @@ void GameList::Refresh(bool invalidate_cache, bool invalidate_database, Progress
|
||||||
// don't need unused cache entries
|
// don't need unused cache entries
|
||||||
CloseCacheFileStream();
|
CloseCacheFileStream();
|
||||||
m_cache_map.clear();
|
m_cache_map.clear();
|
||||||
|
m_database.Unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameList::UpdateCompatibilityEntry(GameListCompatibilityEntry new_entry, bool save_to_list /*= true*/)
|
void GameList::UpdateCompatibilityEntry(GameListCompatibilityEntry new_entry, bool save_to_list /*= true*/)
|
||||||
|
@ -1040,7 +1036,7 @@ std::string GameList::GetCoverImagePathForEntry(const GameListEntry* entry) cons
|
||||||
for (const char* extension : extensions)
|
for (const char* extension : extensions)
|
||||||
{
|
{
|
||||||
// use the file title if it differs (e.g. modded games)
|
// use the file title if it differs (e.g. modded games)
|
||||||
const std::string_view file_title = System::GetTitleForPath(entry->path.c_str());
|
const std::string_view file_title(FileSystem::GetFileTitleFromPath(entry->path));
|
||||||
if (!file_title.empty() && entry->title != file_title)
|
if (!file_title.empty() && entry->title != file_title)
|
||||||
{
|
{
|
||||||
cover_path.Clear();
|
cover_path.Clear();
|
||||||
|
@ -1048,7 +1044,7 @@ std::string GameList::GetCoverImagePathForEntry(const GameListEntry* entry) cons
|
||||||
cover_path.AppendCharacter(FS_OSPATH_SEPARATOR_CHARACTER);
|
cover_path.AppendCharacter(FS_OSPATH_SEPARATOR_CHARACTER);
|
||||||
cover_path.AppendString("covers");
|
cover_path.AppendString("covers");
|
||||||
cover_path.AppendCharacter(FS_OSPATH_SEPARATOR_CHARACTER);
|
cover_path.AppendCharacter(FS_OSPATH_SEPARATOR_CHARACTER);
|
||||||
cover_path.AppendString(file_title.data(), static_cast<u32>(file_title.size()));
|
cover_path.AppendString(file_title.data());
|
||||||
cover_path.AppendCharacter('.');
|
cover_path.AppendCharacter('.');
|
||||||
cover_path.AppendString(extension);
|
cover_path.AppendString(extension);
|
||||||
if (FileSystem::FileExists(cover_path))
|
if (FileSystem::FileExists(cover_path))
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "common/cd_image.h"
|
||||||
#include "core/types.h"
|
#include "core/types.h"
|
||||||
#include "game_database.h"
|
#include "game_database.h"
|
||||||
#include "game_settings.h"
|
#include "game_settings.h"
|
||||||
|
@ -9,7 +10,6 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class CDImage;
|
|
||||||
class ByteStream;
|
class ByteStream;
|
||||||
class ProgressCallback;
|
class ProgressCallback;
|
||||||
|
|
||||||
|
@ -140,12 +140,14 @@ private:
|
||||||
|
|
||||||
GameListEntry* GetMutableEntryForPath(const char* path);
|
GameListEntry* GetMutableEntryForPath(const char* path);
|
||||||
|
|
||||||
static bool GetExeListEntry(const char* path, GameListEntry* entry);
|
static bool GetExeListEntry(const std::string& path, GameListEntry* entry);
|
||||||
static bool GetPsfListEntry(const char* path, GameListEntry* entry);
|
static bool GetPsfListEntry(const std::string& path, GameListEntry* entry);
|
||||||
|
|
||||||
bool GetGameListEntry(const std::string& path, GameListEntry* entry);
|
bool GetGameListEntry(const std::string& path, GameListEntry* entry);
|
||||||
bool GetGameListEntryFromCache(const std::string& path, GameListEntry* entry);
|
bool GetGameListEntryFromCache(const std::string& path, GameListEntry* entry);
|
||||||
void ScanDirectory(const char* path, bool recursive, ProgressCallback* progress);
|
void ScanDirectory(const char* path, bool recursive, ProgressCallback* progress);
|
||||||
|
bool AddFileFromCache(const std::string& path, u64 timestamp);
|
||||||
|
bool ScanFile(std::string path, u64 timestamp);
|
||||||
|
|
||||||
void LoadCache();
|
void LoadCache();
|
||||||
bool LoadEntriesFromCache(ByteStream* stream);
|
bool LoadEntriesFromCache(ByteStream* stream);
|
||||||
|
|
Loading…
Reference in a new issue