From 8842a0f402ffe60a80ce7894bfd36afd039009fc Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 23 Feb 2019 17:49:06 +0100 Subject: [PATCH 1/3] Keep track of GameTDB ID separately from game ID The difference between Dolphin's game IDs and GameTDB's game IDs is that GameTDB uses four characters for non-disc titles, whereas Dolphin uses six characters for all titles. This fixes: - TitleDatabase considering Datel discs to be NHL Hitz 2002 - Gecko code downloading not working for discs with IDs starting with P - Cover downloading mixing up discs with channels (e.g. Mario Kart Wii and Mario Kart Channel) and making extra HTTP requests. (Android was actually doing a better job at this than DolphinQt!) --- .../dolphinemu/dolphinemu/model/GameFile.java | 4 ++- .../dolphinemu/utils/CoverHelper.java | 7 ++-- Source/Android/jni/GameList/GameFile.cpp | 6 ++++ Source/Core/Core/ConfigManager.cpp | 26 ++++++++------- Source/Core/Core/ConfigManager.h | 5 +-- Source/Core/Core/GeckoCodeConfig.cpp | 16 ++-------- Source/Core/Core/GeckoCodeConfig.h | 2 +- Source/Core/Core/IOS/ES/Formats.cpp | 14 ++++++++ Source/Core/Core/IOS/ES/Formats.h | 8 ++++- Source/Core/Core/TitleDatabase.cpp | 18 +++++------ Source/Core/Core/TitleDatabase.h | 16 +++------- Source/Core/DiscIO/Volume.h | 1 + Source/Core/DiscIO/VolumeGC.cpp | 11 +++++++ Source/Core/DiscIO/VolumeGC.h | 1 + Source/Core/DiscIO/VolumeWad.cpp | 5 +++ Source/Core/DiscIO/VolumeWad.h | 1 + Source/Core/DiscIO/VolumeWii.cpp | 5 +++ Source/Core/DiscIO/VolumeWii.h | 1 + .../Core/DolphinQt/Config/GeckoCodeWidget.cpp | 6 ++-- .../Core/DolphinQt/Config/GeckoCodeWidget.h | 1 + Source/Core/UICommon/GameFile.cpp | 32 ++++++------------- Source/Core/UICommon/GameFile.h | 2 ++ Source/Core/UICommon/GameFileCache.cpp | 2 +- 23 files changed, 106 insertions(+), 84 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameFile.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameFile.java index 45d0eb9060..5a247137ab 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameFile.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameFile.java @@ -30,6 +30,8 @@ public class GameFile public native String getGameId(); + public native String getGameTdbId(); + public native int getDiscNumber(); public native int getRevision(); @@ -43,7 +45,7 @@ public class GameFile public String getCoverPath() { return Environment.getExternalStorageDirectory().getPath() + - "/dolphin-emu/Cache/GameCovers/" + getGameId() + ".png"; + "/dolphin-emu/Cache/GameCovers/" + getGameTdbId() + ".png"; } public String getCustomCoverPath() diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CoverHelper.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CoverHelper.java index a6184e8681..2c0cd393a0 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CoverHelper.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CoverHelper.java @@ -12,10 +12,7 @@ public final class CoverHelper public static String buildGameTDBUrl(GameFile game, String region) { - String gameId = game.getGameId(); - if (game.getPlatform() == 2) // WiiWare - gameId = gameId.substring(0, 4); - return String.format(baseUrl, region, gameId); + return String.format(baseUrl, region, game.getGameTdbId()); } public static String getRegion(GameFile game) @@ -77,4 +74,4 @@ public final class CoverHelper // Do nothing } } -} \ No newline at end of file +} diff --git a/Source/Android/jni/GameList/GameFile.cpp b/Source/Android/jni/GameList/GameFile.cpp index b84426a1ca..ee889709fd 100644 --- a/Source/Android/jni/GameList/GameFile.cpp +++ b/Source/Android/jni/GameList/GameFile.cpp @@ -123,6 +123,12 @@ JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getGameI return ToJString(env, GetRef(env, obj)->GetGameID()); } +JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getGameTdbId(JNIEnv* env, + jobject obj) +{ + return ToJString(env, GetRef(env, obj)->GetGameTDBID()); +} + JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getDiscNumber(JNIEnv* env, jobject obj) { diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 95b1222c17..cd6f21e19a 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -680,7 +680,7 @@ void SConfig::LoadJitDebugSettings(IniFile& ini) void SConfig::ResetRunningGameMetadata() { - SetRunningGameMetadata("00000000", 0, 0, Core::TitleDatabase::TitleType::Other); + SetRunningGameMetadata("00000000", "", 0, 0); } void SConfig::SetRunningGameMetadata(const DiscIO::Volume& volume, @@ -688,14 +688,14 @@ void SConfig::SetRunningGameMetadata(const DiscIO::Volume& volume, { if (partition == volume.GetGamePartition()) { - SetRunningGameMetadata(volume.GetGameID(), volume.GetTitleID().value_or(0), - volume.GetRevision().value_or(0), Core::TitleDatabase::TitleType::Other); + SetRunningGameMetadata(volume.GetGameID(), volume.GetGameTDBID(), + volume.GetTitleID().value_or(0), volume.GetRevision().value_or(0)); } else { - SetRunningGameMetadata(volume.GetGameID(partition), volume.GetTitleID(partition).value_or(0), - volume.GetRevision(partition).value_or(0), - Core::TitleDatabase::TitleType::Other); + SetRunningGameMetadata(volume.GetGameID(partition), volume.GetGameTDBID(), + volume.GetTitleID(partition).value_or(0), + volume.GetRevision(partition).value_or(0)); } } @@ -710,16 +710,18 @@ void SConfig::SetRunningGameMetadata(const IOS::ES::TMDReader& tmd) if (!DVDInterface::UpdateRunningGameMetadata(tmd_title_id)) { // If not launching a disc game, just read everything from the TMD. - SetRunningGameMetadata(tmd.GetGameID(), tmd_title_id, tmd.GetTitleVersion(), - Core::TitleDatabase::TitleType::Channel); + const std::string game_id = tmd.GetGameID(); + SetRunningGameMetadata(game_id, game_id, tmd_title_id, tmd.GetTitleVersion()); } } -void SConfig::SetRunningGameMetadata(const std::string& game_id, u64 title_id, u16 revision, - Core::TitleDatabase::TitleType type) +void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id, + u64 title_id, u16 revision) { - const bool was_changed = m_game_id != game_id || m_title_id != title_id || m_revision != revision; + const bool was_changed = m_game_id != game_id || m_gametdb_id != gametdb_id || + m_title_id != title_id || m_revision != revision; m_game_id = game_id; + m_gametdb_id = gametdb_id; m_title_id = title_id; m_revision = revision; @@ -747,7 +749,7 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, u64 title_id, u } const Core::TitleDatabase title_database; - m_title_description = title_database.Describe(m_game_id, type); + m_title_description = title_database.Describe(m_gametdb_id); NOTICE_LOG(CORE, "Active title: %s", m_title_description.c_str()); Config::AddLayer(ConfigLoaders::GenerateGlobalGameConfigLoader(game_id, revision)); diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index 8b95fde543..50d79ea82c 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -367,12 +367,13 @@ private: void LoadAutoUpdateSettings(IniFile& ini); void LoadJitDebugSettings(IniFile& ini); - void SetRunningGameMetadata(const std::string& game_id, u64 title_id, u16 revision, - Core::TitleDatabase::TitleType type); + void SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id, + u64 title_id, u16 revision); static SConfig* m_Instance; std::string m_game_id; + std::string m_gametdb_id; std::string m_title_description; u64 m_title_id; u16 m_revision; diff --git a/Source/Core/Core/GeckoCodeConfig.cpp b/Source/Core/Core/GeckoCodeConfig.cpp index 2365794b8f..e1d522ba1b 100644 --- a/Source/Core/Core/GeckoCodeConfig.cpp +++ b/Source/Core/Core/GeckoCodeConfig.cpp @@ -16,21 +16,9 @@ namespace Gecko { -std::vector DownloadCodes(std::string gameid, bool* succeeded) +std::vector DownloadCodes(std::string gametdb_id, bool* succeeded) { - switch (gameid[0]) - { - case 'R': - case 'S': - case 'G': - break; - default: - // All channels (WiiWare, VirtualConsole, etc) are identified by their first four characters - gameid = gameid.substr(0, 4); - break; - } - - std::string endpoint{"https://www.geckocodes.org/txt.php?txt=" + gameid}; + std::string endpoint{"https://www.geckocodes.org/txt.php?txt=" + gametdb_id}; Common::HttpRequest http; // Circumvent high-tech DDOS protection diff --git a/Source/Core/Core/GeckoCodeConfig.h b/Source/Core/Core/GeckoCodeConfig.h index 88e5b92df5..66781fc224 100644 --- a/Source/Core/Core/GeckoCodeConfig.h +++ b/Source/Core/Core/GeckoCodeConfig.h @@ -13,6 +13,6 @@ class IniFile; namespace Gecko { std::vector LoadCodes(const IniFile& globalIni, const IniFile& localIni); -std::vector DownloadCodes(std::string gameid, bool* succeeded); +std::vector DownloadCodes(std::string gametdb_id, bool* succeeded); void SaveCodes(IniFile& inifile, const std::vector& gcodes); } diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index 41cfda2605..c4100a2fc6 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -323,6 +323,20 @@ std::string TMDReader::GetGameID() const return StringFromFormat("%016" PRIx64, GetTitleId()); } +std::string TMDReader::GetGameTDBID() const +{ + const u8* begin = m_bytes.data() + offsetof(TMDHeader, title_id) + 4; + const u8* end = begin + 4; + + const bool all_printable = + std::all_of(begin, end, [](char c) { return std::isprint(c, std::locale::classic()); }); + + if (all_printable) + return std::string(begin, end); + + return StringFromFormat("%016" PRIx64, GetTitleId()); +} + u16 TMDReader::GetNumContents() const { return Common::swap16(m_bytes.data() + offsetof(TMDHeader, num_contents)); diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h index d525b79fa2..21950c072b 100644 --- a/Source/Core/Core/IOS/ES/Formats.h +++ b/Source/Core/Core/IOS/ES/Formats.h @@ -205,9 +205,15 @@ public: // Constructs a 6-character game ID in the format typically used by Dolphin. // If the 6-character game ID would contain unprintable characters, - // the title ID converted to hexadecimal is returned instead. + // the title ID converted to 16 hexadecimal digits is returned instead. std::string GetGameID() const; + // Constructs a 4-character game ID in the format typically used by GameTDB. + // If the 4-character game ID would contain unprintable characters, + // the title ID converted to 16 hexadecimal digits is returned instead + // (a format which GameTDB does not actually use). + std::string GetGameTDBID() const; + u16 GetNumContents() const; bool GetContent(u16 index, Content* content) const; std::vector GetContents() const; diff --git a/Source/Core/Core/TitleDatabase.cpp b/Source/Core/Core/TitleDatabase.cpp index 7000fe723d..9a2a48f8d3 100644 --- a/Source/Core/Core/TitleDatabase.cpp +++ b/Source/Core/Core/TitleDatabase.cpp @@ -167,12 +167,10 @@ TitleDatabase::TitleDatabase() TitleDatabase::~TitleDatabase() = default; -const std::string& TitleDatabase::GetTitleName(const std::string& game_id, TitleType type) const +const std::string& TitleDatabase::GetTitleName(const std::string& gametdb_id) const { - const auto& map = IsWiiTitle(game_id) ? m_wii_title_map : m_gc_title_map; - const std::string key = - type == TitleType::Channel && game_id.length() == 6 ? game_id.substr(0, 4) : game_id; - const auto iterator = map.find(key); + 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; } @@ -181,14 +179,14 @@ const std::string& TitleDatabase::GetChannelName(u64 title_id) 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, TitleType::Channel); + return GetTitleName(id); } -std::string TitleDatabase::Describe(const std::string& game_id, TitleType type) const +std::string TitleDatabase::Describe(const std::string& gametdb_id) const { - const std::string& title_name = GetTitleName(game_id, type); + const std::string& title_name = GetTitleName(gametdb_id); if (title_name.empty()) - return game_id; - return StringFromFormat("%s (%s)", title_name.c_str(), game_id.c_str()); + return gametdb_id; + return StringFromFormat("%s (%s)", title_name.c_str(), gametdb_id.c_str()); } } // namespace Core diff --git a/Source/Core/Core/TitleDatabase.h b/Source/Core/Core/TitleDatabase.h index 0eb6cd1d9e..528c16d325 100644 --- a/Source/Core/Core/TitleDatabase.h +++ b/Source/Core/Core/TitleDatabase.h @@ -18,21 +18,15 @@ public: TitleDatabase(); ~TitleDatabase(); - enum class TitleType - { - Channel, - Other, - }; - - // Get a user friendly title name for a game ID. + // 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& game_id, TitleType = TitleType::Other) const; + const std::string& GetTitleName(const std::string& gametdb_id) const; - // Same as above, but takes a title ID instead of a game ID, and can only find names of channels. + // 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; - // Get a description for a game ID (title name if available + game ID). - std::string Describe(const std::string& game_id, TitleType = TitleType::Other) const; + // Get a description for a GameTDB ID (title name if available + GameTDB ID). + std::string Describe(const std::string& gametdb_id) const; private: std::unordered_map m_wii_title_map; diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index 4620234313..3b70f765b7 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -76,6 +76,7 @@ public: return offset; } virtual std::string GetGameID(const Partition& partition = PARTITION_NONE) const = 0; + virtual std::string GetGameTDBID(const Partition& partition = PARTITION_NONE) const = 0; virtual std::string GetMakerID(const Partition& partition = PARTITION_NONE) const = 0; virtual std::optional GetRevision(const Partition& partition = PARTITION_NONE) const = 0; virtual std::string GetInternalName(const Partition& partition = PARTITION_NONE) const = 0; diff --git a/Source/Core/DiscIO/VolumeGC.cpp b/Source/Core/DiscIO/VolumeGC.cpp index 1275fcfb78..e3db29c1f5 100644 --- a/Source/Core/DiscIO/VolumeGC.cpp +++ b/Source/Core/DiscIO/VolumeGC.cpp @@ -72,6 +72,17 @@ std::string VolumeGC::GetGameID(const Partition& partition) const return DecodeString(id); } +std::string VolumeGC::GetGameTDBID(const Partition& partition) const +{ + const std::string game_id = GetGameID(partition); + + // Don't return an ID for Datel discs that are using the game ID of NHL Hitz 2002 + if (game_id == "GNHE5d" && !GetBootDOLOffset(*this, partition).has_value()) + return ""; + + return game_id; +} + Region VolumeGC::GetRegion() const { const std::optional region_code = ReadSwapped(0x458, PARTITION_NONE); diff --git a/Source/Core/DiscIO/VolumeGC.h b/Source/Core/DiscIO/VolumeGC.h index d7fcc82b48..1bf6139768 100644 --- a/Source/Core/DiscIO/VolumeGC.h +++ b/Source/Core/DiscIO/VolumeGC.h @@ -34,6 +34,7 @@ public: const Partition& partition = PARTITION_NONE) const override; const FileSystem* GetFileSystem(const Partition& partition = PARTITION_NONE) const override; std::string GetGameID(const Partition& partition = PARTITION_NONE) const override; + std::string GetGameTDBID(const Partition& partition = PARTITION_NONE) const override; std::string GetMakerID(const Partition& partition = PARTITION_NONE) const override; std::optional GetRevision(const Partition& partition = PARTITION_NONE) const override; std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override; diff --git a/Source/Core/DiscIO/VolumeWad.cpp b/Source/Core/DiscIO/VolumeWad.cpp index 7d927ae73d..a49488506b 100644 --- a/Source/Core/DiscIO/VolumeWad.cpp +++ b/Source/Core/DiscIO/VolumeWad.cpp @@ -105,6 +105,11 @@ std::string VolumeWAD::GetGameID(const Partition& partition) const return m_tmd.GetGameID(); } +std::string VolumeWAD::GetGameTDBID(const Partition& partition) const +{ + return m_tmd.GetGameTDBID(); +} + std::string VolumeWAD::GetMakerID(const Partition& partition) const { char temp[2]; diff --git a/Source/Core/DiscIO/VolumeWad.h b/Source/Core/DiscIO/VolumeWad.h index e89c684975..32d8bfe3a6 100644 --- a/Source/Core/DiscIO/VolumeWad.h +++ b/Source/Core/DiscIO/VolumeWad.h @@ -35,6 +35,7 @@ public: std::optional GetTitleID(const Partition& partition = PARTITION_NONE) const override; const IOS::ES::TMDReader& GetTMD(const Partition& partition = PARTITION_NONE) const override; std::string GetGameID(const Partition& partition = PARTITION_NONE) const override; + std::string GetGameTDBID(const Partition& partition = PARTITION_NONE) const override; std::string GetMakerID(const Partition& partition = PARTITION_NONE) const override; std::optional GetRevision(const Partition& partition = PARTITION_NONE) const override; std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override diff --git a/Source/Core/DiscIO/VolumeWii.cpp b/Source/Core/DiscIO/VolumeWii.cpp index 29d822fca9..777d774d82 100644 --- a/Source/Core/DiscIO/VolumeWii.cpp +++ b/Source/Core/DiscIO/VolumeWii.cpp @@ -278,6 +278,11 @@ std::string VolumeWii::GetGameID(const Partition& partition) const return DecodeString(id); } +std::string VolumeWii::GetGameTDBID(const Partition& partition) const +{ + return GetGameID(partition); +} + Region VolumeWii::GetRegion() const { const std::optional region_code = m_reader->ReadSwapped(0x4E000); diff --git a/Source/Core/DiscIO/VolumeWii.h b/Source/Core/DiscIO/VolumeWii.h index 7ad657c26d..48010d5b37 100644 --- a/Source/Core/DiscIO/VolumeWii.h +++ b/Source/Core/DiscIO/VolumeWii.h @@ -45,6 +45,7 @@ public: u64 partition_data_offset); u64 PartitionOffsetToRawOffset(u64 offset, const Partition& partition) const override; std::string GetGameID(const Partition& partition) const override; + std::string GetGameTDBID(const Partition& partition) const override; std::string GetMakerID(const Partition& partition) const override; std::optional GetRevision(const Partition& partition) const override; std::string GetInternalName(const Partition& partition) const override; diff --git a/Source/Core/DolphinQt/Config/GeckoCodeWidget.cpp b/Source/Core/DolphinQt/Config/GeckoCodeWidget.cpp index d60c374702..e320b32347 100755 --- a/Source/Core/DolphinQt/Config/GeckoCodeWidget.cpp +++ b/Source/Core/DolphinQt/Config/GeckoCodeWidget.cpp @@ -26,8 +26,8 @@ #include "UICommon/GameFile.h" GeckoCodeWidget::GeckoCodeWidget(const UICommon::GameFile& game, bool restart_required) - : m_game(game), m_game_id(game.GetGameID()), m_game_revision(game.GetRevision()), - m_restart_required(restart_required) + : m_game(game), m_game_id(game.GetGameID()), m_gametdb_id(game.GetGameTDBID()), + m_game_revision(game.GetRevision()), m_restart_required(restart_required) { CreateWidgets(); ConnectWidgets(); @@ -251,7 +251,7 @@ void GeckoCodeWidget::DownloadCodes() { bool success; - std::vector codes = Gecko::DownloadCodes(m_game_id, &success); + std::vector codes = Gecko::DownloadCodes(m_gametdb_id, &success); if (!success) { diff --git a/Source/Core/DolphinQt/Config/GeckoCodeWidget.h b/Source/Core/DolphinQt/Config/GeckoCodeWidget.h index 6bfdfc744f..430cc0d15a 100644 --- a/Source/Core/DolphinQt/Config/GeckoCodeWidget.h +++ b/Source/Core/DolphinQt/Config/GeckoCodeWidget.h @@ -48,6 +48,7 @@ private: const UICommon::GameFile& m_game; std::string m_game_id; + std::string m_gametdb_id; u16 m_game_revision; CheatWarningWidget* m_warning; diff --git a/Source/Core/UICommon/GameFile.cpp b/Source/Core/UICommon/GameFile.cpp index d7cc55e2b7..ed62d69ce4 100644 --- a/Source/Core/UICommon/GameFile.cpp +++ b/Source/Core/UICommon/GameFile.cpp @@ -132,6 +132,7 @@ GameFile::GameFile(const std::string& path) m_internal_name = volume->GetInternalName(); m_game_id = volume->GetGameID(); + m_gametdb_id = volume->GetGameTDBID(); m_title_id = volume->GetTitleID().value_or(0); m_maker_id = volume->GetMakerID(); m_revision = volume->GetRevision().value_or(0); @@ -198,9 +199,8 @@ void GameFile::DownloadDefaultCover() const auto cover_path = File::GetUserPath(D_COVERCACHE_IDX) + DIR_SEP; - // If covers have already been downloaded, abort - if (File::Exists(cover_path + m_game_id + ".png") || - File::Exists(cover_path + m_game_id.substr(0, 4) + ".png")) + // If the cover has already been downloaded, abort + if (File::Exists(cover_path + m_gametdb_id + ".png")) return; Common::HttpRequest request; @@ -249,22 +249,13 @@ void GameFile::DownloadDefaultCover() break; } - auto response = request.Get(StringFromFormat(COVER_URL, region_code.c_str(), m_game_id.c_str())); + auto response = + request.Get(StringFromFormat(COVER_URL, region_code.c_str(), m_gametdb_id.c_str())); if (response) { File::WriteStringToFile(std::string(response.value().begin(), response.value().end()), - cover_path + m_game_id + ".png"); - return; - } - - response = - request.Get(StringFromFormat(COVER_URL, region_code.c_str(), m_game_id.substr(0, 4).c_str())); - - if (response) - { - File::WriteStringToFile(std::string(response.value().begin(), response.value().end()), - cover_path + m_game_id.substr(0, 4) + ".png"); + cover_path + m_gametdb_id + ".png"); } } @@ -277,10 +268,7 @@ bool GameFile::DefaultCoverChanged() std::string contents; - File::ReadFileToString(cover_path + m_game_id + ".png", contents); - - if (contents.empty()) - File::ReadFileToString(cover_path + m_game_id.substr(0, 4).c_str() + ".png", contents); + File::ReadFileToString(cover_path + m_gametdb_id + ".png", contents); if (contents.empty()) return false; @@ -328,6 +316,7 @@ void GameFile::DoState(PointerWrap& p) p.Do(m_descriptions); p.Do(m_internal_name); p.Do(m_game_id); + p.Do(m_gametdb_id); p.Do(m_title_id); p.Do(m_maker_id); @@ -433,10 +422,7 @@ void GameFile::CustomBannerCommit() const std::string& GameFile::GetName(const Core::TitleDatabase& title_database) const { - const auto type = m_platform == DiscIO::Platform::WiiWAD ? - Core::TitleDatabase::TitleType::Channel : - Core::TitleDatabase::TitleType::Other; - const std::string& custom_name = title_database.GetTitleName(m_game_id, type); + const std::string& custom_name = title_database.GetTitleName(m_gametdb_id); return custom_name.empty() ? GetName() : custom_name; } diff --git a/Source/Core/UICommon/GameFile.h b/Source/Core/UICommon/GameFile.h index e5b4257db6..1790050ea9 100644 --- a/Source/Core/UICommon/GameFile.h +++ b/Source/Core/UICommon/GameFile.h @@ -67,6 +67,7 @@ public: std::vector GetLanguages() const; const std::string& GetInternalName() const { return m_internal_name; } const std::string& GetGameID() const { return m_game_id; } + const std::string& GetGameTDBID() const { return m_gametdb_id; } u64 GetTitleID() const { return m_title_id; } const std::string& GetMakerID() const { return m_maker_id; } u16 GetRevision() const { return m_revision; } @@ -120,6 +121,7 @@ private: std::map m_descriptions{}; std::string m_internal_name{}; std::string m_game_id{}; + std::string m_gametdb_id{}; u64 m_title_id{}; std::string m_maker_id{}; diff --git a/Source/Core/UICommon/GameFileCache.cpp b/Source/Core/UICommon/GameFileCache.cpp index aad527e612..2782ec2751 100644 --- a/Source/Core/UICommon/GameFileCache.cpp +++ b/Source/Core/UICommon/GameFileCache.cpp @@ -27,7 +27,7 @@ namespace UICommon { -static constexpr u32 CACHE_REVISION = 14; // Last changed in PR 7441 +static constexpr u32 CACHE_REVISION = 15; // Last changed in PR 7816 std::vector FindAllGamePaths(const std::vector& directories_to_scan, bool recursive_scan) From 9df763b4acd8857fdcd3a919d4bd9ce6f4447e53 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 23 Feb 2019 19:18:16 +0100 Subject: [PATCH 2/3] TitleDatabase: Don't merge multiple languages into same map Instead of selecting languages based on the user config at the time of TitleDatabase creation and merging the different languages into one map for GC and one map for Wii, have one map for each language, and have the caller supply the language they want. This makes us not need the IsGCTitle function, which is inaccurate for IDs that start with D. --- Source/Core/Core/ConfigManager.cpp | 2 +- Source/Core/Core/TitleDatabase.cpp | 172 ++++++++++------------------- Source/Core/Core/TitleDatabase.h | 20 +++- Source/Core/DolphinQt/MenuBar.cpp | 4 +- Source/Core/UICommon/GameFile.cpp | 25 +++-- Source/Core/UICommon/GameFile.h | 1 + 6 files changed, 91 insertions(+), 133 deletions(-) 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& From 71e6243c54c7a66f8216533fecfbf0fa69cabaca Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 23 Feb 2019 19:36:05 +0100 Subject: [PATCH 3/3] TitleDatabase: Don't hardcode HBC IDs that actually are in GameTDB --- Source/Core/Core/TitleDatabase.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/Core/Core/TitleDatabase.cpp b/Source/Core/Core/TitleDatabase.cpp index 2d3f610220..e21239e0d0 100644 --- a/Source/Core/Core/TitleDatabase.cpp +++ b/Source/Core/Core/TitleDatabase.cpp @@ -74,13 +74,12 @@ TitleDatabase::TitleDatabase() 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. + // Titles that aren't 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_base_map.emplace("0000000100000002", GetStringT("Wii Menu")); - for (const auto& id : {"HAXX", "JODI", "00010001af1bf516", "LULZ", "OHBC"}) + for (const auto& id : {"HAXX", "00010001af1bf516"}) m_base_map.emplace(id, "The Homebrew Channel"); }