diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index cd6f21e19a..56842a8490 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -749,7 +749,7 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri } const Core::TitleDatabase title_database; - m_title_description = title_database.Describe(m_gametdb_id); + m_title_description = title_database.Describe(m_gametdb_id, GetCurrentLanguage(bWii)); NOTICE_LOG(CORE, "Active title: %s", m_title_description.c_str()); Config::AddLayer(ConfigLoaders::GenerateGlobalGameConfigLoader(game_id, revision)); diff --git a/Source/Core/Core/TitleDatabase.cpp b/Source/Core/Core/TitleDatabase.cpp index 9a2a48f8d3..2d3f610220 100644 --- a/Source/Core/Core/TitleDatabase.cpp +++ b/Source/Core/Core/TitleDatabase.cpp @@ -21,47 +21,15 @@ namespace Core { static const std::string EMPTY_STRING; -static std::string GetLanguageCode(DiscIO::Language language) -{ - switch (language) - { - case DiscIO::Language::Japanese: - return "ja"; - case DiscIO::Language::English: - return "en"; - case DiscIO::Language::German: - return "de"; - case DiscIO::Language::French: - return "fr"; - case DiscIO::Language::Spanish: - return "es"; - case DiscIO::Language::Italian: - return "it"; - case DiscIO::Language::Dutch: - return "nl"; - case DiscIO::Language::SimplifiedChinese: - return "zh_CN"; - case DiscIO::Language::TraditionalChinese: - return "zh_TW"; - case DiscIO::Language::Korean: - return "ko"; - default: - return "en"; - } -} - using Map = std::unordered_map; -// Note that this function will not overwrite entries that already are in the maps -static bool LoadMap(const std::string& file_path, Map& map, - std::function predicate) +static Map LoadMap(const std::string& file_path) { + Map map; + std::ifstream txt; File::OpenFStream(txt, file_path, std::ios::in); - if (!txt.is_open()) - return false; - std::string line; while (std::getline(txt, line)) { @@ -72,119 +40,93 @@ static bool LoadMap(const std::string& file_path, Map& map, if (equals_index != std::string::npos) { const std::string game_id = StripSpaces(line.substr(0, equals_index)); - if (game_id.length() >= 4 && predicate(game_id)) + if (game_id.length() >= 4) map.emplace(game_id, StripSpaces(line.substr(equals_index + 1))); } } - return true; + return map; } -// This should only be used with the common game ID format (used by WiiTDBs), not Dolphin's. -// Otherwise, TurboGrafx-16 VC games (with the system ID P) will be misdetected as GameCube titles. -// The formats differ in that Dolphin's uses 6 characters for non-disc titles instead of 4. -static bool IsGCTitle(const std::string& game_id) +void TitleDatabase::AddLazyMap(DiscIO::Language language, const std::string& language_code) { - const char system_id = game_id[0]; - return game_id.length() == 6 && - (system_id == 'G' || system_id == 'D' || system_id == 'U' || system_id == 'P'); -} - -static bool IsWiiTitle(const std::string& game_id) -{ - // Assume that any non-GameCube title is a Wii title. - return !IsGCTitle(game_id); -} - -static bool IsJapaneseGCTitle(const std::string& game_id) -{ - if (!IsGCTitle(game_id)) - return false; - - return DiscIO::CountryCodeToCountry(game_id[3], DiscIO::Platform::GameCubeDisc) == - DiscIO::Country::Japan; -} - -static bool IsNonJapaneseGCTitle(const std::string& game_id) -{ - if (!IsGCTitle(game_id)) - return false; - - return DiscIO::CountryCodeToCountry(game_id[3], DiscIO::Platform::GameCubeDisc) != - DiscIO::Country::Japan; -} - -// Note that this function will not overwrite entries that already are in the maps -static bool LoadMap(const std::string& file_path, Map& gc_map, Map& wii_map) -{ - Map map; - if (!LoadMap(file_path, map, [](const auto& game_id) { return true; })) - return false; - - for (auto& entry : map) - { - auto& destination_map = IsGCTitle(entry.first) ? gc_map : wii_map; - destination_map.emplace(std::move(entry)); - } - return true; + m_title_maps[language] = [language_code]() -> Map { + return LoadMap(File::GetSysDirectory() + "wiitdb-" + language_code + ".txt"); + }; } TitleDatabase::TitleDatabase() { - // Load the user databases. + // User database const std::string& load_directory = File::GetUserPath(D_LOAD_IDX); - if (!LoadMap(load_directory + "wiitdb.txt", m_gc_title_map, m_wii_title_map)) - LoadMap(load_directory + "titles.txt", m_gc_title_map, m_wii_title_map); + m_user_title_map = LoadMap(load_directory + "wiitdb.txt"); + if (m_user_title_map.empty()) + m_user_title_map = LoadMap(load_directory + "titles.txt"); - if (!SConfig::GetInstance().m_use_builtin_title_database) - return; - - // Load the database in the console language. - // Note: The GameCube language setting can't be set to Japanese, - // so instead, we use Japanese names iff the games are NTSC-J. - const std::string gc_code = GetLanguageCode(SConfig::GetInstance().GetCurrentLanguage(false)); - const std::string wii_code = GetLanguageCode(SConfig::GetInstance().GetCurrentLanguage(true)); - LoadMap(File::GetSysDirectory() + "wiitdb-ja.txt", m_gc_title_map, IsJapaneseGCTitle); - if (gc_code != "en") - { - LoadMap(File::GetSysDirectory() + "wiitdb-" + gc_code + ".txt", m_gc_title_map, - IsNonJapaneseGCTitle); - } - if (wii_code != "en") - LoadMap(File::GetSysDirectory() + "wiitdb-" + wii_code + ".txt", m_wii_title_map, IsWiiTitle); - - // Load the English database as the base database. - LoadMap(File::GetSysDirectory() + "wiitdb-en.txt", m_gc_title_map, m_wii_title_map); + // Pre-defined databases (one per language) + AddLazyMap(DiscIO::Language::Japanese, "ja"); + AddLazyMap(DiscIO::Language::English, "en"); + AddLazyMap(DiscIO::Language::German, "de"); + AddLazyMap(DiscIO::Language::French, "fr"); + AddLazyMap(DiscIO::Language::Spanish, "es"); + AddLazyMap(DiscIO::Language::Italian, "it"); + AddLazyMap(DiscIO::Language::Dutch, "nl"); + AddLazyMap(DiscIO::Language::SimplifiedChinese, "zh_CN"); + AddLazyMap(DiscIO::Language::TraditionalChinese, "zh_TW"); + AddLazyMap(DiscIO::Language::Korean, "ko"); // Titles that cannot be part of the Wii TDB, // but common enough to justify having entries for them. // i18n: "Wii Menu" (or System Menu) refers to the Wii's main menu, // which is (usually) the first thing users see when a Wii console starts. - m_wii_title_map.emplace("0000000100000002", GetStringT("Wii Menu")); + m_base_map.emplace("0000000100000002", GetStringT("Wii Menu")); for (const auto& id : {"HAXX", "JODI", "00010001af1bf516", "LULZ", "OHBC"}) - m_wii_title_map.emplace(id, "The Homebrew Channel"); + m_base_map.emplace(id, "The Homebrew Channel"); } TitleDatabase::~TitleDatabase() = default; -const std::string& TitleDatabase::GetTitleName(const std::string& gametdb_id) const +const std::string& TitleDatabase::GetTitleName(const std::string& gametdb_id, + DiscIO::Language language) const { - const auto& map = IsWiiTitle(gametdb_id) ? m_wii_title_map : m_gc_title_map; - const auto iterator = map.find(gametdb_id); - return iterator != map.end() ? iterator->second : EMPTY_STRING; + auto it = m_user_title_map.find(gametdb_id); + if (it != m_user_title_map.end()) + return it->second; + + if (!SConfig::GetInstance().m_use_builtin_title_database) + return EMPTY_STRING; + + const Map& map = *m_title_maps.at(language); + it = map.find(gametdb_id); + if (it != map.end()) + return it->second; + + if (language != DiscIO::Language::English) + { + const Map& english_map = *m_title_maps.at(DiscIO::Language::English); + it = english_map.find(gametdb_id); + if (it != english_map.end()) + return it->second; + } + + it = m_base_map.find(gametdb_id); + if (it != m_base_map.end()) + return it->second; + + return EMPTY_STRING; } -const std::string& TitleDatabase::GetChannelName(u64 title_id) const +const std::string& TitleDatabase::GetChannelName(u64 title_id, DiscIO::Language language) const { const std::string id{ {static_cast((title_id >> 24) & 0xff), static_cast((title_id >> 16) & 0xff), static_cast((title_id >> 8) & 0xff), static_cast(title_id & 0xff)}}; - return GetTitleName(id); + return GetTitleName(id, language); } -std::string TitleDatabase::Describe(const std::string& gametdb_id) const +std::string TitleDatabase::Describe(const std::string& gametdb_id, DiscIO::Language language) const { - const std::string& title_name = GetTitleName(gametdb_id); + const std::string& title_name = GetTitleName(gametdb_id, language); if (title_name.empty()) return gametdb_id; return StringFromFormat("%s (%s)", title_name.c_str(), gametdb_id.c_str()); diff --git a/Source/Core/Core/TitleDatabase.h b/Source/Core/Core/TitleDatabase.h index 528c16d325..cb1e1475d6 100644 --- a/Source/Core/Core/TitleDatabase.h +++ b/Source/Core/Core/TitleDatabase.h @@ -8,6 +8,12 @@ #include #include "Common/CommonTypes.h" +#include "Common/Lazy.h" + +namespace DiscIO +{ +enum class Language; +} namespace Core { @@ -20,16 +26,20 @@ public: // Get a user friendly title name for a GameTDB ID. // This falls back to returning an empty string if none could be found. - const std::string& GetTitleName(const std::string& gametdb_id) const; + const std::string& GetTitleName(const std::string& gametdb_id, DiscIO::Language language) const; // Same as above, but takes a title ID instead of a GameTDB ID, and only works for channels. - const std::string& GetChannelName(u64 title_id) const; + const std::string& GetChannelName(u64 title_id, DiscIO::Language language) const; // Get a description for a GameTDB ID (title name if available + GameTDB ID). - std::string Describe(const std::string& gametdb_id) const; + std::string Describe(const std::string& gametdb_id, DiscIO::Language language) const; private: - std::unordered_map m_wii_title_map; - std::unordered_map m_gc_title_map; + void AddLazyMap(DiscIO::Language language, const std::string& language_code); + + std::unordered_map>> + m_title_maps; + std::unordered_map m_base_map; + std::unordered_map m_user_title_map; }; } // namespace Core diff --git a/Source/Core/DolphinQt/MenuBar.cpp b/Source/Core/DolphinQt/MenuBar.cpp index e533ab73ae..f973335cac 100644 --- a/Source/Core/DolphinQt/MenuBar.cpp +++ b/Source/Core/DolphinQt/MenuBar.cpp @@ -43,6 +43,7 @@ #include "Core/TitleDatabase.h" #include "Core/WiiUtils.h" +#include "DiscIO/Enums.h" #include "DiscIO/NANDImporter.h" #include "DiscIO/WiiSaveBanner.h" @@ -1038,11 +1039,12 @@ void MenuBar::CheckNAND() { std::string title_listings; Core::TitleDatabase title_db; + const DiscIO::Language language = SConfig::GetInstance().GetCurrentLanguage(true); for (const u64 title_id : result.titles_to_remove) { title_listings += StringFromFormat("%016" PRIx64, title_id); - const std::string database_name = title_db.GetChannelName(title_id); + const std::string database_name = title_db.GetChannelName(title_id, language); if (!database_name.empty()) { title_listings += " - " + database_name; diff --git a/Source/Core/UICommon/GameFile.cpp b/Source/Core/UICommon/GameFile.cpp index ed62d69ce4..b963450cc7 100644 --- a/Source/Core/UICommon/GameFile.cpp +++ b/Source/Core/UICommon/GameFile.cpp @@ -50,7 +50,7 @@ static const std::string EMPTY_STRING; static bool UseGameCovers() { // We ifdef this out on Android because accessing the config before emulation start makes us crash. -// The Android GUI doesn't support covers anyway, so this doesn't make us lose out on functionality. +// The Android GUI handles covers in Java anyway, so this doesn't make us lose any functionality. #ifdef ANDROID return false; #else @@ -58,6 +58,17 @@ static bool UseGameCovers() #endif } +DiscIO::Language GameFile::GetConfigLanguage() const +{ +#ifdef ANDROID + // TODO: Make the Android app load the config at app start instead of emulation start + // so that we can access the user's preference here + return DiscIO::Language::English; +#else + return SConfig::GetInstance().GetCurrentLanguage(DiscIO::IsWii(m_platform)); +#endif +} + bool operator==(const GameBanner& lhs, const GameBanner& rhs) { return std::tie(lhs.buffer, lhs.width, lhs.height) == std::tie(rhs.buffer, rhs.width, rhs.height); @@ -94,15 +105,7 @@ const std::string& GameFile::Lookup(DiscIO::Language language, const std::string& GameFile::LookupUsingConfigLanguage(const std::map& strings) const { -#ifdef ANDROID - // TODO: Make the Android app load the config at app start instead of emulation start - // so that we can access the user's preference here - const DiscIO::Language language = DiscIO::Language::English; -#else - const bool wii = DiscIO::IsWii(m_platform); - const DiscIO::Language language = SConfig::GetInstance().GetCurrentLanguage(wii); -#endif - return Lookup(language, strings); + return Lookup(GetConfigLanguage(), strings); } GameFile::GameFile(const std::string& path) @@ -422,7 +425,7 @@ void GameFile::CustomBannerCommit() const std::string& GameFile::GetName(const Core::TitleDatabase& title_database) const { - const std::string& custom_name = title_database.GetTitleName(m_gametdb_id); + const std::string& custom_name = title_database.GetTitleName(m_gametdb_id, GetConfigLanguage()); return custom_name.empty() ? GetName() : custom_name; } diff --git a/Source/Core/UICommon/GameFile.h b/Source/Core/UICommon/GameFile.h index 1790050ea9..a31dc21b8a 100644 --- a/Source/Core/UICommon/GameFile.h +++ b/Source/Core/UICommon/GameFile.h @@ -96,6 +96,7 @@ public: void CustomCoverCommit(); private: + DiscIO::Language GetConfigLanguage() const; static const std::string& Lookup(DiscIO::Language language, const std::map& strings); const std::string&