From 047ddb94a223b7967423485e596a4de8373ed4d0 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 6 Sep 2015 14:28:44 +0200 Subject: [PATCH 1/3] DolphinQt: Support ELF and DOL files in the game list --- Source/Core/DolphinQt/GameList/GameFile.cpp | 45 ++++++++++++++----- Source/Core/DolphinQt/GameList/GameFile.h | 5 ++- .../Core/DolphinQt/GameList/GameTracker.cpp | 5 +++ Source/Core/DolphinQt/Utils/Resources.cpp | 4 +- 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/Source/Core/DolphinQt/GameList/GameFile.cpp b/Source/Core/DolphinQt/GameList/GameFile.cpp index 03c3ddd037..7e22d389c9 100644 --- a/Source/Core/DolphinQt/GameList/GameFile.cpp +++ b/Source/Core/DolphinQt/GameList/GameFile.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include #include #include @@ -24,7 +25,7 @@ #include "DolphinQt/GameList/GameFile.h" #include "DolphinQt/Utils/Utils.h" -static const u32 CACHE_REVISION = 0x00B; // Last changed in PR 2598 +static const u32 CACHE_REVISION = 0x00C; // Last changed in PR 2993 static const u32 DATASTREAM_REVISION = 15; // Introduced in Qt 5.2 static QMap ConvertLocalizedStrings(std::map strings) @@ -71,6 +72,9 @@ static QString GetLanguageString(DiscIO::IVolume::ELanguage language, QMapGetDiscNumber(); m_revision = volume->GetRevision(); - QFileInfo info(m_file_name); - m_folder_name = info.absoluteDir().dirName(); - ReadBanner(*volume); m_valid = true; @@ -132,6 +133,13 @@ GameFile::GameFile(const QString& fileName) ini.GetIfExists("EmuState", "EmulationIssues", &issues_temp); m_issues = QString::fromStdString(issues_temp); } + + if (!IsValid() && IsElfOrDol()) + { + m_valid = true; + m_file_size = info.size(); + m_platform = DiscIO::IVolume::ELF_DOL; + } } bool GameFile::LoadFromCache() @@ -160,8 +168,7 @@ bool GameFile::LoadFromCache() QMap short_names; QMap long_names; QMap descriptions; - stream >> m_folder_name - >> short_names + stream >> short_names >> long_names >> descriptions >> m_company @@ -203,8 +210,7 @@ void GameFile::SaveToCache() stream.setVersion(DATASTREAM_REVISION); stream << CACHE_REVISION; - stream << m_folder_name - << CastLocalizedStrings(m_short_names) + stream << CastLocalizedStrings(m_short_names) << CastLocalizedStrings(m_long_names) << CastLocalizedStrings(m_descriptions) << m_company @@ -219,7 +225,22 @@ void GameFile::SaveToCache() << m_revision; } -QString GameFile::CreateCacheFilename() +bool GameFile::IsElfOrDol() const +{ + const std::string name = m_file_name.toStdString(); + const size_t pos = name.rfind('.'); + + if (pos != std::string::npos) + { + std::string ext = name.substr(pos); + std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); + + return ext == ".elf" || ext == ".dol"; + } + return false; +} + +QString GameFile::CreateCacheFilename() const { std::string filename, pathname, extension; SplitPath(m_file_name.toStdString(), &pathname, &filename, &extension); @@ -278,9 +299,9 @@ QString GameFile::GetName(bool prefer_long) const if (name.isEmpty()) { // No usable name, return filename (better than nothing) - std::string nametemp; - SplitPath(m_file_name.toStdString(), nullptr, &nametemp, nullptr); - name = QString::fromStdString(nametemp); + std::string name_temp, extension; + SplitPath(m_file_name.toStdString(), nullptr, &name_temp, &extension); + name = QString::fromStdString(name_temp + extension); } return name; } diff --git a/Source/Core/DolphinQt/GameList/GameFile.h b/Source/Core/DolphinQt/GameList/GameFile.h index e62fcd1a79..8fe94b67a9 100644 --- a/Source/Core/DolphinQt/GameList/GameFile.h +++ b/Source/Core/DolphinQt/GameList/GameFile.h @@ -66,7 +66,7 @@ private: quint64 m_file_size = 0; quint64 m_volume_size = 0; - DiscIO::IVolume::ECountry m_country; + DiscIO::IVolume::ECountry m_country = DiscIO::IVolume::COUNTRY_UNKNOWN; DiscIO::IVolume::EPlatform m_platform; u16 m_revision = 0; @@ -78,7 +78,8 @@ private: bool LoadFromCache(); void SaveToCache(); - QString CreateCacheFilename(); + bool IsElfOrDol() const; + QString CreateCacheFilename() const; void ReadBanner(const DiscIO::IVolume& volume); }; diff --git a/Source/Core/DolphinQt/GameList/GameTracker.cpp b/Source/Core/DolphinQt/GameList/GameTracker.cpp index 7213024fe1..e7221b0d35 100644 --- a/Source/Core/DolphinQt/GameList/GameTracker.cpp +++ b/Source/Core/DolphinQt/GameList/GameTracker.cpp @@ -102,6 +102,11 @@ void DGameTracker::ScanForGames() } if (SConfig::GetInstance().m_ListWad) exts.push_back("*.wad"); + if (SConfig::GetInstance().m_ListElfDol) + { + exts.push_back("*.dol"); + exts.push_back("*.elf"); + } auto rFilenames = DoFileSearch(exts, SConfig::GetInstance().m_ISOFolder, SConfig::GetInstance().m_RecursiveISOFolder); QList newItems; diff --git a/Source/Core/DolphinQt/Utils/Resources.cpp b/Source/Core/DolphinQt/Utils/Resources.cpp index 25a9c4abfd..8456e958cd 100644 --- a/Source/Core/DolphinQt/Utils/Resources.cpp +++ b/Source/Core/DolphinQt/Utils/Resources.cpp @@ -40,7 +40,7 @@ void Resources::Init() m_regions[DiscIO::IVolume::COUNTRY_WORLD].load(GIFN("Flag_Europe")); // Uses European flag as a placeholder m_regions[DiscIO::IVolume::COUNTRY_UNKNOWN].load(GIFN("Flag_Unknown")); - m_platforms.resize(3); + m_platforms.resize(4); m_platforms[0].load(GIFN("Platform_Gamecube")); m_platforms[1].load(GIFN("Platform_Wii")); m_platforms[2].load(GIFN("Platform_Wad")); @@ -78,6 +78,8 @@ void Resources::UpdatePixmaps() // TODO: toolbar[MEMCARD]; // TODO: toolbar[HOTKEYS]; m_pixmaps[BANNER_MISSING].load(GIFN("nobanner")); + // TODO: Make this consistent with the other files + m_platforms[3].load(GIFN("fileplatform")); } QString Resources::GetImageFilename(QString name, QString dir) From 3e6d62ae9e19ae3c0c5c237ecd33c9cc06beae26 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 6 Sep 2015 18:03:02 +0200 Subject: [PATCH 2/3] DolphinQt: Support banners in Homebrew Channel format HBC uses files named icon.png for icons. This change makes Dolphin support that file name, and also [executable file name].png in case someone wants to have multiple files in one folder. The HBC banner support is mainly intended for DOL and ELF files, but it can also be used to override banners of disc images, something that wasn't possible in the past. That sure was simple compared to the wx version of this commit... --- Source/Core/DolphinQt/GameList/GameFile.cpp | 14 +++++++++++++- Source/Core/DolphinQt/GameList/GameFile.h | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Source/Core/DolphinQt/GameList/GameFile.cpp b/Source/Core/DolphinQt/GameList/GameFile.cpp index 7e22d389c9..a3f2d40d8c 100644 --- a/Source/Core/DolphinQt/GameList/GameFile.cpp +++ b/Source/Core/DolphinQt/GameList/GameFile.cpp @@ -73,7 +73,8 @@ GameFile::GameFile(const QString& fileName) : m_file_name(fileName) { QFileInfo info(m_file_name); - m_folder_name = info.absoluteDir().dirName(); + QDir directory = info.absoluteDir(); + m_folder_name = directory.dirName(); if (LoadFromCache()) { @@ -140,6 +141,16 @@ GameFile::GameFile(const QString& fileName) m_file_size = info.size(); m_platform = DiscIO::IVolume::ELF_DOL; } + + // PNG files can optionally be used as banners. Typical for DOLs and ELFs, but also works with + // volumes. icon.png is the file name used by Homebrew Channel. The ability to use a PNG file + // with the same name as the main file is provided as an alternative for those who want to have + // multiple files in one folder instead of having a Homebrew Channel-style folder structure. + QImage banner(directory.filePath(info.baseName() + SL(".png"))); + if (banner.isNull()) + banner.load(directory.filePath(SL("icon.png"))); + if (!banner.isNull()) + m_banner = QPixmap::fromImage(banner); } bool GameFile::LoadFromCache() @@ -259,6 +270,7 @@ QString GameFile::CreateCacheFilename() const return fullname; } +// Outputs to m_banner void GameFile::ReadBanner(const DiscIO::IVolume& volume) { int width, height; diff --git a/Source/Core/DolphinQt/GameList/GameFile.h b/Source/Core/DolphinQt/GameList/GameFile.h index 8fe94b67a9..635cb553e9 100644 --- a/Source/Core/DolphinQt/GameList/GameFile.h +++ b/Source/Core/DolphinQt/GameList/GameFile.h @@ -81,5 +81,6 @@ private: bool IsElfOrDol() const; QString CreateCacheFilename() const; + // Outputs to m_banner void ReadBanner(const DiscIO::IVolume& volume); }; From ec8ede26009900bab4e674bcae1bd16483cbe4be Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 7 Sep 2015 08:49:19 +0200 Subject: [PATCH 3/3] DolphinQt: Support XML metadata in Homebrew Channel format --- Source/Core/DolphinQt/GameList/GameFile.cpp | 55 +++++++++++++++++++-- Source/Core/DolphinQt/GameList/GameFile.h | 3 ++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/Source/Core/DolphinQt/GameList/GameFile.cpp b/Source/Core/DolphinQt/GameList/GameFile.cpp index a3f2d40d8c..f10dd0f201 100644 --- a/Source/Core/DolphinQt/GameList/GameFile.cpp +++ b/Source/Core/DolphinQt/GameList/GameFile.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "Common/Common.h" #include "Common/CommonPaths.h" @@ -142,10 +143,14 @@ GameFile::GameFile(const QString& fileName) m_platform = DiscIO::IVolume::ELF_DOL; } - // PNG files can optionally be used as banners. Typical for DOLs and ELFs, but also works with - // volumes. icon.png is the file name used by Homebrew Channel. The ability to use a PNG file - // with the same name as the main file is provided as an alternative for those who want to have + // Metadata can optionally be stored in XML and PNG files. Typical for DOLs and ELFs, but also works + // with volumes. icon.png and meta.xml are the file names used by Homebrew Channel. The ability to use + // files with the same name as the main file is provided as an alternative for those who want to have // multiple files in one folder instead of having a Homebrew Channel-style folder structure. + + if (!ReadXML(directory.filePath(info.baseName() + SL(".xml")))) + ReadXML(directory.filePath(SL("meta.xml"))); + QImage banner(directory.filePath(info.baseName() + SL(".png"))); if (banner.isNull()) banner.load(directory.filePath(SL("icon.png"))); @@ -288,6 +293,50 @@ void GameFile::ReadBanner(const DiscIO::IVolume& volume) m_banner = QPixmap::fromImage(banner); } +// Outputs to m_short_names, m_long_names, m_descriptions, m_company. +// Returns whether a file was found, not whether it contained useful data. +bool GameFile::ReadXML(const QString& file_path) +{ + // The format of Homebrew Channel XML metadata is described at: + // http://wiibrew.org/wiki/Homebrew_Channel#Adding_Text + + QFile file(file_path); + if (!file.open(QIODevice::ReadOnly)) + return false; + + QXmlStreamReader reader(&file); + if (reader.readNextStartElement() && reader.name() == SL("app")) + { + while (reader.readNextStartElement()) + { + QStringRef name = reader.name(); + if (name == SL("name")) + { + m_short_names = { { DiscIO::IVolume::LANGUAGE_UNKNOWN, reader.readElementText() } }; + m_long_names = m_short_names; + } + else if (name == SL("short_description")) + { + m_descriptions = { { DiscIO::IVolume::LANGUAGE_UNKNOWN, reader.readElementText() } }; + } + else if (name == SL("coder")) + { + m_company = reader.readElementText(); + } + else + { + reader.skipCurrentElement(); + } + // 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; +} + QString GameFile::GetDescription(DiscIO::IVolume::ELanguage language) const { return GetLanguageString(language, m_descriptions); diff --git a/Source/Core/DolphinQt/GameList/GameFile.h b/Source/Core/DolphinQt/GameList/GameFile.h index 635cb553e9..c9e2da0c37 100644 --- a/Source/Core/DolphinQt/GameList/GameFile.h +++ b/Source/Core/DolphinQt/GameList/GameFile.h @@ -83,4 +83,7 @@ private: // Outputs to m_banner void ReadBanner(const DiscIO::IVolume& volume); + // Outputs to m_short_names, m_long_names, m_descriptions, m_company. + // Returns whether a file was found, not whether it contained useful data. + bool ReadXML(const QString& file_path); };