summaryrefslogtreecommitdiff
path: root/Source/Core/UICommon/GameFile.cpp
diff options
context:
space:
mode:
authorJosJuice <josjuice@gmail.com>2019-08-13 18:01:27 +0200
committerJosJuice <josjuice@gmail.com>2019-09-25 12:25:33 +0200
commit59f27ae4e1713d922fe71fc6ba95fbc399aac814 (patch)
tree06200f016eba6dd38fbee1067232490b6d5af14d /Source/Core/UICommon/GameFile.cpp
parentedfb0f66b6685fe1d4314ca27cce9492a7937683 (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.cpp84
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;