GameList: Improve custom title cover handling
Some checks are pending
Automated Builds / 💻 Windows (push) Waiting to run
Automated Builds / 🐧 Linux AppImage (push) Waiting to run
Automated Builds / 🐧 Linux Cross-Compiled AppImage (push) Waiting to run
Automated Builds / 📦 Linux Flatpak (push) Waiting to run
Automated Builds / 🍎 MacOS (push) Waiting to run
Automated Builds / 📤 Create Release (push) Blocked by required conditions

Prefer the custom title path over the serial if one is present.

Also use sanitized names.
This commit is contained in:
Stenzek 2025-09-12 23:28:37 +10:00
parent f45350898b
commit f0ff520a9d
No known key found for this signature in database
4 changed files with 37 additions and 38 deletions

View File

@ -1201,56 +1201,51 @@ void GameList::CreateDiscSetEntries(const std::vector<std::string>& excluded_pat
std::string GameList::GetCoverImagePathForEntry(const Entry* entry)
{
return GetCoverImagePath(entry->path, entry->serial, entry->GetSaveTitle());
return GetCoverImagePath(entry->path, entry->serial, entry->GetSaveTitle(), entry->has_custom_title);
}
static std::string GetFullCoverPath(std::string_view filename, std::string_view extension)
static std::string GetFullCoverPath(std::string_view title, std::string_view extension)
{
return fmt::format("{}" FS_OSPATH_SEPARATOR_STR "{}.{}", EmuFolders::Covers, filename, extension);
std::string filename = fmt::format("{}.{}", title, extension);
Path::SanitizeFileName(&filename);
return Path::Combine(EmuFolders::Covers, filename);
}
std::string GameList::GetCoverImagePath(const std::string_view path, const std::string_view serial,
const std::string_view title)
const std::string_view title, bool is_custom_title)
{
static constexpr const std::array extensions = {"jpg", "jpeg", "png", "webp"};
std::string ret;
for (const char* extension : extensions)
{
// Prioritize lookup by serial (Most specific)
if (!serial.empty())
const auto try_name = [&ret](std::string_view name) {
for (const char* extension : extensions)
{
std::string cover_path(GetFullCoverPath(serial, extension));
std::string cover_path = GetFullCoverPath(name, extension);
if (FileSystem::FileExists(cover_path.c_str()))
{
ret = std::move(cover_path);
return ret;
return true;
}
}
return false;
};
// Try file title (for modded games or specific like above)
const std::string_view file_title(Path::GetFileTitle(path));
if (!file_title.empty() && title != file_title)
{
std::string cover_path(GetFullCoverPath(file_title, extension));
if (FileSystem::FileExists(cover_path.c_str()))
{
ret = std::move(cover_path);
return ret;
}
}
// Check the title first if this is a custom title
if (is_custom_title && try_name(title))
return ret;
// Last resort, check the game title
if (!title.empty())
{
std::string cover_path(GetFullCoverPath(title, extension));
if (FileSystem::FileExists(cover_path.c_str()))
{
ret = std::move(cover_path);
return ret;
}
}
}
// Prioritize lookup by serial (Most specific)
if (!serial.empty() && try_name(serial))
return ret;
// Try file title (for modded games or specific like above)
const std::string_view file_title = Path::GetFileTitle(path);
if (!file_title.empty() && (title != file_title || is_custom_title) && try_name(file_title))
return ret;
// Last resort, check the game title
if (!title.empty() && !is_custom_title && try_name(title))
return ret;
return ret;
}

View File

@ -143,7 +143,8 @@ std::string FormatTimestamp(std::time_t timestamp);
TinyString FormatTimespan(std::time_t timespan, bool long_format = false);
std::string GetCoverImagePathForEntry(const Entry* entry);
std::string GetCoverImagePath(const std::string_view path, const std::string_view serial, const std::string_view title);
std::string GetCoverImagePath(const std::string_view path, const std::string_view serial, const std::string_view title,
bool is_custom_title);
std::string GetNewCoverImagePathForEntry(const Entry* entry, const char* new_filename, bool use_serial);
/// Returns a list of (title, entry) for entries matching serials. Titles will match the gamedb title,

View File

@ -312,9 +312,11 @@ void GameListModel::loadOrGenerateCover(const GameList::Entry* ge)
QtAsyncTask::create(this, [path = ge->path, serial = ge->serial, save_title = std::string(ge->GetSaveTitle()),
display_title = QtUtils::StringViewToQString(ge->GetDisplayTitle(m_show_localized_titles)),
placeholder_image = m_placeholder_image, list = this, width = getCoverArtSize(),
height = getCoverArtSize(), scale = m_cover_scale, dpr = m_device_pixel_ratio]() mutable {
height = getCoverArtSize(), scale = m_cover_scale, dpr = m_device_pixel_ratio,
is_custom_title = ge->has_custom_title]() mutable {
QImage image;
loadOrGenerateCover(image, placeholder_image, width, height, scale, dpr, path, serial, save_title, display_title);
loadOrGenerateCover(image, placeholder_image, width, height, scale, dpr, path, serial, save_title, display_title,
is_custom_title);
return [path = std::move(path), image = std::move(image), list, scale]() { list->coverLoaded(path, image, scale); };
});
}
@ -353,9 +355,10 @@ void GameListModel::createPlaceholderImage(QImage& image, const QImage& placehol
void GameListModel::loadOrGenerateCover(QImage& image, const QImage& placeholder_image, int width, int height,
float scale, qreal dpr, const std::string& path, const std::string& serial,
const std::string& save_title, const QString& display_title)
const std::string& save_title, const QString& display_title,
bool is_custom_title)
{
const std::string cover_path(GameList::GetCoverImagePath(path, serial, save_title));
const std::string cover_path = GameList::GetCoverImagePath(path, serial, save_title, is_custom_title);
if (!cover_path.empty())
{
image.load(QString::fromStdString(cover_path));

View File

@ -130,7 +130,7 @@ private:
static void loadOrGenerateCover(QImage& image, const QImage& placeholder_image, int width, int height, float scale,
qreal dpr, const std::string& path, const std::string& serial,
const std::string& save_title, const QString& display_title);
const std::string& save_title, const QString& display_title, bool is_custom_title);
static void createPlaceholderImage(QImage& image, const QImage& placeholder_image, int width, int height, float scale,
const QString& title);