Unify ISOFile (wx) with GameFile (Qt) and put it in UICommon

The original reason I wanted to do this was so that we can replace
the Android-specific code with this in the future, but of course,
just deduplicating between DolphinWX and DolphinQt2 is nice too.

Fixes:

- DolphinQt2 showing the wrong size for split WBFS disc images.

- DolphinQt2 being case sensitive when checking if a file is a DOL/ELF.

- DolphinQt2 not detecting when a Wii banner has become available
after the game list cache was created.

Removes:

- DolphinWX's ability to load PNGs as custom banners. But it was
already rather broken (see https://bugs.dolphin-emu.org/issues/10365
and https://bugs.dolphin-emu.org/issues/10366). The reason I removed
this was because PNG decoding relied on wx code and we don't have any
good non-wx/Qt code for loading PNG files right now (let's not use
SOIL), but we should be able to use libpng directly to implement PNG
loading in the future.

- DolphinQt2's ability to ignore a cached game if the last modified
time differs. We currently don't have a non-wx/Qt way to get the time.
This commit is contained in:
JosJuice
2017-12-31 20:33:36 +01:00
parent 1add238a28
commit 1f1dae367d
67 changed files with 1373 additions and 1736 deletions

View File

@ -1,481 +0,0 @@
// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <QDir>
#include <QFileInfo>
#include <QImage>
#include <QSharedPointer>
#include "Common/Assert.h"
#include "Common/FileUtil.h"
#include "Common/NandPaths.h"
#include "Common/StringUtil.h"
#include "Core/ConfigManager.h"
#include "Core/HW/WiiSaveCrypted.h"
#include "Core/IOS/ES/ES.h"
#include "Core/IOS/IOS.h"
#include "Core/WiiUtils.h"
#include "DiscIO/Blob.h"
#include "DiscIO/Enums.h"
#include "DiscIO/Volume.h"
#include "DolphinQt2/GameList/GameFile.h"
#include "DolphinQt2/Resources.h"
#include "DolphinQt2/Settings.h"
QList<DiscIO::Language> GameFile::GetAvailableLanguages() const
{
return m_long_names.keys();
}
static QMap<DiscIO::Language, QString>
ConvertLanguageMap(const std::map<DiscIO::Language, std::string>& map)
{
QMap<DiscIO::Language, QString> result;
for (auto entry : map)
result.insert(entry.first, QString::fromStdString(entry.second).trimmed());
return result;
}
GameFile::GameFile()
{
m_valid = false;
}
GameFile::GameFile(const QString& path) : m_path(path)
{
m_valid = false;
if (!LoadFileInfo(path))
return;
if (TryLoadVolume())
{
LoadState();
}
else if (!TryLoadElfDol())
{
return;
}
m_valid = true;
}
bool GameFile::IsValid() const
{
if (!m_valid)
return false;
if (m_platform == DiscIO::Platform::WII_WAD && !IOS::ES::IsChannel(m_title_id))
return false;
return true;
}
void GameFile::ReadBanner(const DiscIO::Volume& volume)
{
int width, height;
std::vector<u32> buffer = volume.GetBanner(&width, &height);
QImage banner(width, height, QImage::Format_RGB888);
for (int i = 0; i < width * height; i++)
{
int x = i % width, y = i / width;
banner.setPixel(x, y, qRgb((buffer[i] & 0xFF0000) >> 16, (buffer[i] & 0x00FF00) >> 8,
(buffer[i] & 0x0000FF) >> 0));
}
if (!banner.isNull())
m_banner = QPixmap::fromImage(banner);
}
bool GameFile::LoadFileInfo(const QString& path)
{
QFileInfo info(path);
if (!info.exists() || !info.isReadable())
return false;
m_last_modified = info.lastModified();
m_size = info.size();
return true;
}
void GameFile::LoadState()
{
IniFile ini = SConfig::LoadGameIni(m_game_id.toStdString(), m_revision);
std::string issues_temp;
ini.GetIfExists("EmuState", "EmulationStateId", &m_rating);
ini.GetIfExists("EmuState", "EmulationIssues", &issues_temp);
m_issues = QString::fromStdString(issues_temp);
}
bool GameFile::IsElfOrDol()
{
QString extension = GetFileExtension();
return extension == QStringLiteral("elf") || extension == QStringLiteral("dol");
}
bool GameFile::TryLoadVolume()
{
QSharedPointer<DiscIO::Volume> volume(
DiscIO::CreateVolumeFromFilename(m_path.toStdString()).release());
if (volume == nullptr)
return false;
m_game_id = QString::fromStdString(volume->GetGameID());
std::string maker_id = volume->GetMakerID();
m_title_id = volume->GetTitleID().value_or(0);
m_maker = QString::fromStdString(DiscIO::GetCompanyFromID(maker_id));
m_maker_id = QString::fromStdString(maker_id);
m_revision = volume->GetRevision().value_or(0);
m_internal_name = QString::fromStdString(volume->GetInternalName());
m_short_names = ConvertLanguageMap(volume->GetShortNames());
m_long_names = ConvertLanguageMap(volume->GetLongNames());
m_short_makers = ConvertLanguageMap(volume->GetShortMakers());
m_long_makers = ConvertLanguageMap(volume->GetLongMakers());
m_descriptions = ConvertLanguageMap(volume->GetDescriptions());
m_disc_number = volume->GetDiscNumber().value_or(0);
m_platform = volume->GetVolumeType();
m_region = volume->GetRegion();
m_country = volume->GetCountry();
m_blob_type = volume->GetBlobType();
m_raw_size = volume->GetRawSize();
m_apploader_date = QString::fromStdString(volume->GetApploaderDate());
ReadBanner(*volume);
return true;
}
bool GameFile::TryLoadElfDol()
{
if (!IsElfOrDol())
return false;
m_revision = 0;
m_platform = DiscIO::Platform::ELF_DOL;
m_region = DiscIO::Region::UNKNOWN_REGION;
m_country = DiscIO::Country::COUNTRY_UNKNOWN;
m_blob_type = DiscIO::BlobType::DIRECTORY;
m_raw_size = m_size;
m_rating = 0;
return true;
}
QString GameFile::GetFileName() const
{
return QFileInfo(m_path).fileName();
}
QString GameFile::GetFileExtension() const
{
return QFileInfo(m_path).suffix();
}
QString GameFile::GetFileFolder() const
{
return QFileInfo(m_path).dir().dirName();
}
QString GameFile::GetBannerString(const QMap<DiscIO::Language, QString>& m) const
{
// Try the settings language, then English, then just pick one.
if (m.isEmpty())
return QString();
bool wii = m_platform != DiscIO::Platform::GAMECUBE_DISC;
DiscIO::Language current_lang = SConfig::GetInstance().GetCurrentLanguage(wii);
if (m.contains(current_lang))
return m[current_lang];
if (m.contains(DiscIO::Language::LANGUAGE_ENGLISH))
return m[DiscIO::Language::LANGUAGE_ENGLISH];
return m.first();
}
QString GameFile::GetPlatform() const
{
switch (m_platform)
{
case DiscIO::Platform::GAMECUBE_DISC:
return QObject::tr("GameCube");
case DiscIO::Platform::WII_DISC:
return QObject::tr("Wii");
case DiscIO::Platform::WII_WAD:
return QObject::tr("Wii Channel");
case DiscIO::Platform::ELF_DOL:
return QObject::tr("ELF/DOL");
default:
return QObject::tr("Unknown");
}
}
QString GameFile::GetCountry() const
{
switch (m_country)
{
case DiscIO::Country::COUNTRY_EUROPE:
return QObject::tr("Europe");
case DiscIO::Country::COUNTRY_JAPAN:
return QObject::tr("Japan");
case DiscIO::Country::COUNTRY_USA:
return QObject::tr("USA");
case DiscIO::Country::COUNTRY_AUSTRALIA:
return QObject::tr("Australia");
case DiscIO::Country::COUNTRY_FRANCE:
return QObject::tr("France");
case DiscIO::Country::COUNTRY_GERMANY:
return QObject::tr("Germany");
case DiscIO::Country::COUNTRY_ITALY:
return QObject::tr("Italy");
case DiscIO::Country::COUNTRY_KOREA:
return QObject::tr("Korea");
case DiscIO::Country::COUNTRY_NETHERLANDS:
return QObject::tr("Netherlands");
case DiscIO::Country::COUNTRY_RUSSIA:
return QObject::tr("Russia");
case DiscIO::Country::COUNTRY_SPAIN:
return QObject::tr("Spain");
case DiscIO::Country::COUNTRY_TAIWAN:
return QObject::tr("Taiwan");
case DiscIO::Country::COUNTRY_WORLD:
return QObject::tr("World");
default:
return QObject::tr("Unknown");
}
}
QString GameFile::GetLanguage(DiscIO::Language lang) const
{
switch (lang)
{
case DiscIO::Language::LANGUAGE_JAPANESE:
return QObject::tr("Japanese");
case DiscIO::Language::LANGUAGE_ENGLISH:
return QObject::tr("English");
case DiscIO::Language::LANGUAGE_GERMAN:
return QObject::tr("German");
case DiscIO::Language::LANGUAGE_FRENCH:
return QObject::tr("French");
case DiscIO::Language::LANGUAGE_SPANISH:
return QObject::tr("Spanish");
case DiscIO::Language::LANGUAGE_ITALIAN:
return QObject::tr("Italian");
case DiscIO::Language::LANGUAGE_DUTCH:
return QObject::tr("Dutch");
case DiscIO::Language::LANGUAGE_SIMPLIFIED_CHINESE:
return QObject::tr("Simplified Chinese");
case DiscIO::Language::LANGUAGE_TRADITIONAL_CHINESE:
return QObject::tr("Traditional Chinese");
case DiscIO::Language::LANGUAGE_KOREAN:
return QObject::tr("Korean");
default:
return QObject::tr("Unknown");
}
}
QString GameFile::GetUniqueID() const
{
std::vector<std::string> info;
if (!GetGameID().isEmpty())
info.push_back(GetGameID().toStdString());
if (GetRevision() != 0)
{
info.push_back("Revision " + std::to_string(GetRevision()));
}
std::string name = m_long_names[DiscIO::Language::LANGUAGE_ENGLISH].toStdString();
if (name.empty())
{
if (!m_long_names.isEmpty())
name = m_long_names.begin().value().toStdString();
else
{
std::string filename, extension;
name = SplitPath(m_path.toStdString(), nullptr, &filename, &extension);
name = filename + extension;
}
}
int disc_number = GetDiscNumber() + 1;
std::string lower_name = name;
std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(), ::tolower);
if (disc_number > 1 &&
lower_name.find(std::string("disc ") + std::to_string(disc_number)) == std::string::npos &&
lower_name.find(std::string("disc") + std::to_string(disc_number)) == std::string::npos)
{
info.push_back("Disc " + std::to_string(disc_number));
}
if (info.empty())
return QString::fromStdString(name);
std::ostringstream ss;
std::copy(info.begin(), info.end() - 1, std::ostream_iterator<std::string>(ss, ", "));
ss << info.back();
return QString::fromStdString(name + " (" + ss.str() + ")");
}
bool GameFile::IsInstalled() const
{
_assert_(m_platform == DiscIO::Platform::WII_WAD);
const std::string content_dir =
Common::GetTitleContentPath(m_title_id, Common::FromWhichRoot::FROM_CONFIGURED_ROOT);
if (!File::IsDirectory(content_dir))
return false;
// Since this isn't IOS and we only need a simple way to figure out if a title is installed,
// we make the (reasonable) assumption that having more than just the TMD in the content
// directory means that the title is installed.
const auto entries = File::ScanDirectoryTree(content_dir, false);
return std::any_of(entries.children.begin(), entries.children.end(),
[](const auto& file) { return file.virtualName != "title.tmd"; });
}
bool GameFile::Install()
{
_assert_(m_platform == DiscIO::Platform::WII_WAD);
bool installed = WiiUtils::InstallWAD(m_path.toStdString());
if (installed)
Settings::Instance().NANDRefresh();
return installed;
}
bool GameFile::Uninstall()
{
_assert_(m_platform == DiscIO::Platform::WII_WAD);
IOS::HLE::Kernel ios;
return ios.GetES()->DeleteTitleContent(m_title_id) == IOS::HLE::IPC_SUCCESS;
}
bool GameFile::ExportWiiSave()
{
return CWiiSaveCrypted::ExportWiiSave(m_title_id);
}
QString GameFile::GetWiiFSPath() const
{
_assert_(m_platform == DiscIO::Platform::WII_DISC || m_platform == DiscIO::Platform::WII_WAD);
const std::string path = Common::GetTitleDataPath(m_title_id, Common::FROM_CONFIGURED_ROOT);
return QString::fromStdString(path);
}
// Convert an integer size to a friendly string representation.
QString FormatSize(qint64 size)
{
QStringList units{QStringLiteral("KB"), QStringLiteral("MB"), QStringLiteral("GB"),
QStringLiteral("TB")};
QStringListIterator i(units);
QString unit = QStringLiteral("B");
double num = (double)size;
while (num > 1024.0 && i.hasNext())
{
unit = i.next();
num /= 1024.0;
}
return QStringLiteral("%1 %2").arg(QString::number(num, 'f', 1)).arg(unit);
}
template <typename T, typename U = std::enable_if_t<std::is_enum<T>::value>>
QDataStream& operator<<(QDataStream& out, const T& enum_value)
{
out << static_cast<std::underlying_type_t<T>>(enum_value);
return out;
}
template <typename T, typename U = std::enable_if_t<std::is_enum<T>::value>>
QDataStream& operator>>(QDataStream& in, T& enum_value)
{
std::underlying_type_t<T> tmp;
in >> tmp;
enum_value = static_cast<T>(tmp);
return in;
}
// Some C++ implementations define uint64_t as an 'unsigned long', but QDataStream only has built-in
// overloads for quint64, which is an 'unsigned long long' on Unix
QDataStream& operator<<(QDataStream& out, const unsigned long& integer)
{
out << static_cast<quint64>(integer);
return out;
}
QDataStream& operator>>(QDataStream& in, unsigned long& integer)
{
quint64 tmp;
in >> tmp;
integer = static_cast<unsigned long>(tmp);
return in;
}
QDataStream& operator<<(QDataStream& out, const GameFile& file)
{
out << file.m_last_modified;
out << file.m_path;
out << file.m_title_id;
out << file.m_game_id;
out << file.m_maker_id;
out << file.m_maker;
out << file.m_long_makers;
out << file.m_short_makers;
out << file.m_internal_name;
out << file.m_long_names;
out << file.m_short_names;
out << file.m_platform;
out << file.m_region;
out << file.m_country;
out << file.m_blob_type;
out << file.m_size;
out << file.m_raw_size;
out << file.m_descriptions;
out << file.m_revision;
out << file.m_disc_number;
out << file.m_issues;
out << file.m_rating;
out << file.m_apploader_date;
out << file.m_banner;
return out;
}
QDataStream& operator>>(QDataStream& in, GameFile& file)
{
in >> file.m_last_modified;
in >> file.m_path;
in >> file.m_title_id;
in >> file.m_game_id;
in >> file.m_maker_id;
in >> file.m_maker;
in >> file.m_long_makers;
in >> file.m_short_makers;
in >> file.m_internal_name;
in >> file.m_long_names;
in >> file.m_short_names;
in >> file.m_platform;
in >> file.m_region;
in >> file.m_country;
in >> file.m_blob_type;
in >> file.m_size;
in >> file.m_raw_size;
in >> file.m_descriptions;
in >> file.m_revision;
in >> file.m_disc_number;
in >> file.m_issues;
in >> file.m_rating;
in >> file.m_apploader_date;
in >> file.m_banner;
file.m_valid = true;
return in;
}

