GameList: Show game mod descriptor .json files in game list.
This commit is contained in:
parent
aa0595589a
commit
da161faff4
|
@ -30,7 +30,7 @@ import java.util.Set;
|
||||||
public final class FileBrowserHelper
|
public final class FileBrowserHelper
|
||||||
{
|
{
|
||||||
public static final HashSet<String> GAME_EXTENSIONS = new HashSet<>(Arrays.asList(
|
public static final HashSet<String> GAME_EXTENSIONS = new HashSet<>(Arrays.asList(
|
||||||
"gcm", "tgc", "iso", "ciso", "gcz", "wbfs", "wia", "rvz", "wad", "dol", "elf"));
|
"gcm", "tgc", "iso", "ciso", "gcz", "wbfs", "wia", "rvz", "wad", "dol", "elf", "json"));
|
||||||
|
|
||||||
public static final HashSet<String> GAME_LIKE_EXTENSIONS = new HashSet<>(GAME_EXTENSIONS);
|
public static final HashSet<String> GAME_LIKE_EXTENSIONS = new HashSet<>(GAME_EXTENSIONS);
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,8 @@ std::string GetName(BlobType blob_type, bool translate)
|
||||||
return "WIA";
|
return "WIA";
|
||||||
case BlobType::RVZ:
|
case BlobType::RVZ:
|
||||||
return "RVZ";
|
return "RVZ";
|
||||||
|
case BlobType::MOD_DESCRIPTOR:
|
||||||
|
return translate_str("Mod");
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ enum class BlobType
|
||||||
TGC,
|
TGC,
|
||||||
WIA,
|
WIA,
|
||||||
RVZ,
|
RVZ,
|
||||||
|
MOD_DESCRIPTOR,
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string GetName(BlobType blob_type, bool translate);
|
std::string GetName(BlobType blob_type, bool translate);
|
||||||
|
|
|
@ -352,16 +352,17 @@ void GameList::ShowContextMenu(const QPoint&)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const auto game = GetSelectedGame();
|
const auto game = GetSelectedGame();
|
||||||
|
const bool is_mod_descriptor = game->IsModDescriptor();
|
||||||
DiscIO::Platform platform = game->GetPlatform();
|
DiscIO::Platform platform = game->GetPlatform();
|
||||||
menu->addAction(tr("&Properties"), this, &GameList::OpenProperties);
|
menu->addAction(tr("&Properties"), this, &GameList::OpenProperties);
|
||||||
if (platform != DiscIO::Platform::ELFOrDOL)
|
if (!is_mod_descriptor && platform != DiscIO::Platform::ELFOrDOL)
|
||||||
{
|
{
|
||||||
menu->addAction(tr("&Wiki"), this, &GameList::OpenWiki);
|
menu->addAction(tr("&Wiki"), this, &GameList::OpenWiki);
|
||||||
}
|
}
|
||||||
|
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
|
|
||||||
if (DiscIO::IsDisc(platform))
|
if (!is_mod_descriptor && DiscIO::IsDisc(platform))
|
||||||
{
|
{
|
||||||
menu->addAction(tr("Start with Riivolution Patches..."), this,
|
menu->addAction(tr("Start with Riivolution Patches..."), this,
|
||||||
&GameList::StartWithRiivolution);
|
&GameList::StartWithRiivolution);
|
||||||
|
@ -382,7 +383,7 @@ void GameList::ShowContextMenu(const QPoint&)
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (platform == DiscIO::Platform::WiiDisc)
|
if (!is_mod_descriptor && platform == DiscIO::Platform::WiiDisc)
|
||||||
{
|
{
|
||||||
auto* perform_disc_update = menu->addAction(tr("Perform System Update"), this,
|
auto* perform_disc_update = menu->addAction(tr("Perform System Update"), this,
|
||||||
[this, file_path = game->GetFilePath()] {
|
[this, file_path = game->GetFilePath()] {
|
||||||
|
@ -394,7 +395,7 @@ void GameList::ShowContextMenu(const QPoint&)
|
||||||
perform_disc_update->setEnabled(!Core::IsRunning() || !SConfig::GetInstance().bWii);
|
perform_disc_update->setEnabled(!Core::IsRunning() || !SConfig::GetInstance().bWii);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (platform == DiscIO::Platform::WiiWAD)
|
if (!is_mod_descriptor && platform == DiscIO::Platform::WiiWAD)
|
||||||
{
|
{
|
||||||
QAction* wad_install_action = new QAction(tr("Install to the NAND"), menu);
|
QAction* wad_install_action = new QAction(tr("Install to the NAND"), menu);
|
||||||
QAction* wad_uninstall_action = new QAction(tr("Uninstall from the NAND"), menu);
|
QAction* wad_uninstall_action = new QAction(tr("Uninstall from the NAND"), menu);
|
||||||
|
@ -420,14 +421,15 @@ void GameList::ShowContextMenu(const QPoint&)
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (platform == DiscIO::Platform::WiiWAD || platform == DiscIO::Platform::WiiDisc)
|
if (!is_mod_descriptor &&
|
||||||
|
(platform == DiscIO::Platform::WiiWAD || platform == DiscIO::Platform::WiiDisc))
|
||||||
{
|
{
|
||||||
menu->addAction(tr("Open Wii &Save Folder"), this, &GameList::OpenWiiSaveFolder);
|
menu->addAction(tr("Open Wii &Save Folder"), this, &GameList::OpenWiiSaveFolder);
|
||||||
menu->addAction(tr("Export Wii Save"), this, &GameList::ExportWiiSave);
|
menu->addAction(tr("Export Wii Save"), this, &GameList::ExportWiiSave);
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (platform == DiscIO::Platform::GameCubeDisc)
|
if (!is_mod_descriptor && platform == DiscIO::Platform::GameCubeDisc)
|
||||||
{
|
{
|
||||||
menu->addAction(tr("Open GameCube &Save Folder"), this, &GameList::OpenGCSaveFolder);
|
menu->addAction(tr("Open GameCube &Save Folder"), this, &GameList::OpenGCSaveFolder);
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
|
|
|
@ -27,7 +27,7 @@ static const QStringList game_filters{
|
||||||
QStringLiteral("*.[gG][cC][zZ]"), QStringLiteral("*.[wW][bB][fF][sS]"),
|
QStringLiteral("*.[gG][cC][zZ]"), QStringLiteral("*.[wW][bB][fF][sS]"),
|
||||||
QStringLiteral("*.[wW][iI][aA]"), QStringLiteral("*.[rR][vV][zZ]"),
|
QStringLiteral("*.[wW][iI][aA]"), QStringLiteral("*.[rR][vV][zZ]"),
|
||||||
QStringLiteral("*.[wW][aA][dD]"), QStringLiteral("*.[eE][lL][fF]"),
|
QStringLiteral("*.[wW][aA][dD]"), QStringLiteral("*.[eE][lL][fF]"),
|
||||||
QStringLiteral("*.[dD][oO][lL]")};
|
QStringLiteral("*.[dD][oO][lL]"), QStringLiteral("*.[jJ][sS][oO][nN]")};
|
||||||
|
|
||||||
GameTracker::GameTracker(QObject* parent) : QFileSystemWatcher(parent)
|
GameTracker::GameTracker(QObject* parent) : QFileSystemWatcher(parent)
|
||||||
{
|
{
|
||||||
|
|
|
@ -733,8 +733,10 @@ QStringList MainWindow::PromptFileNames()
|
||||||
QStringList paths = DolphinFileDialog::getOpenFileNames(
|
QStringList paths = DolphinFileDialog::getOpenFileNames(
|
||||||
this, tr("Select a File"),
|
this, tr("Select a File"),
|
||||||
settings.value(QStringLiteral("mainwindow/lastdir"), QString{}).toString(),
|
settings.value(QStringLiteral("mainwindow/lastdir"), QString{}).toString(),
|
||||||
tr("All GC/Wii files (*.elf *.dol *.gcm *.iso *.tgc *.wbfs *.ciso *.gcz *.wia *.rvz *.wad "
|
QStringLiteral("%1 (*.elf *.dol *.gcm *.iso *.tgc *.wbfs *.ciso *.gcz *.wia *.rvz *.wad "
|
||||||
"*.dff *.m3u);;All Files (*)"));
|
"*.dff *.m3u *.json);;%2 (*)")
|
||||||
|
.arg(tr("All GC/Wii files"))
|
||||||
|
.arg(tr("All Files")));
|
||||||
|
|
||||||
if (!paths.isEmpty())
|
if (!paths.isEmpty())
|
||||||
{
|
{
|
||||||
|
|
|
@ -44,8 +44,10 @@ void PathPane::BrowseDefaultGame()
|
||||||
{
|
{
|
||||||
QString file = QDir::toNativeSeparators(DolphinFileDialog::getOpenFileName(
|
QString file = QDir::toNativeSeparators(DolphinFileDialog::getOpenFileName(
|
||||||
this, tr("Select a Game"), Settings::Instance().GetDefaultGame(),
|
this, tr("Select a Game"), Settings::Instance().GetDefaultGame(),
|
||||||
tr("All GC/Wii files (*.elf *.dol *.gcm *.iso *.tgc *.wbfs "
|
QStringLiteral("%1 (*.elf *.dol *.gcm *.iso *.tgc *.wbfs *.ciso *.gcz *.wia *.rvz *.wad "
|
||||||
"*.ciso *.gcz *.wia *.rvz *.wad *.m3u);;All Files (*)")));
|
"*.m3u *.json);;%2 (*)")
|
||||||
|
.arg(tr("All GC/Wii files"))
|
||||||
|
.arg(tr("All Files"))));
|
||||||
|
|
||||||
if (!file.isEmpty())
|
if (!file.isEmpty())
|
||||||
Settings::Instance().SetDefaultGame(file);
|
Settings::Instance().SetDefaultGame(file);
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include "DiscIO/Blob.h"
|
#include "DiscIO/Blob.h"
|
||||||
#include "DiscIO/DiscExtractor.h"
|
#include "DiscIO/DiscExtractor.h"
|
||||||
#include "DiscIO/Enums.h"
|
#include "DiscIO/Enums.h"
|
||||||
|
#include "DiscIO/GameModDescriptor.h"
|
||||||
#include "DiscIO/Volume.h"
|
#include "DiscIO/Volume.h"
|
||||||
#include "DiscIO/WiiSaveBanner.h"
|
#include "DiscIO/WiiSaveBanner.h"
|
||||||
|
|
||||||
|
@ -163,6 +164,32 @@ GameFile::GameFile(std::string path) : m_file_path(std::move(path))
|
||||||
m_platform = DiscIO::Platform::ELFOrDOL;
|
m_platform = DiscIO::Platform::ELFOrDOL;
|
||||||
m_blob_type = DiscIO::BlobType::DIRECTORY;
|
m_blob_type = DiscIO::BlobType::DIRECTORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!IsValid() && GetExtension() == ".json")
|
||||||
|
{
|
||||||
|
auto descriptor = DiscIO::ParseGameModDescriptorFile(m_file_path);
|
||||||
|
if (descriptor)
|
||||||
|
{
|
||||||
|
GameFile proxy(descriptor->base_file);
|
||||||
|
if (proxy.IsValid())
|
||||||
|
{
|
||||||
|
m_valid = true;
|
||||||
|
m_file_size = File::GetSize(m_file_path);
|
||||||
|
m_long_names.emplace(DiscIO::Language::English, std::move(descriptor->display_name));
|
||||||
|
m_internal_name = proxy.GetInternalName();
|
||||||
|
m_game_id = proxy.GetGameID();
|
||||||
|
m_gametdb_id = proxy.GetGameTDBID();
|
||||||
|
m_title_id = proxy.GetTitleID();
|
||||||
|
m_maker_id = proxy.GetMakerID();
|
||||||
|
m_region = proxy.GetRegion();
|
||||||
|
m_country = proxy.GetCountry();
|
||||||
|
m_platform = proxy.GetPlatform();
|
||||||
|
m_revision = proxy.GetRevision();
|
||||||
|
m_disc_number = proxy.GetDiscNumber();
|
||||||
|
m_blob_type = DiscIO::BlobType::MOD_DESCRIPTOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GameFile::~GameFile() = default;
|
GameFile::~GameFile() = default;
|
||||||
|
@ -470,6 +497,18 @@ bool GameFile::ReadPNGBanner(const std::string& path)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GameFile::TryLoadGameModDescriptorBanner()
|
||||||
|
{
|
||||||
|
if (m_blob_type != DiscIO::BlobType::MOD_DESCRIPTOR)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto descriptor = DiscIO::ParseGameModDescriptorFile(m_file_path);
|
||||||
|
if (!descriptor)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return ReadPNGBanner(descriptor->banner);
|
||||||
|
}
|
||||||
|
|
||||||
bool GameFile::CustomBannerChanged()
|
bool GameFile::CustomBannerChanged()
|
||||||
{
|
{
|
||||||
std::string path, name;
|
std::string path, name;
|
||||||
|
@ -481,11 +520,15 @@ bool GameFile::CustomBannerChanged()
|
||||||
{
|
{
|
||||||
// Homebrew Channel icon naming. Typical for DOLs and ELFs, but we also support it for volumes.
|
// Homebrew Channel icon naming. Typical for DOLs and ELFs, but we also support it for volumes.
|
||||||
if (!ReadPNGBanner(path + "icon.png"))
|
if (!ReadPNGBanner(path + "icon.png"))
|
||||||
|
{
|
||||||
|
// If it's a game mod descriptor file, it may specify its own custom banner.
|
||||||
|
if (!TryLoadGameModDescriptorBanner())
|
||||||
{
|
{
|
||||||
// If no custom icon is found, go back to the non-custom one.
|
// If no custom icon is found, go back to the non-custom one.
|
||||||
m_pending.custom_banner = {};
|
m_pending.custom_banner = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return m_pending.custom_banner != m_custom_banner;
|
return m_pending.custom_banner != m_custom_banner;
|
||||||
}
|
}
|
||||||
|
@ -499,6 +542,8 @@ const std::string& GameFile::GetName(const Core::TitleDatabase& title_database)
|
||||||
{
|
{
|
||||||
if (!m_custom_name.empty())
|
if (!m_custom_name.empty())
|
||||||
return m_custom_name;
|
return m_custom_name;
|
||||||
|
if (IsModDescriptor())
|
||||||
|
return GetName(Variant::LongAndPossiblyCustom);
|
||||||
|
|
||||||
const std::string& database_name = title_database.GetTitleName(m_gametdb_id, GetConfigLanguage());
|
const std::string& database_name = title_database.GetTitleName(m_gametdb_id, GetConfigLanguage());
|
||||||
return database_name.empty() ? GetName(Variant::LongAndPossiblyCustom) : database_name;
|
return database_name.empty() ? GetName(Variant::LongAndPossiblyCustom) : database_name;
|
||||||
|
@ -652,6 +697,7 @@ bool GameFile::ShouldShowFileFormatDetails() const
|
||||||
case DiscIO::BlobType::PLAIN:
|
case DiscIO::BlobType::PLAIN:
|
||||||
break;
|
break;
|
||||||
case DiscIO::BlobType::DRIVE:
|
case DiscIO::BlobType::DRIVE:
|
||||||
|
case DiscIO::BlobType::MOD_DESCRIPTOR:
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
|
@ -699,6 +745,11 @@ bool GameFile::ShouldAllowConversion() const
|
||||||
return DiscIO::IsDisc(m_platform) && m_volume_size_is_accurate;
|
return DiscIO::IsDisc(m_platform) && m_volume_size_is_accurate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GameFile::IsModDescriptor() const
|
||||||
|
{
|
||||||
|
return m_blob_type == DiscIO::BlobType::MOD_DESCRIPTOR;
|
||||||
|
}
|
||||||
|
|
||||||
const GameBanner& GameFile::GetBannerImage() const
|
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;
|
||||||
|
|
|
@ -107,6 +107,7 @@ public:
|
||||||
bool IsVolumeSizeAccurate() const { return m_volume_size_is_accurate; }
|
bool IsVolumeSizeAccurate() const { return m_volume_size_is_accurate; }
|
||||||
bool IsDatelDisc() const { return m_is_datel_disc; }
|
bool IsDatelDisc() const { return m_is_datel_disc; }
|
||||||
bool IsNKit() const { return m_is_nkit; }
|
bool IsNKit() const { return m_is_nkit; }
|
||||||
|
bool IsModDescriptor() const;
|
||||||
const GameBanner& GetBannerImage() const;
|
const GameBanner& GetBannerImage() const;
|
||||||
const GameCover& GetCoverImage() const;
|
const GameCover& GetCoverImage() const;
|
||||||
void DoState(PointerWrap& p);
|
void DoState(PointerWrap& p);
|
||||||
|
@ -132,6 +133,7 @@ private:
|
||||||
bool IsElfOrDol() const;
|
bool IsElfOrDol() const;
|
||||||
bool ReadXMLMetadata(const std::string& path);
|
bool ReadXMLMetadata(const std::string& path);
|
||||||
bool ReadPNGBanner(const std::string& path);
|
bool ReadPNGBanner(const std::string& path);
|
||||||
|
bool TryLoadGameModDescriptorBanner();
|
||||||
|
|
||||||
// IMPORTANT: Nearly all data members must be save/restored in DoState.
|
// IMPORTANT: Nearly all data members must be save/restored in DoState.
|
||||||
// If anything is changed, make sure DoState handles it properly and
|
// If anything is changed, make sure DoState handles it properly and
|
||||||
|
|
|
@ -27,13 +27,14 @@
|
||||||
|
|
||||||
namespace UICommon
|
namespace UICommon
|
||||||
{
|
{
|
||||||
static constexpr u32 CACHE_REVISION = 20; // Last changed in PR 9461
|
static constexpr u32 CACHE_REVISION = 21; // Last changed in PR 10187
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
static const std::vector<std::string> search_extensions = {
|
static const std::vector<std::string> search_extensions = {".gcm", ".tgc", ".iso", ".ciso",
|
||||||
".gcm", ".tgc", ".iso", ".ciso", ".gcz", ".wbfs", ".wia", ".rvz", ".wad", ".dol", ".elf"};
|
".gcz", ".wbfs", ".wia", ".rvz",
|
||||||
|
".wad", ".dol", ".elf", ".json"};
|
||||||
|
|
||||||
// TODO: We could process paths iteratively as they are found
|
// TODO: We could process paths iteratively as they are found
|
||||||
return Common::DoFileSearch(directories_to_scan, search_extensions, recursive_scan);
|
return Common::DoFileSearch(directories_to_scan, search_extensions, recursive_scan);
|
||||||
|
|
Loading…
Reference in New Issue