Merge pull request #2993 from JosJuice/dol-elf-qt
DolphinQt: Support ELF and DOL files in the game list
This commit is contained in:
commit
5db4a60995
|
@ -2,12 +2,14 @@
|
||||||
// Licensed under GPLv2+
|
// Licensed under GPLv2+
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
|
||||||
#include "Common/Common.h"
|
#include "Common/Common.h"
|
||||||
#include "Common/CommonPaths.h"
|
#include "Common/CommonPaths.h"
|
||||||
|
@ -24,7 +26,7 @@
|
||||||
#include "DolphinQt/GameList/GameFile.h"
|
#include "DolphinQt/GameList/GameFile.h"
|
||||||
#include "DolphinQt/Utils/Utils.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 const u32 DATASTREAM_REVISION = 15; // Introduced in Qt 5.2
|
||||||
|
|
||||||
static QMap<DiscIO::IVolume::ELanguage, QString> ConvertLocalizedStrings(std::map<DiscIO::IVolume::ELanguage, std::string> strings)
|
static QMap<DiscIO::IVolume::ELanguage, QString> ConvertLocalizedStrings(std::map<DiscIO::IVolume::ELanguage, std::string> strings)
|
||||||
|
@ -71,6 +73,10 @@ static QString GetLanguageString(DiscIO::IVolume::ELanguage language, QMap<DiscI
|
||||||
GameFile::GameFile(const QString& fileName)
|
GameFile::GameFile(const QString& fileName)
|
||||||
: m_file_name(fileName)
|
: m_file_name(fileName)
|
||||||
{
|
{
|
||||||
|
QFileInfo info(m_file_name);
|
||||||
|
QDir directory = info.absoluteDir();
|
||||||
|
m_folder_name = directory.dirName();
|
||||||
|
|
||||||
if (LoadFromCache())
|
if (LoadFromCache())
|
||||||
{
|
{
|
||||||
m_valid = true;
|
m_valid = true;
|
||||||
|
@ -111,9 +117,6 @@ GameFile::GameFile(const QString& fileName)
|
||||||
m_disc_number = volume->GetDiscNumber();
|
m_disc_number = volume->GetDiscNumber();
|
||||||
m_revision = volume->GetRevision();
|
m_revision = volume->GetRevision();
|
||||||
|
|
||||||
QFileInfo info(m_file_name);
|
|
||||||
m_folder_name = info.absoluteDir().dirName();
|
|
||||||
|
|
||||||
ReadBanner(*volume);
|
ReadBanner(*volume);
|
||||||
|
|
||||||
m_valid = true;
|
m_valid = true;
|
||||||
|
@ -132,6 +135,27 @@ GameFile::GameFile(const QString& fileName)
|
||||||
ini.GetIfExists("EmuState", "EmulationIssues", &issues_temp);
|
ini.GetIfExists("EmuState", "EmulationIssues", &issues_temp);
|
||||||
m_issues = QString::fromStdString(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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")));
|
||||||
|
if (!banner.isNull())
|
||||||
|
m_banner = QPixmap::fromImage(banner);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameFile::LoadFromCache()
|
bool GameFile::LoadFromCache()
|
||||||
|
@ -160,8 +184,7 @@ bool GameFile::LoadFromCache()
|
||||||
QMap<u8, QString> short_names;
|
QMap<u8, QString> short_names;
|
||||||
QMap<u8, QString> long_names;
|
QMap<u8, QString> long_names;
|
||||||
QMap<u8, QString> descriptions;
|
QMap<u8, QString> descriptions;
|
||||||
stream >> m_folder_name
|
stream >> short_names
|
||||||
>> short_names
|
|
||||||
>> long_names
|
>> long_names
|
||||||
>> descriptions
|
>> descriptions
|
||||||
>> m_company
|
>> m_company
|
||||||
|
@ -203,8 +226,7 @@ void GameFile::SaveToCache()
|
||||||
stream.setVersion(DATASTREAM_REVISION);
|
stream.setVersion(DATASTREAM_REVISION);
|
||||||
stream << CACHE_REVISION;
|
stream << CACHE_REVISION;
|
||||||
|
|
||||||
stream << m_folder_name
|
stream << CastLocalizedStrings<u8>(m_short_names)
|
||||||
<< CastLocalizedStrings<u8>(m_short_names)
|
|
||||||
<< CastLocalizedStrings<u8>(m_long_names)
|
<< CastLocalizedStrings<u8>(m_long_names)
|
||||||
<< CastLocalizedStrings<u8>(m_descriptions)
|
<< CastLocalizedStrings<u8>(m_descriptions)
|
||||||
<< m_company
|
<< m_company
|
||||||
|
@ -219,7 +241,22 @@ void GameFile::SaveToCache()
|
||||||
<< m_revision;
|
<< 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;
|
std::string filename, pathname, extension;
|
||||||
SplitPath(m_file_name.toStdString(), &pathname, &filename, &extension);
|
SplitPath(m_file_name.toStdString(), &pathname, &filename, &extension);
|
||||||
|
@ -238,6 +275,7 @@ QString GameFile::CreateCacheFilename()
|
||||||
return fullname;
|
return fullname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Outputs to m_banner
|
||||||
void GameFile::ReadBanner(const DiscIO::IVolume& volume)
|
void GameFile::ReadBanner(const DiscIO::IVolume& volume)
|
||||||
{
|
{
|
||||||
int width, height;
|
int width, height;
|
||||||
|
@ -255,6 +293,50 @@ void GameFile::ReadBanner(const DiscIO::IVolume& volume)
|
||||||
m_banner = QPixmap::fromImage(banner);
|
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
|
QString GameFile::GetDescription(DiscIO::IVolume::ELanguage language) const
|
||||||
{
|
{
|
||||||
return GetLanguageString(language, m_descriptions);
|
return GetLanguageString(language, m_descriptions);
|
||||||
|
@ -278,9 +360,9 @@ QString GameFile::GetName(bool prefer_long) const
|
||||||
if (name.isEmpty())
|
if (name.isEmpty())
|
||||||
{
|
{
|
||||||
// No usable name, return filename (better than nothing)
|
// No usable name, return filename (better than nothing)
|
||||||
std::string nametemp;
|
std::string name_temp, extension;
|
||||||
SplitPath(m_file_name.toStdString(), nullptr, &nametemp, nullptr);
|
SplitPath(m_file_name.toStdString(), nullptr, &name_temp, &extension);
|
||||||
name = QString::fromStdString(nametemp);
|
name = QString::fromStdString(name_temp + extension);
|
||||||
}
|
}
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ private:
|
||||||
quint64 m_file_size = 0;
|
quint64 m_file_size = 0;
|
||||||
quint64 m_volume_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;
|
DiscIO::IVolume::EPlatform m_platform;
|
||||||
u16 m_revision = 0;
|
u16 m_revision = 0;
|
||||||
|
|
||||||
|
@ -78,7 +78,12 @@ private:
|
||||||
bool LoadFromCache();
|
bool LoadFromCache();
|
||||||
void SaveToCache();
|
void SaveToCache();
|
||||||
|
|
||||||
QString CreateCacheFilename();
|
bool IsElfOrDol() const;
|
||||||
|
QString CreateCacheFilename() const;
|
||||||
|
|
||||||
|
// Outputs to m_banner
|
||||||
void ReadBanner(const DiscIO::IVolume& volume);
|
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);
|
||||||
};
|
};
|
||||||
|
|
|
@ -102,6 +102,11 @@ void DGameTracker::ScanForGames()
|
||||||
}
|
}
|
||||||
if (SConfig::GetInstance().m_ListWad)
|
if (SConfig::GetInstance().m_ListWad)
|
||||||
exts.push_back("*.wad");
|
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);
|
auto rFilenames = DoFileSearch(exts, SConfig::GetInstance().m_ISOFolder, SConfig::GetInstance().m_RecursiveISOFolder);
|
||||||
QList<GameFile*> newItems;
|
QList<GameFile*> newItems;
|
||||||
|
|
|
@ -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_WORLD].load(GIFN("Flag_Europe")); // Uses European flag as a placeholder
|
||||||
m_regions[DiscIO::IVolume::COUNTRY_UNKNOWN].load(GIFN("Flag_Unknown"));
|
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[0].load(GIFN("Platform_Gamecube"));
|
||||||
m_platforms[1].load(GIFN("Platform_Wii"));
|
m_platforms[1].load(GIFN("Platform_Wii"));
|
||||||
m_platforms[2].load(GIFN("Platform_Wad"));
|
m_platforms[2].load(GIFN("Platform_Wad"));
|
||||||
|
@ -77,6 +77,8 @@ void Resources::UpdatePixmaps()
|
||||||
// TODO: toolbar[MEMCARD];
|
// TODO: toolbar[MEMCARD];
|
||||||
// TODO: toolbar[HOTKEYS];
|
// TODO: toolbar[HOTKEYS];
|
||||||
m_pixmaps[BANNER_MISSING].load(GIFN("nobanner"));
|
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)
|
QString Resources::GetImageFilename(QString name, QString dir)
|
||||||
|
|
Loading…
Reference in New Issue