mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-21 05:09:34 -06:00
GameFile: Support HBC-style XML metadata
This feature was originally exclusive to the previous iteration of DolphinQt (the one that was the reason for the current iteration being named DolphinQt2 initially). https://bugs.dolphin-emu.org/issues/8949
This commit is contained in:
@ -17,6 +17,8 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <pugixml.hpp>
|
||||
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Common/CommonPaths.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
@ -317,6 +319,9 @@ void GameFile::DoState(PointerWrap& p)
|
||||
p.Do(m_disc_number);
|
||||
p.Do(m_apploader_date);
|
||||
|
||||
p.Do(m_custom_name);
|
||||
p.Do(m_custom_description);
|
||||
p.Do(m_custom_maker);
|
||||
m_volume_banner.DoState(p);
|
||||
m_custom_banner.DoState(p);
|
||||
m_default_cover.DoState(p);
|
||||
@ -333,6 +338,58 @@ bool GameFile::IsElfOrDol() const
|
||||
return name_end == ".elf" || name_end == ".dol";
|
||||
}
|
||||
|
||||
bool GameFile::ReadXMLMetadata(const std::string& path)
|
||||
{
|
||||
std::string data;
|
||||
if (!File::ReadFileToString(path, data))
|
||||
return false;
|
||||
|
||||
pugi::xml_document doc;
|
||||
// We use load_buffer instead of load_file to avoid path encoding problems on Windows
|
||||
if (!doc.load_buffer(data.data(), data.size()))
|
||||
return false;
|
||||
|
||||
const pugi::xml_node app_node = doc.child("app");
|
||||
m_pending.custom_name = app_node.child("name").text().as_string();
|
||||
m_pending.custom_maker = app_node.child("coder").text().as_string();
|
||||
m_pending.custom_description = app_node.child("short_description").text().as_string();
|
||||
|
||||
// Elements that we aren't using:
|
||||
// version (can be written in any format)
|
||||
// release_date (YYYYmmddHHMMSS format)
|
||||
// long_description (can be several screens long!)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameFile::XMLMetadataChanged()
|
||||
{
|
||||
std::string path, name;
|
||||
SplitPath(m_file_path, &path, &name, nullptr);
|
||||
|
||||
// This XML file naming format is intended as an alternative to the Homebrew Channel naming
|
||||
// for those who don't want to have a Homebrew Channel style folder structure.
|
||||
if (!ReadXMLMetadata(path + name + ".xml"))
|
||||
{
|
||||
// Homebrew Channel naming. Typical for DOLs and ELFs, but we also support it for volumes.
|
||||
if (!ReadXMLMetadata(path + "meta.xml"))
|
||||
{
|
||||
// If no XML metadata is found, remove any old XML metadata from memory.
|
||||
m_pending.custom_banner = {};
|
||||
}
|
||||
}
|
||||
|
||||
return m_pending.custom_name != m_custom_name && m_pending.custom_maker != m_custom_maker &&
|
||||
m_pending.custom_description != m_custom_description;
|
||||
}
|
||||
|
||||
void GameFile::XMLMetadataCommit()
|
||||
{
|
||||
m_custom_name = std::move(m_pending.custom_name);
|
||||
m_custom_description = std::move(m_pending.custom_description);
|
||||
m_custom_maker = std::move(m_pending.custom_maker);
|
||||
}
|
||||
|
||||
bool GameFile::WiiBannerChanged()
|
||||
{
|
||||
// Wii banners can only be read if there is a save file.
|
||||
@ -389,7 +446,7 @@ bool GameFile::CustomBannerChanged()
|
||||
std::string path, name;
|
||||
SplitPath(m_file_path, &path, &name, nullptr);
|
||||
|
||||
// This icon naming format is intended as an alternative to Homebrew Channel icons
|
||||
// This icon naming format is intended as an alternative to the Homebrew Channel naming
|
||||
// for those who don't want to have a Homebrew Channel style folder structure.
|
||||
if (!ReadPNGBanner(path + name + ".png"))
|
||||
{
|
||||
@ -411,12 +468,18 @@ 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, GetConfigLanguage());
|
||||
return custom_name.empty() ? GetName() : custom_name;
|
||||
if (!m_custom_name.empty())
|
||||
return m_custom_name;
|
||||
|
||||
const std::string& database_name = title_database.GetTitleName(m_gametdb_id, GetConfigLanguage());
|
||||
return database_name.empty() ? GetName(true) : database_name;
|
||||
}
|
||||
|
||||
const std::string& GameFile::GetName(bool long_name) const
|
||||
const std::string& GameFile::GetName(bool allow_custom_name, bool long_name) const
|
||||
{
|
||||
if (allow_custom_name && !m_custom_name.empty())
|
||||
return m_custom_name;
|
||||
|
||||
const std::string& name = long_name ? GetLongName() : GetShortName();
|
||||
if (!name.empty())
|
||||
return name;
|
||||
@ -425,8 +488,11 @@ const std::string& GameFile::GetName(bool long_name) const
|
||||
return m_file_name;
|
||||
}
|
||||
|
||||
const std::string& GameFile::GetMaker(bool long_maker) const
|
||||
const std::string& GameFile::GetMaker(bool allow_custom_maker, bool long_maker) const
|
||||
{
|
||||
if (allow_custom_maker && !m_custom_maker.empty())
|
||||
return m_custom_maker;
|
||||
|
||||
const std::string& maker = long_maker ? GetLongMaker() : GetShortMaker();
|
||||
if (!maker.empty())
|
||||
return maker;
|
||||
@ -437,6 +503,14 @@ const std::string& GameFile::GetMaker(bool long_maker) const
|
||||
return EMPTY_STRING;
|
||||
}
|
||||
|
||||
const std::string& GameFile::GetDescription(bool allow_custom_description) const
|
||||
{
|
||||
if (allow_custom_description && !m_custom_description.empty())
|
||||
return m_custom_description;
|
||||
|
||||
return LookupUsingConfigLanguage(m_descriptions);
|
||||
}
|
||||
|
||||
std::vector<DiscIO::Language> GameFile::GetLanguages() const
|
||||
{
|
||||
std::vector<DiscIO::Language> languages;
|
||||
|
Reference in New Issue
Block a user