View File

@ -1,125 +0,0 @@
// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <QDateTime>
#include <QFile>
#include <QMap>
#include <QPixmap>
#include <QString>
#include "Common/CommonTypes.h"
namespace DiscIO
{
enum class BlobType;
enum class Country;
enum class Language;
enum class Region;
enum class Platform;
class Volume;
}
class GameFile final
{
public:
GameFile();
explicit GameFile(const QString& path);
bool IsValid() const;
// These will be properly initialized before we try to load the file.
QString GetFilePath() const { return m_path; }
QString GetFileName() const;
QString GetFileExtension() const;
QString GetFileFolder() const;
qint64 GetFileSize() const { return m_size; }
QDateTime GetLastModified() const { return m_last_modified; }
// The rest will not.
QString GetGameID() const { return m_game_id; }
QString GetMakerID() const { return m_maker_id; }
QString GetMaker() const { return m_maker; }
u64 GetTitleID() const { return m_title_id; }
u16 GetRevision() const { return m_revision; }
QString GetInternalName() const { return m_internal_name; }
QString GetUniqueID() const;
u8 GetDiscNumber() const { return m_disc_number; }
u64 GetRawSize() const { return m_raw_size; }
QPixmap GetBanner() const { return m_banner; }
QString GetIssues() const { return m_issues; }
int GetRating() const { return m_rating; }
QString GetApploaderDate() const { return m_apploader_date; }
DiscIO::Platform GetPlatformID() const { return m_platform; }
QString GetPlatform() const;
DiscIO::Region GetRegion() const { return m_region; }
DiscIO::Country GetCountryID() const { return m_country; }
QString GetCountry() const;
DiscIO::BlobType GetBlobType() const { return m_blob_type; }
QString GetWiiFSPath() const;
bool IsInstalled() const;
// Banner details
QString GetLanguage(DiscIO::Language lang) const;
QList<DiscIO::Language> GetAvailableLanguages() const;
QString GetShortName() const { return GetBannerString(m_short_names); }
QString GetShortMaker() const { return GetBannerString(m_short_makers); }
QString GetLongName() const { return GetBannerString(m_long_names); }
QString GetLongMaker() const { return GetBannerString(m_long_makers); }
QString GetDescription() const { return GetBannerString(m_descriptions); }
QString GetShortName(DiscIO::Language lang) const { return m_short_names[lang]; }
QString GetShortMaker(DiscIO::Language lang) const { return m_short_makers[lang]; }
QString GetLongName(DiscIO::Language lang) const { return m_long_names[lang]; }
QString GetLongMaker(DiscIO::Language lang) const { return m_long_makers[lang]; }
QString GetDescription(DiscIO::Language lang) const { return m_descriptions[lang]; }
bool Install();
bool Uninstall();
bool ExportWiiSave();
friend QDataStream& operator<<(QDataStream& out, const GameFile& file);
friend QDataStream& operator>>(QDataStream& in, GameFile& file);
private:
QString GetBannerString(const QMap<DiscIO::Language, QString>& m) const;
void ReadBanner(const DiscIO::Volume& volume);
bool LoadFileInfo(const QString& path);
void LoadState();
bool IsElfOrDol();
bool TryLoadElfDol();
bool TryLoadVolume();
bool m_valid;
QString m_path;
QDateTime m_last_modified;
qint64 m_size = 0;
QString m_game_id;
QString m_maker;
QString m_maker_id;
u16 m_revision = 0;
u64 m_title_id = 0;
QString m_internal_name;
QMap<DiscIO::Language, QString> m_short_names;
QMap<DiscIO::Language, QString> m_long_names;
QMap<DiscIO::Language, QString> m_short_makers;
QMap<DiscIO::Language, QString> m_long_makers;
QMap<DiscIO::Language, QString> m_descriptions;
u8 m_disc_number = 0;
DiscIO::Region m_region;
DiscIO::Platform m_platform;
DiscIO::Country m_country;
DiscIO::BlobType m_blob_type;
u64 m_raw_size = 0;
QPixmap m_banner;
QString m_issues;
int m_rating = 0;
QString m_apploader_date;
};
QString FormatSize(qint64 size);
QDataStream& operator<<(QDataStream& out, const GameFile& file);
QDataStream& operator>>(QDataStream& in, GameFile& file);
QDataStream& operator<<(QDataStream& out, const unsigned long& file);
QDataStream& operator>>(QDataStream& in, unsigned long& file);

