mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-30 01:29:42 -06:00
Qt/GameList: Add option to show covers in grid mode
This commit is contained in:
@ -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
|
||||
|
@ -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{};
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
Reference in New Issue
Block a user