Merge pull request #7816 from JosJuice/titledatabase-edge-cases
Fix edge cases in TitleDatabase, cover downloading, Gecko code downloading
This commit is contained in:
commit
19f4772e47
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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, GetCurrentLanguage(bWii));
|
||||
NOTICE_LOG(CORE, "Active title: %s", m_title_description.c_str());
|
||||
|
||||
Config::AddLayer(ConfigLoaders::GenerateGlobalGameConfigLoader(game_id, revision));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -16,21 +16,9 @@
|
|||
|
||||
namespace Gecko
|
||||
{
|
||||
std::vector<GeckoCode> DownloadCodes(std::string gameid, bool* succeeded)
|
||||
std::vector<GeckoCode> 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
|
||||
|
|
|
@ -13,6 +13,6 @@ class IniFile;
|
|||
namespace Gecko
|
||||
{
|
||||
std::vector<GeckoCode> LoadCodes(const IniFile& globalIni, const IniFile& localIni);
|
||||
std::vector<GeckoCode> DownloadCodes(std::string gameid, bool* succeeded);
|
||||
std::vector<GeckoCode> DownloadCodes(std::string gametdb_id, bool* succeeded);
|
||||
void SaveCodes(IniFile& inifile, const std::vector<GeckoCode>& gcodes);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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<Content> GetContents() const;
|
||||
|
|
|
@ -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<std::string, std::string>;
|
||||
|
||||
// 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<bool(const std::string& game_id)> 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,123 +40,94 @@ 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;
|
||||
// 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");
|
||||
|
||||
// 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);
|
||||
|
||||
// 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_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");
|
||||
m_base_map.emplace("0000000100000002", GetStringT("Wii Menu"));
|
||||
for (const auto& id : {"HAXX", "00010001af1bf516"})
|
||||
m_base_map.emplace(id, "The Homebrew Channel");
|
||||
}
|
||||
|
||||
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,
|
||||
DiscIO::Language language) 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 : 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<char>((title_id >> 24) & 0xff), static_cast<char>((title_id >> 16) & 0xff),
|
||||
static_cast<char>((title_id >> 8) & 0xff), static_cast<char>(title_id & 0xff)}};
|
||||
return GetTitleName(id, TitleType::Channel);
|
||||
return GetTitleName(id, language);
|
||||
}
|
||||
|
||||
std::string TitleDatabase::Describe(const std::string& game_id, TitleType type) const
|
||||
std::string TitleDatabase::Describe(const std::string& gametdb_id, DiscIO::Language language) const
|
||||
{
|
||||
const std::string& title_name = GetTitleName(game_id, type);
|
||||
const std::string& title_name = GetTitleName(gametdb_id, language);
|
||||
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
|
||||
|
|
|
@ -8,6 +8,12 @@
|
|||
#include <unordered_map>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Lazy.h"
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
enum class Language;
|
||||
}
|
||||
|
||||
namespace Core
|
||||
{
|
||||
|
@ -18,24 +24,22 @@ 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, DiscIO::Language language) const;
|
||||
|
||||
// Same as above, but takes a title ID instead of a game ID, and can only find names of channels.
|
||||
const std::string& GetChannelName(u64 title_id) 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, DiscIO::Language language) 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, DiscIO::Language language) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::string> m_wii_title_map;
|
||||
std::unordered_map<std::string, std::string> m_gc_title_map;
|
||||
void AddLazyMap(DiscIO::Language language, const std::string& language_code);
|
||||
|
||||
std::unordered_map<DiscIO::Language, Common::Lazy<std::unordered_map<std::string, std::string>>>
|
||||
m_title_maps;
|
||||
std::unordered_map<std::string, std::string> m_base_map;
|
||||
std::unordered_map<std::string, std::string> m_user_title_map;
|
||||
};
|
||||
} // namespace Core
|
||||
|
|
|
@ -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<u16> GetRevision(const Partition& partition = PARTITION_NONE) const = 0;
|
||||
virtual std::string GetInternalName(const Partition& partition = PARTITION_NONE) const = 0;
|
||||
|
|
|
@ -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<u32> region_code = ReadSwapped<u32>(0x458, PARTITION_NONE);
|
||||
|
|
|
@ -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<u16> GetRevision(const Partition& partition = PARTITION_NONE) const override;
|
||||
std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override;
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -35,6 +35,7 @@ public:
|
|||
std::optional<u64> 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<u16> GetRevision(const Partition& partition = PARTITION_NONE) const override;
|
||||
std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override
|
||||
|
|
|
@ -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<u32> region_code = m_reader->ReadSwapped<u32>(0x4E000);
|
||||
|
|
|
@ -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<u16> GetRevision(const Partition& partition) const override;
|
||||
std::string GetInternalName(const Partition& partition) const override;
|
||||
|
|
|
@ -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<Gecko::GeckoCode> codes = Gecko::DownloadCodes(m_game_id, &success);
|
||||
std::vector<Gecko::GeckoCode> codes = Gecko::DownloadCodes(m_gametdb_id, &success);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<DiscIO::Language, std::string>& 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)
|
||||
|
@ -132,6 +135,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 +202,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 +252,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 +271,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 +319,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 +425,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, GetConfigLanguage());
|
||||
return custom_name.empty() ? GetName() : custom_name;
|
||||
}
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ public:
|
|||
std::vector<DiscIO::Language> 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; }
|
||||
|
@ -95,6 +96,7 @@ public:
|
|||
void CustomCoverCommit();
|
||||
|
||||
private:
|
||||
DiscIO::Language GetConfigLanguage() const;
|
||||
static const std::string& Lookup(DiscIO::Language language,
|
||||
const std::map<DiscIO::Language, std::string>& strings);
|
||||
const std::string&
|
||||
|
@ -120,6 +122,7 @@ private:
|
|||
std::map<DiscIO::Language, std::string> 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{};
|
||||
|
||||
|
|
|
@ -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<std::string> FindAllGamePaths(const std::vector<std::string>& directories_to_scan,
|
||||
bool recursive_scan)
|
||||
|
|
Loading…
Reference in New Issue