GameList: Add custom title/regions
Largely inspired by https://github.com/PCSX2/pcsx2/pull/9330, but almost entirely rewritten.
This commit is contained in:
@ -11,6 +11,7 @@
|
||||
|
||||
#include "util/cd_image.h"
|
||||
#include "util/http_downloader.h"
|
||||
#include "util/ini_settings_interface.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/byte_stream.h"
|
||||
@ -69,13 +70,19 @@ static bool GetExeListEntry(const std::string& path, Entry* entry);
|
||||
static bool GetPsfListEntry(const std::string& path, Entry* entry);
|
||||
static bool GetDiscListEntry(const std::string& path, Entry* entry);
|
||||
|
||||
static bool GetGameListEntryFromCache(const std::string& path, Entry* entry);
|
||||
static void ApplyCustomAttributes(const std::string& path, Entry* entry,
|
||||
const INISettingsInterface& custom_attributes_ini);
|
||||
static bool RescanCustomAttributesForPath(const std::string& path, const INISettingsInterface& custom_attributes_ini);
|
||||
static bool GetGameListEntryFromCache(const std::string& path, Entry* entry,
|
||||
const INISettingsInterface& custom_attributes_ini);
|
||||
static Entry* GetMutableEntryForPath(std::string_view path);
|
||||
static void ScanDirectory(const char* path, bool recursive, bool only_cache,
|
||||
const std::vector<std::string>& excluded_paths, const PlayedTimeMap& played_time_map,
|
||||
ProgressCallback* progress);
|
||||
static bool AddFileFromCache(const std::string& path, std::time_t timestamp, const PlayedTimeMap& played_time_map);
|
||||
const INISettingsInterface& custom_attributes_ini, ProgressCallback* progress);
|
||||
static bool AddFileFromCache(const std::string& path, std::time_t timestamp, const PlayedTimeMap& played_time_map,
|
||||
const INISettingsInterface& custom_attributes_ini);
|
||||
static bool ScanFile(std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock,
|
||||
const PlayedTimeMap& played_time_map);
|
||||
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini);
|
||||
|
||||
static std::string GetCacheFilename();
|
||||
static void LoadCache();
|
||||
@ -92,6 +99,8 @@ static std::string MakePlayedTimeLine(const std::string& serial, const PlayedTim
|
||||
static PlayedTimeMap LoadPlayedTimeMap(const std::string& path);
|
||||
static PlayedTimeEntry UpdatePlayedTimeFile(const std::string& path, const std::string& serial, std::time_t last_time,
|
||||
std::time_t add_time);
|
||||
|
||||
static std::string GetCustomPropertiesFile();
|
||||
} // namespace GameList
|
||||
|
||||
static std::vector<GameList::Entry> s_entries;
|
||||
@ -307,7 +316,8 @@ bool GameList::PopulateEntryFromPath(const std::string& path, Entry* entry)
|
||||
return GetDiscListEntry(path, entry);
|
||||
}
|
||||
|
||||
bool GameList::GetGameListEntryFromCache(const std::string& path, Entry* entry)
|
||||
bool GameList::GetGameListEntryFromCache(const std::string& path, Entry* entry,
|
||||
const INISettingsInterface& custom_attributes_ini)
|
||||
{
|
||||
auto iter = s_cache_map.find(path);
|
||||
if (iter == s_cache_map.end())
|
||||
@ -315,6 +325,7 @@ bool GameList::GetGameListEntryFromCache(const std::string& path, Entry* entry)
|
||||
|
||||
*entry = std::move(iter->second);
|
||||
s_cache_map.erase(iter);
|
||||
ApplyCustomAttributes(path, entry, custom_attributes_ini);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -491,7 +502,7 @@ static bool IsPathExcluded(const std::vector<std::string>& excluded_paths, const
|
||||
|
||||
void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache,
|
||||
const std::vector<std::string>& excluded_paths, const PlayedTimeMap& played_time_map,
|
||||
ProgressCallback* progress)
|
||||
const INISettingsInterface& custom_attributes_ini, ProgressCallback* progress)
|
||||
{
|
||||
INFO_LOG("Scanning {}{}", path, recursive ? " (recursively)" : "");
|
||||
|
||||
@ -521,15 +532,15 @@ void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache,
|
||||
}
|
||||
|
||||
std::unique_lock lock(s_mutex);
|
||||
if (GetEntryForPath(ffd.FileName) || AddFileFromCache(ffd.FileName, ffd.ModificationTime, played_time_map) ||
|
||||
only_cache)
|
||||
if (GetEntryForPath(ffd.FileName) ||
|
||||
AddFileFromCache(ffd.FileName, ffd.ModificationTime, played_time_map, custom_attributes_ini) || only_cache)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
progress->SetStatusText(SmallString::from_format(TRANSLATE_FS("GameList", "Scanning '{}'..."),
|
||||
FileSystem::GetDisplayNameFromPath(ffd.FileName)));
|
||||
ScanFile(std::move(ffd.FileName), ffd.ModificationTime, lock, played_time_map);
|
||||
ScanFile(std::move(ffd.FileName), ffd.ModificationTime, lock, played_time_map, custom_attributes_ini);
|
||||
progress->SetProgressValue(files_scanned);
|
||||
}
|
||||
|
||||
@ -537,10 +548,11 @@ void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache,
|
||||
progress->PopState();
|
||||
}
|
||||
|
||||
bool GameList::AddFileFromCache(const std::string& path, std::time_t timestamp, const PlayedTimeMap& played_time_map)
|
||||
bool GameList::AddFileFromCache(const std::string& path, std::time_t timestamp, const PlayedTimeMap& played_time_map,
|
||||
const INISettingsInterface& custom_attributes_ini)
|
||||
{
|
||||
Entry entry;
|
||||
if (!GetGameListEntryFromCache(path, &entry) || entry.last_modified_time != timestamp)
|
||||
if (!GetGameListEntryFromCache(path, &entry, custom_attributes_ini) || entry.last_modified_time != timestamp)
|
||||
return false;
|
||||
|
||||
auto iter = played_time_map.find(entry.serial);
|
||||
@ -555,7 +567,7 @@ bool GameList::AddFileFromCache(const std::string& path, std::time_t timestamp,
|
||||
}
|
||||
|
||||
bool GameList::ScanFile(std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock,
|
||||
const PlayedTimeMap& played_time_map)
|
||||
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini)
|
||||
{
|
||||
// don't block UI while scanning
|
||||
lock.unlock();
|
||||
@ -575,18 +587,97 @@ bool GameList::ScanFile(std::string path, std::time_t timestamp, std::unique_loc
|
||||
WARNING_LOG("Failed to write entry '{}' to cache", entry.path);
|
||||
}
|
||||
|
||||
auto iter = played_time_map.find(entry.serial);
|
||||
const auto iter = played_time_map.find(entry.serial);
|
||||
if (iter != played_time_map.end())
|
||||
{
|
||||
entry.last_played_time = iter->second.last_played_time;
|
||||
entry.total_played_time = iter->second.total_played_time;
|
||||
}
|
||||
|
||||
ApplyCustomAttributes(path, &entry, custom_attributes_ini);
|
||||
|
||||
lock.lock();
|
||||
s_entries.push_back(std::move(entry));
|
||||
|
||||
// replace if present
|
||||
auto it = std::find_if(s_entries.begin(), s_entries.end(),
|
||||
[&entry](const Entry& existing_entry) { return (existing_entry.path == entry.path); });
|
||||
if (it != s_entries.end())
|
||||
*it = std::move(entry);
|
||||
else
|
||||
s_entries.push_back(std::move(entry));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameList::RescanCustomAttributesForPath(const std::string& path, const INISettingsInterface& custom_attributes_ini)
|
||||
{
|
||||
FILESYSTEM_STAT_DATA sd;
|
||||
if (!FileSystem::StatFile(path.c_str(), &sd))
|
||||
return false;
|
||||
|
||||
{
|
||||
// cancel if excluded
|
||||
const std::vector<std::string> excluded_paths(Host::GetBaseStringListSetting("GameList", "ExcludedPaths"));
|
||||
if (IsPathExcluded(excluded_paths, path))
|
||||
return false;
|
||||
}
|
||||
|
||||
Entry entry;
|
||||
if (!PopulateEntryFromPath(path, &entry))
|
||||
return false;
|
||||
|
||||
entry.path = std::move(path);
|
||||
entry.last_modified_time = sd.ModificationTime;
|
||||
|
||||
const PlayedTimeMap played_time_map(LoadPlayedTimeMap(GetPlayedTimeFile()));
|
||||
const auto iter = played_time_map.find(entry.serial);
|
||||
if (iter != played_time_map.end())
|
||||
{
|
||||
entry.last_played_time = iter->second.last_played_time;
|
||||
entry.total_played_time = iter->second.total_played_time;
|
||||
}
|
||||
|
||||
ApplyCustomAttributes(path, &entry, custom_attributes_ini);
|
||||
|
||||
std::unique_lock lock(s_mutex);
|
||||
|
||||
// replace if present
|
||||
auto it = std::find_if(s_entries.begin(), s_entries.end(),
|
||||
[&entry](const Entry& existing_entry) { return (existing_entry.path == entry.path); });
|
||||
if (it != s_entries.end())
|
||||
*it = std::move(entry);
|
||||
else
|
||||
s_entries.push_back(std::move(entry));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameList::ApplyCustomAttributes(const std::string& path, Entry* entry,
|
||||
const INISettingsInterface& custom_attributes_ini)
|
||||
{
|
||||
std::optional<std::string> custom_title = custom_attributes_ini.GetOptionalStringValue(path.c_str(), "Title");
|
||||
if (custom_title.has_value())
|
||||
{
|
||||
entry->title = std::move(custom_title.value());
|
||||
entry->has_custom_title = true;
|
||||
}
|
||||
const std::optional<SmallString> custom_region_str =
|
||||
custom_attributes_ini.GetOptionalSmallStringValue(path.c_str(), "Region");
|
||||
if (custom_region_str.has_value())
|
||||
{
|
||||
const std::optional<DiscRegion> custom_region = Settings::ParseDiscRegionName(custom_region_str.value());
|
||||
if (custom_region.has_value())
|
||||
{
|
||||
entry->region = custom_region.value();
|
||||
entry->has_custom_region = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
WARNING_LOG("Invalid region '{}' in custom attributes for '{}'", custom_region_str.value(), path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_lock<std::recursive_mutex> GameList::GetLock()
|
||||
{
|
||||
return std::unique_lock<std::recursive_mutex>(s_mutex);
|
||||
@ -599,7 +690,12 @@ const GameList::Entry* GameList::GetEntryByIndex(u32 index)
|
||||
|
||||
const GameList::Entry* GameList::GetEntryForPath(std::string_view path)
|
||||
{
|
||||
for (const Entry& entry : s_entries)
|
||||
return GetMutableEntryForPath(path);
|
||||
}
|
||||
|
||||
GameList::Entry* GameList::GetMutableEntryForPath(std::string_view path)
|
||||
{
|
||||
for (Entry& entry : s_entries)
|
||||
{
|
||||
// Use case-insensitive compare on Windows, since it's the same file.
|
||||
#ifdef _WIN32
|
||||
@ -709,6 +805,8 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback*
|
||||
const std::vector<std::string> dirs(Host::GetBaseStringListSetting("GameList", "Paths"));
|
||||
std::vector<std::string> recursive_dirs(Host::GetBaseStringListSetting("GameList", "RecursivePaths"));
|
||||
const PlayedTimeMap played_time(LoadPlayedTimeMap(GetPlayedTimeFile()));
|
||||
INISettingsInterface custom_attributes_ini(GetCustomPropertiesFile());
|
||||
custom_attributes_ini.Load();
|
||||
|
||||
#ifdef __ANDROID__
|
||||
recursive_dirs.push_back(Path::Combine(EmuFolders::DataRoot, "games"));
|
||||
@ -726,7 +824,7 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback*
|
||||
if (progress->IsCancelled())
|
||||
break;
|
||||
|
||||
ScanDirectory(dir.c_str(), false, only_cache, excluded_paths, played_time, progress);
|
||||
ScanDirectory(dir.c_str(), false, only_cache, excluded_paths, played_time, custom_attributes_ini, progress);
|
||||
progress->SetProgressValue(++directory_counter);
|
||||
}
|
||||
for (const std::string& dir : recursive_dirs)
|
||||
@ -734,7 +832,7 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback*
|
||||
if (progress->IsCancelled())
|
||||
break;
|
||||
|
||||
ScanDirectory(dir.c_str(), true, only_cache, excluded_paths, played_time, progress);
|
||||
ScanDirectory(dir.c_str(), true, only_cache, excluded_paths, played_time, custom_attributes_ini, progress);
|
||||
progress->SetProgressValue(++directory_counter);
|
||||
}
|
||||
}
|
||||
@ -1425,3 +1523,109 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, boo
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GameList::GetCustomPropertiesFile()
|
||||
{
|
||||
return Path::Combine(EmuFolders::DataRoot, "custom_properties.ini");
|
||||
}
|
||||
|
||||
void GameList::SaveCustomTitleForPath(const std::string& path, const std::string& custom_title)
|
||||
{
|
||||
INISettingsInterface custom_attributes_ini(GetCustomPropertiesFile());
|
||||
custom_attributes_ini.Load();
|
||||
|
||||
if (!custom_title.empty())
|
||||
{
|
||||
custom_attributes_ini.SetStringValue(path.c_str(), "Title", custom_title.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
custom_attributes_ini.DeleteValue(path.c_str(), "Title");
|
||||
custom_attributes_ini.RemoveEmptySections();
|
||||
}
|
||||
|
||||
Error error;
|
||||
if (!custom_attributes_ini.Save(&error))
|
||||
{
|
||||
ERROR_LOG("Failed to save custom attributes: {}", error.GetDescription());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!custom_title.empty())
|
||||
{
|
||||
// Can skip the rescan and just update the value directly.
|
||||
auto lock = GetLock();
|
||||
Entry* entry = GetMutableEntryForPath(path);
|
||||
if (entry)
|
||||
{
|
||||
entry->title = custom_title;
|
||||
entry->has_custom_title = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let the cache update by rescanning. Only need to do this on deletion, to get the original value.
|
||||
RescanCustomAttributesForPath(path, custom_attributes_ini);
|
||||
}
|
||||
}
|
||||
|
||||
void GameList::SaveCustomRegionForPath(const std::string& path, const std::optional<DiscRegion> custom_region)
|
||||
{
|
||||
INISettingsInterface custom_attributes_ini(GetCustomPropertiesFile());
|
||||
custom_attributes_ini.Load();
|
||||
|
||||
if (custom_region.has_value())
|
||||
{
|
||||
custom_attributes_ini.SetStringValue(path.c_str(), "Region", Settings::GetDiscRegionName(custom_region.value()));
|
||||
}
|
||||
else
|
||||
{
|
||||
custom_attributes_ini.DeleteValue(path.c_str(), "Region");
|
||||
custom_attributes_ini.RemoveEmptySections();
|
||||
}
|
||||
|
||||
Error error;
|
||||
if (!custom_attributes_ini.Save(&error))
|
||||
{
|
||||
ERROR_LOG("Failed to save custom attributes: {}", error.GetDescription());
|
||||
return;
|
||||
}
|
||||
|
||||
if (custom_region.has_value())
|
||||
{
|
||||
// Can skip the rescan and just update the value directly.
|
||||
auto lock = GetLock();
|
||||
Entry* entry = GetMutableEntryForPath(path);
|
||||
if (entry)
|
||||
{
|
||||
entry->region = custom_region.value();
|
||||
entry->has_custom_region = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let the cache update by rescanning. Only need to do this on deletion, to get the original value.
|
||||
RescanCustomAttributesForPath(path, custom_attributes_ini);
|
||||
}
|
||||
}
|
||||
|
||||
std::string GameList::GetCustomTitleForPath(const std::string_view path)
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
std::unique_lock lock(s_mutex);
|
||||
const GameList::Entry* entry = GetEntryForPath(path);
|
||||
if (entry && entry->has_custom_title)
|
||||
ret = entry->title;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::optional<DiscRegion> GameList::GetCustomRegionForPath(const std::string_view path)
|
||||
{
|
||||
const GameList::Entry* entry = GetEntryForPath(path);
|
||||
if (entry && entry->has_custom_region)
|
||||
return entry->region;
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@ -59,6 +59,8 @@ struct Entry
|
||||
u8 max_blocks = 0;
|
||||
s8 disc_set_index = -1;
|
||||
bool disc_set_member = false;
|
||||
bool has_custom_title = false;
|
||||
bool has_custom_region = false;
|
||||
|
||||
GameDatabase::CompatibilityRating compatibility = GameDatabase::CompatibilityRating::Unknown;
|
||||
|
||||
@ -122,6 +124,12 @@ GetMatchingEntriesForSerial(const std::span<const std::string> serials);
|
||||
bool DownloadCovers(const std::vector<std::string>& url_templates, bool use_serial = false,
|
||||
ProgressCallback* progress = nullptr,
|
||||
std::function<void(const Entry*, std::string)> save_callback = {});
|
||||
|
||||
// Custom properties support
|
||||
void SaveCustomTitleForPath(const std::string& path, const std::string& custom_title);
|
||||
void SaveCustomRegionForPath(const std::string& path, const std::optional<DiscRegion> custom_region);
|
||||
std::string GetCustomTitleForPath(const std::string_view path);
|
||||
std::optional<DiscRegion> GetCustomRegionForPath(const std::string_view path);
|
||||
}; // namespace GameList
|
||||
|
||||
namespace Host {
|
||||
|
||||
@ -186,7 +186,8 @@ static std::string s_running_game_serial;
|
||||
static std::string s_running_game_title;
|
||||
static const GameDatabase::Entry* s_running_game_entry = nullptr;
|
||||
static System::GameHash s_running_game_hash;
|
||||
static bool s_was_fast_booted;
|
||||
static bool s_running_game_custom_title = false;
|
||||
static bool s_was_fast_booted = false;
|
||||
|
||||
static bool s_system_executing = false;
|
||||
static bool s_system_interrupted = false;
|
||||
@ -988,20 +989,6 @@ DiscRegion System::GetRegionForPsf(const char* path)
|
||||
return psf.GetRegion();
|
||||
}
|
||||
|
||||
std::optional<DiscRegion> System::GetRegionForPath(const char* image_path)
|
||||
{
|
||||
if (IsExeFileName(image_path))
|
||||
return GetRegionForExe(image_path);
|
||||
else if (IsPsfFileName(image_path))
|
||||
return GetRegionForPsf(image_path);
|
||||
|
||||
std::unique_ptr<CDImage> cdi = CDImage::Open(image_path, false, nullptr);
|
||||
if (!cdi)
|
||||
return {};
|
||||
|
||||
return GetRegionForImage(cdi.get());
|
||||
}
|
||||
|
||||
std::string System::GetGameSettingsPath(std::string_view game_serial)
|
||||
{
|
||||
// multi-disc games => always use the first disc
|
||||
@ -1507,7 +1494,7 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
||||
return false;
|
||||
}
|
||||
|
||||
disc_region = GetRegionForImage(disc.get());
|
||||
disc_region = GameList::GetCustomRegionForPath(parameters.filename).value_or(GetRegionForImage(disc.get()));
|
||||
if (s_region == ConsoleRegion::Auto)
|
||||
{
|
||||
if (disc_region != DiscRegion::Other)
|
||||
@ -2583,7 +2570,8 @@ bool System::LoadStateFromStream(ByteStream* state, Error* error, bool update_di
|
||||
CDROM::Reset();
|
||||
if (media)
|
||||
{
|
||||
const DiscRegion region = GetRegionForImage(media.get());
|
||||
const DiscRegion region =
|
||||
GameList::GetCustomRegionForPath(media_filename).value_or(GetRegionForImage(media.get()));
|
||||
CDROM::InsertMedia(std::move(media), region);
|
||||
if (g_settings.cdrom_load_image_to_ram)
|
||||
CDROM::PrecacheMedia();
|
||||
@ -3385,7 +3373,9 @@ std::unique_ptr<MemoryCard> System::GetMemoryCardForSlot(u32 slot, MemoryCardTyp
|
||||
|
||||
// But prefer a disc-specific card if one already exists.
|
||||
std::string disc_card_path = g_settings.GetGameMemoryCardPath(
|
||||
Path::SanitizeFileName(s_running_game_entry ? s_running_game_entry->title : s_running_game_title), slot);
|
||||
Path::SanitizeFileName((s_running_game_entry && !s_running_game_custom_title) ? s_running_game_entry->title :
|
||||
s_running_game_title),
|
||||
slot);
|
||||
if (disc_card_path != card_path)
|
||||
{
|
||||
if (card_path.empty() || !g_settings.memory_card_use_playlist_title ||
|
||||
@ -3627,7 +3617,7 @@ bool System::InsertMedia(const char* path)
|
||||
return false;
|
||||
}
|
||||
|
||||
const DiscRegion region = GetRegionForImage(image.get());
|
||||
const DiscRegion region = GameList::GetCustomRegionForPath(path).value_or(GetRegionForImage(image.get()));
|
||||
UpdateRunningGame(path, image.get(), false);
|
||||
CDROM::InsertMedia(std::move(image), region);
|
||||
INFO_LOG("Inserted media from {} ({}, {})", s_running_game_path, s_running_game_serial, s_running_game_title);
|
||||
@ -3668,14 +3658,19 @@ void System::UpdateRunningGame(const char* path, CDImage* image, bool booting)
|
||||
s_running_game_title.clear();
|
||||
s_running_game_entry = nullptr;
|
||||
s_running_game_hash = 0;
|
||||
s_running_game_custom_title = false;
|
||||
|
||||
if (path && std::strlen(path) > 0)
|
||||
{
|
||||
s_running_game_path = path;
|
||||
s_running_game_title = GameList::GetCustomTitleForPath(s_running_game_path);
|
||||
s_running_game_custom_title = !s_running_game_title.empty();
|
||||
|
||||
if (IsExeFileName(path))
|
||||
{
|
||||
s_running_game_title = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path));
|
||||
if (s_running_game_title.empty())
|
||||
s_running_game_title = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path));
|
||||
|
||||
s_running_game_hash = GetGameHashFromFile(path);
|
||||
if (s_running_game_hash != 0)
|
||||
s_running_game_serial = GetGameHashId(s_running_game_hash);
|
||||
@ -3683,7 +3678,8 @@ void System::UpdateRunningGame(const char* path, CDImage* image, bool booting)
|
||||
else if (IsPsfFileName(path))
|
||||
{
|
||||
// TODO: We could pull the title from the PSF.
|
||||
s_running_game_title = Path::GetFileTitle(path);
|
||||
if (s_running_game_title.empty())
|
||||
s_running_game_title = Path::GetFileTitle(path);
|
||||
}
|
||||
// Check for an audio CD. Those shouldn't set any title.
|
||||
else if (image && image->GetTrack(1).mode != CDImage::TrackMode::Audio)
|
||||
@ -3695,19 +3691,24 @@ void System::UpdateRunningGame(const char* path, CDImage* image, bool booting)
|
||||
if (s_running_game_entry)
|
||||
{
|
||||
s_running_game_serial = s_running_game_entry->serial;
|
||||
s_running_game_title = s_running_game_entry->title;
|
||||
if (s_running_game_title.empty())
|
||||
s_running_game_title = s_running_game_entry->title;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_running_game_serial = std::move(id);
|
||||
s_running_game_title = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path));
|
||||
if (s_running_game_title.empty())
|
||||
s_running_game_title = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path));
|
||||
}
|
||||
|
||||
if (image->HasSubImages())
|
||||
{
|
||||
std::string image_title = image->GetMetadata("title");
|
||||
if (!image_title.empty())
|
||||
{
|
||||
s_running_game_title = std::move(image_title);
|
||||
s_running_game_custom_title = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,7 +140,6 @@ DiscRegion GetRegionFromSystemArea(CDImage* cdi);
|
||||
DiscRegion GetRegionForImage(CDImage* cdi);
|
||||
DiscRegion GetRegionForExe(const char* path);
|
||||
DiscRegion GetRegionForPsf(const char* path);
|
||||
std::optional<DiscRegion> GetRegionForPath(const char* image_path);
|
||||
|
||||
/// Returns the path for the game settings ini file for the specified serial.
|
||||
std::string GetGameSettingsPath(std::string_view game_serial);
|
||||
|
||||
Reference in New Issue
Block a user