View File

@ -1,76 +0,0 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "GameFileCache.h"
#include <QDataStream>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include "Common/FileUtil.h"
#include "Core/ConfigManager.h"
#include "DolphinQt2/Settings.h"
static const int CACHE_VERSION = 4; // Last changed in PR #6109
static const int DATASTREAM_VERSION = QDataStream::Qt_5_0;
GameFileCache::GameFileCache()
: m_path(QString::fromStdString(File::GetUserPath(D_CACHE_IDX) + "qt_gamefile.cache"))
{
}
bool GameFileCache::IsCached(const QString& path) const
{
return m_gamefiles.contains(path);
}
GameFile GameFileCache::GetFile(const QString& path) const
{
return m_gamefiles[path];
}
void GameFileCache::Load()
{
QFile file(m_path);
if (!file.open(QIODevice::ReadOnly))
return;
QDataStream stream(&file);
stream.setVersion(DATASTREAM_VERSION);
qint32 cache_version;
stream >> cache_version;
// If the cache file is using an older version, ignore it and create it from scratch
if (cache_version != CACHE_VERSION)
return;
stream >> m_gamefiles;
}
void GameFileCache::Save() const
{
QFile file(m_path);
if (!file.open(QIODevice::WriteOnly))
return;
QDataStream stream(&file);
stream.setVersion(DATASTREAM_VERSION);
stream << static_cast<qint32>(CACHE_VERSION);
stream << m_gamefiles;
}
void GameFileCache::Update(const GameFile& gamefile)
{
m_gamefiles[gamefile.GetFilePath()] = gamefile;
}
QList<QString> GameFileCache::GetCached() const
{
return m_gamefiles.keys();
}

