Skip to content
Permalink
Browse files

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!)
  • Loading branch information...
JosJuice committed Feb 23, 2019
1 parent d27036e commit 8842a0f402ffe60a80ce7894bfd36afd039009fc
@@ -30,6 +30,8 @@ private GameFile(long pointer)

public native String getGameId();

public native String getGameTdbId();

public native int getDiscNumber();

public native int getRevision();
@@ -43,7 +45,7 @@ private GameFile(long pointer)
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 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 static void saveCover(Bitmap cover, String path)
// 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,22 +680,22 @@ 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,
const DiscIO::Partition& partition)
{
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));
@@ -367,12 +367,13 @@ struct SConfig
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 @@ class TMDReader final : public SignedBlobReader

// 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;
@@ -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<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);
}

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
@@ -18,21 +18,15 @@ class TitleDatabase final
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<std::string, std::string> m_wii_title_map;
@@ -76,6 +76,7 @@ class Volume
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 @@ class VolumeGC : public Volume
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 @@ class VolumeWAD : public Volume
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 @@ class VolumeWii : public Volume
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 @@ class GeckoCodeWidget : public QWidget

const UICommon::GameFile& m_game;
std::string m_game_id;
std::string m_gametdb_id;
u16 m_game_revision;

CheatWarningWidget* m_warning;

0 comments on commit 8842a0f

Please sign in to comment.
You can’t perform that action at this time.