summaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
authorspycrab <spycrab@users.noreply.github.com>2018-07-30 03:16:37 +0200
committerspycrab <spycrab@users.noreply.github.com>2018-07-30 03:16:37 +0200
commit8fb3085b0778ccdb90fdca91a7600b89db518b3b (patch)
tree9beda711a1c742ef302950b56c10f0793cca98b3 /Source
parentae0748ab078c1853c9e74ed8ebe729e44b6b03a2 (diff)
Revert "Revert "Qt/GameList: Add option to show covers in grid mode""
Diffstat (limited to 'Source')
-rw-r--r--Source/Core/Common/CommonPaths.h1
-rw-r--r--Source/Core/Common/FileUtil.cpp2
-rw-r--r--Source/Core/Common/FileUtil.h1
-rw-r--r--Source/Core/Core/Config/UISettings.cpp1
-rw-r--r--Source/Core/Core/Config/UISettings.h1
-rw-r--r--Source/Core/DolphinQt/GameList/GameList.cpp42
-rw-r--r--Source/Core/DolphinQt/GameList/GameList.h4
-rw-r--r--Source/Core/DolphinQt/GameList/GameListModel.cpp10
-rw-r--r--Source/Core/DolphinQt/GameList/GameListModel.h4
-rw-r--r--Source/Core/DolphinQt/GameList/GameTracker.cpp13
-rw-r--r--Source/Core/DolphinQt/GameList/GameTracker.h1
-rw-r--r--Source/Core/DolphinQt/GameList/GridProxyModel.cpp35
-rw-r--r--Source/Core/DolphinQt/Settings.cpp10
-rw-r--r--Source/Core/DolphinQt/Settings.h4
-rw-r--r--Source/Core/DolphinQt/Settings/InterfacePane.cpp17
-rw-r--r--Source/Core/DolphinQt/Settings/InterfacePane.h1
-rw-r--r--Source/Core/UICommon/GameFile.cpp154
-rw-r--r--Source/Core/UICommon/GameFile.h17
-rw-r--r--Source/Core/UICommon/GameFileCache.cpp16
-rw-r--r--Source/Core/UICommon/UICommon.cpp1
20 files changed, 327 insertions, 8 deletions
diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h
index 3022e7c5b6..0977f96a9e 100644
--- a/Source/Core/Common/CommonPaths.h
+++ b/Source/Core/Common/CommonPaths.h
@@ -39,6 +39,7 @@
#define GAMESETTINGS_DIR "GameSettings"
#define MAPS_DIR "Maps"
#define CACHE_DIR "Cache"
+#define COVERCACHE_DIR "GameCovers"
#define SHADERCACHE_DIR "Shaders"
#define STATESAVES_DIR "StateSaves"
#define SCREENSHOTS_DIR "ScreenShots"
diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp
index 41cc73240e..2784d1ebda 100644
--- a/Source/Core/Common/FileUtil.cpp
+++ b/Source/Core/Common/FileUtil.cpp
@@ -759,6 +759,7 @@ static void RebuildUserDirectories(unsigned int dir_index)
s_user_paths[D_GAMESETTINGS_IDX] = s_user_paths[D_USER_IDX] + GAMESETTINGS_DIR DIR_SEP;
s_user_paths[D_MAPS_IDX] = s_user_paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
s_user_paths[D_CACHE_IDX] = s_user_paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
+ s_user_paths[D_COVERCACHE_IDX] = s_user_paths[D_CACHE_IDX] + COVERCACHE_DIR DIR_SEP;
s_user_paths[D_SHADERCACHE_IDX] = s_user_paths[D_CACHE_IDX] + SHADERCACHE_DIR DIR_SEP;
s_user_paths[D_SHADERS_IDX] = s_user_paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
s_user_paths[D_STATESAVES_IDX] = s_user_paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
@@ -814,6 +815,7 @@ static void RebuildUserDirectories(unsigned int dir_index)
break;
case D_CACHE_IDX:
+ s_user_paths[D_COVERCACHE_IDX] = s_user_paths[D_CACHE_IDX] + COVERCACHE_DIR DIR_SEP;
s_user_paths[D_SHADERCACHE_IDX] = s_user_paths[D_CACHE_IDX] + SHADERCACHE_DIR DIR_SEP;
break;
diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h
index 035cecb48f..8f4c3633eb 100644
--- a/Source/Core/Common/FileUtil.h
+++ b/Source/Core/Common/FileUtil.h
@@ -29,6 +29,7 @@ enum
// settings (per game)
D_MAPS_IDX,
D_CACHE_IDX,
+ D_COVERCACHE_IDX,
D_SHADERCACHE_IDX,
D_SHADERS_IDX,
D_STATESAVES_IDX,
diff --git a/Source/Core/Core/Config/UISettings.cpp b/Source/Core/Core/Config/UISettings.cpp
index 889c215f36..2c705a82c5 100644
--- a/Source/Core/Core/Config/UISettings.cpp
+++ b/Source/Core/Core/Config/UISettings.cpp
@@ -10,5 +10,6 @@ namespace Config
const ConfigInfo<bool> MAIN_USE_DISCORD_PRESENCE{{System::Main, "General", "UseDiscordPresence"},
true};
+const ConfigInfo<bool> MAIN_USE_GAME_COVERS{{System::Main, "General", "UseGameCovers"}, false};
} // namespace Config
diff --git a/Source/Core/Core/Config/UISettings.h b/Source/Core/Core/Config/UISettings.h
index e515c2472d..014854860a 100644
--- a/Source/Core/Core/Config/UISettings.h
+++ b/Source/Core/Core/Config/UISettings.h
@@ -17,5 +17,6 @@ namespace Config
// UI.General
extern const ConfigInfo<bool> MAIN_USE_DISCORD_PRESENCE;
+extern const ConfigInfo<bool> MAIN_USE_GAME_COVERS;
} // namespace Config
diff --git a/Source/Core/DolphinQt/GameList/GameList.cpp b/Source/Core/DolphinQt/GameList/GameList.cpp
index 4167fb8c15..c1c2e7b363 100644
--- a/Source/Core/DolphinQt/GameList/GameList.cpp
+++ b/Source/Core/DolphinQt/GameList/GameList.cpp
@@ -21,6 +21,7 @@
#include <QMenu>
#include <QMessageBox>
#include <QProgressDialog>
+#include <QShortcut>
#include <QSortFilterProxyModel>
#include <QTableView>
#include <QUrl>
@@ -73,6 +74,15 @@ GameList::GameList(QWidget* parent) : QStackedWidget(parent)
addWidget(m_empty);
m_prefer_list = Settings::Instance().GetPreferredView();
ConsiderViewChange();
+
+ auto* zoom_in = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Plus), this);
+ auto* zoom_out = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Minus), this);
+
+ connect(zoom_in, &QShortcut::activated, this, &GameList::ZoomIn);
+ connect(zoom_out, &QShortcut::activated, this, &GameList::ZoomOut);
+
+ connect(&Settings::Instance(), &Settings::MetadataRefreshCompleted, this,
+ [this] { m_grid_proxy->invalidate(); });
}
void GameList::MakeListView()
@@ -854,3 +864,35 @@ void GameList::SetSearchTerm(const QString& term)
UpdateColumnVisibility();
}
+
+void GameList::ZoomIn()
+{
+ m_model->SetScale(m_model->GetScale() + 0.1);
+
+ m_list_proxy->invalidate();
+ m_grid_proxy->invalidate();
+
+ UpdateFont();
+}
+
+void GameList::ZoomOut()
+{
+ if (m_model->GetScale() <= 0.1)
+ return;
+
+ m_model->SetScale(m_model->GetScale() - 0.1);
+
+ m_list_proxy->invalidate();
+ m_grid_proxy->invalidate();
+
+ UpdateFont();
+}
+
+void GameList::UpdateFont()
+{
+ QFont f;
+
+ f.setPointSizeF(m_model->GetScale() * f.pointSize());
+
+ m_grid->setFont(f);
+}
diff --git a/Source/Core/DolphinQt/GameList/GameList.h b/Source/Core/DolphinQt/GameList/GameList.h
index 2e730c558d..2d42c7cbc3 100644
--- a/Source/Core/DolphinQt/GameList/GameList.h
+++ b/Source/Core/DolphinQt/GameList/GameList.h
@@ -62,6 +62,9 @@ private:
void ChangeDisc();
void UpdateColumnVisibility();
+ void ZoomIn();
+ void ZoomOut();
+
void OnHeaderViewChanged();
void OnSectionResized(int index, int, int);
@@ -71,6 +74,7 @@ private:
// We only have two views, just use a bool to distinguish.
void SetPreferredView(bool list);
void ConsiderViewChange();
+ void UpdateFont();
GameListModel* m_model;
QSortFilterProxyModel* m_list_proxy;
diff --git a/Source/Core/DolphinQt/GameList/GameListModel.cpp b/Source/Core/DolphinQt/GameList/GameListModel.cpp
index c2367bb985..890140340d 100644
--- a/Source/Core/DolphinQt/GameList/GameListModel.cpp
+++ b/Source/Core/DolphinQt/GameList/GameListModel.cpp
@@ -283,3 +283,13 @@ void GameListModel::SetSearchTerm(const QString& term)
{
m_term = term;
}
+
+void GameListModel::SetScale(float scale)
+{
+ m_scale = scale;
+}
+
+float GameListModel::GetScale() const
+{
+ return m_scale;
+}
diff --git a/Source/Core/DolphinQt/GameList/GameListModel.h b/Source/Core/DolphinQt/GameList/GameListModel.h
index 62116dd314..99cc28e080 100644
--- a/Source/Core/DolphinQt/GameList/GameListModel.h
+++ b/Source/Core/DolphinQt/GameList/GameListModel.h
@@ -59,6 +59,9 @@ public:
void UpdateGame(const std::shared_ptr<const UICommon::GameFile>& game);
void RemoveGame(const std::string& path);
+ void SetScale(float scale);
+ float GetScale() const;
+
private:
// Index in m_games, or -1 if it isn't found
int FindGame(const std::string& path) const;
@@ -67,4 +70,5 @@ private:
QList<std::shared_ptr<const UICommon::GameFile>> m_games;
Core::TitleDatabase m_title_database;
QString m_term;
+ float m_scale = 1.0;
};
diff --git a/Source/Core/DolphinQt/GameList/GameTracker.cpp b/Source/Core/DolphinQt/GameList/GameTracker.cpp
index 7d87ae7469..056bcf0e66 100644
--- a/Source/Core/DolphinQt/GameList/GameTracker.cpp
+++ b/Source/Core/DolphinQt/GameList/GameTracker.cpp
@@ -44,6 +44,10 @@ GameTracker::GameTracker(QObject* parent) : QFileSystemWatcher(parent)
}
});
+ connect(&Settings::Instance(), &Settings::MetadataRefreshRequested, this, [this] {
+ m_load_thread.EmplaceItem(Command{CommandType::UpdateMetadata, {}});
+ });
+
m_load_thread.Reset([this](Command command) {
switch (command.type)
{
@@ -64,6 +68,13 @@ GameTracker::GameTracker(QObject* parent) : QFileSystemWatcher(parent)
case CommandType::UpdateFile:
UpdateFileInternal(command.path);
break;
+ case CommandType::UpdateMetadata:
+ m_cache.UpdateAdditionalMetadata(
+ [this](const std::shared_ptr<const UICommon::GameFile>& game) {
+ emit GameUpdated(game);
+ });
+ QueueOnObject(this, [this] { Settings::Instance().NotifyMetadataRefreshComplete(); });
+ break;
}
});
@@ -121,6 +132,8 @@ void GameTracker::StartInternal()
cache_updated |= m_cache.UpdateAdditionalMetadata(emit_game_updated);
if (cache_updated)
m_cache.Save();
+
+ QueueOnObject(this, [this] { Settings::Instance().NotifyMetadataRefreshComplete(); });
}
bool GameTracker::AddPath(const QString& dir)
diff --git a/Source/Core/DolphinQt/GameList/GameTracker.h b/Source/Core/DolphinQt/GameList/GameTracker.h
index db41cf3ef4..d212d983a2 100644
--- a/Source/Core/DolphinQt/GameList/GameTracker.h
+++ b/Source/Core/DolphinQt/GameList/GameTracker.h
@@ -70,6 +70,7 @@ private:
RemoveDirectory,
UpdateDirectory,
UpdateFile,
+ UpdateMetadata
};
struct Command
diff --git a/Source/Core/DolphinQt/GameList/GridProxyModel.cpp b/Source/Core/DolphinQt/GameList/GridProxyModel.cpp
index 516357ac76..36df8306c0 100644
--- a/Source/Core/DolphinQt/GameList/GridProxyModel.cpp
+++ b/Source/Core/DolphinQt/GameList/GridProxyModel.cpp
@@ -4,11 +4,16 @@
#include "DolphinQt/GameList/GridProxyModel.h"
+#include <QImage>
#include <QPixmap>
#include <QSize>
#include "DolphinQt/GameList/GameListModel.h"
+#include "Core/Config/UISettings.h"
+
+#include "UICommon/GameFile.h"
+
const QSize LARGE_BANNER_SIZE(144, 48);
GridProxyModel::GridProxyModel(QObject* parent) : QSortFilterProxyModel(parent)
@@ -27,12 +32,30 @@ QVariant GridProxyModel::data(const QModelIndex& i, int role) const
}
else if (role == Qt::DecorationRole)
{
- auto pixmap = sourceModel()
- ->data(sourceModel()->index(source_index.row(), GameListModel::COL_BANNER),
- Qt::DecorationRole)
- .value<QPixmap>();
- return pixmap.scaled(LARGE_BANNER_SIZE * pixmap.devicePixelRatio(), Qt::KeepAspectRatio,
- Qt::SmoothTransformation);
+ auto* model = static_cast<GameListModel*>(sourceModel());
+
+ const auto& buffer = model->GetGameFile(source_index.row())->GetCoverImage().buffer;
+
+ QPixmap pixmap;
+
+ if (buffer.empty() || !Config::Get(Config::MAIN_USE_GAME_COVERS))
+ {
+ pixmap = model
+ ->data(model->index(source_index.row(), GameListModel::COL_BANNER),
+ Qt::DecorationRole)
+ .value<QPixmap>();
+
+ return pixmap.scaled(LARGE_BANNER_SIZE * model->GetScale() * pixmap.devicePixelRatio(),
+ Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ }
+ else
+ {
+ pixmap = QPixmap::fromImage(QImage::fromData(
+ reinterpret_cast<const unsigned char*>(&buffer[0]), static_cast<int>(buffer.size())));
+
+ return pixmap.scaled(QSize(160, 224) * model->GetScale() * pixmap.devicePixelRatio(),
+ Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ }
}
return QVariant();
}
diff --git a/Source/Core/DolphinQt/Settings.cpp b/Source/Core/DolphinQt/Settings.cpp
index c1dce6b29d..a8ffc41396 100644
--- a/Source/Core/DolphinQt/Settings.cpp
+++ b/Source/Core/DolphinQt/Settings.cpp
@@ -137,6 +137,16 @@ void Settings::RefreshGameList()
emit GameListRefreshRequested();
}
+void Settings::RefreshMetadata()
+{
+ emit MetadataRefreshRequested();
+}
+
+void Settings::NotifyMetadataRefreshComplete()
+{
+ emit MetadataRefreshCompleted();
+}
+
void Settings::ReloadTitleDB()
{
emit TitleDBReloadRequested();
diff --git a/Source/Core/DolphinQt/Settings.h b/Source/Core/DolphinQt/Settings.h
index 68303530fa..4d98acb532 100644
--- a/Source/Core/DolphinQt/Settings.h
+++ b/Source/Core/DolphinQt/Settings.h
@@ -75,6 +75,8 @@ public:
QString GetDefaultGame() const;
void SetDefaultGame(QString path);
void RefreshGameList();
+ void RefreshMetadata();
+ void NotifyMetadataRefreshComplete();
void ReloadTitleDB();
bool IsAutoRefreshEnabled() const;
void SetAutoRefreshEnabled(bool enabled);
@@ -144,6 +146,8 @@ signals:
void DefaultGameChanged(const QString&);
void GameListRefreshRequested();
void TitleDBReloadRequested();
+ void MetadataRefreshRequested();
+ void MetadataRefreshCompleted();
void AutoRefreshToggled(bool enabled);
void HideCursorChanged();
void KeepWindowOnTopChanged(bool top);
diff --git a/Source/Core/DolphinQt/Settings/InterfacePane.cpp b/Source/Core/DolphinQt/Settings/InterfacePane.cpp
index af1225f90a..ad6f66e842 100644
--- a/Source/Core/DolphinQt/Settings/InterfacePane.cpp
+++ b/Source/Core/DolphinQt/Settings/InterfacePane.cpp
@@ -19,10 +19,14 @@
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
+#include "Core/Config/UISettings.h"
#include "Core/ConfigManager.h"
+#include "DolphinQt/GameList/GameListModel.h"
#include "DolphinQt/Settings.h"
+#include "UICommon/GameFile.h"
+
static QComboBox* MakeLanguageComboBox()
{
static const struct
@@ -144,11 +148,14 @@ void InterfacePane::CreateUI()
m_checkbox_top_window = new QCheckBox(tr("Keep Window on Top"));
m_checkbox_use_builtin_title_database = new QCheckBox(tr("Use Built-In Database of Game Names"));
m_checkbox_use_userstyle = new QCheckBox(tr("Use Custom User Style"));
+ m_checkbox_use_covers =
+ new QCheckBox(tr("Download Game Covers from GameTDB.com for Use in Grid Mode"));
m_checkbox_show_debugging_ui = new QCheckBox(tr("Show Debugging UI"));
groupbox_layout->addWidget(m_checkbox_top_window);
groupbox_layout->addWidget(m_checkbox_use_builtin_title_database);
groupbox_layout->addWidget(m_checkbox_use_userstyle);
+ groupbox_layout->addWidget(m_checkbox_use_covers);
groupbox_layout->addWidget(m_checkbox_show_debugging_ui);
}
@@ -179,6 +186,7 @@ void InterfacePane::ConnectLayout()
connect(m_checkbox_top_window, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig);
connect(m_checkbox_use_builtin_title_database, &QCheckBox::toggled, this,
&InterfacePane::OnSaveConfig);
+ connect(m_checkbox_use_covers, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig);
connect(m_checkbox_show_debugging_ui, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig);
connect(m_combobox_theme,
static_cast<void (QComboBox::*)(const QString&)>(&QComboBox::currentIndexChanged),
@@ -229,6 +237,7 @@ void InterfacePane::LoadConfig()
m_checkbox_enable_osd->setChecked(startup_params.bOnScreenDisplayMessages);
m_checkbox_show_active_title->setChecked(startup_params.m_show_active_title);
m_checkbox_pause_on_focus_lost->setChecked(startup_params.m_PauseOnFocusLost);
+ m_checkbox_use_covers->setChecked(Config::Get(Config::MAIN_USE_GAME_COVERS));
m_checkbox_hide_mouse->setChecked(Settings::Instance().GetHideCursor());
}
@@ -264,5 +273,13 @@ void InterfacePane::OnSaveConfig()
tr("You must restart Dolphin in order for the change to take effect."));
}
+ const bool use_covers = m_checkbox_use_covers->isChecked();
+
+ if (use_covers != Config::Get(Config::MAIN_USE_GAME_COVERS))
+ {
+ Config::SetBase(Config::MAIN_USE_GAME_COVERS, use_covers);
+ Settings::Instance().RefreshMetadata();
+ }
+
settings.SaveSettings();
}
diff --git a/Source/Core/DolphinQt/Settings/InterfacePane.h b/Source/Core/DolphinQt/Settings/InterfacePane.h
index e3c2a707bd..44ed9df96e 100644
--- a/Source/Core/DolphinQt/Settings/InterfacePane.h
+++ b/Source/Core/DolphinQt/Settings/InterfacePane.h
@@ -35,6 +35,7 @@ private:
QCheckBox* m_checkbox_use_builtin_title_database;
QCheckBox* m_checkbox_use_userstyle;
QCheckBox* m_checkbox_show_debugging_ui;
+ QCheckBox* m_checkbox_use_covers;
QCheckBox* m_checkbox_confirm_on_stop;
QCheckBox* m_checkbox_use_panic_handlers;
diff --git a/Source/Core/UICommon/GameFile.cpp b/Source/Core/UICommon/GameFile.cpp
index af8421e5d9..2177bc9922 100644
--- a/Source/Core/UICommon/GameFile.cpp
+++ b/Source/Core/UICommon/GameFile.cpp
@@ -23,6 +23,7 @@
#include "Common/File.h"
#include "Common/FileUtil.h"
#include "Common/Hash.h"
+#include "Common/HttpRequest.h"
#include "Common/Image.h"
#include "Common/IniFile.h"
#include "Common/NandPaths.h"
@@ -30,6 +31,7 @@
#include "Common/Swap.h"
#include "Core/Boot/Boot.h"
+#include "Core/Config/UISettings.h"
#include "Core/ConfigManager.h"
#include "Core/IOS/ES/Formats.h"
#include "Core/TitleDatabase.h"
@@ -39,6 +41,8 @@
#include "DiscIO/Volume.h"
#include "DiscIO/WiiSaveBanner.h"
+constexpr const char* COVER_URL = "https://art.gametdb.com/wii/cover/%s/%s.png";
+
namespace UICommon
{
static const std::string EMPTY_STRING;
@@ -149,6 +153,144 @@ bool GameFile::IsValid() const
return true;
}
+bool GameFile::CustomCoverChanged()
+{
+ if (!m_custom_cover.buffer.empty() || !Config::Get(Config::MAIN_USE_GAME_COVERS))
+ return false;
+
+ std::string path, name;
+ SplitPath(m_file_path, &path, &name, nullptr);
+
+ std::string contents;
+
+ // This icon naming format is intended as an alternative to Homebrew Channel icons
+ // for those who don't want to have a Homebrew Channel style folder structure.
+ bool success = File::Exists(path + name + ".cover.png") &&
+ File::ReadFileToString(path + name + ".cover.png", contents);
+
+ if (!success)
+ {
+ success =
+ File::Exists(path + "cover.png") && File::ReadFileToString(path + "cover.png", contents);
+ }
+
+ if (success)
+ m_pending.custom_cover.buffer = {contents.begin(), contents.end()};
+
+ return success;
+}
+
+void GameFile::DownloadDefaultCover()
+{
+#ifndef ANDROID
+ if (!m_default_cover.buffer.empty() || !Config::Get(Config::MAIN_USE_GAME_COVERS))
+ return;
+
+ const auto cover_path = File::GetUserPath(D_COVERCACHE_IDX) + DIR_SEP;
+
+ // If covers have already been downloaded, abort
+ if (File::Exists(cover_path + m_game_id + ".png") ||
+ File::Exists(cover_path + m_game_id.substr(0, 4) + ".png"))
+ return;
+
+ Common::HttpRequest request;
+
+ std::string region_code;
+
+ auto user_lang = SConfig::GetInstance().GetCurrentLanguage(DiscIO::IsWii(GetPlatform()));
+
+ switch (m_region)
+ {
+ case DiscIO::Region::NTSC_J:
+ region_code = "JA";
+ break;
+ case DiscIO::Region::NTSC_U:
+ region_code = "US";
+ break;
+ case DiscIO::Region::NTSC_K:
+ region_code = "KO";
+ break;
+ case DiscIO::Region::PAL:
+ switch (user_lang)
+ {
+ case DiscIO::Language::German:
+ region_code = "DE";
+ break;
+ case DiscIO::Language::French:
+ region_code = "FR";
+ break;
+ case DiscIO::Language::Spanish:
+ region_code = "ES";
+ break;
+ case DiscIO::Language::Italian:
+ region_code = "IT";
+ break;
+ case DiscIO::Language::Dutch:
+ region_code = "NL";
+ break;
+ case DiscIO::Language::English:
+ default:
+ region_code = "EN";
+ break;
+ }
+ break;
+ case DiscIO::Region::Unknown:
+ region_code = "EN";
+ break;
+ }
+
+ auto response = request.Get(StringFromFormat(COVER_URL, region_code.c_str(), m_game_id.c_str()));
+
+ if (response)
+ {
+ File::WriteStringToFile(std::string(response.value().begin(), response.value().end()),
+ cover_path + m_game_id + ".png");
+ return;
+ }
+
+ response =
+ request.Get(StringFromFormat(COVER_URL, region_code.c_str(), m_game_id.substr(0, 4).c_str()));
+
+ if (response)
+ {
+ File::WriteStringToFile(std::string(response.value().begin(), response.value().end()),
+ cover_path + m_game_id.substr(0, 4) + ".png");
+ }
+#endif
+}
+
+bool GameFile::DefaultCoverChanged()
+{
+ if (!m_default_cover.buffer.empty() || !Config::Get(Config::MAIN_USE_GAME_COVERS))
+ return false;
+
+ const auto cover_path = File::GetUserPath(D_COVERCACHE_IDX) + DIR_SEP;
+
+ std::string contents;
+
+ File::ReadFileToString(cover_path + m_game_id + ".png", contents);
+
+ if (contents.empty())
+ File::ReadFileToString(cover_path + m_game_id.substr(0, 4).c_str() + ".png", contents);
+
+ if (contents.empty())
+ return false;
+
+ m_pending.default_cover.buffer = {contents.begin(), contents.end()};
+
+ return true;
+}
+
+void GameFile::CustomCoverCommit()
+{
+ m_custom_cover = std::move(m_pending.custom_cover);
+}
+
+void GameFile::DefaultCoverCommit()
+{
+ m_default_cover = std::move(m_pending.default_cover);
+}
+
void GameBanner::DoState(PointerWrap& p)
{
p.Do(buffer);
@@ -156,6 +298,11 @@ void GameBanner::DoState(PointerWrap& p)
p.Do(height);
}
+void GameCover::DoState(PointerWrap& p)
+{
+ p.Do(buffer);
+}
+
void GameFile::DoState(PointerWrap& p)
{
p.Do(m_valid);
@@ -185,6 +332,8 @@ void GameFile::DoState(PointerWrap& p)
m_volume_banner.DoState(p);
m_custom_banner.DoState(p);
+ m_default_cover.DoState(p);
+ m_custom_cover.DoState(p);
}
bool GameFile::IsElfOrDol() const
@@ -353,4 +502,9 @@ const GameBanner& GameFile::GetBannerImage() const
return m_custom_banner.empty() ? m_volume_banner : m_custom_banner;
}
+const GameCover& GameFile::GetCoverImage() const
+{
+ return m_custom_cover.empty() ? m_default_cover : m_custom_cover;
+}
+
} // namespace UICommon
diff --git a/Source/Core/UICommon/GameFile.h b/Source/Core/UICommon/GameFile.h
index 60d874a375..e5b4257db6 100644
--- a/Source/Core/UICommon/GameFile.h
+++ b/Source/Core/UICommon/GameFile.h
@@ -30,6 +30,13 @@ struct GameBanner
void DoState(PointerWrap& p);
};
+struct GameCover
+{
+ std::vector<u8> buffer{};
+ bool empty() const { return buffer.empty(); }
+ void DoState(PointerWrap& p);
+};
+
bool operator==(const GameBanner& lhs, const GameBanner& rhs);
bool operator!=(const GameBanner& lhs, const GameBanner& rhs);
@@ -75,11 +82,17 @@ public:
u64 GetFileSize() const { return m_file_size; }
u64 GetVolumeSize() const { return m_volume_size; }
const GameBanner& GetBannerImage() const;
+ const GameCover& GetCoverImage() const;
void DoState(PointerWrap& p);
bool WiiBannerChanged();
void WiiBannerCommit();
bool CustomBannerChanged();
void CustomBannerCommit();
+ void DownloadDefaultCover();
+ bool DefaultCoverChanged();
+ void DefaultCoverCommit();
+ bool CustomCoverChanged();
+ void CustomCoverCommit();
private:
static const std::string& Lookup(DiscIO::Language language,
@@ -120,6 +133,8 @@ private:
GameBanner m_volume_banner{};
GameBanner m_custom_banner{};
+ GameCover m_default_cover{};
+ GameCover m_custom_cover{};
// The following data members allow GameFileCache to construct updated versions
// of GameFiles in a threadsafe way. They should not be handled in DoState.
@@ -127,6 +142,8 @@ private:
{
GameBanner volume_banner;
GameBanner custom_banner;
+ GameCover default_cover;
+ GameCover custom_cover;
} m_pending{};
};
diff --git a/Source/Core/UICommon/GameFileCache.cpp b/Source/Core/UICommon/GameFileCache.cpp
index 074dc7fe62..82ade8e964 100644
--- a/Source/Core/UICommon/GameFileCache.cpp
+++ b/Source/Core/UICommon/GameFileCache.cpp
@@ -27,7 +27,7 @@
namespace UICommon
{
-static constexpr u32 CACHE_REVISION = 11; // Last changed in PR 7058
+static constexpr u32 CACHE_REVISION = 12; // Last changed in PR 7285
std::vector<std::string> FindAllGamePaths(const std::vector<std::string>& directories_to_scan,
bool recursive_scan)
@@ -165,7 +165,14 @@ bool GameFileCache::UpdateAdditionalMetadata(std::shared_ptr<GameFile>* game_fil
{
const bool wii_banner_changed = (*game_file)->WiiBannerChanged();
const bool custom_banner_changed = (*game_file)->CustomBannerChanged();
- if (!wii_banner_changed && !custom_banner_changed)
+
+ (*game_file)->DownloadDefaultCover();
+
+ const bool default_cover_changed = (*game_file)->DefaultCoverChanged();
+ const bool custom_cover_changed = (*game_file)->CustomCoverChanged();
+
+ if (!wii_banner_changed && !custom_banner_changed && !default_cover_changed &&
+ !custom_cover_changed)
return false;
// If a cached file needs an update, apply the updates to a copy and delete the original.
@@ -176,6 +183,11 @@ bool GameFileCache::UpdateAdditionalMetadata(std::shared_ptr<GameFile>* game_fil
copy->WiiBannerCommit();
if (custom_banner_changed)
copy->CustomBannerCommit();
+ if (default_cover_changed)
+ copy->DefaultCoverCommit();
+ if (custom_cover_changed)
+ copy->CustomCoverCommit();
+
*game_file = std::move(copy);
return true;
diff --git a/Source/Core/UICommon/UICommon.cpp b/Source/Core/UICommon/UICommon.cpp
index c84d4af04b..2055c4d863 100644
--- a/Source/Core/UICommon/UICommon.cpp
+++ b/Source/Core/UICommon/UICommon.cpp
@@ -158,6 +158,7 @@ void CreateDirectories()
{
File::CreateFullPath(File::GetUserPath(D_USER_IDX));
File::CreateFullPath(File::GetUserPath(D_CACHE_IDX));
+ File::CreateFullPath(File::GetUserPath(D_COVERCACHE_IDX));
File::CreateFullPath(File::GetUserPath(D_CONFIG_IDX));
File::CreateFullPath(File::GetUserPath(D_DUMPDSP_IDX));
File::CreateFullPath(File::GetUserPath(D_DUMPSSL_IDX));