View File

@ -1,29 +0,0 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <QList>
#include <QMap>
#include <QString>
#include "DolphinQt2/GameList/GameFile.h"
class GameFileCache
{
public:
explicit GameFileCache();
void Update(const GameFile& gamefile);
void Save() const;
void Load();
bool IsCached(const QString& path) const;
GameFile GetFile(const QString& path) const;
QList<QString> GetCached() const;
private:
QString m_path;
QMap<QString, GameFile> m_gamefiles;
};

View File

@ -21,6 +21,8 @@
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/HW/DVD/DVDInterface.h"
#include "Core/HW/WiiSaveCrypted.h"
#include "Core/WiiUtils.h"
#include "DiscIO/Blob.h"
#include "DiscIO/Enums.h"
@ -157,11 +159,11 @@ void GameList::MakeGridView()
void GameList::ShowContextMenu(const QPoint&)
{
const auto game = GetSelectedGame();
if (game.isNull())
if (!game)
return;
QMenu* menu = new QMenu(this);
DiscIO::Platform platform = game->GetPlatformID();
DiscIO::Platform platform = game->GetPlatform();
AddAction(menu, tr("&Properties"), this, &GameList::OpenProperties);
AddAction(menu, tr("&Wiki"), this, &GameList::OpenWiki);
menu->addSeparator();
@ -188,7 +190,7 @@ void GameList::ShowContextMenu(const QPoint&)
if (platform == DiscIO::Platform::WII_DISC)
{
auto* perform_disc_update = AddAction(menu, tr("Perform System Update"), this, [this] {
WiiUpdate::PerformDiscUpdate(GetSelectedGame()->GetFilePath().toStdString(), this);
WiiUpdate::PerformDiscUpdate(GetSelectedGame()->GetFilePath(), this);
});
perform_disc_update->setEnabled(!Core::IsRunning() || !SConfig::GetInstance().bWii);
}
@ -209,7 +211,8 @@ void GameList::ShowContextMenu(const QPoint&)
connect(&Settings::Instance(), &Settings::EmulationStateChanged, menu, [=](Core::State state) {
wad_install_action->setEnabled(state == Core::State::Uninitialized);
wad_uninstall_action->setEnabled(state == Core::State::Uninitialized && game->IsInstalled());
wad_uninstall_action->setEnabled(state == Core::State::Uninitialized &&
WiiUtils::IsTitleInstalled(game->GetTitleID()));
});
menu->addSeparator();
@ -228,7 +231,7 @@ void GameList::ShowContextMenu(const QPoint&)
QAction* netplay_host = new QAction(tr("Host with NetPlay"), menu);
connect(netplay_host, &QAction::triggered,
[this, game] { emit NetPlayHost(game->GetUniqueID()); });
[this, game] { emit NetPlayHost(QString::fromStdString(game->GetUniqueIdentifier())); });
connect(&Settings::Instance(), &Settings::EmulationStateChanged, menu, [=](Core::State state) {
netplay_host->setEnabled(state == Core::State::Uninitialized);
@ -253,7 +256,7 @@ void GameList::ExportWiiSave()
{
QMessageBox result_dialog(this);
const bool success = GetSelectedGame()->ExportWiiSave();
const bool success = CWiiSaveCrypted::ExportWiiSave(GetSelectedGame()->GetTitleID());
result_dialog.setIcon(success ? QMessageBox::Information : QMessageBox::Critical);
result_dialog.setText(success ? tr("Successfully exported save files") :
@ -263,7 +266,7 @@ void GameList::ExportWiiSave()
void GameList::OpenWiki()
{
QString game_id = GetSelectedGame()->GetGameID();
QString game_id = QString::fromStdString(GetSelectedGame()->GetGameID());
QString url = QStringLiteral("https://wiki.dolphin-emu.org/index.php?title=").append(game_id);
QDesktopServices::openUrl(QUrl(url));
}
@ -275,7 +278,7 @@ void GameList::CompressISO()
const bool compressed = (file->GetBlobType() == DiscIO::BlobType::GCZ);
if (!compressed && file->GetPlatformID() == DiscIO::Platform::WII_DISC)
if (!compressed && file->GetPlatform() == DiscIO::Platform::WII_DISC)
{
QMessageBox wii_warning(this);
wii_warning.setIcon(QMessageBox::Warning);
@ -292,9 +295,9 @@ void GameList::CompressISO()
QString dst_path = QFileDialog::getSaveFileName(
this, compressed ? tr("Select where you want to save the decompressed image") :
tr("Select where you want to save the compressed image"),
QFileInfo(GetSelectedGame()->GetFilePath())
QFileInfo(QString::fromStdString(GetSelectedGame()->GetFilePath()))
.dir()
.absoluteFilePath(file->GetGameID())
.absoluteFilePath(QString::fromStdString(file->GetGameID()))
.append(compressed ? QStringLiteral(".gcm") : QStringLiteral(".gcz")),
compressed ? tr("Uncompressed GC/Wii images (*.iso *.gcm)") :
tr("Compressed GC/Wii images (*.gcz)"));
@ -310,13 +313,13 @@ void GameList::CompressISO()
if (compressed)
{
good = DiscIO::DecompressBlobToFile(original_path.toStdString(), dst_path.toStdString(),
&CompressCB, &progress_dialog);
good = DiscIO::DecompressBlobToFile(original_path, dst_path.toStdString(), &CompressCB,
&progress_dialog);
}
else
{
good = DiscIO::CompressFileToBlob(original_path.toStdString(), dst_path.toStdString(),
file->GetPlatformID() == DiscIO::Platform::WII_DISC ? 1 : 0,
good = DiscIO::CompressFileToBlob(original_path, dst_path.toStdString(),
file->GetPlatform() == DiscIO::Platform::WII_DISC ? 1 : 0,
16384, &CompressCB, &progress_dialog);
}
@ -336,7 +339,7 @@ void GameList::InstallWAD()
{
QMessageBox result_dialog(this);
const bool success = GetSelectedGame()->Install();
const bool success = WiiUtils::InstallWAD(GetSelectedGame()->GetFilePath());
result_dialog.setIcon(success ? QMessageBox::Information : QMessageBox::Critical);
result_dialog.setText(success ? tr("Successfully installed this title to the NAND.") :
@ -358,7 +361,7 @@ void GameList::UninstallWAD()
QMessageBox result_dialog(this);
const bool success = GetSelectedGame()->Uninstall();
const bool success = WiiUtils::UninstallTitle(GetSelectedGame()->GetTitleID());
result_dialog.setIcon(success ? QMessageBox::Information : QMessageBox::Critical);
result_dialog.setText(success ? tr("Successfully removed this title from the NAND.") :
@ -368,24 +371,25 @@ void GameList::UninstallWAD()
void GameList::SetDefaultISO()
{
Settings::Instance().SetDefaultGame(GetSelectedGame()->GetFilePath());
SConfig::GetInstance().m_strDefaultISO = GetSelectedGame()->GetFilePath();
}
void GameList::OpenContainingFolder()
{
QUrl url = QUrl::fromLocalFile(QFileInfo(GetSelectedGame()->GetFilePath()).dir().absolutePath());
QUrl url = QUrl::fromLocalFile(
QFileInfo(QString::fromStdString(GetSelectedGame()->GetFilePath())).dir().absolutePath());
QDesktopServices::openUrl(url);
}
void GameList::OpenSaveFolder()
{
QUrl url = QUrl::fromLocalFile(GetSelectedGame()->GetWiiFSPath());
QUrl url = QUrl::fromLocalFile(QString::fromStdString(GetSelectedGame()->GetWiiFSPath()));
QDesktopServices::openUrl(url);
}
void GameList::DeleteFile()
{
const auto game = GetSelectedGame()->GetFilePath();
const std::string game = GetSelectedGame()->GetFilePath();
QMessageBox confirm_dialog(this);
confirm_dialog.setIcon(QMessageBox::Warning);
@ -399,7 +403,7 @@ void GameList::DeleteFile()
while (!deletion_successful)
{
deletion_successful = File::Delete(game.toStdString());
deletion_successful = File::Delete(game);
if (deletion_successful)
{
@ -424,11 +428,10 @@ void GameList::DeleteFile()
void GameList::ChangeDisc()
{
Core::RunAsCPUThread(
[this] { DVDInterface::ChangeDisc(GetSelectedGame()->GetFilePath().toStdString()); });
Core::RunAsCPUThread([this] { DVDInterface::ChangeDisc(GetSelectedGame()->GetFilePath()); });
}
QSharedPointer<GameFile> GameList::GetSelectedGame() const
std::shared_ptr<const UICommon::GameFile> GameList::GetSelectedGame() const
{
QAbstractItemView* view;
QSortFilterProxyModel* proxy;

View File

@ -4,22 +4,25 @@
#pragma once
#include <memory>
#include <QLabel>
#include <QListView>
#include <QSortFilterProxyModel>
#include <QStackedWidget>
#include <QTableView>
#include "DolphinQt2/GameList/GameFile.h"
#include "DolphinQt2/GameList/GameListModel.h"
#include "UICommon/GameFile.h"
class GameList final : public QStackedWidget
{
Q_OBJECT
public:
explicit GameList(QWidget* parent = nullptr);
QSharedPointer<GameFile> GetSelectedGame() const;
std::shared_ptr<const UICommon::GameFile> GetSelectedGame() const;
void SetListView() { SetPreferredView(true); }
void SetGridView() { SetPreferredView(false); }
@ -30,7 +33,7 @@ public:
signals:
void GameSelected();
void NetPlayHost(const QString& game_id);
void SelectionChanged(QSharedPointer<GameFile> game_file);
void SelectionChanged(std::shared_ptr<const UICommon::GameFile> game_file);
void OpenGeneralSettings();
private:

View File

@ -3,17 +3,23 @@
// Refer to the license.txt file included.
#include "DolphinQt2/GameList/GameListModel.h"
#include <QPixmap>
#include "Core/ConfigManager.h"
#include "DiscIO/Enums.h"
#include "DolphinQt2/QtUtils/ImageConverter.h"
#include "DolphinQt2/Resources.h"
#include "DolphinQt2/Settings.h"
#include "UICommon/UICommon.h"
const QSize GAMECUBE_BANNER_SIZE(96, 32);
GameListModel::GameListModel(QObject* parent) : QAbstractTableModel(parent)
{
connect(&m_tracker, &GameTracker::GameLoaded, this, &GameListModel::UpdateGame);
connect(&m_tracker, &GameTracker::GameRemoved, this, &GameListModel::RemoveGame);
connect(&m_tracker, &GameTracker::GameRemoved, this,
[this](const QString& path) { RemoveGame(path.toStdString()); });
connect(&Settings::Instance(), &Settings::PathAdded, &m_tracker, &GameTracker::AddDirectory);
connect(&Settings::Instance(), &Settings::PathRemoved, &m_tracker, &GameTracker::RemoveDirectory);
@ -26,8 +32,6 @@ GameListModel::GameListModel(QObject* parent) : QAbstractTableModel(parent)
emit layoutAboutToBeChanged();
emit layoutChanged();
});
// TODO: Reload m_title_database when the language changes
}
QVariant GameListModel::data(const QModelIndex& index, int role) const
@ -35,78 +39,68 @@ QVariant GameListModel::data(const QModelIndex& index, int role) const
if (!index.isValid())
return QVariant();
QSharedPointer<GameFile> game = m_games[index.row()];
const UICommon::GameFile& game = *m_games[index.row()];
switch (index.column())
{
case COL_PLATFORM:
if (role == Qt::DecorationRole)
return Resources::GetPlatform(static_cast<int>(game->GetPlatformID()));
return Resources::GetPlatform(static_cast<int>(game.GetPlatform()));
if (role == Qt::InitialSortOrderRole)
return static_cast<int>(game->GetPlatformID());
return static_cast<int>(game.GetPlatform());
break;
case COL_COUNTRY:
if (role == Qt::DecorationRole)
return Resources::GetCountry(static_cast<int>(game->GetCountryID()));
return Resources::GetCountry(static_cast<int>(game.GetCountry()));
if (role == Qt::InitialSortOrderRole)
return static_cast<int>(game->GetCountryID());
return static_cast<int>(game.GetCountry());
break;
case COL_RATING:
if (role == Qt::DecorationRole)
return Resources::GetRating(game->GetRating());
return Resources::GetRating(game.GetEmuState());
if (role == Qt::InitialSortOrderRole)
return game->GetRating();
return game.GetEmuState();
break;
case COL_BANNER:
if (role == Qt::DecorationRole)
{
// GameCube banners are 96x32, but Wii banners are 192x64.
// TODO: use custom banners from rom directory like DolphinWX?
QPixmap banner = game->GetBanner();
QPixmap banner = ToQPixmap(game.GetBannerImage());
if (banner.isNull())
banner = Resources::GetMisc(Resources::BANNER_MISSING);
banner.setDevicePixelRatio(std::max(banner.width() / GAMECUBE_BANNER_SIZE.width(),
banner.height() / GAMECUBE_BANNER_SIZE.height()));
banner.setDevicePixelRatio(
std::max(static_cast<qreal>(banner.width()) / GAMECUBE_BANNER_SIZE.width(),
static_cast<qreal>(banner.height()) / GAMECUBE_BANNER_SIZE.height()));
return banner;
}
break;
case COL_TITLE:
if (role == Qt::DisplayRole || role == Qt::InitialSortOrderRole)
{
QString display_name = QString::fromStdString(m_title_database.GetTitleName(
game->GetGameID().toStdString(), game->GetPlatformID() == DiscIO::Platform::WII_WAD ?
Core::TitleDatabase::TitleType::Channel :
Core::TitleDatabase::TitleType::Other));
if (display_name.isEmpty())
display_name = game->GetLongName();
if (display_name.isEmpty())
display_name = game->GetFileName();
return display_name;
}
return QString::fromStdString(game.GetName());
break;
case COL_ID:
if (role == Qt::DisplayRole || role == Qt::InitialSortOrderRole)
return game->GetGameID();
return QString::fromStdString(game.GetGameID());
break;
case COL_DESCRIPTION:
if (role == Qt::DisplayRole || role == Qt::InitialSortOrderRole)
return game->GetDescription();
return QString::fromStdString(game.GetDescription());
break;
case COL_MAKER:
if (role == Qt::DisplayRole || role == Qt::InitialSortOrderRole)
return game->GetMaker();
return QString::fromStdString(game.GetMaker());
break;
case COL_FILE_NAME:
if (role == Qt::DisplayRole || role == Qt::InitialSortOrderRole)
return game->GetFileName();
return QString::fromStdString(game.GetFileName());
break;
case COL_SIZE:
if (role == Qt::DisplayRole)
return FormatSize(game->GetFileSize());
return QString::fromStdString(UICommon::FormatSize(game.GetFileSize()));
if (role == Qt::InitialSortOrderRole)
return game->GetFileSize();
return static_cast<quint64>(game.GetFileSize());
break;
}
@ -154,10 +148,10 @@ int GameListModel::columnCount(const QModelIndex& parent) const
bool GameListModel::ShouldDisplayGameListItem(int index) const
{
QSharedPointer<GameFile> game = m_games[index];
const UICommon::GameFile& game = *m_games[index];
const bool show_platform = [&game] {
switch (game->GetPlatformID())
switch (game.GetPlatform())
{
case DiscIO::Platform::GAMECUBE_DISC:
return SConfig::GetInstance().m_ListGC;
@ -175,7 +169,7 @@ bool GameListModel::ShouldDisplayGameListItem(int index) const
if (!show_platform)
return false;
switch (game->GetCountryID())
switch (game.GetCountry())
{
case DiscIO::Country::COUNTRY_AUSTRALIA:
return SConfig::GetInstance().m_ListAustralia;
@ -209,16 +203,14 @@ bool GameListModel::ShouldDisplayGameListItem(int index) const
}
}
QSharedPointer<GameFile> GameListModel::GetGameFile(int index) const
std::shared_ptr<const UICommon::GameFile> GameListModel::GetGameFile(int index) const
{
return m_games[index];
}
void GameListModel::UpdateGame(const QSharedPointer<GameFile>& game)
void GameListModel::UpdateGame(const std::shared_ptr<const UICommon::GameFile>& game)
{
QString path = game->GetFilePath();
int index = FindGame(path);
int index = FindGame(game->GetFilePath());
if (index < 0)
{
beginInsertRows(QModelIndex(), m_games.size(), m_games.size());
@ -232,7 +224,7 @@ void GameListModel::UpdateGame(const QSharedPointer<GameFile>& game)
}
}
void GameListModel::RemoveGame(const QString& path)
void GameListModel::RemoveGame(const std::string& path)
{
int entry = FindGame(path);
if (entry < 0)
@ -243,7 +235,7 @@ void GameListModel::RemoveGame(const QString& path)
endRemoveRows();
}
int GameListModel::FindGame(const QString& path) const
int GameListModel::FindGame(const std::string& path) const
{
for (int i = 0; i < m_games.size(); i++)
{

View File

@ -4,12 +4,14 @@
#pragma once
#include <memory>
#include <string>
#include <QAbstractTableModel>
#include <QString>
#include "Core/TitleDatabase.h"
#include "DolphinQt2/GameList/GameFile.h"
#include "DolphinQt2/GameList/GameTracker.h"
#include "UICommon/GameFile.h"
class GameListModel final : public QAbstractTableModel
{
@ -25,11 +27,14 @@ public:
int rowCount(const QModelIndex& parent) const override;
int columnCount(const QModelIndex& parent) const override;
QSharedPointer<GameFile> GetGameFile(int index) const;
// Path of the Game at the specified index.
QString GetPath(int index) const { return m_games[index]->GetFilePath(); }
// Unique ID of the Game at the specified index
QString GetUniqueID(int index) const { return m_games[index]->GetUniqueID(); }
std::shared_ptr<const UICommon::GameFile> GetGameFile(int index) const;
// Path of the game at the specified index.
QString GetPath(int index) const { return QString::fromStdString(m_games[index]->GetFilePath()); }
// Unique identifier of the game at the specified index.
QString GetUniqueIdentifier(int index) const
{
return QString::fromStdString(m_games[index]->GetUniqueIdentifier());
}
bool ShouldDisplayGameListItem(int index) const;
enum
{
@ -46,14 +51,13 @@ public:
NUM_COLS
};
void UpdateGame(const QSharedPointer<GameFile>& game);
void RemoveGame(const QString& path);
void UpdateGame(const std::shared_ptr<const UICommon::GameFile>& game);
void RemoveGame(const std::string& path);
private:
// Index in m_games, or -1 if it isn't found
int FindGame(const QString& path) const;
int FindGame(const std::string& path) const;
GameTracker m_tracker;
QList<QSharedPointer<GameFile>> m_games;
Core::TitleDatabase m_title_database;
QList<std::shared_ptr<const UICommon::GameFile>> m_games;
};

View File

@ -7,7 +7,6 @@
#include <QFile>
#include "DiscIO/DirectoryBlob.h"
#include "DolphinQt2/GameList/GameFileCache.h"
#include "DolphinQt2/GameList/GameTracker.h"
#include "DolphinQt2/QtUtils/QueueOnObject.h"
#include "DolphinQt2/Settings.h"
@ -19,13 +18,15 @@ static const QStringList game_filters{
GameTracker::GameTracker(QObject* parent) : QFileSystemWatcher(parent)
{
qRegisterMetaType<QSharedPointer<GameFile>>();
qRegisterMetaType<std::shared_ptr<const UICommon::GameFile>>();
connect(this, &QFileSystemWatcher::directoryChanged, this, &GameTracker::UpdateDirectory);
connect(this, &QFileSystemWatcher::fileChanged, this, &GameTracker::UpdateFile);
cache.Load();
m_cache.Load();
m_load_thread.Reset([this](const QString& path) { LoadGame(path); });
// TODO: When language changes, reload m_title_database and call m_cache.UpdateAdditionalMetadata
}
void GameTracker::AddDirectory(const QString& dir)
@ -130,25 +131,12 @@ void GameTracker::UpdateFile(const QString& file)
void GameTracker::LoadGame(const QString& path)
{
if (!DiscIO::ShouldHideFromGameList(path.toStdString()))
const std::string converted_path = path.toStdString();
if (!DiscIO::ShouldHideFromGameList(converted_path))
{
if (cache.IsCached(path))
{
const QDateTime last_modified = QFileInfo(path).lastModified();
auto cached_file = cache.GetFile(path);
if (cached_file.GetLastModified() >= last_modified)
{
emit GameLoaded(QSharedPointer<GameFile>::create(cached_file));
return;
}
}
auto game = QSharedPointer<GameFile>::create(path);
if (game->IsValid())
{
emit GameLoaded(game);
cache.Update(*game);
cache.Save();
}
bool cache_changed = false;
emit GameLoaded(m_cache.AddOrGet(converted_path, &cache_changed, m_title_database));
if (cache_changed)
m_cache.Save();
}
}

View File

@ -4,15 +4,17 @@
#pragma once
#include <memory>
#include <QFileSystemWatcher>
#include <QMap>
#include <QSet>
#include <QSharedPointer>
#include <QString>
#include "Common/WorkQueueThread.h"
#include "DolphinQt2/GameList/GameFile.h"
#include "DolphinQt2/GameList/GameFileCache.h"
#include "Core/TitleDatabase.h"
#include "UICommon/GameFile.h"
#include "UICommon/GameFileCache.h"
// Watches directories and loads GameFiles in a separate thread.
// To use this, just add directories using AddDirectory, and listen for the
@ -28,7 +30,7 @@ public:
void RemoveDirectory(const QString& dir);
signals:
void GameLoaded(QSharedPointer<GameFile> game);
void GameLoaded(std::shared_ptr<const UICommon::GameFile> game);
void GameRemoved(const QString& path);
private:
@ -40,7 +42,8 @@ private:
// game path -> directories that track it
QMap<QString, QSet<QString>> m_tracked_files;
Common::WorkQueueThread<QString> m_load_thread;
GameFileCache cache;
UICommon::GameFileCache m_cache;
Core::TitleDatabase m_title_database;
};
Q_DECLARE_METATYPE(QSharedPointer<GameFile>)
Q_DECLARE_METATYPE(std::shared_ptr<const UICommon::GameFile>)

View File

@ -2,6 +2,7 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <QPixmap>
#include <QSize>
#include "DolphinQt2/GameList/GameListModel.h"