diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index eb30e9c083..2cf0d6fa46 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -17,6 +17,7 @@ set(SRCS NetPlayServer.cpp PatchEngine.cpp State.cpp + TitleDatabase.cpp WiiRoot.cpp Boot/Boot_BS2Emu.cpp Boot/Boot.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 87a03f73a3..0134b33d45 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -285,6 +285,7 @@ + @@ -512,6 +513,7 @@ + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 7967b61478..14da13445a 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -168,6 +168,7 @@ + ActionReplay @@ -871,6 +872,7 @@ + ActionReplay diff --git a/Source/Core/Core/TitleDatabase.cpp b/Source/Core/Core/TitleDatabase.cpp new file mode 100644 index 0000000000..41d576f1fe --- /dev/null +++ b/Source/Core/Core/TitleDatabase.cpp @@ -0,0 +1,151 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include + +#include "Core/TitleDatabase.h" + +#include "Common/FileUtil.h" +#include "Common/MsgHandler.h" +#include "Common/StringUtil.h" +#include "Core/ConfigManager.h" +#include "DiscIO/Enums.h" + +namespace Core +{ +static std::string GetLanguageCode(DiscIO::Language language) +{ + switch (language) + { + case DiscIO::Language::LANGUAGE_JAPANESE: + return "ja"; + case DiscIO::Language::LANGUAGE_ENGLISH: + return "en"; + case DiscIO::Language::LANGUAGE_GERMAN: + return "de"; + case DiscIO::Language::LANGUAGE_FRENCH: + return "fr"; + case DiscIO::Language::LANGUAGE_SPANISH: + return "es"; + case DiscIO::Language::LANGUAGE_ITALIAN: + return "it"; + case DiscIO::Language::LANGUAGE_DUTCH: + return "nl"; + case DiscIO::Language::LANGUAGE_SIMPLIFIED_CHINESE: + return "zh_CN"; + case DiscIO::Language::LANGUAGE_TRADITIONAL_CHINESE: + return "zh_TW"; + case DiscIO::Language::LANGUAGE_KOREAN: + return "ko"; + default: + return "en"; + } +} + +using Map = std::unordered_map; + +static bool LoadMap(const std::string& file_path, Map& map, + std::function predicate) +{ + std::ifstream txt; + OpenFStream(txt, file_path, std::ios::in); + + if (!txt.is_open()) + return false; + + std::string line; + while (std::getline(txt, line)) + { + if (line.empty()) + continue; + + const size_t equals_index = line.find('='); + if (equals_index != std::string::npos) + { + const std::string game_id = StripSpaces(line.substr(0, equals_index)); + if (predicate(game_id)) + map[game_id] = StripSpaces(line.substr(equals_index + 1)); + } + } + return true; +} + +// This should only be used with the standard game ID format (used by WiiTDBs), not Dolphin's. +// The main difference is that Dolphin uses 6 characters for non-disc titles (instead of 4). +static bool IsGCTitle(const std::string& game_id) +{ + 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 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; +} + +TitleDatabase::TitleDatabase() +{ + // 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")); + for (const auto& id : {"HAXX", "JODI", "00010001af1bf516", "LULZ", "OHBC"}) + m_wii_title_map.emplace(id, "The Homebrew Channel"); + + // Load the English database as the base database. + LoadMap(File::GetSysDirectory() + "wiitdb-en.txt", m_gc_title_map, m_wii_title_map); + + // Load the database in the console language. + const std::string gc_code = GetLanguageCode(SConfig::GetInstance().GetCurrentLanguage(false)); + const std::string wii_code = GetLanguageCode(SConfig::GetInstance().GetCurrentLanguage(true)); + if (gc_code != "en") + LoadMap(File::GetSysDirectory() + "wiitdb-" + gc_code + ".txt", m_gc_title_map, IsGCTitle); + if (wii_code != "en") + LoadMap(File::GetSysDirectory() + "wiitdb-" + wii_code + ".txt", m_wii_title_map, IsWiiTitle); + + // Load the user databases. + 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); +} + +TitleDatabase::~TitleDatabase() = default; + +std::string TitleDatabase::GetTitleName(const std::string& game_id, TitleType type) 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); + return iterator != map.end() ? iterator->second : ""; +} + +std::string TitleDatabase::Describe(const std::string& game_id, TitleType type) const +{ + const std::string title_name = GetTitleName(game_id, type); + if (title_name.empty()) + return game_id; + return StringFromFormat("%s (%s)", title_name.c_str(), game_id.c_str()); +} +} // namespace Core diff --git a/Source/Core/Core/TitleDatabase.h b/Source/Core/Core/TitleDatabase.h new file mode 100644 index 0000000000..23871bdf6a --- /dev/null +++ b/Source/Core/Core/TitleDatabase.h @@ -0,0 +1,38 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "Common/CommonTypes.h" + +namespace Core +{ +// Reader for title database files. +class TitleDatabase final +{ +public: + TitleDatabase(); + ~TitleDatabase(); + + enum class TitleType + { + Channel, + Other, + }; + + // Get a user friendly title name for a game ID. + // This falls back to returning an empty string if none could be found. + std::string GetTitleName(const std::string& game_id, TitleType = TitleType::Other) 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; + +private: + std::unordered_map m_wii_title_map; + std::unordered_map m_gc_title_map; +}; +} // namespace Core