diff options
| author | JosJuice <josjuice@gmail.com> | 2019-08-13 18:01:27 +0200 |
|---|---|---|
| committer | JosJuice <josjuice@gmail.com> | 2019-09-25 12:25:33 +0200 |
| commit | 59f27ae4e1713d922fe71fc6ba95fbc399aac814 (patch) | |
| tree | 06200f016eba6dd38fbee1067232490b6d5af14d /Source/Core/UICommon/GameFile.cpp | |
| parent | edfb0f66b6685fe1d4314ca27cce9492a7937683 (diff) | |
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
Diffstat (limited to 'Source/Core/UICommon/GameFile.cpp')
| -rw-r--r-- | Source/Core/UICommon/GameFile.cpp | 84 |
1 files changed, 79 insertions, 5 deletions
diff --git a/Source/Core/UICommon/GameFile.cpp b/Source/Core/UICommon/GameFile.cpp index f5aefd7f01..d29f5ad2ae 100644 --- a/Source/Core/UICommon/GameFile.cpp +++ b/Source/Core/UICommon/GameFile.cpp @@ -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; |
