Revert "Revert "Qt/GameList: Add option to show covers in grid mode""
This commit is contained in:
parent
ae0748ab07
commit
8fb3085b07
|
@ -39,6 +39,7 @@
|
||||||
#define GAMESETTINGS_DIR "GameSettings"
|
#define GAMESETTINGS_DIR "GameSettings"
|
||||||
#define MAPS_DIR "Maps"
|
#define MAPS_DIR "Maps"
|
||||||
#define CACHE_DIR "Cache"
|
#define CACHE_DIR "Cache"
|
||||||
|
#define COVERCACHE_DIR "GameCovers"
|
||||||
#define SHADERCACHE_DIR "Shaders"
|
#define SHADERCACHE_DIR "Shaders"
|
||||||
#define STATESAVES_DIR "StateSaves"
|
#define STATESAVES_DIR "StateSaves"
|
||||||
#define SCREENSHOTS_DIR "ScreenShots"
|
#define SCREENSHOTS_DIR "ScreenShots"
|
||||||
|
|
|
@ -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_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_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_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_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_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;
|
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;
|
break;
|
||||||
|
|
||||||
case D_CACHE_IDX:
|
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;
|
s_user_paths[D_SHADERCACHE_IDX] = s_user_paths[D_CACHE_IDX] + SHADERCACHE_DIR DIR_SEP;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ enum
|
||||||
// settings (per game)
|
// settings (per game)
|
||||||
D_MAPS_IDX,
|
D_MAPS_IDX,
|
||||||
D_CACHE_IDX,
|
D_CACHE_IDX,
|
||||||
|
D_COVERCACHE_IDX,
|
||||||
D_SHADERCACHE_IDX,
|
D_SHADERCACHE_IDX,
|
||||||
D_SHADERS_IDX,
|
D_SHADERS_IDX,
|
||||||
D_STATESAVES_IDX,
|
D_STATESAVES_IDX,
|
||||||
|
|
|
@ -10,5 +10,6 @@ namespace Config
|
||||||
|
|
||||||
const ConfigInfo<bool> MAIN_USE_DISCORD_PRESENCE{{System::Main, "General", "UseDiscordPresence"},
|
const ConfigInfo<bool> MAIN_USE_DISCORD_PRESENCE{{System::Main, "General", "UseDiscordPresence"},
|
||||||
true};
|
true};
|
||||||
|
const ConfigInfo<bool> MAIN_USE_GAME_COVERS{{System::Main, "General", "UseGameCovers"}, false};
|
||||||
|
|
||||||
} // namespace Config
|
} // namespace Config
|
||||||
|
|
|
@ -17,5 +17,6 @@ namespace Config
|
||||||
// UI.General
|
// UI.General
|
||||||
|
|
||||||
extern const ConfigInfo<bool> MAIN_USE_DISCORD_PRESENCE;
|
extern const ConfigInfo<bool> MAIN_USE_DISCORD_PRESENCE;
|
||||||
|
extern const ConfigInfo<bool> MAIN_USE_GAME_COVERS;
|
||||||
|
|
||||||
} // namespace Config
|
} // namespace Config
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QProgressDialog>
|
#include <QProgressDialog>
|
||||||
|
#include <QShortcut>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
#include <QTableView>
|
#include <QTableView>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
@ -73,6 +74,15 @@ GameList::GameList(QWidget* parent) : QStackedWidget(parent)
|
||||||
addWidget(m_empty);
|
addWidget(m_empty);
|
||||||
m_prefer_list = Settings::Instance().GetPreferredView();
|
m_prefer_list = Settings::Instance().GetPreferredView();
|
||||||
ConsiderViewChange();
|
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()
|
void GameList::MakeListView()
|
||||||
|
@ -854,3 +864,35 @@ void GameList::SetSearchTerm(const QString& term)
|
||||||
|
|
||||||
UpdateColumnVisibility();
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -62,6 +62,9 @@ private:
|
||||||
void ChangeDisc();
|
void ChangeDisc();
|
||||||
void UpdateColumnVisibility();
|
void UpdateColumnVisibility();
|
||||||
|
|
||||||
|
void ZoomIn();
|
||||||
|
void ZoomOut();
|
||||||
|
|
||||||
void OnHeaderViewChanged();
|
void OnHeaderViewChanged();
|
||||||
void OnSectionResized(int index, int, int);
|
void OnSectionResized(int index, int, int);
|
||||||
|
|
||||||
|
@ -71,6 +74,7 @@ private:
|
||||||
// We only have two views, just use a bool to distinguish.
|
// We only have two views, just use a bool to distinguish.
|
||||||
void SetPreferredView(bool list);
|
void SetPreferredView(bool list);
|
||||||
void ConsiderViewChange();
|
void ConsiderViewChange();
|
||||||
|
void UpdateFont();
|
||||||
|
|
||||||
GameListModel* m_model;
|
GameListModel* m_model;
|
||||||
QSortFilterProxyModel* m_list_proxy;
|
QSortFilterProxyModel* m_list_proxy;
|
||||||
|
|
|
@ -283,3 +283,13 @@ void GameListModel::SetSearchTerm(const QString& term)
|
||||||
{
|
{
|
||||||
m_term = term;
|
m_term = term;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameListModel::SetScale(float scale)
|
||||||
|
{
|
||||||
|
m_scale = scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GameListModel::GetScale() const
|
||||||
|
{
|
||||||
|
return m_scale;
|
||||||
|
}
|
||||||
|
|
|
@ -59,6 +59,9 @@ public:
|
||||||
void UpdateGame(const std::shared_ptr<const UICommon::GameFile>& game);
|
void UpdateGame(const std::shared_ptr<const UICommon::GameFile>& game);
|
||||||
void RemoveGame(const std::string& path);
|
void RemoveGame(const std::string& path);
|
||||||
|
|
||||||
|
void SetScale(float scale);
|
||||||
|
float GetScale() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Index in m_games, or -1 if it isn't found
|
// Index in m_games, or -1 if it isn't found
|
||||||
int FindGame(const std::string& path) const;
|
int FindGame(const std::string& path) const;
|
||||||
|
@ -67,4 +70,5 @@ private:
|
||||||
QList<std::shared_ptr<const UICommon::GameFile>> m_games;
|
QList<std::shared_ptr<const UICommon::GameFile>> m_games;
|
||||||
Core::TitleDatabase m_title_database;
|
Core::TitleDatabase m_title_database;
|
||||||
QString m_term;
|
QString m_term;
|
||||||
|
float m_scale = 1.0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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) {
|
m_load_thread.Reset([this](Command command) {
|
||||||
switch (command.type)
|
switch (command.type)
|
||||||
{
|
{
|
||||||
|
@ -64,6 +68,13 @@ GameTracker::GameTracker(QObject* parent) : QFileSystemWatcher(parent)
|
||||||
case CommandType::UpdateFile:
|
case CommandType::UpdateFile:
|
||||||
UpdateFileInternal(command.path);
|
UpdateFileInternal(command.path);
|
||||||
break;
|
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);
|
cache_updated |= m_cache.UpdateAdditionalMetadata(emit_game_updated);
|
||||||
if (cache_updated)
|
if (cache_updated)
|
||||||
m_cache.Save();
|
m_cache.Save();
|
||||||
|
|
||||||
|
QueueOnObject(this, [this] { Settings::Instance().NotifyMetadataRefreshComplete(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameTracker::AddPath(const QString& dir)
|
bool GameTracker::AddPath(const QString& dir)
|
||||||
|
|
|
@ -70,6 +70,7 @@ private:
|
||||||
RemoveDirectory,
|
RemoveDirectory,
|
||||||
UpdateDirectory,
|
UpdateDirectory,
|
||||||
UpdateFile,
|
UpdateFile,
|
||||||
|
UpdateMetadata
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Command
|
struct Command
|
||||||
|
|
|
@ -4,11 +4,16 @@
|
||||||
|
|
||||||
#include "DolphinQt/GameList/GridProxyModel.h"
|
#include "DolphinQt/GameList/GridProxyModel.h"
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QSize>
|
#include <QSize>
|
||||||
|
|
||||||
#include "DolphinQt/GameList/GameListModel.h"
|
#include "DolphinQt/GameList/GameListModel.h"
|
||||||
|
|
||||||
|
#include "Core/Config/UISettings.h"
|
||||||
|
|
||||||
|
#include "UICommon/GameFile.h"
|
||||||
|
|
||||||
const QSize LARGE_BANNER_SIZE(144, 48);
|
const QSize LARGE_BANNER_SIZE(144, 48);
|
||||||
|
|
||||||
GridProxyModel::GridProxyModel(QObject* parent) : QSortFilterProxyModel(parent)
|
GridProxyModel::GridProxyModel(QObject* parent) : QSortFilterProxyModel(parent)
|
||||||
|
@ -27,12 +32,30 @@ QVariant GridProxyModel::data(const QModelIndex& i, int role) const
|
||||||
}
|
}
|
||||||
else if (role == Qt::DecorationRole)
|
else if (role == Qt::DecorationRole)
|
||||||
{
|
{
|
||||||
auto pixmap = sourceModel()
|
auto* model = static_cast<GameListModel*>(sourceModel());
|
||||||
->data(sourceModel()->index(source_index.row(), GameListModel::COL_BANNER),
|
|
||||||
Qt::DecorationRole)
|
const auto& buffer = model->GetGameFile(source_index.row())->GetCoverImage().buffer;
|
||||||
.value<QPixmap>();
|
|
||||||
return pixmap.scaled(LARGE_BANNER_SIZE * pixmap.devicePixelRatio(), Qt::KeepAspectRatio,
|
QPixmap pixmap;
|
||||||
Qt::SmoothTransformation);
|
|
||||||
|
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();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,6 +137,16 @@ void Settings::RefreshGameList()
|
||||||
emit GameListRefreshRequested();
|
emit GameListRefreshRequested();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Settings::RefreshMetadata()
|
||||||
|
{
|
||||||
|
emit MetadataRefreshRequested();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::NotifyMetadataRefreshComplete()
|
||||||
|
{
|
||||||
|
emit MetadataRefreshCompleted();
|
||||||
|
}
|
||||||
|
|
||||||
void Settings::ReloadTitleDB()
|
void Settings::ReloadTitleDB()
|
||||||
{
|
{
|
||||||
emit TitleDBReloadRequested();
|
emit TitleDBReloadRequested();
|
||||||
|
|
|
@ -75,6 +75,8 @@ public:
|
||||||
QString GetDefaultGame() const;
|
QString GetDefaultGame() const;
|
||||||
void SetDefaultGame(QString path);
|
void SetDefaultGame(QString path);
|
||||||
void RefreshGameList();
|
void RefreshGameList();
|
||||||
|
void RefreshMetadata();
|
||||||
|
void NotifyMetadataRefreshComplete();
|
||||||
void ReloadTitleDB();
|
void ReloadTitleDB();
|
||||||
bool IsAutoRefreshEnabled() const;
|
bool IsAutoRefreshEnabled() const;
|
||||||
void SetAutoRefreshEnabled(bool enabled);
|
void SetAutoRefreshEnabled(bool enabled);
|
||||||
|
@ -144,6 +146,8 @@ signals:
|
||||||
void DefaultGameChanged(const QString&);
|
void DefaultGameChanged(const QString&);
|
||||||
void GameListRefreshRequested();
|
void GameListRefreshRequested();
|
||||||
void TitleDBReloadRequested();
|
void TitleDBReloadRequested();
|
||||||
|
void MetadataRefreshRequested();
|
||||||
|
void MetadataRefreshCompleted();
|
||||||
void AutoRefreshToggled(bool enabled);
|
void AutoRefreshToggled(bool enabled);
|
||||||
void HideCursorChanged();
|
void HideCursorChanged();
|
||||||
void KeepWindowOnTopChanged(bool top);
|
void KeepWindowOnTopChanged(bool top);
|
||||||
|
|
|
@ -19,10 +19,14 @@
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
|
|
||||||
|
#include "Core/Config/UISettings.h"
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
|
|
||||||
|
#include "DolphinQt/GameList/GameListModel.h"
|
||||||
#include "DolphinQt/Settings.h"
|
#include "DolphinQt/Settings.h"
|
||||||
|
|
||||||
|
#include "UICommon/GameFile.h"
|
||||||
|
|
||||||
static QComboBox* MakeLanguageComboBox()
|
static QComboBox* MakeLanguageComboBox()
|
||||||
{
|
{
|
||||||
static const struct
|
static const struct
|
||||||
|
@ -144,11 +148,14 @@ void InterfacePane::CreateUI()
|
||||||
m_checkbox_top_window = new QCheckBox(tr("Keep Window on Top"));
|
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_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_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"));
|
m_checkbox_show_debugging_ui = new QCheckBox(tr("Show Debugging UI"));
|
||||||
|
|
||||||
groupbox_layout->addWidget(m_checkbox_top_window);
|
groupbox_layout->addWidget(m_checkbox_top_window);
|
||||||
groupbox_layout->addWidget(m_checkbox_use_builtin_title_database);
|
groupbox_layout->addWidget(m_checkbox_use_builtin_title_database);
|
||||||
groupbox_layout->addWidget(m_checkbox_use_userstyle);
|
groupbox_layout->addWidget(m_checkbox_use_userstyle);
|
||||||
|
groupbox_layout->addWidget(m_checkbox_use_covers);
|
||||||
groupbox_layout->addWidget(m_checkbox_show_debugging_ui);
|
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_top_window, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig);
|
||||||
connect(m_checkbox_use_builtin_title_database, &QCheckBox::toggled, this,
|
connect(m_checkbox_use_builtin_title_database, &QCheckBox::toggled, this,
|
||||||
&InterfacePane::OnSaveConfig);
|
&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_checkbox_show_debugging_ui, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig);
|
||||||
connect(m_combobox_theme,
|
connect(m_combobox_theme,
|
||||||
static_cast<void (QComboBox::*)(const QString&)>(&QComboBox::currentIndexChanged),
|
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_enable_osd->setChecked(startup_params.bOnScreenDisplayMessages);
|
||||||
m_checkbox_show_active_title->setChecked(startup_params.m_show_active_title);
|
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_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());
|
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."));
|
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();
|
settings.SaveSettings();
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ private:
|
||||||
QCheckBox* m_checkbox_use_builtin_title_database;
|
QCheckBox* m_checkbox_use_builtin_title_database;
|
||||||
QCheckBox* m_checkbox_use_userstyle;
|
QCheckBox* m_checkbox_use_userstyle;
|
||||||
QCheckBox* m_checkbox_show_debugging_ui;
|
QCheckBox* m_checkbox_show_debugging_ui;
|
||||||
|
QCheckBox* m_checkbox_use_covers;
|
||||||
|
|
||||||
QCheckBox* m_checkbox_confirm_on_stop;
|
QCheckBox* m_checkbox_confirm_on_stop;
|
||||||
QCheckBox* m_checkbox_use_panic_handlers;
|
QCheckBox* m_checkbox_use_panic_handlers;
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "Common/File.h"
|
#include "Common/File.h"
|
||||||
#include "Common/FileUtil.h"
|
#include "Common/FileUtil.h"
|
||||||
#include "Common/Hash.h"
|
#include "Common/Hash.h"
|
||||||
|
#include "Common/HttpRequest.h"
|
||||||
#include "Common/Image.h"
|
#include "Common/Image.h"
|
||||||
#include "Common/IniFile.h"
|
#include "Common/IniFile.h"
|
||||||
#include "Common/NandPaths.h"
|
#include "Common/NandPaths.h"
|
||||||
|
@ -30,6 +31,7 @@
|
||||||
#include "Common/Swap.h"
|
#include "Common/Swap.h"
|
||||||
|
|
||||||
#include "Core/Boot/Boot.h"
|
#include "Core/Boot/Boot.h"
|
||||||
|
#include "Core/Config/UISettings.h"
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/IOS/ES/Formats.h"
|
#include "Core/IOS/ES/Formats.h"
|
||||||
#include "Core/TitleDatabase.h"
|
#include "Core/TitleDatabase.h"
|
||||||
|
@ -39,6 +41,8 @@
|
||||||
#include "DiscIO/Volume.h"
|
#include "DiscIO/Volume.h"
|
||||||
#include "DiscIO/WiiSaveBanner.h"
|
#include "DiscIO/WiiSaveBanner.h"
|
||||||
|
|
||||||
|
constexpr const char* COVER_URL = "https://art.gametdb.com/wii/cover/%s/%s.png";
|
||||||
|
|
||||||
namespace UICommon
|
namespace UICommon
|
||||||
{
|
{
|
||||||
static const std::string EMPTY_STRING;
|
static const std::string EMPTY_STRING;
|
||||||
|
@ -149,6 +153,144 @@ bool GameFile::IsValid() const
|
||||||
return true;
|
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)
|
void GameBanner::DoState(PointerWrap& p)
|
||||||
{
|
{
|
||||||
p.Do(buffer);
|
p.Do(buffer);
|
||||||
|
@ -156,6 +298,11 @@ void GameBanner::DoState(PointerWrap& p)
|
||||||
p.Do(height);
|
p.Do(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameCover::DoState(PointerWrap& p)
|
||||||
|
{
|
||||||
|
p.Do(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
void GameFile::DoState(PointerWrap& p)
|
void GameFile::DoState(PointerWrap& p)
|
||||||
{
|
{
|
||||||
p.Do(m_valid);
|
p.Do(m_valid);
|
||||||
|
@ -185,6 +332,8 @@ void GameFile::DoState(PointerWrap& p)
|
||||||
|
|
||||||
m_volume_banner.DoState(p);
|
m_volume_banner.DoState(p);
|
||||||
m_custom_banner.DoState(p);
|
m_custom_banner.DoState(p);
|
||||||
|
m_default_cover.DoState(p);
|
||||||
|
m_custom_cover.DoState(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameFile::IsElfOrDol() const
|
bool GameFile::IsElfOrDol() const
|
||||||
|
@ -353,4 +502,9 @@ const GameBanner& GameFile::GetBannerImage() const
|
||||||
return m_custom_banner.empty() ? m_volume_banner : m_custom_banner;
|
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
|
} // namespace UICommon
|
||||||
|
|
|
@ -30,6 +30,13 @@ struct GameBanner
|
||||||
void DoState(PointerWrap& p);
|
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);
|
||||||
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 GetFileSize() const { return m_file_size; }
|
||||||
u64 GetVolumeSize() const { return m_volume_size; }
|
u64 GetVolumeSize() const { return m_volume_size; }
|
||||||
const GameBanner& GetBannerImage() const;
|
const GameBanner& GetBannerImage() const;
|
||||||
|
const GameCover& GetCoverImage() const;
|
||||||
void DoState(PointerWrap& p);
|
void DoState(PointerWrap& p);
|
||||||
bool WiiBannerChanged();
|
bool WiiBannerChanged();
|
||||||
void WiiBannerCommit();
|
void WiiBannerCommit();
|
||||||
bool CustomBannerChanged();
|
bool CustomBannerChanged();
|
||||||
void CustomBannerCommit();
|
void CustomBannerCommit();
|
||||||
|
void DownloadDefaultCover();
|
||||||
|
bool DefaultCoverChanged();
|
||||||
|
void DefaultCoverCommit();
|
||||||
|
bool CustomCoverChanged();
|
||||||
|
void CustomCoverCommit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const std::string& Lookup(DiscIO::Language language,
|
static const std::string& Lookup(DiscIO::Language language,
|
||||||
|
@ -120,6 +133,8 @@ private:
|
||||||
|
|
||||||
GameBanner m_volume_banner{};
|
GameBanner m_volume_banner{};
|
||||||
GameBanner m_custom_banner{};
|
GameBanner m_custom_banner{};
|
||||||
|
GameCover m_default_cover{};
|
||||||
|
GameCover m_custom_cover{};
|
||||||
|
|
||||||
// The following data members allow GameFileCache to construct updated versions
|
// The following data members allow GameFileCache to construct updated versions
|
||||||
// of GameFiles in a threadsafe way. They should not be handled in DoState.
|
// of GameFiles in a threadsafe way. They should not be handled in DoState.
|
||||||
|
@ -127,6 +142,8 @@ private:
|
||||||
{
|
{
|
||||||
GameBanner volume_banner;
|
GameBanner volume_banner;
|
||||||
GameBanner custom_banner;
|
GameBanner custom_banner;
|
||||||
|
GameCover default_cover;
|
||||||
|
GameCover custom_cover;
|
||||||
} m_pending{};
|
} m_pending{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
|
|
||||||
namespace UICommon
|
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,
|
std::vector<std::string> FindAllGamePaths(const std::vector<std::string>& directories_to_scan,
|
||||||
bool recursive_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 wii_banner_changed = (*game_file)->WiiBannerChanged();
|
||||||
const bool custom_banner_changed = (*game_file)->CustomBannerChanged();
|
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;
|
return false;
|
||||||
|
|
||||||
// If a cached file needs an update, apply the updates to a copy and delete the original.
|
// 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();
|
copy->WiiBannerCommit();
|
||||||
if (custom_banner_changed)
|
if (custom_banner_changed)
|
||||||
copy->CustomBannerCommit();
|
copy->CustomBannerCommit();
|
||||||
|
if (default_cover_changed)
|
||||||
|
copy->DefaultCoverCommit();
|
||||||
|
if (custom_cover_changed)
|
||||||
|
copy->CustomCoverCommit();
|
||||||
|
|
||||||
*game_file = std::move(copy);
|
*game_file = std::move(copy);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -158,6 +158,7 @@ void CreateDirectories()
|
||||||
{
|
{
|
||||||
File::CreateFullPath(File::GetUserPath(D_USER_IDX));
|
File::CreateFullPath(File::GetUserPath(D_USER_IDX));
|
||||||
File::CreateFullPath(File::GetUserPath(D_CACHE_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_CONFIG_IDX));
|
||||||
File::CreateFullPath(File::GetUserPath(D_DUMPDSP_IDX));
|
File::CreateFullPath(File::GetUserPath(D_DUMPDSP_IDX));
|
||||||
File::CreateFullPath(File::GetUserPath(D_DUMPSSL_IDX));
|
File::CreateFullPath(File::GetUserPath(D_DUMPSSL_IDX));
|
||||||
|
|
Loading…
Reference in New Issue