summaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
authorJosJuice <josjuice@gmail.com>2017-12-31 20:33:36 +0100
committerJosJuice <josjuice@gmail.com>2018-03-09 13:08:38 +0100
commit1f1dae367d8aeb525df5558ce648d6f287983008 (patch)
tree7933d673aad8869eef5fb3e74e1260274323e673 /Source
parent1add238a284ab4baf5d5550d5f1af525226172c7 (diff)
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.
Diffstat (limited to 'Source')
-rw-r--r--Source/Core/Core/WiiUtils.cpp22
-rw-r--r--Source/Core/Core/WiiUtils.h4
-rw-r--r--Source/Core/DiscIO/Blob.h2
-rw-r--r--Source/Core/DiscIO/Enums.cpp107
-rw-r--r--Source/Core/DiscIO/Enums.h7
-rw-r--r--Source/Core/DolphinQt2/CMakeLists.txt3
-rw-r--r--Source/Core/DolphinQt2/Config/ARCodeWidget.cpp6
-rw-r--r--Source/Core/DolphinQt2/Config/ARCodeWidget.h10
-rw-r--r--Source/Core/DolphinQt2/Config/FilesystemWidget.cpp4
-rw-r--r--Source/Core/DolphinQt2/Config/FilesystemWidget.h6
-rw-r--r--Source/Core/DolphinQt2/Config/GameConfigWidget.cpp14
-rw-r--r--Source/Core/DolphinQt2/Config/GameConfigWidget.h8
-rw-r--r--Source/Core/DolphinQt2/Config/GeckoCodeWidget.cpp6
-rw-r--r--Source/Core/DolphinQt2/Config/GeckoCodeWidget.h10
-rw-r--r--Source/Core/DolphinQt2/Config/InfoWidget.cpp56
-rw-r--r--Source/Core/DolphinQt2/Config/InfoWidget.h15
-rw-r--r--Source/Core/DolphinQt2/Config/NewPatchDialog.h1
-rw-r--r--Source/Core/DolphinQt2/Config/PatchesWidget.cpp4
-rw-r--r--Source/Core/DolphinQt2/Config/PatchesWidget.h6
-rw-r--r--Source/Core/DolphinQt2/Config/PropertiesDialog.cpp12
-rw-r--r--Source/Core/DolphinQt2/Config/PropertiesDialog.h7
-rw-r--r--Source/Core/DolphinQt2/DolphinQt2.vcxproj6
-rw-r--r--Source/Core/DolphinQt2/GameList/GameFile.cpp481
-rw-r--r--Source/Core/DolphinQt2/GameList/GameFile.h125
-rw-r--r--Source/Core/DolphinQt2/GameList/GameFileCache.cpp76
-rw-r--r--Source/Core/DolphinQt2/GameList/GameFileCache.h29
-rw-r--r--Source/Core/DolphinQt2/GameList/GameList.cpp51
-rw-r--r--Source/Core/DolphinQt2/GameList/GameList.h9
-rw-r--r--Source/Core/DolphinQt2/GameList/GameListModel.cpp78
-rw-r--r--Source/Core/DolphinQt2/GameList/GameListModel.h28
-rw-r--r--Source/Core/DolphinQt2/GameList/GameTracker.cpp32
-rw-r--r--Source/Core/DolphinQt2/GameList/GameTracker.h15
-rw-r--r--Source/Core/DolphinQt2/GameList/GridProxyModel.cpp1
-rw-r--r--Source/Core/DolphinQt2/MainWindow.cpp13
-rw-r--r--Source/Core/DolphinQt2/MainWindow.h2
-rw-r--r--Source/Core/DolphinQt2/MenuBar.cpp14
-rw-r--r--Source/Core/DolphinQt2/MenuBar.h12
-rw-r--r--Source/Core/DolphinQt2/NetPlay/GameListDialog.cpp2
-rw-r--r--Source/Core/DolphinQt2/NetPlay/NetPlayDialog.cpp2
-rw-r--r--Source/Core/DolphinQt2/NetPlay/NetPlaySetupDialog.cpp2
-rw-r--r--Source/Core/DolphinQt2/QtUtils/ImageConverter.cpp33
-rw-r--r--Source/Core/DolphinQt2/QtUtils/ImageConverter.h19
-rw-r--r--Source/Core/DolphinQt2/Resources.h1
-rw-r--r--Source/Core/DolphinWX/CMakeLists.txt1
-rw-r--r--Source/Core/DolphinWX/DolphinWX.vcxproj4
-rw-r--r--Source/Core/DolphinWX/DolphinWX.vcxproj.filters11
-rw-r--r--Source/Core/DolphinWX/FrameTools.cpp19
-rw-r--r--Source/Core/DolphinWX/GameListCtrl.cpp317
-rw-r--r--Source/Core/DolphinWX/GameListCtrl.h23
-rw-r--r--Source/Core/DolphinWX/ISOFile.cpp370
-rw-r--r--Source/Core/DolphinWX/ISOFile.h140
-rw-r--r--Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp1
-rw-r--r--Source/Core/DolphinWX/ISOProperties/ISOProperties.cpp10
-rw-r--r--Source/Core/DolphinWX/ISOProperties/ISOProperties.h10
-rw-r--r--Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp18
-rw-r--r--Source/Core/DolphinWX/ISOProperties/InfoPanel.h13
-rw-r--r--Source/Core/DolphinWX/NetPlay/NetWindow.cpp7
-rw-r--r--Source/Core/DolphinWX/WxUtils.cpp23
-rw-r--r--Source/Core/DolphinWX/WxUtils.h12
-rw-r--r--Source/Core/UICommon/CMakeLists.txt2
-rw-r--r--Source/Core/UICommon/GameFile.cpp312
-rw-r--r--Source/Core/UICommon/GameFile.h153
-rw-r--r--Source/Core/UICommon/GameFileCache.cpp235
-rw-r--r--Source/Core/UICommon/GameFileCache.h58
-rw-r--r--Source/Core/UICommon/UICommon.cpp21
-rw-r--r--Source/Core/UICommon/UICommon.h4
-rw-r--r--Source/Core/UICommon/UICommon.vcxproj4
67 files changed, 1373 insertions, 1736 deletions
diff --git a/Source/Core/Core/WiiUtils.cpp b/Source/Core/Core/WiiUtils.cpp
index 3fd653ffde..84ae9a3d20 100644
--- a/Source/Core/Core/WiiUtils.cpp
+++ b/Source/Core/Core/WiiUtils.cpp
@@ -160,6 +160,28 @@ bool InstallWAD(const std::string& wad_path)
return InstallWAD(ios, DiscIO::WiiWAD{wad_path}, InstallType::Permanent);
}
+bool UninstallTitle(u64 title_id)
+{
+ IOS::HLE::Kernel ios;
+ return ios.GetES()->DeleteTitleContent(title_id) == IOS::HLE::IPC_SUCCESS;
+}
+
+bool IsTitleInstalled(u64 title_id)
+{
+ const std::string content_dir =
+ Common::GetTitleContentPath(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"; });
+}
+
// Common functionality for system updaters.
class SystemUpdater
{
diff --git a/Source/Core/Core/WiiUtils.h b/Source/Core/Core/WiiUtils.h
index b07863fa3b..8427901f71 100644
--- a/Source/Core/Core/WiiUtils.h
+++ b/Source/Core/Core/WiiUtils.h
@@ -38,6 +38,10 @@ bool InstallWAD(IOS::HLE::Kernel& ios, const DiscIO::WiiWAD& wad, InstallType ty
// and does a permanent install.
bool InstallWAD(const std::string& wad_path);
+bool UninstallTitle(u64 title_id);
+
+bool IsTitleInstalled(u64 title_id);
+
enum class UpdateResult
{
Succeeded,
diff --git a/Source/Core/DiscIO/Blob.h b/Source/Core/DiscIO/Blob.h
index 20107bef0f..703cf1199b 100644
--- a/Source/Core/DiscIO/Blob.h
+++ b/Source/Core/DiscIO/Blob.h
@@ -25,7 +25,7 @@
namespace DiscIO
{
-// Increment CACHE_REVISION (GameListCtrl.cpp) if the enum below is modified
+// Increment CACHE_REVISION (GameFileCache.cpp) if the enum below is modified
enum class BlobType
{
PLAIN,
diff --git a/Source/Core/DiscIO/Enums.cpp b/Source/Core/DiscIO/Enums.cpp
index c7b69f5fa1..a91e760e60 100644
--- a/Source/Core/DiscIO/Enums.cpp
+++ b/Source/Core/DiscIO/Enums.cpp
@@ -5,12 +5,112 @@
#include <map>
#include <string>
+#include "Common/Assert.h"
+#include "Common/Common.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
+#include "Common/MsgHandler.h"
#include "DiscIO/Enums.h"
namespace DiscIO
{
+std::string GetName(Country country, bool translate)
+{
+ std::string name;
+
+ switch (country)
+ {
+ case Country::COUNTRY_EUROPE:
+ name = _trans("Europe");
+ break;
+ case Country::COUNTRY_JAPAN:
+ name = _trans("Japan");
+ break;
+ case Country::COUNTRY_USA:
+ name = _trans("USA");
+ break;
+ case Country::COUNTRY_AUSTRALIA:
+ name = _trans("Australia");
+ break;
+ case Country::COUNTRY_FRANCE:
+ name = _trans("France");
+ break;
+ case Country::COUNTRY_GERMANY:
+ name = _trans("Germany");
+ break;
+ case Country::COUNTRY_ITALY:
+ name = _trans("Italy");
+ break;
+ case Country::COUNTRY_KOREA:
+ name = _trans("Korea");
+ break;
+ case Country::COUNTRY_NETHERLANDS:
+ name = _trans("Netherlands");
+ break;
+ case Country::COUNTRY_RUSSIA:
+ name = _trans("Russia");
+ break;
+ case Country::COUNTRY_SPAIN:
+ name = _trans("Spain");
+ break;
+ case Country::COUNTRY_TAIWAN:
+ name = _trans("Taiwan");
+ break;
+ case Country::COUNTRY_WORLD:
+ name = _trans("World");
+ break;
+ default:
+ name = _trans("Unknown");
+ break;
+ }
+
+ return translate ? GetStringT(name.c_str()) : name;
+}
+
+std::string GetName(Language language, bool translate)
+{
+ std::string name;
+
+ switch (language)
+ {
+ case Language::LANGUAGE_JAPANESE:
+ name = _trans("Japanese");
+ break;
+ case Language::LANGUAGE_ENGLISH:
+ name = _trans("English");
+ break;
+ case Language::LANGUAGE_GERMAN:
+ name = _trans("German");
+ break;
+ case Language::LANGUAGE_FRENCH:
+ name = _trans("French");
+ break;
+ case Language::LANGUAGE_SPANISH:
+ name = _trans("Spanish");
+ break;
+ case Language::LANGUAGE_ITALIAN:
+ name = _trans("Italian");
+ break;
+ case Language::LANGUAGE_DUTCH:
+ name = _trans("Dutch");
+ break;
+ case Language::LANGUAGE_SIMPLIFIED_CHINESE:
+ name = _trans("Simplified Chinese");
+ break;
+ case Language::LANGUAGE_TRADITIONAL_CHINESE:
+ name = _trans("Traditional Chinese");
+ break;
+ case Language::LANGUAGE_KOREAN:
+ name = _trans("Korean");
+ break;
+ default:
+ name = _trans("Unknown");
+ break;
+ }
+
+ return translate ? GetStringT(name.c_str()) : name;
+}
+
bool IsDisc(Platform volume_type)
{
return volume_type == Platform::GAMECUBE_DISC || volume_type == Platform::WII_DISC;
@@ -26,7 +126,7 @@ bool IsNTSC(Region region)
return region == Region::NTSC_J || region == Region::NTSC_U || region == Region::NTSC_K;
}
-// Increment CACHE_REVISION (GameListCtrl.cpp) if the code below is modified
+// Increment CACHE_REVISION (GameFileCache.cpp) if the code below is modified
Country TypicalCountryForRegion(Region region)
{
@@ -230,7 +330,7 @@ std::string GetSysMenuVersionString(u16 title_version)
}
}
-std::string GetCompanyFromID(const std::string& company_id)
+const std::string& GetCompanyFromID(const std::string& company_id)
{
static const std::map<std::string, std::string> companies = {
{"01", "Nintendo"},
@@ -517,10 +617,11 @@ std::string GetCompanyFromID(const std::string& company_id)
{"ZW", "Judo Baby"},
{"ZX", "TopWare Interactive"}};
+ static const std::string EMPTY_STRING;
auto iterator = companies.find(company_id);
if (iterator != companies.end())
return iterator->second;
else
- return "";
+ return EMPTY_STRING;
}
}
diff --git a/Source/Core/DiscIO/Enums.h b/Source/Core/DiscIO/Enums.h
index fda94c43be..eefeb8dfac 100644
--- a/Source/Core/DiscIO/Enums.h
+++ b/Source/Core/DiscIO/Enums.h
@@ -10,7 +10,7 @@
namespace DiscIO
{
-// Increment CACHE_REVISION (GameListCtrl.cpp) if these enums are modified
+// Increment CACHE_REVISION (GameFileCache.cpp) if these enums are modified
enum class Platform
{
@@ -68,6 +68,9 @@ enum class Language
LANGUAGE_UNKNOWN
};
+std::string GetName(Country country, bool translate);
+std::string GetName(Language language, bool translate);
+
bool IsDisc(Platform volume_type);
bool IsWii(Platform volume_type);
bool IsNTSC(Region region);
@@ -82,5 +85,5 @@ Country CountrySwitch(u8 country_code);
Region GetSysMenuRegion(u16 title_version);
std::string GetSysMenuVersionString(u16 title_version);
-std::string GetCompanyFromID(const std::string& company_id);
+const std::string& GetCompanyFromID(const std::string& company_id);
}
diff --git a/Source/Core/DolphinQt2/CMakeLists.txt b/Source/Core/DolphinQt2/CMakeLists.txt
index 36dfa6d599..352ee72610 100644
--- a/Source/Core/DolphinQt2/CMakeLists.txt
+++ b/Source/Core/DolphinQt2/CMakeLists.txt
@@ -82,8 +82,6 @@ set(SRCS
Debugger/RegisterColumn.cpp
Debugger/RegisterWidget.cpp
Debugger/WatchWidget.cpp
- GameList/GameFileCache.cpp
- GameList/GameFile.cpp
GameList/GameList.cpp
GameList/GameListModel.cpp
GameList/GameTracker.cpp
@@ -98,6 +96,7 @@ set(SRCS
NetPlay/PadMappingDialog.cpp
QtUtils/DoubleClickEventFilter.cpp
QtUtils/ElidedButton.cpp
+ QtUtils/ImageConverter.cpp
QtUtils/ListTabWidget.cpp
QtUtils/WindowActivationEventFilter.cpp
QtUtils/AspectRatioWidget.cpp
diff --git a/Source/Core/DolphinQt2/Config/ARCodeWidget.cpp b/Source/Core/DolphinQt2/Config/ARCodeWidget.cpp
index c68fc0c19c..9bb29338e2 100644
--- a/Source/Core/DolphinQt2/Config/ARCodeWidget.cpp
+++ b/Source/Core/DolphinQt2/Config/ARCodeWidget.cpp
@@ -16,10 +16,10 @@
#include "Core/ConfigManager.h"
#include "DolphinQt2/Config/CheatCodeEditor.h"
#include "DolphinQt2/Config/CheatWarningWidget.h"
-#include "DolphinQt2/GameList/GameFile.h"
+#include "UICommon/GameFile.h"
-ARCodeWidget::ARCodeWidget(const GameFile& game)
- : m_game(game), m_game_id(game.GetGameID().toStdString()), m_game_revision(game.GetRevision())
+ARCodeWidget::ARCodeWidget(const UICommon::GameFile& game)
+ : m_game(game), m_game_id(game.GetGameID()), m_game_revision(game.GetRevision())
{
CreateWidgets();
ConnectWidgets();
diff --git a/Source/Core/DolphinQt2/Config/ARCodeWidget.h b/Source/Core/DolphinQt2/Config/ARCodeWidget.h
index 7b3154ead1..8fda60976f 100644
--- a/Source/Core/DolphinQt2/Config/ARCodeWidget.h
+++ b/Source/Core/DolphinQt2/Config/ARCodeWidget.h
@@ -12,8 +12,12 @@
#include "Common/CommonTypes.h"
#include "Core/ActionReplay.h"
-class CheatWarningWidget;
+namespace UICommon
+{
class GameFile;
+}
+
+class CheatWarningWidget;
class QLabel;
class QListWidget;
class QListWidgetItem;
@@ -23,7 +27,7 @@ class ARCodeWidget : public QWidget
{
Q_OBJECT
public:
- explicit ARCodeWidget(const GameFile& game);
+ explicit ARCodeWidget(const UICommon::GameFile& game);
signals:
void OpenGeneralSettings();
@@ -41,7 +45,7 @@ private:
void OnCodeEditPressed();
void OnCodeRemovePressed();
- const GameFile& m_game;
+ const UICommon::GameFile& m_game;
std::string m_game_id;
u16 m_game_revision;
diff --git a/Source/Core/DolphinQt2/Config/FilesystemWidget.cpp b/Source/Core/DolphinQt2/Config/FilesystemWidget.cpp
index ccbb2b28b1..73ff255fb3 100644
--- a/Source/Core/DolphinQt2/Config/FilesystemWidget.cpp
+++ b/Source/Core/DolphinQt2/Config/FilesystemWidget.cpp
@@ -37,8 +37,8 @@ enum class EntryType
};
Q_DECLARE_METATYPE(EntryType);
-FilesystemWidget::FilesystemWidget(const GameFile& game)
- : m_game(game), m_volume(DiscIO::CreateVolumeFromFilename(game.GetFilePath().toStdString()))
+FilesystemWidget::FilesystemWidget(const UICommon::GameFile& game)
+ : m_game(game), m_volume(DiscIO::CreateVolumeFromFilename(game.GetFilePath()))
{
CreateWidgets();
ConnectWidgets();
diff --git a/Source/Core/DolphinQt2/Config/FilesystemWidget.h b/Source/Core/DolphinQt2/Config/FilesystemWidget.h
index 88fdf16ce0..820168c8b5 100644
--- a/Source/Core/DolphinQt2/Config/FilesystemWidget.h
+++ b/Source/Core/DolphinQt2/Config/FilesystemWidget.h
@@ -8,7 +8,7 @@
#include <memory>
#include "DiscIO/Volume.h"
-#include "DolphinQt2/GameList/GameFile.h"
+#include "UICommon/GameFile.h"
class QStandardItem;
class QStandardItemModel;
@@ -24,7 +24,7 @@ class FilesystemWidget final : public QWidget
{
Q_OBJECT
public:
- explicit FilesystemWidget(const GameFile& game);
+ explicit FilesystemWidget(const UICommon::GameFile& game);
private:
void CreateWidgets();
@@ -46,6 +46,6 @@ private:
QStandardItemModel* m_tree_model;
QTreeView* m_tree_view;
- GameFile m_game;
+ UICommon::GameFile m_game;
std::unique_ptr<DiscIO::Volume> m_volume;
};
diff --git a/Source/Core/DolphinQt2/Config/GameConfigWidget.cpp b/Source/Core/DolphinQt2/Config/GameConfigWidget.cpp
index 2421d056f9..ca0a64f068 100644
--- a/Source/Core/DolphinQt2/Config/GameConfigWidget.cpp
+++ b/Source/Core/DolphinQt2/Config/GameConfigWidget.cpp
@@ -23,7 +23,7 @@
#include "Core/ConfigLoaders/GameConfigLoader.h"
#include "Core/ConfigManager.h"
#include "DolphinQt2/Config/Graphics/GraphicsSlider.h"
-#include "DolphinQt2/GameList/GameFile.h"
+#include "UICommon/GameFile.h"
constexpr int DETERMINISM_NOT_SET_INDEX = 0;
constexpr int DETERMINISM_AUTO_INDEX = 1;
@@ -35,9 +35,9 @@ constexpr const char* DETERMINISM_AUTO_STRING = "auto";
constexpr const char* DETERMINISM_NONE_STRING = "none";
constexpr const char* DETERMINISM_FAKE_COMPLETION_STRING = "fake-completion";
-GameConfigWidget::GameConfigWidget(const GameFile& game) : m_game(game)
+GameConfigWidget::GameConfigWidget(const UICommon::GameFile& game) : m_game(game)
{
- m_game_id = m_game.GetGameID().toStdString();
+ m_game_id = m_game.GetGameID();
m_gameini_local_path =
QString::fromStdString(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini");
m_gameini_local = SConfig::LoadLocalGameIni(m_game_id, m_game.GetRevision());
@@ -241,8 +241,8 @@ void GameConfigWidget::SaveCheckBox(QCheckBox* checkbox, const std::string& sect
void GameConfigWidget::LoadSettings()
{
// Load state information
- m_state_combo->setCurrentIndex(m_game.GetRating());
- m_state_comment_edit->setText(m_game.GetIssues());
+ m_state_combo->setCurrentIndex(m_game.GetEmuState());
+ m_state_comment_edit->setText(QString::fromStdString(m_game.GetIssues()));
// Load game-specific settings
@@ -301,10 +301,10 @@ void GameConfigWidget::SaveSettings()
QString comment = m_state_comment_edit->text();
int state = m_state_combo->currentIndex();
- if (comment != m_game.GetIssues())
+ if (comment != QString::fromStdString(m_game.GetIssues()))
m_gameini_local.GetOrCreateSection("EmuState")->Set("EmulationIssues", comment.toStdString());
- if (state != m_game.GetRating())
+ if (state != m_game.GetEmuState())
m_gameini_local.GetOrCreateSection("EmuState")->Set("EmulationStateId", state);
// Save game-specific settings
diff --git a/Source/Core/DolphinQt2/Config/GameConfigWidget.h b/Source/Core/DolphinQt2/Config/GameConfigWidget.h
index 2062d55224..d33c15122e 100644
--- a/Source/Core/DolphinQt2/Config/GameConfigWidget.h
+++ b/Source/Core/DolphinQt2/Config/GameConfigWidget.h
@@ -11,7 +11,11 @@
#include "Common/IniFile.h"
+namespace UICommon
+{
class GameFile;
+}
+
class QCheckBox;
class QComboBox;
class QGroupBox;
@@ -25,7 +29,7 @@ class GameConfigWidget : public QWidget
{
Q_OBJECT
public:
- explicit GameConfigWidget(const GameFile& game);
+ explicit GameConfigWidget(const UICommon::GameFile& game);
private:
void CreateWidgets();
@@ -65,6 +69,6 @@ private:
IniFile m_gameini_local;
IniFile m_gameini_default;
- const GameFile& m_game;
+ const UICommon::GameFile& m_game;
std::string m_game_id;
};
diff --git a/Source/Core/DolphinQt2/Config/GeckoCodeWidget.cpp b/Source/Core/DolphinQt2/Config/GeckoCodeWidget.cpp
index 92f2c03af2..79fbc1a089 100644
--- a/Source/Core/DolphinQt2/Config/GeckoCodeWidget.cpp
+++ b/Source/Core/DolphinQt2/Config/GeckoCodeWidget.cpp
@@ -20,10 +20,10 @@
#include "Core/GeckoCodeConfig.h"
#include "DolphinQt2/Config/CheatCodeEditor.h"
#include "DolphinQt2/Config/CheatWarningWidget.h"
-#include "DolphinQt2/GameList/GameFile.h"
+#include "UICommon/GameFile.h"
-GeckoCodeWidget::GeckoCodeWidget(const GameFile& game)
- : m_game(game), m_game_id(game.GetGameID().toStdString()), m_game_revision(game.GetRevision())
+GeckoCodeWidget::GeckoCodeWidget(const UICommon::GameFile& game)
+ : m_game(game), m_game_id(game.GetGameID()), m_game_revision(game.GetRevision())
{
CreateWidgets();
ConnectWidgets();
diff --git a/Source/Core/DolphinQt2/Config/GeckoCodeWidget.h b/Source/Core/DolphinQt2/Config/GeckoCodeWidget.h
index 5d68632967..cf885d0e25 100644
--- a/Source/Core/DolphinQt2/Config/GeckoCodeWidget.h
+++ b/Source/Core/DolphinQt2/Config/GeckoCodeWidget.h
@@ -13,18 +13,22 @@
#include "Core/GeckoCode.h"
class CheatWarningWidget;
-class GameFile;
class QLabel;
class QListWidget;
class QListWidgetItem;
class QTextEdit;
class QPushButton;
+namespace UICommon
+{
+class GameFile;
+}
+
class GeckoCodeWidget : public QWidget
{
Q_OBJECT
public:
- explicit GeckoCodeWidget(const GameFile& game);
+ explicit GeckoCodeWidget(const UICommon::GameFile& game);
signals:
void OpenGeneralSettings();
@@ -43,7 +47,7 @@ private:
void DownloadCodes();
void SaveCodes();
- const GameFile& m_game;
+ const UICommon::GameFile& m_game;
std::string m_game_id;
u16 m_game_revision;
diff --git a/Source/Core/DolphinQt2/Config/InfoWidget.cpp b/Source/Core/DolphinQt2/Config/InfoWidget.cpp
index bbf8aa4867..7c4661004b 100644
--- a/Source/Core/DolphinQt2/Config/InfoWidget.cpp
+++ b/Source/Core/DolphinQt2/Config/InfoWidget.cpp
@@ -16,8 +16,10 @@
#include "DiscIO/Blob.h"
#include "DiscIO/Enums.h"
#include "DolphinQt2/Config/InfoWidget.h"
+#include "DolphinQt2/QtUtils/ImageConverter.h"
+#include "UICommon/UICommon.h"
-InfoWidget::InfoWidget(const GameFile& game) : m_game(game)
+InfoWidget::InfoWidget(const UICommon::GameFile& game) : m_game(game)
{
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(CreateISODetails());
@@ -32,17 +34,19 @@ QGroupBox* InfoWidget::CreateISODetails()
QLineEdit* file_path = CreateValueDisplay(m_game.GetFilePath());
QLineEdit* internal_name = CreateValueDisplay(m_game.GetInternalName());
- QString game_id_string = m_game.GetGameID();
+
+ QString game_id_string = QString::fromStdString(m_game.GetGameID());
if (const u64 title_id = m_game.GetTitleID())
game_id_string += QStringLiteral(" (%1)").arg(title_id, 16, 16, QLatin1Char('0'));
QLineEdit* game_id = CreateValueDisplay(game_id_string);
- QLineEdit* country = CreateValueDisplay(m_game.GetCountry());
+
+ QLineEdit* country = CreateValueDisplay(DiscIO::GetName(m_game.GetCountry(), true));
QLineEdit* maker = CreateValueDisplay(m_game.GetMaker());
- QLineEdit* maker_id = CreateValueDisplay(QStringLiteral("0x") + m_game.GetMakerID());
+ QLineEdit* maker_id = CreateValueDisplay("0x" + m_game.GetMakerID());
QLineEdit* disc_number = CreateValueDisplay(QString::number(m_game.GetDiscNumber()));
QLineEdit* revision = CreateValueDisplay(QString::number(m_game.GetRevision()));
QLineEdit* apploader_date = CreateValueDisplay(m_game.GetApploaderDate());
- QLineEdit* iso_size = CreateValueDisplay(FormatSize(m_game.GetFileSize()));
+ QLineEdit* iso_size = CreateValueDisplay(UICommon::FormatSize(m_game.GetFileSize()));
QWidget* checksum = CreateChecksumComputer();
layout->addRow(tr("File Path:"), file_path);
@@ -75,7 +79,7 @@ QGroupBox* InfoWidget::CreateBannerDetails()
CreateLanguageSelector();
layout->addRow(tr("Show Language:"), m_language_selector);
- if (m_game.GetPlatformID() == DiscIO::Platform::GAMECUBE_DISC)
+ if (m_game.GetPlatform() == DiscIO::Platform::GAMECUBE_DISC)
{
layout->addRow(tr("Short Name:"), m_short_name);
layout->addRow(tr("Short Maker:"), m_short_maker);
@@ -83,27 +87,26 @@ QGroupBox* InfoWidget::CreateBannerDetails()
layout->addRow(tr("Long Maker:"), m_long_maker);
layout->addRow(tr("Description:"), m_description);
}
- else if (DiscIO::IsWii(m_game.GetPlatformID()))
+ else if (DiscIO::IsWii(m_game.GetPlatform()))
{
layout->addRow(tr("Name:"), m_long_name);
}
- if (!m_game.GetBanner().isNull())
- {
- layout->addRow(tr("Banner:"), CreateBannerGraphic());
- }
+ QPixmap banner = ToQPixmap(m_game.GetBannerImage());
+ if (!banner.isNull())
+ layout->addRow(tr("Banner:"), CreateBannerGraphic(banner));
group->setLayout(layout);
return group;
}
-QWidget* InfoWidget::CreateBannerGraphic()
+QWidget* InfoWidget::CreateBannerGraphic(const QPixmap& image)
{
QWidget* widget = new QWidget();
QHBoxLayout* layout = new QHBoxLayout();
QLabel* banner = new QLabel();
- banner->setPixmap(m_game.GetBanner());
+ banner->setPixmap(image);
QPushButton* save = new QPushButton(tr("Save as..."));
connect(save, &QPushButton::clicked, this, &InfoWidget::SaveBanner);
@@ -117,7 +120,7 @@ void InfoWidget::SaveBanner()
{
QString path = QFileDialog::getSaveFileName(this, tr("Select a File"), QDir::currentPath(),
tr("PNG image file (*.png);; All Files (*)"));
- m_game.GetBanner().save(path, "PNG");
+ ToQPixmap(m_game.GetBannerImage()).save(path, "PNG");
}
QLineEdit* InfoWidget::CreateValueDisplay(const QString& value)
@@ -128,14 +131,18 @@ QLineEdit* InfoWidget::CreateValueDisplay(const QString& value)
return value_display;
}
+QLineEdit* InfoWidget::CreateValueDisplay(const std::string& value)
+{
+ return CreateValueDisplay(QString::fromStdString(value));
+}
+
void InfoWidget::CreateLanguageSelector()
{
m_language_selector = new QComboBox();
- QList<DiscIO::Language> languages = m_game.GetAvailableLanguages();
- for (int i = 0; i < languages.count(); i++)
+ for (DiscIO::Language language : m_game.GetLanguages())
{
- DiscIO::Language language = languages.at(i);
- m_language_selector->addItem(m_game.GetLanguage(language), static_cast<int>(language));
+ m_language_selector->addItem(QString::fromStdString(DiscIO::GetName(language, true)),
+ static_cast<int>(language));
}
if (m_language_selector->count() == 1)
m_language_selector->setDisabled(true);
@@ -149,11 +156,11 @@ void InfoWidget::ChangeLanguage()
{
DiscIO::Language language =
static_cast<DiscIO::Language>(m_language_selector->currentData().toInt());
- m_short_name->setText(m_game.GetShortName(language));
- m_short_maker->setText(m_game.GetShortMaker(language));
- m_long_name->setText(m_game.GetLongName(language));
- m_long_maker->setText(m_game.GetLongMaker(language));
- m_description->setText(m_game.GetDescription(language));
+ m_short_name->setText(QString::fromStdString(m_game.GetShortName(language)));
+ m_short_maker->setText(QString::fromStdString(m_game.GetShortMaker(language)));
+ m_long_name->setText(QString::fromStdString(m_game.GetLongName(language)));
+ m_long_maker->setText(QString::fromStdString(m_game.GetLongMaker(language)));
+ m_description->setText(QString::fromStdString(m_game.GetDescription(language)));
}
QWidget* InfoWidget::CreateChecksumComputer()
@@ -176,8 +183,7 @@ void InfoWidget::ComputeChecksum()
{
QCryptographicHash hash(QCryptographicHash::Md5);
hash.reset();
- std::unique_ptr<DiscIO::BlobReader> file(
- DiscIO::CreateBlobReader(m_game.GetFilePath().toStdString()));
+ std::unique_ptr<DiscIO::BlobReader> file(DiscIO::CreateBlobReader(m_game.GetFilePath()));
std::vector<u8> file_data(8 * 1080 * 1080); // read 1MB at a time
u64 game_size = file->GetDataSize();
u64 read_offset = 0;
diff --git a/Source/Core/DolphinQt2/Config/InfoWidget.h b/Source/Core/DolphinQt2/Config/InfoWidget.h
index 45e8aee05c..26e456e513 100644
--- a/Source/Core/DolphinQt2/Config/InfoWidget.h
+++ b/Source/Core/DolphinQt2/Config/InfoWidget.h
@@ -4,20 +4,23 @@
#pragma once
+#include <string>
+
#include <QWidget>
-#include "DolphinQt2/GameList/GameFile.h"
+#include "UICommon/GameFile.h"
class QComboBox;
class QGroupBox;
-class QTextEdit;
class QLineEdit;
+class QPixmap;
+class QTextEdit;
class InfoWidget final : public QWidget
{
Q_OBJECT
public:
- explicit InfoWidget(const GameFile& game);
+ explicit InfoWidget(const UICommon::GameFile& game);
private:
void ComputeChecksum();
@@ -26,13 +29,13 @@ private:
QGroupBox* CreateBannerDetails();
QGroupBox* CreateISODetails();
- QLineEdit* CreateValueDisplay() { return CreateValueDisplay(QStringLiteral("")); };
QLineEdit* CreateValueDisplay(const QString& value);
+ QLineEdit* CreateValueDisplay(const std::string& value = "");
QWidget* CreateChecksumComputer();
void CreateLanguageSelector();
- QWidget* CreateBannerGraphic();
+ QWidget* CreateBannerGraphic(const QPixmap& image);
- GameFile m_game;
+ UICommon::GameFile m_game;
QLineEdit* m_checksum_result;
QComboBox* m_language_selector;
QLineEdit* m_long_name;
diff --git a/Source/Core/DolphinQt2/Config/NewPatchDialog.h b/Source/Core/DolphinQt2/Config/NewPatchDialog.h
index 6419545daf..c3e132efb8 100644
--- a/Source/Core/DolphinQt2/Config/NewPatchDialog.h
+++ b/Source/Core/DolphinQt2/Config/NewPatchDialog.h
@@ -10,7 +10,6 @@
#include <QWidget>
#include "Core/PatchEngine.h"
-#include "DolphinQt2/GameList/GameFile.h"
class QDialogButtonBox;
class QGroupBox;
diff --git a/Source/Core/DolphinQt2/Config/PatchesWidget.cpp b/Source/Core/DolphinQt2/Config/PatchesWidget.cpp
index a922371669..b0af60eb36 100644
--- a/Source/Core/DolphinQt2/Config/PatchesWidget.cpp
+++ b/Source/Core/DolphinQt2/Config/PatchesWidget.cpp
@@ -14,8 +14,8 @@
#include "Core/ConfigManager.h"
#include "DolphinQt2/Config/NewPatchDialog.h"
-PatchesWidget::PatchesWidget(const GameFile& game)
- : m_game(game), m_game_id(game.GetGameID().toStdString()), m_game_revision(game.GetRevision())
+PatchesWidget::PatchesWidget(const UICommon::GameFile& game)
+ : m_game(game), m_game_id(game.GetGameID()), m_game_revision(game.GetRevision())
{
IniFile game_ini_local;
game_ini_local.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini");
diff --git a/Source/Core/DolphinQt2/Config/PatchesWidget.h b/Source/Core/DolphinQt2/Config/PatchesWidget.h
index 86a0174607..f8998f121b 100644
--- a/Source/Core/DolphinQt2/Config/PatchesWidget.h
+++ b/Source/Core/DolphinQt2/Config/PatchesWidget.h
@@ -10,7 +10,7 @@
#include <QWidget>
#include "Core/PatchEngine.h"
-#include "DolphinQt2/GameList/GameFile.h"
+#include "UICommon/GameFile.h"
class QListWidget;
class QListWidgetItem;
@@ -19,7 +19,7 @@ class QPushButton;
class PatchesWidget : public QWidget
{
public:
- explicit PatchesWidget(const GameFile& game);
+ explicit PatchesWidget(const UICommon::GameFile& game);
private:
void CreateWidgets();
@@ -39,7 +39,7 @@ private:
QPushButton* m_remove_button;
std::vector<PatchEngine::Patch> m_patches;
- const GameFile& m_game;
+ const UICommon::GameFile& m_game;
std::string m_game_id;
u16 m_game_revision;
};
diff --git a/Source/Core/DolphinQt2/Config/PropertiesDialog.cpp b/Source/Core/DolphinQt2/Config/PropertiesDialog.cpp
index cd2355d5d7..55b69859ed 100644
--- a/Source/Core/DolphinQt2/Config/PropertiesDialog.cpp
+++ b/Source/Core/DolphinQt2/Config/PropertiesDialog.cpp
@@ -13,11 +13,15 @@
#include "DolphinQt2/Config/InfoWidget.h"
#include "DolphinQt2/Config/PatchesWidget.h"
#include "DolphinQt2/Config/PropertiesDialog.h"
+#include "UICommon/GameFile.h"
-PropertiesDialog::PropertiesDialog(QWidget* parent, const GameFile& game) : QDialog(parent)
+PropertiesDialog::PropertiesDialog(QWidget* parent, const UICommon::GameFile& game)
+ : QDialog(parent)
{
- setWindowTitle(
- QStringLiteral("%1: %2 - %3").arg(game.GetFileName(), game.GetGameID(), game.GetLongName()));
+ setWindowTitle(QStringLiteral("%1: %2 - %3")
+ .arg(QString::fromStdString(game.GetFileName()),
+ QString::fromStdString(game.GetGameID()),
+ QString::fromStdString(game.GetLongName())));
QVBoxLayout* layout = new QVBoxLayout();
QTabWidget* tab_widget = new QTabWidget(this);
@@ -39,7 +43,7 @@ PropertiesDialog::PropertiesDialog(QWidget* parent, const GameFile& game) : QDia
tab_widget->addTab(gecko, tr("Gecko Codes"));
tab_widget->addTab(info, tr("Info"));
- if (DiscIO::IsDisc(game.GetPlatformID()))
+ if (DiscIO::IsDisc(game.GetPlatform()))
{
FilesystemWidget* filesystem = new FilesystemWidget(game);
tab_widget->addTab(filesystem, tr("Filesystem"));
diff --git a/Source/Core/DolphinQt2/Config/PropertiesDialog.h b/Source/Core/DolphinQt2/Config/PropertiesDialog.h
index 85f1ef64f5..5a4bb6af29 100644
--- a/Source/Core/DolphinQt2/Config/PropertiesDialog.h
+++ b/Source/Core/DolphinQt2/Config/PropertiesDialog.h
@@ -6,13 +6,16 @@
#include <QDialog>
-#include "DolphinQt2/GameList/GameFile.h"
+namespace UICommon
+{
+class GameFile;
+}
class PropertiesDialog final : public QDialog
{
Q_OBJECT
public:
- explicit PropertiesDialog(QWidget* parent, const GameFile& game);
+ explicit PropertiesDialog(QWidget* parent, const UICommon::GameFile& game);
signals:
void OpenGeneralSettings();
diff --git a/Source/Core/DolphinQt2/DolphinQt2.vcxproj b/Source/Core/DolphinQt2/DolphinQt2.vcxproj
index 87eec88595..45877e96a3 100644
--- a/Source/Core/DolphinQt2/DolphinQt2.vcxproj
+++ b/Source/Core/DolphinQt2/DolphinQt2.vcxproj
@@ -97,7 +97,6 @@
<QtMoc Include="Debugger\NewBreakpointDialog.h" />
<QtMoc Include="Debugger\RegisterWidget.h" />
<QtMoc Include="Debugger\WatchWidget.h" />
- <QtMoc Include="GameList\GameFile.h" />
<QtMoc Include="GameList\GameList.h" />
<QtMoc Include="GameList\GameListModel.h" />
<QtMoc Include="GameList\GameTracker.h" />
@@ -250,8 +249,6 @@
<ClCompile Include="Debugger\RegisterWidget.cpp" />
<ClCompile Include="Debugger\WatchWidget.cpp" />
<ClCompile Include="GCMemcardManager.cpp" />
- <ClCompile Include="GameList\GameFile.cpp" />
- <ClCompile Include="GameList\GameFileCache.cpp" />
<ClCompile Include="GameList\GameList.cpp" />
<ClCompile Include="GameList\GameListModel.cpp" />
<ClCompile Include="GameList\GameTracker.cpp" />
@@ -271,6 +268,7 @@
<ClCompile Include="NetPlay\PadMappingDialog.cpp" />
<ClCompile Include="QtUtils\DoubleClickEventFilter.cpp" />
<ClCompile Include="QtUtils\ElidedButton.cpp" />
+ <ClCompile Include="QtUtils\ImageConverter.cpp" />
<ClCompile Include="QtUtils\ListTabWidget.cpp" />
<ClCompile Include="QtUtils\WindowActivationEventFilter.cpp" />
<ClCompile Include="QtUtils\AspectRatioWidget.cpp" />
@@ -311,9 +309,9 @@
<ClInclude Include="Config\Mapping\WiimoteEmuMotionControl.h" />
<ClInclude Include="Config\RegisterColumn.h" />
<ClInclude Include="Config\RegisterWidget.h" />
- <ClInclude Include="GameList\GameFileCache.h" />
<ClInclude Include="QtUtils\BlockUserInputFilter.h" />
<ClInclude Include="QtUtils\ElidedButton.h" />
+ <ClInclude Include="QtUtils\ImageConverter.h" />
<ClInclude Include="QtUtils\ListTabWidget.h" />
<ClInclude Include="Resources.h" />
<ClInclude Include="Settings\GameCubePane.h" />
diff --git a/Source/Core/DolphinQt2/GameList/GameFile.cpp b/Source/Core/DolphinQt2/GameList/GameFile.cpp
deleted file mode 100644
index 9f5314a444..0000000000
--- a/Source/Core/DolphinQt2/GameList/GameFile.cpp
+++ /dev/null
@@ -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;
-}
diff --git a/Source/Core/DolphinQt2/GameList/GameFile.h b/Source/Core/DolphinQt2/GameList/GameFile.h
deleted file mode 100644
index c5791ed7cc..0000000000
--- a/Source/Core/DolphinQt2/GameList/GameFile.h
+++ /dev/null
@@ -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);
diff --git a/Source/Core/DolphinQt2/GameList/GameFileCache.cpp b/Source/Core/DolphinQt2/GameList/GameFileCache.cpp
deleted file mode 100644
index 167218c438..0000000000
--- a/Source/Core/DolphinQt2/GameList/GameFileCache.cpp
+++ /dev/null
@@ -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();
-}
diff --git a/Source/Core/DolphinQt2/GameList/GameFileCache.h b/Source/Core/DolphinQt2/GameList/GameFileCache.h
deleted file mode 100644
index df4cd75f16..0000000000
--- a/Source/Core/DolphinQt2/GameList/GameFileCache.h
+++ /dev/null
@@ -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;
-};
diff --git a/Source/Core/DolphinQt2/GameList/GameList.cpp b/Source/Core/DolphinQt2/GameList/GameList.cpp
index 62a4ba39f1..996d86fe4c 100644
--- a/Source/Core/DolphinQt2/GameList/GameList.cpp
+++ b/Source/Core/DolphinQt2/GameList/GameList.cpp
@@ -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;
diff --git a/Source/Core/DolphinQt2/GameList/GameList.h b/Source/Core/DolphinQt2/GameList/GameList.h
index fb564cde92..d58827dac9 100644
--- a/Source/Core/DolphinQt2/GameList/GameList.h
+++ b/Source/Core/DolphinQt2/GameList/GameList.h
@@ -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:
diff --git a/Source/Core/DolphinQt2/GameList/GameListModel.cpp b/Source/Core/DolphinQt2/GameList/GameListModel.cpp
index 01744150a3..7dd5cf4d0b 100644
--- a/Source/Core/DolphinQt2/GameList/GameListModel.cpp
+++ b/Source/Core/DolphinQt2/GameList/GameListModel.cpp
@@ -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++)
{
diff --git a/Source/Core/DolphinQt2/GameList/GameListModel.h b/Source/Core/DolphinQt2/GameList/GameListModel.h
index 5781add1c7..8a8585da1a 100644
--- a/Source/Core/DolphinQt2/GameList/GameListModel.h
+++ b/Source/Core/DolphinQt2/GameList/GameListModel.h
@@ -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;
};
diff --git a/Source/Core/DolphinQt2/GameList/GameTracker.cpp b/Source/Core/DolphinQt2/GameList/GameTracker.cpp
index 85539f4d33..94000057b6 100644
--- a/Source/Core/DolphinQt2/GameList/GameTracker.cpp
+++ b/Source/Core/DolphinQt2/GameList/GameTracker.cpp
@@ -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();
}
}
diff --git a/Source/Core/DolphinQt2/GameList/GameTracker.h b/Source/Core/DolphinQt2/GameList/GameTracker.h
index b063b7dcbe..500f1b9aac 100644
--- a/Source/Core/DolphinQt2/GameList/GameTracker.h
+++ b/Source/Core/DolphinQt2/GameList/GameTracker.h
@@ -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>)
diff --git a/Source/Core/DolphinQt2/GameList/GridProxyModel.cpp b/Source/Core/DolphinQt2/GameList/GridProxyModel.cpp
index e48d4a337b..e6af8114be 100644
--- a/Source/Core/DolphinQt2/GameList/GridProxyModel.cpp
+++ b/Source/Core/DolphinQt2/GameList/GridProxyModel.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
+#include <QPixmap>
#include <QSize>
#include "DolphinQt2/GameList/GameListModel.h"
diff --git a/Source/Core/DolphinQt2/MainWindow.cpp b/Source/Core/DolphinQt2/MainWindow.cpp
index 811d4c79af..073e6341a6 100644
--- a/Source/Core/DolphinQt2/MainWindow.cpp
+++ b/Source/Core/DolphinQt2/MainWindow.cpp
@@ -5,6 +5,7 @@
#include <QApplication>
#include <QCloseEvent>
+#include <QDateTime>
#include <QDir>
#include <QDragEnterEvent>
#include <QDropEvent>
@@ -386,7 +387,7 @@ void MainWindow::Play(const std::optional<std::string>& savestate_path)
}
else
{
- QSharedPointer<GameFile> selection = m_game_list->GetSelectedGame();
+ std::shared_ptr<const UICommon::GameFile> selection = m_game_list->GetSelectedGame();
if (selection)
{
StartGame(selection->GetFilePath(), savestate_path);
@@ -394,7 +395,7 @@ void MainWindow::Play(const std::optional<std::string>& savestate_path)
}
else
{
- auto default_path = QString::fromStdString(SConfig::GetInstance().m_strDefaultISO);
+ QString default_path = QString::fromStdString(SConfig::GetInstance().m_strDefaultISO);
if (!default_path.isEmpty() && QFile::exists(default_path))
{
StartGame(default_path, savestate_path);
@@ -536,7 +537,13 @@ void MainWindow::ScreenShot()
void MainWindow::StartGame(const QString& path, const std::optional<std::string>& savestate_path)
{
- StartGame(BootParameters::GenerateFromFile(path.toStdString(), savestate_path));
+ StartGame(path.toStdString(), savestate_path);
+}
+
+void MainWindow::StartGame(const std::string& path,
+ const std::optional<std::string>& savestate_path)
+{
+ StartGame(BootParameters::GenerateFromFile(path, savestate_path));
}
void MainWindow::StartGame(std::unique_ptr<BootParameters>&& parameters)
diff --git a/Source/Core/DolphinQt2/MainWindow.h b/Source/Core/DolphinQt2/MainWindow.h
index 8c57a91e74..dadb6803b8 100644
--- a/Source/Core/DolphinQt2/MainWindow.h
+++ b/Source/Core/DolphinQt2/MainWindow.h
@@ -11,6 +11,7 @@
#include <memory>
#include <optional>
+#include <string>
#include "DolphinQt2/GameList/GameList.h"
#include "DolphinQt2/MenuBar.h"
@@ -94,6 +95,7 @@ private:
void InitCoreCallbacks();
void StartGame(const QString& path, const std::optional<std::string>& savestate_path = {});
+ void StartGame(const std::string& path, const std::optional<std::string>& savestate_path = {});
void StartGame(std::unique_ptr<BootParameters>&& parameters);
void ShowRenderWidget();
void HideRenderWidget();
diff --git a/Source/Core/DolphinQt2/MenuBar.cpp b/Source/Core/DolphinQt2/MenuBar.cpp
index 2563160f25..c9bc84a1bd 100644
--- a/Source/Core/DolphinQt2/MenuBar.cpp
+++ b/Source/Core/DolphinQt2/MenuBar.cpp
@@ -34,10 +34,11 @@
#include "DiscIO/WiiSaveBanner.h"
#include "DolphinQt2/AboutDialog.h"
-#include "DolphinQt2/GameList/GameFile.h"
#include "DolphinQt2/QtUtils/ActionHelper.h"
#include "DolphinQt2/Settings.h"
+#include "UICommon/GameFile.h"
+
MenuBar::MenuBar(QWidget* parent) : QMenuBar(parent)
{
AddFileMenu();
@@ -593,8 +594,9 @@ void MenuBar::InstallWAD()
QMessageBox result_dialog(this);
- if (GameFile(wad_file).Install())
+ if (WiiUtils::InstallWAD(wad_file.toStdString()))
{
+ Settings::Instance().NANDRefresh();
result_dialog.setIcon(QMessageBox::Information);
result_dialog.setText(tr("Successfully installed this title to the NAND."));
}
@@ -698,12 +700,12 @@ void MenuBar::NANDExtractCertificates()
}
}
-void MenuBar::OnSelectionChanged(QSharedPointer<GameFile> game_file)
+void MenuBar::OnSelectionChanged(std::shared_ptr<const UICommon::GameFile> game_file)
{
- bool is_null = game_file.isNull();
+ const bool game_selected = !!game_file;
- m_recording_play->setEnabled(!Core::IsRunning() && !is_null);
- m_recording_start->setEnabled(!Movie::IsPlayingInput() && !is_null);
+ m_recording_play->setEnabled(game_selected && !Core::IsRunning());
+ m_recording_start->setEnabled(game_selected && !Movie::IsPlayingInput());
}
void MenuBar::OnRecordingStatusChanged(bool recording)
diff --git a/Source/Core/DolphinQt2/MenuBar.h b/Source/Core/DolphinQt2/MenuBar.h
index 3e80ed8cf9..b2fb2d9275 100644
--- a/Source/Core/DolphinQt2/MenuBar.h
+++ b/Source/Core/DolphinQt2/MenuBar.h
@@ -5,13 +5,12 @@
#pragma once
#include <array>
+#include <memory>
#include <string>
#include <QMenu>
#include <QMenuBar>
-#include "DolphinQt2/GameList/GameFile.h"
-
namespace Core
{
enum class State;
@@ -22,6 +21,11 @@ namespace DiscIO
enum class Region;
};
+namespace UICommon
+{
+class GameFile;
+}
+
class MenuBar final : public QMenuBar
{
Q_OBJECT
@@ -89,7 +93,7 @@ signals:
void ExportRecording();
void ShowTASInput();
- void SelectionChanged(QSharedPointer<GameFile> game_file);
+ void SelectionChanged(std::shared_ptr<const UICommon::GameFile> game_file);
void RecordingStatusChanged(bool recording);
void ReadOnlyModeChanged(bool read_only);
@@ -120,7 +124,7 @@ private:
void CheckNAND();
void NANDExtractCertificates();
- void OnSelectionChanged(QSharedPointer<GameFile> game_file);
+ void OnSelectionChanged(std::shared_ptr<const UICommon::GameFile> game_file);
void OnRecordingStatusChanged(bool recording);
void OnReadOnlyModeChanged(bool read_only);
void OnDebugModeToggled(bool enabled);
diff --git a/Source/Core/DolphinQt2/NetPlay/GameListDialog.cpp b/Source/Core/DolphinQt2/NetPlay/GameListDialog.cpp
index 71155abab5..00c5a36ea4 100644
--- a/Source/Core/DolphinQt2/NetPlay/GameListDialog.cpp
+++ b/Source/Core/DolphinQt2/NetPlay/GameListDialog.cpp
@@ -51,7 +51,7 @@ void GameListDialog::PopulateGameList()
for (int i = 0; i < game_list_model->rowCount(QModelIndex()); i++)
{
- auto* item = new QListWidgetItem(game_list_model->GetUniqueID(i));
+ auto* item = new QListWidgetItem(game_list_model->GetUniqueIdentifier(i));
m_game_list->addItem(item);
}
diff --git a/Source/Core/DolphinQt2/NetPlay/NetPlayDialog.cpp b/Source/Core/DolphinQt2/NetPlay/NetPlayDialog.cpp
index 77a17486b5..5747b46837 100644
--- a/Source/Core/DolphinQt2/NetPlay/NetPlayDialog.cpp
+++ b/Source/Core/DolphinQt2/NetPlay/NetPlayDialog.cpp
@@ -532,7 +532,7 @@ std::string NetPlayDialog::FindGame(const std::string& game)
return RunOnObject(this, [this, game] {
for (int i = 0; i < m_game_list_model->rowCount(QModelIndex()); i++)
{
- if (m_game_list_model->GetUniqueID(i).toStdString() == game)
+ if (m_game_list_model->GetUniqueIdentifier(i).toStdString() == game)
return m_game_list_model->GetPath(i).toStdString();
}
return std::string("");
diff --git a/Source/Core/DolphinQt2/NetPlay/NetPlaySetupDialog.cpp b/Source/Core/DolphinQt2/NetPlay/NetPlaySetupDialog.cpp
index 922186d9ca..89fe579d5f 100644
--- a/Source/Core/DolphinQt2/NetPlay/NetPlaySetupDialog.cpp
+++ b/Source/Core/DolphinQt2/NetPlay/NetPlaySetupDialog.cpp
@@ -245,7 +245,7 @@ void NetPlaySetupDialog::PopulateGameList()
m_host_games->clear();
for (int i = 0; i < m_game_list_model->rowCount(QModelIndex()); i++)
{
- auto title = m_game_list_model->GetUniqueID(i);
+ auto title = m_game_list_model->GetUniqueIdentifier(i);
auto path = m_game_list_model->GetPath(i);
auto* item = new QListWidgetItem(title);
diff --git a/Source/Core/DolphinQt2/QtUtils/ImageConverter.cpp b/Source/Core/DolphinQt2/QtUtils/ImageConverter.cpp
new file mode 100644
index 0000000000..32fadf788e
--- /dev/null
+++ b/Source/Core/DolphinQt2/QtUtils/ImageConverter.cpp
@@ -0,0 +1,33 @@
+// Copyright 2018 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "DolphinQt2/QtUtils/ImageConverter.h"
+
+#include <vector>
+
+#include <QPixmap>
+
+#include "Common/CommonTypes.h"
+#include "UICommon/GameFile.h"
+
+QPixmap ToQPixmap(const UICommon::GameBanner& banner)
+{
+ return ToQPixmap(banner.buffer, banner.width, banner.height);
+}
+
+QPixmap ToQPixmap(const std::vector<u32>& buffer, int width, int height)
+{
+ QImage image(width, height, QImage::Format_RGB888);
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ const u32 color = buffer[y * width + x];
+ image.setPixel(
+ x, y, qRgb((color & 0xFF0000) >> 16, (color & 0x00FF00) >> 8, (color & 0x0000FF) >> 0));
+ }
+ }
+
+ return QPixmap::fromImage(image);
+}
diff --git a/Source/Core/DolphinQt2/QtUtils/ImageConverter.h b/Source/Core/DolphinQt2/QtUtils/ImageConverter.h
new file mode 100644
index 0000000000..98a07fc89c
--- /dev/null
+++ b/Source/Core/DolphinQt2/QtUtils/ImageConverter.h
@@ -0,0 +1,19 @@
+// Copyright 2018 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+
+#include "Common/CommonTypes.h"
+
+class QPixmap;
+
+namespace UICommon
+{
+struct GameBanner;
+}
+
+QPixmap ToQPixmap(const UICommon::GameBanner& banner);
+QPixmap ToQPixmap(const std::vector<u32>& buffer, int width, int height);
diff --git a/Source/Core/DolphinQt2/Resources.h b/Source/Core/DolphinQt2/Resources.h
index 0d2909441b..bb93d4ce61 100644
--- a/Source/Core/DolphinQt2/Resources.h
+++ b/Source/Core/DolphinQt2/Resources.h
@@ -5,6 +5,7 @@
#pragma once
#include <QList>
+#include <QPixmap>
// Store for various QPixmaps that will be used repeatedly.
class Resources final
diff --git a/Source/Core/DolphinWX/CMakeLists.txt b/Source/Core/DolphinWX/CMakeLists.txt
index d1ed79c781..8c101c0395 100644
--- a/Source/Core/DolphinWX/CMakeLists.txt
+++ b/Source/Core/DolphinWX/CMakeLists.txt
@@ -67,7 +67,6 @@ set(SRCS
FrameAui.cpp
FrameTools.cpp
GameListCtrl.cpp
- ISOFile.cpp
LogConfigWindow.cpp
LogWindow.cpp
Main.cpp
diff --git a/Source/Core/DolphinWX/DolphinWX.vcxproj b/Source/Core/DolphinWX/DolphinWX.vcxproj
index 1904e297ff..417fe3be09 100644
--- a/Source/Core/DolphinWX/DolphinWX.vcxproj
+++ b/Source/Core/DolphinWX/DolphinWX.vcxproj
@@ -111,7 +111,6 @@
<ClCompile Include="Input\GuitarInputConfigDiag.cpp" />
<ClCompile Include="Input\DrumsInputConfigDiag.cpp" />
<ClCompile Include="Input\TurntableInputConfigDiag.cpp" />
- <ClCompile Include="ISOFile.cpp" />
<ClCompile Include="LogConfigWindow.cpp" />
<ClCompile Include="LogWindow.cpp" />
<ClCompile Include="Main.cpp" />
@@ -194,7 +193,6 @@
<ClInclude Include="Input\GuitarInputConfigDiag.h" />
<ClInclude Include="Input\DrumsInputConfigDiag.h" />
<ClInclude Include="Input\TurntableInputConfigDiag.h" />
- <ClInclude Include="ISOFile.h" />
<ClInclude Include="LogConfigWindow.h" />
<ClInclude Include="LogWindow.h" />
<ClInclude Include="Main.h" />
@@ -299,4 +297,4 @@
<Message Text="Copy: @(BinaryFiles) -&gt; $(BinaryOutputDir)" Importance="High" />
<Copy SourceFiles="@(BinaryFiles)" DestinationFolder="$(BinaryOutputDir)" />
</Target>
-</Project>
+</Project> \ No newline at end of file
diff --git a/Source/Core/DolphinWX/DolphinWX.vcxproj.filters b/Source/Core/DolphinWX/DolphinWX.vcxproj.filters
index 5555786156..9eaf714335 100644
--- a/Source/Core/DolphinWX/DolphinWX.vcxproj.filters
+++ b/Source/Core/DolphinWX/DolphinWX.vcxproj.filters
@@ -19,9 +19,6 @@
<Filter Include="GUI\Video">
<UniqueIdentifier>{80626e3b-e13b-41c3-bd63-4ef1faf92924}</UniqueIdentifier>
</Filter>
- <Filter Include="Misc">
- <UniqueIdentifier>{4352dc64-398e-4a96-ba4a-824dffa2004c}</UniqueIdentifier>
- </Filter>
<Filter Include="Resources">
<UniqueIdentifier>{d6bc4dd6-06ed-46ad-b327-04afb26e10ec}</UniqueIdentifier>
</Filter>
@@ -154,9 +151,6 @@
<ClCompile Include="VideoConfigDiag.cpp">
<Filter>GUI\Video</Filter>
</ClCompile>
- <ClCompile Include="ISOFile.cpp">
- <Filter>Misc</Filter>
- </ClCompile>
<ClCompile Include="AboutDolphin.cpp">
<Filter>GUI</Filter>
</ClCompile>
@@ -381,9 +375,6 @@
<ClInclude Include="VideoConfigDiag.h">
<Filter>GUI\Video</Filter>
</ClInclude>
- <ClInclude Include="ISOFile.h">
- <Filter>Misc</Filter>
- </ClInclude>
<ClInclude Include="resource.h">
<Filter>Resources</Filter>
</ClInclude>
@@ -507,4 +498,4 @@
<ItemGroup>
<Image Include="$(CoreDir)..\..\Installer\Dolphin.ico" />
</ItemGroup>
-</Project>
+</Project> \ No newline at end of file
diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp
index ded328f7bc..a61b98f9c2 100644
--- a/Source/Core/DolphinWX/FrameTools.cpp
+++ b/Source/Core/DolphinWX/FrameTools.cpp
@@ -77,7 +77,6 @@
#include "DolphinWX/Frame.h"
#include "DolphinWX/GameListCtrl.h"
#include "DolphinWX/Globals.h"
-#include "DolphinWX/ISOFile.h"
#include "DolphinWX/Input/HotkeyInputConfigDiag.h"
#include "DolphinWX/Input/InputConfigDiag.h"
#include "DolphinWX/LogWindow.h"
@@ -92,6 +91,7 @@
#include "InputCommon/ControllerInterface/ControllerInterface.h"
+#include "UICommon/GameFile.h"
#include "UICommon/UICommon.h"
#include "VideoCommon/RenderBase.h"
@@ -318,7 +318,7 @@ void CFrame::BootGame(const std::string& filename, const std::optional<std::stri
if (m_game_list_ctrl->GetSelectedISO() != nullptr)
{
if (m_game_list_ctrl->GetSelectedISO()->IsValid())
- bootfile = m_game_list_ctrl->GetSelectedISO()->GetFileName();
+ bootfile = m_game_list_ctrl->GetSelectedISO()->GetFilePath();
}
else if (!StartUp.m_strDefaultISO.empty() && File::Exists(StartUp.m_strDefaultISO))
{
@@ -1229,10 +1229,10 @@ void CFrame::OnInstallWAD(wxCommandEvent& event)
{
case IDM_LIST_INSTALL_WAD:
{
- const GameListItem* iso = m_game_list_ctrl->GetSelectedISO();
+ const UICommon::GameFile* iso = m_game_list_ctrl->GetSelectedISO();
if (!iso)
return;
- fileName = iso->GetFileName();
+ fileName = iso->GetFilePath();
break;
}
case IDM_MENU_INSTALL_WAD:
@@ -1258,7 +1258,7 @@ void CFrame::OnInstallWAD(wxCommandEvent& event)
void CFrame::OnUninstallWAD(wxCommandEvent&)
{
- const GameListItem* file = m_game_list_ctrl->GetSelectedISO();
+ const UICommon::GameFile* file = m_game_list_ctrl->GetSelectedISO();
if (!file)
return;
@@ -1268,9 +1268,8 @@ void CFrame::OnUninstallWAD(wxCommandEvent&)
return;
}
- u64 title_id = file->GetTitleID();
- IOS::HLE::Kernel ios;
- if (ios.GetES()->DeleteTitleContent(title_id) < 0)
+ const u64 title_id = file->GetTitleID();
+ if (!WiiUtils::UninstallTitle(title_id))
{
PanicAlertT("Failed to remove this title from the NAND.");
return;
@@ -1490,11 +1489,11 @@ void CFrame::OnPerformOnlineWiiUpdate(wxCommandEvent& event)
void CFrame::OnPerformDiscWiiUpdate(wxCommandEvent&)
{
- const GameListItem* iso = m_game_list_ctrl->GetSelectedISO();
+ const UICommon::GameFile* iso = m_game_list_ctrl->GetSelectedISO();
if (!iso)
return;
- const std::string file_name = iso->GetFileName();
+ const std::string file_name = iso->GetFilePath();
const WiiUtils::UpdateResult result = ShowUpdateProgress(this, WiiUtils::DoDiscUpdate, file_name);
ShowUpdateResult(result);
diff --git a/Source/Core/DolphinWX/GameListCtrl.cpp b/Source/Core/DolphinWX/GameListCtrl.cpp
index 99e66ba21e..3418e9c7f8 100644
--- a/Source/Core/DolphinWX/GameListCtrl.cpp
+++ b/Source/Core/DolphinWX/GameListCtrl.cpp
@@ -4,13 +4,11 @@
#include <algorithm>
#include <cinttypes>
-#include <cmath>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <memory>
#include <string>
-#include <unordered_set>
#include <utility>
#include <vector>
#include <wx/app.h>
@@ -39,9 +37,7 @@
#include "Common/CDUtils.h"
#include "Common/CommonPaths.h"
#include "Common/CommonTypes.h"
-#include "Common/FileSearch.h"
#include "Common/FileUtil.h"
-#include "Common/MathUtil.h"
#include "Common/StringUtil.h"
#include "Common/SysConf.h"
#include "Common/Thread.h"
@@ -53,18 +49,20 @@
#include "Core/HW/WiiSaveCrypted.h"
#include "Core/Movie.h"
#include "Core/TitleDatabase.h"
+#include "Core/WiiUtils.h"
#include "DiscIO/Blob.h"
-#include "DiscIO/DirectoryBlob.h"
#include "DiscIO/Enums.h"
#include "DiscIO/Volume.h"
#include "DolphinWX/Frame.h"
#include "DolphinWX/GameListCtrl.h"
#include "DolphinWX/Globals.h"
-#include "DolphinWX/ISOFile.h"
#include "DolphinWX/ISOProperties/ISOProperties.h"
#include "DolphinWX/Main.h"
#include "DolphinWX/NetPlay/NetPlayLauncher.h"
#include "DolphinWX/WxUtils.h"
+#include "UICommon/GameFile.h"
+#include "UICommon/GameFileCache.h"
+#include "UICommon/UICommon.h"
struct CompressionProgress final
{
@@ -82,11 +80,9 @@ public:
wxProgressDialog* dialog;
};
-static constexpr u32 CACHE_REVISION = 6; // Last changed in PR 6109
-
static bool sorted = false;
-static int CompareGameListItems(const GameListItem* iso1, const GameListItem* iso2,
+static int CompareGameListItems(const UICommon::GameFile* iso1, const UICommon::GameFile* iso2,
long sortData = GameListCtrl::COLUMN_TITLE)
{
int t = 1;
@@ -101,14 +97,14 @@ static int CompareGameListItems(const GameListItem* iso1, const GameListItem* is
{
case GameListCtrl::COLUMN_MAKER:
{
- int maker_cmp = strcasecmp(iso1->GetCompany().c_str(), iso2->GetCompany().c_str()) * t;
+ int maker_cmp = strcasecmp(iso1->GetMaker().c_str(), iso2->GetMaker().c_str()) * t;
if (maker_cmp != 0)
return maker_cmp;
break;
}
case GameListCtrl::COLUMN_FILENAME:
- return wxStricmp(wxFileNameFromPath(iso1->GetFileName()),
- wxFileNameFromPath(iso2->GetFileName())) *
+ return wxStricmp(wxFileNameFromPath(iso1->GetFilePath()),
+ wxFileNameFromPath(iso2->GetFilePath())) *
t;
case GameListCtrl::COLUMN_ID:
{
@@ -162,8 +158,8 @@ static int CompareGameListItems(const GameListItem* iso1, const GameListItem* is
if (iso1->GetDiscNumber() != iso2->GetDiscNumber())
return t * (iso1->GetDiscNumber() > iso2->GetDiscNumber() ? 1 : -1);
- wxString iso1_filename = wxFileNameFromPath(iso1->GetFileName());
- wxString iso2_filename = wxFileNameFromPath(iso2->GetFileName());
+ wxString iso1_filename = wxFileNameFromPath(iso1->GetFilePath());
+ wxString iso2_filename = wxFileNameFromPath(iso2->GetFilePath());
if (iso1_filename != iso2_filename)
return t * wxStricmp(iso1_filename, iso2_filename);
@@ -171,7 +167,7 @@ static int CompareGameListItems(const GameListItem* iso1, const GameListItem* is
return 0;
}
-static bool ShouldDisplayGameListItem(const GameListItem& item)
+static bool ShouldDisplayGameListItem(const UICommon::GameFile& item)
{
const bool show_platform = [&item] {
switch (item.GetPlatform())
@@ -281,9 +277,9 @@ GameListCtrl::GameListCtrl(bool disable_scanning, wxWindow* parent, const wxWind
if (!disable_scanning)
{
m_scan_thread = std::thread([&] {
- Common::SetCurrentThreadName("gamelist scanner");
+ Common::SetCurrentThreadName("Game list scanner");
- if (SyncCacheFile(false))
+ if (m_cache.Load())
QueueEvent(new wxCommandEvent(DOLPHIN_EVT_REFRESH_GAMELIST));
// Always do an initial scan to catch new files and perform the more expensive per-file
@@ -422,11 +418,10 @@ void GameListCtrl::RefreshList()
m_shown_files.clear();
{
std::unique_lock<std::mutex> lk(m_cache_mutex);
- for (auto& item : m_cached_files)
- {
- if (ShouldDisplayGameListItem(*item))
- m_shown_files.push_back(item);
- }
+ m_cache.ForEach([this](const std::shared_ptr<const UICommon::GameFile>& game_file) {
+ if (ShouldDisplayGameListItem(*game_file))
+ m_shown_files.push_back(game_file);
+ });
}
// Drives are not cached. Not sure if this is required, but better to err on the
@@ -436,7 +431,7 @@ void GameListCtrl::RefreshList()
std::unique_lock<std::mutex> lk(m_title_database_mutex);
for (const auto& drive : cdio_get_devices())
{
- auto file = std::make_shared<GameListItem>(drive);
+ auto file = std::make_shared<UICommon::GameFile>(drive);
if (file->IsValid())
{
if (file->EmuStateChanged())
@@ -538,24 +533,6 @@ void GameListCtrl::RefreshList()
SetFocus();
}
-static wxString NiceSizeFormat(u64 size)
-{
- // Return a pretty filesize string from byte count.
- // e.g. 1134278 -> "1.08 MiB"
-
- const char* const unit_symbols[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"};
-
- // Find largest power of 2 less than size.
- // div 10 to get largest named unit less than size
- // 10 == log2(1024) (number of B in a KiB, KiB in a MiB, etc)
- // Max value is 63 / 10 = 6
- const int unit = IntLog2(std::max<u64>(size, 1)) / 10;
-
- // Don't need exact values, only 5 most significant digits
- double unit_size = std::pow(2, unit * 10);
- return wxString::Format("%.2f %s", size / unit_size, unit_symbols[unit]);
-}
-
// Update the column content of the item at index
void GameListCtrl::UpdateItemAtColumn(long index, int column)
{
@@ -573,11 +550,11 @@ void GameListCtrl::UpdateItemAtColumn(long index, int column)
{
int image_index = m_image_indexes.utility_banner[0]; // nobanner
- if (iso_file.GetBannerImage().IsOk())
+ wxImage banner = WxUtils::ToWxImage(iso_file.GetBannerImage());
+ if (banner.IsOk())
{
wxImageList* img_list = GetImageList(wxIMAGE_LIST_SMALL);
- image_index = img_list->Add(
- WxUtils::ScaleImageToBitmap(iso_file.GetBannerImage(), this, img_list->GetSize()));
+ image_index = img_list->Add(WxUtils::ScaleImageToBitmap(banner, this, img_list->GetSize()));
}
SetItemColumnImage(index, COLUMN_BANNER, image_index);
@@ -599,10 +576,10 @@ void GameListCtrl::UpdateItemAtColumn(long index, int column)
break;
}
case COLUMN_MAKER:
- SetItem(index, COLUMN_MAKER, StrToWxStr(iso_file.GetCompany()), -1);
+ SetItem(index, COLUMN_MAKER, StrToWxStr(iso_file.GetMaker()), -1);
break;
case COLUMN_FILENAME:
- SetItem(index, COLUMN_FILENAME, wxFileNameFromPath(StrToWxStr(iso_file.GetFileName())), -1);
+ SetItem(index, COLUMN_FILENAME, wxFileNameFromPath(StrToWxStr(iso_file.GetFilePath())), -1);
break;
case COLUMN_EMULATION_STATE:
SetItemColumnImage(index, COLUMN_EMULATION_STATE,
@@ -613,7 +590,7 @@ void GameListCtrl::UpdateItemAtColumn(long index, int column)
m_image_indexes.flag[static_cast<size_t>(iso_file.GetCountry())]);
break;
case COLUMN_SIZE:
- SetItem(index, COLUMN_SIZE, NiceSizeFormat(iso_file.GetFileSize()), -1);
+ SetItem(index, COLUMN_SIZE, UICommon::FormatSize(iso_file.GetFileSize()), -1);
break;
case COLUMN_ID:
SetItem(index, COLUMN_ID, iso_file.GetGameID(), -1);
@@ -676,76 +653,6 @@ void GameListCtrl::SetColors()
}
}
-void GameListCtrl::DoState(PointerWrap* p, u32 size)
-{
- struct
- {
- u32 Revision;
- u32 ExpectedSize;
- } header = {CACHE_REVISION, size};
- p->Do(header);
- if (p->GetMode() == PointerWrap::MODE_READ)
- {
- if (header.Revision != CACHE_REVISION || header.ExpectedSize != size)
- {
- p->SetMode(PointerWrap::MODE_MEASURE);
- return;
- }
- }
- p->DoEachElement(m_cached_files, [](PointerWrap& state, std::shared_ptr<GameListItem>& elem) {
- if (state.GetMode() == PointerWrap::MODE_READ)
- {
- elem = std::make_shared<GameListItem>();
- }
- elem->DoState(state);
- });
-}
-
-bool GameListCtrl::SyncCacheFile(bool write)
-{
- std::string filename(File::GetUserPath(D_CACHE_IDX) + "wx_gamelist.cache");
- const char* open_mode = write ? "wb" : "rb";
- File::IOFile f(filename, open_mode);
- if (!f)
- return false;
- bool success = false;
- if (write)
- {
- // Measure the size of the buffer.
- u8* ptr = nullptr;
- PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
- DoState(&p);
- const size_t buffer_size = reinterpret_cast<size_t>(ptr);
-
- // Then actually do the write.
- std::vector<u8> buffer(buffer_size);
- ptr = &buffer[0];
- p.SetMode(PointerWrap::MODE_WRITE);
- DoState(&p, buffer_size);
- if (f.WriteBytes(buffer.data(), buffer.size()))
- success = true;
- }
- else
- {
- std::vector<u8> buffer(f.GetSize());
- if (buffer.size() && f.ReadBytes(buffer.data(), buffer.size()))
- {
- u8* ptr = buffer.data();
- PointerWrap p(&ptr, PointerWrap::MODE_READ);
- DoState(&p, buffer.size());
- if (p.GetMode() == PointerWrap::MODE_READ)
- success = true;
- }
- }
- if (!success)
- {
- // If some file operation failed, try to delete the probably-corrupted cache
- f.Close();
- File::Delete(filename);
- }
- return success;
-}
-
void GameListCtrl::RescanList()
{
auto post_status = [&](const wxString& status) {
@@ -757,107 +664,39 @@ void GameListCtrl::RescanList()
post_status(_("Scanning..."));
- const std::vector<std::string> search_extensions = {".gcm", ".tgc", ".iso", ".ciso", ".gcz",
- ".wbfs", ".wad", ".dol", ".elf"};
- // TODO This could process paths iteratively as they are found
- const std::vector<std::string> search_results_vector =
- Common::DoFileSearch(SConfig::GetInstance().m_ISOFolder, search_extensions,
- SConfig::GetInstance().m_RecursiveISOFolder);
-
- // Copy search results into a set, except ones that match DiscIO::ShouldHideFromGameList.
- // TODO Prevent DoFileSearch from looking inside /files/ directories of DirectoryBlobs at all?
- // TODO Make DoFileSearch support filter predicates so we don't have remove things afterwards?
- std::unordered_set<std::string> search_results;
- search_results.reserve(search_results_vector.size());
- for (const std::string& path : search_results_vector)
- {
- if (!DiscIO::ShouldHideFromGameList(path))
- search_results.insert(path);
- }
+ const std::vector<std::string> game_paths = UICommon::FindAllGamePaths(
+ SConfig::GetInstance().m_ISOFolder, SConfig::GetInstance().m_RecursiveISOFolder);
// Reload the TitleDatabase
{
- std::unique_lock<std::mutex> lk(m_title_database_mutex);
+ std::unique_lock<std::mutex> lock(m_title_database_mutex);
m_title_database = {};
}
bool cache_changed = false;
+
{
std::unique_lock<std::mutex> lk(m_cache_mutex);
-
- // Delete paths that aren't in search_results from m_cached_files,
- // while simultaneously deleting paths that aren't in m_cached_files from search_results.
- // For the sake of speed, we don't care about maintaining the order of m_cached_files.
- {
- auto it = m_cached_files.begin();
- auto end = m_cached_files.end();
- while (it != end)
- {
- if (search_results.erase((*it)->GetFileName()))
- {
- ++it;
- }
- else
- {
- cache_changed = true;
- --end;
- *it = std::move(*end);
- }
- }
- m_cached_files.erase(it, m_cached_files.end());
- }
-
- // Now that the previous loop has run, search_results only contains paths that
- // aren't in m_cached_files, so we simply add all of them to m_cached_files.
- for (const auto& path : search_results)
+ if (m_cache.Update(game_paths))
{
- auto file = std::make_shared<GameListItem>(path);
- if (file->IsValid())
- {
- cache_changed = true;
- m_cached_files.push_back(std::move(file));
- }
+ cache_changed = true;
+ QueueEvent(new wxCommandEvent(DOLPHIN_EVT_REFRESH_GAMELIST));
}
}
- // The common case is that just a file has been added/removed, so trigger a refresh ASAP with the
- // assumption that other properties of files will not change at the same time (which will be fine
- // and just causes a double refresh).
- if (cache_changed)
- QueueEvent(new wxCommandEvent(DOLPHIN_EVT_REFRESH_GAMELIST));
- // If any cached files need updates, apply the updates to a copy and delete the original - this
- // makes the UI thread's use of cached files safe. Note however, it is assumed that RefreshList
- // will not iterate m_cached_files while the scan thread is modifying the list itself.
- bool refresh_needed = false;
{
std::unique_lock<std::mutex> lk(m_cache_mutex);
- for (auto& file : m_cached_files)
+ if (m_cache.UpdateAdditionalMetadata(m_title_database))
{
- bool emu_state_changed = file->EmuStateChanged();
- bool banner_changed = file->BannerChanged();
- bool custom_title_changed = file->CustomNameChanged(m_title_database);
- if (emu_state_changed || banner_changed || custom_title_changed)
- {
- cache_changed = refresh_needed = true;
- auto copy = std::make_shared<GameListItem>(*file);
- if (emu_state_changed)
- copy->EmuStateCommit();
- if (banner_changed)
- copy->BannerCommit();
- if (custom_title_changed)
- copy->CustomNameCommit();
- file = std::move(copy);
- }
+ cache_changed = true;
+ QueueEvent(new wxCommandEvent(DOLPHIN_EVT_REFRESH_GAMELIST));
}
}
- // Only post UI event to update the displayed list if something actually changed
- if (refresh_needed)
- QueueEvent(new wxCommandEvent(DOLPHIN_EVT_REFRESH_GAMELIST));
post_status("");
if (cache_changed)
- SyncCacheFile(true);
+ m_cache.Save();
}
void GameListCtrl::OnRefreshGameList(wxCommandEvent& WXUNUSED(event))
@@ -871,7 +710,7 @@ void GameListCtrl::OnRescanGameList(wxCommandEvent& event)
{
// Knock out the cache on a purge event
std::unique_lock<std::mutex> lk(m_cache_mutex);
- m_cached_files.clear();
+ m_cache.Clear();
}
m_scan_trigger.Set();
}
@@ -893,7 +732,7 @@ void GameListCtrl::OnColBeginDrag(wxListEvent& event)
event.Veto();
}
-const GameListItem* GameListCtrl::GetISO(size_t index) const
+const UICommon::GameFile* GameListCtrl::GetISO(size_t index) const
{
if (index < m_shown_files.size())
return m_shown_files[index].get();
@@ -907,8 +746,8 @@ static int wxCALLBACK wxListCompare(wxIntPtr item1, wxIntPtr item2, wxIntPtr sor
// return 1 if item1 > item2
// return -1 if item1 < item2
// return 0 for identity
- const GameListItem* iso1 = caller->GetISO(item1);
- const GameListItem* iso2 = caller->GetISO(item2);
+ const UICommon::GameFile* iso1 = caller->GetISO(item1);
+ const UICommon::GameFile* iso2 = caller->GetISO(item2);
if (iso1 == iso2)
return 0;
@@ -1034,7 +873,7 @@ void GameListCtrl::OnMouseMotion(wxMouseEvent& event)
// Emulation status
static const char* const emuState[] = {"Broken", "Intro", "In-Game", "Playable", "Perfect"};
- const GameListItem* iso = GetISO(GetItemData(item));
+ const UICommon::GameFile* iso = GetISO(GetItemData(item));
const int emu_state = iso->GetEmuState();
const std::string& issues = iso->GetIssues();
@@ -1093,22 +932,6 @@ void GameListCtrl::OnLeftClick(wxMouseEvent& event)
event.Skip();
}
-static bool IsWADInstalled(const GameListItem& wad)
-{
- const std::string content_dir =
- Common::GetTitleContentPath(wad.GetTitleID(), 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"; });
-}
-
void GameListCtrl::OnRightClick(wxMouseEvent& event)
{
// Focus the clicked item.
@@ -1125,7 +948,7 @@ void GameListCtrl::OnRightClick(wxMouseEvent& event)
}
if (GetSelectedItemCount() == 1)
{
- const GameListItem* selected_iso = GetSelectedISO();
+ const UICommon::GameFile* selected_iso = GetSelectedISO();
if (selected_iso)
{
wxMenu popupMenu;
@@ -1160,7 +983,7 @@ void GameListCtrl::OnRightClick(wxMouseEvent& event)
popupMenu.AppendCheckItem(IDM_SET_DEFAULT_ISO, _("Set as &default ISO"));
// First we have to decide a starting value when we append it
- if (selected_iso->GetFileName() == SConfig::GetInstance().m_strDefaultISO)
+ if (selected_iso->GetFilePath() == SConfig::GetInstance().m_strDefaultISO)
popupMenu.FindItem(IDM_SET_DEFAULT_ISO)->Check();
popupMenu.AppendSeparator();
@@ -1194,7 +1017,7 @@ void GameListCtrl::OnRightClick(wxMouseEvent& event)
for (auto* menu_item : {install_wad_item, uninstall_wad_item})
menu_item->Enable(!Core::IsRunning() || !SConfig::GetInstance().bWii);
- if (!IsWADInstalled(*selected_iso))
+ if (!WiiUtils::IsTitleInstalled(selected_iso->GetTitleID()))
uninstall_wad_item->Enable(false);
}
@@ -1214,7 +1037,7 @@ void GameListCtrl::OnRightClick(wxMouseEvent& event)
}
}
-const GameListItem* GameListCtrl::GetSelectedISO() const
+const UICommon::GameFile* GameListCtrl::GetSelectedISO() const
{
if (m_shown_files.empty())
return nullptr;
@@ -1229,9 +1052,9 @@ const GameListItem* GameListCtrl::GetSelectedISO() const
return GetISO(GetItemData(item));
}
-std::vector<const GameListItem*> GameListCtrl::GetAllSelectedISOs() const
+std::vector<const UICommon::GameFile*> GameListCtrl::GetAllSelectedISOs() const
{
- std::vector<const GameListItem*> result;
+ std::vector<const UICommon::GameFile*> result;
long item = -1;
while (true)
{
@@ -1257,18 +1080,18 @@ bool GameListCtrl::IsHidingItems()
void GameListCtrl::OnOpenContainingFolder(wxCommandEvent& WXUNUSED(event))
{
- const GameListItem* iso = GetSelectedISO();
+ const UICommon::GameFile* iso = GetSelectedISO();
if (!iso)
return;
- wxFileName path = wxFileName::FileName(StrToWxStr(iso->GetFileName()));
+ wxFileName path = wxFileName::FileName(StrToWxStr(iso->GetFilePath()));
path.MakeAbsolute();
WxUtils::Explore(WxStrToStr(path.GetPath()));
}
void GameListCtrl::OnOpenSaveFolder(wxCommandEvent& WXUNUSED(event))
{
- const GameListItem* iso = GetSelectedISO();
+ const UICommon::GameFile* iso = GetSelectedISO();
if (!iso)
return;
std::string path = iso->GetWiiFSPath();
@@ -1278,7 +1101,7 @@ void GameListCtrl::OnOpenSaveFolder(wxCommandEvent& WXUNUSED(event))
void GameListCtrl::OnExportSave(wxCommandEvent& WXUNUSED(event))
{
- const GameListItem* iso = GetSelectedISO();
+ const UICommon::GameFile* iso = GetSelectedISO();
if (iso)
CWiiSaveCrypted::ExportWiiSave(iso->GetTitleID());
}
@@ -1286,14 +1109,14 @@ void GameListCtrl::OnExportSave(wxCommandEvent& WXUNUSED(event))
// Save this file as the default file
void GameListCtrl::OnSetDefaultISO(wxCommandEvent& event)
{
- const GameListItem* iso = GetSelectedISO();
+ const UICommon::GameFile* iso = GetSelectedISO();
if (!iso)
return;
if (event.IsChecked())
{
// Write the new default value and save it the ini file
- SConfig::GetInstance().m_strDefaultISO = iso->GetFileName();
+ SConfig::GetInstance().m_strDefaultISO = iso->GetFilePath();
SConfig::GetInstance().SaveSettings();
}
else
@@ -1313,15 +1136,15 @@ void GameListCtrl::OnDeleteISO(wxCommandEvent& WXUNUSED(event))
if (wxMessageBox(message, _("Warning"), wxYES_NO | wxICON_EXCLAMATION) == wxYES)
{
- for (const GameListItem* iso : GetAllSelectedISOs())
- File::Delete(iso->GetFileName());
+ for (const UICommon::GameFile* iso : GetAllSelectedISOs())
+ File::Delete(iso->GetFilePath());
m_scan_trigger.Set();
}
}
void GameListCtrl::OnProperties(wxCommandEvent& WXUNUSED(event))
{
- const GameListItem* iso = GetSelectedISO();
+ const UICommon::GameFile* iso = GetSelectedISO();
if (!iso)
return;
@@ -1331,7 +1154,7 @@ void GameListCtrl::OnProperties(wxCommandEvent& WXUNUSED(event))
void GameListCtrl::OnWiki(wxCommandEvent& WXUNUSED(event))
{
- const GameListItem* iso = GetSelectedISO();
+ const UICommon::GameFile* iso = GetSelectedISO();
if (!iso)
return;
@@ -1342,7 +1165,7 @@ void GameListCtrl::OnWiki(wxCommandEvent& WXUNUSED(event))
void GameListCtrl::OnNetPlayHost(wxCommandEvent& WXUNUSED(event))
{
- const GameListItem* iso = GetSelectedISO();
+ const UICommon::GameFile* iso = GetSelectedISO();
if (!iso)
return;
@@ -1380,9 +1203,9 @@ void GameListCtrl::OnMultiDecompressISO(wxCommandEvent& /*event*/)
void GameListCtrl::CompressSelection(bool _compress)
{
- std::vector<const GameListItem*> items_to_compress;
+ std::vector<const UICommon::GameFile*> items_to_compress;
bool wii_compression_warning_accepted = false;
- for (const GameListItem* iso : GetAllSelectedISOs())
+ for (const UICommon::GameFile* iso : GetAllSelectedISOs())
{
// Don't include items that we can't do anything with
if (iso->GetPlatform() != DiscIO::Platform::GAMECUBE_DISC &&
@@ -1425,12 +1248,12 @@ void GameListCtrl::CompressSelection(bool _compress)
CompressionProgress progress(0, items_to_compress.size(), "", &progressDialog);
- for (const GameListItem* iso : items_to_compress)
+ for (const UICommon::GameFile* iso : items_to_compress)
{
if (iso->GetBlobType() != DiscIO::BlobType::GCZ && _compress)
{
std::string FileName;
- SplitPath(iso->GetFileName(), nullptr, &FileName, nullptr);
+ SplitPath(iso->GetFilePath(), nullptr, &FileName, nullptr);
progress.current_filename = FileName;
FileName.append(".gcz");
@@ -1445,14 +1268,14 @@ void GameListCtrl::CompressSelection(bool _compress)
continue;
all_good &=
- DiscIO::CompressFileToBlob(iso->GetFileName(), OutputFileName,
+ DiscIO::CompressFileToBlob(iso->GetFilePath(), OutputFileName,
(iso->GetPlatform() == DiscIO::Platform::WII_DISC) ? 1 : 0,
16384, &MultiCompressCB, &progress);
}
else if (iso->GetBlobType() == DiscIO::BlobType::GCZ && !_compress)
{
std::string FileName;
- SplitPath(iso->GetFileName(), nullptr, &FileName, nullptr);
+ SplitPath(iso->GetFilePath(), nullptr, &FileName, nullptr);
progress.current_filename = FileName;
if (iso->GetPlatform() == DiscIO::Platform::WII_DISC)
FileName.append(".iso");
@@ -1469,7 +1292,7 @@ void GameListCtrl::CompressSelection(bool _compress)
_("Confirm File Overwrite"), wxYES_NO) == wxNO)
continue;
- all_good &= DiscIO::DecompressBlobToFile(iso->GetFileName().c_str(), OutputFileName.c_str(),
+ all_good &= DiscIO::DecompressBlobToFile(iso->GetFilePath().c_str(), OutputFileName.c_str(),
&MultiCompressCB, &progress);
}
@@ -1490,7 +1313,7 @@ bool GameListCtrl::CompressCB(const std::string& text, float percent, void* arg)
void GameListCtrl::OnCompressISO(wxCommandEvent& WXUNUSED(event))
{
- const GameListItem* iso = GetSelectedISO();
+ const UICommon::GameFile* iso = GetSelectedISO();
if (!iso)
return;
@@ -1498,7 +1321,7 @@ void GameListCtrl::OnCompressISO(wxCommandEvent& WXUNUSED(event))
wxString path;
std::string FileName, FilePath, FileExtension;
- SplitPath(iso->GetFileName(), &FilePath, &FileName, &FileExtension);
+ SplitPath(iso->GetFilePath(), &FilePath, &FileName, &FileExtension);
do
{
@@ -1543,10 +1366,10 @@ void GameListCtrl::OnCompressISO(wxCommandEvent& WXUNUSED(event))
if (is_compressed)
all_good =
- DiscIO::DecompressBlobToFile(iso->GetFileName(), WxStrToStr(path), &CompressCB, &dialog);
+ DiscIO::DecompressBlobToFile(iso->GetFilePath(), WxStrToStr(path), &CompressCB, &dialog);
else
all_good = DiscIO::CompressFileToBlob(
- iso->GetFileName(), WxStrToStr(path),
+ iso->GetFilePath(), WxStrToStr(path),
(iso->GetPlatform() == DiscIO::Platform::WII_DISC) ? 1 : 0, 16384, &CompressCB, &dialog);
}
@@ -1558,10 +1381,10 @@ void GameListCtrl::OnCompressISO(wxCommandEvent& WXUNUSED(event))
void GameListCtrl::OnChangeDisc(wxCommandEvent& WXUNUSED(event))
{
- const GameListItem* iso = GetSelectedISO();
+ const UICommon::GameFile* iso = GetSelectedISO();
if (!iso || !Core::IsRunning())
return;
- Core::RunAsCPUThread([&iso] { DVDInterface::ChangeDisc(WxStrToStr(iso->GetFileName())); });
+ Core::RunAsCPUThread([&iso] { DVDInterface::ChangeDisc(WxStrToStr(iso->GetFilePath())); });
}
void GameListCtrl::OnSize(wxSizeEvent& event)
diff --git a/Source/Core/DolphinWX/GameListCtrl.h b/Source/Core/DolphinWX/GameListCtrl.h
index 0f39eb3b6d..6ada590d0e 100644
--- a/Source/Core/DolphinWX/GameListCtrl.h
+++ b/Source/Core/DolphinWX/GameListCtrl.h
@@ -16,7 +16,12 @@
#include "Common/ChunkFile.h"
#include "Common/Event.h"
#include "Common/Flag.h"
-#include "DolphinWX/ISOFile.h"
+#include "UICommon/GameFileCache.h"
+
+namespace UICommon
+{
+class GameFile;
+}
class wxEmuStateTip : public wxTipWindow
{
@@ -46,8 +51,8 @@ public:
~GameListCtrl();
void BrowseForDirectory();
- const GameListItem* GetISO(size_t index) const;
- const GameListItem* GetSelectedISO() const;
+ const UICommon::GameFile* GetISO(size_t index) const;
+ const UICommon::GameFile* GetSelectedISO() const;
static bool IsHidingItems();
@@ -80,9 +85,7 @@ private:
void SetColors();
void RefreshList();
void RescanList();
- void DoState(PointerWrap* p, u32 size = 0);
- bool SyncCacheFile(bool write);
- std::vector<const GameListItem*> GetAllSelectedISOs() const;
+ std::vector<const UICommon::GameFile*> GetAllSelectedISOs() const;
// events
void OnRefreshGameList(wxCommandEvent& event);
@@ -124,9 +127,9 @@ private:
std::vector<int> emu_state;
} m_image_indexes;
- // Actual backing GameListItems are maintained in a background thread and cached to file
- std::vector<std::shared_ptr<GameListItem>> m_cached_files;
- // Locks the list, not the contents
+ // Actual backing GameFiles are maintained in a background thread and cached to file
+ UICommon::GameFileCache m_cache;
+ // Locks the cache object, not the shared_ptr<GameFile>s obtained from it
std::mutex m_cache_mutex;
Core::TitleDatabase m_title_database;
std::mutex m_title_database_mutex;
@@ -134,7 +137,7 @@ private:
Common::Event m_scan_trigger;
Common::Flag m_scan_exiting;
// UI thread's view into the cache
- std::vector<std::shared_ptr<GameListItem>> m_shown_files;
+ std::vector<std::shared_ptr<const UICommon::GameFile>> m_shown_files;
int m_last_column;
int m_last_sort;
diff --git a/Source/Core/DolphinWX/ISOFile.cpp b/Source/Core/DolphinWX/ISOFile.cpp
deleted file mode 100644
index 65e5201dd6..0000000000
--- a/Source/Core/DolphinWX/ISOFile.cpp
+++ /dev/null
@@ -1,370 +0,0 @@
-// Copyright 2008 Dolphin Emulator Project
-// Licensed under GPLv2+
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include <cinttypes>
-#include <cstdio>
-#include <cstring>
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-#include <wx/app.h>
-#include <wx/bitmap.h>
-#include <wx/filefn.h>
-#include <wx/image.h>
-#include <wx/toplevel.h>
-
-#include "Common/ChunkFile.h"
-#include "Common/CommonPaths.h"
-#include "Common/CommonTypes.h"
-#include "Common/FileUtil.h"
-#include "Common/Hash.h"
-#include "Common/IniFile.h"
-#include "Common/NandPaths.h"
-#include "Common/StringUtil.h"
-
-#include "Core/Boot/Boot.h"
-#include "Core/ConfigManager.h"
-#include "Core/IOS/ES/Formats.h"
-#include "Core/TitleDatabase.h"
-
-#include "DiscIO/Blob.h"
-#include "DiscIO/Enums.h"
-#include "DiscIO/Volume.h"
-#include "DiscIO/WiiSaveBanner.h"
-
-#include "DolphinWX/ISOFile.h"
-#include "DolphinWX/WxUtils.h"
-
-static std::string GetLanguageString(DiscIO::Language language,
- std::map<DiscIO::Language, std::string> strings)
-{
- auto end = strings.end();
- auto it = strings.find(language);
- if (it != end)
- return it->second;
-
- // English tends to be a good fallback when the requested language isn't available
- if (language != DiscIO::Language::LANGUAGE_ENGLISH)
- {
- it = strings.find(DiscIO::Language::LANGUAGE_ENGLISH);
- if (it != end)
- return it->second;
- }
-
- // If English isn't available either, just pick something
- if (!strings.empty())
- return strings.cbegin()->second;
-
- return "";
-}
-
-GameListItem::GameListItem(const std::string& filename)
- : m_file_name(filename), m_region(DiscIO::Region::UNKNOWN_REGION),
- m_country(DiscIO::Country::COUNTRY_UNKNOWN)
-{
- {
- std::unique_ptr<DiscIO::Volume> volume(DiscIO::CreateVolumeFromFilename(m_file_name));
- if (volume != nullptr)
- {
- m_platform = volume->GetVolumeType();
-
- m_descriptions = volume->GetDescriptions();
- m_names = volume->GetLongNames();
- if (m_names.empty())
- m_names = volume->GetShortNames();
- m_company = GetLanguageString(DiscIO::Language::LANGUAGE_ENGLISH, volume->GetLongMakers());
- if (m_company.empty())
- m_company = GetLanguageString(DiscIO::Language::LANGUAGE_ENGLISH, volume->GetShortMakers());
-
- m_region = volume->GetRegion();
- m_country = volume->GetCountry();
- m_blob_type = volume->GetBlobType();
- m_file_size = volume->GetRawSize();
- m_volume_size = volume->GetSize();
-
- m_game_id = volume->GetGameID();
- m_title_id = volume->GetTitleID().value_or(0);
- m_disc_number = volume->GetDiscNumber().value_or(0);
- m_revision = volume->GetRevision().value_or(0);
-
- auto& banner = m_volume_banner;
- std::vector<u32> buffer = volume->GetBanner(&banner.width, &banner.height);
- ReadVolumeBanner(&banner.buffer, buffer, banner.width, banner.height);
-
- m_valid = true;
- }
- }
-
- if (m_company.empty() && m_game_id.size() >= 6)
- m_company = DiscIO::GetCompanyFromID(m_game_id.substr(4, 2));
-
- if (!IsValid() && IsElfOrDol())
- {
- m_valid = true;
- m_file_size = File::GetSize(m_file_name);
- m_platform = DiscIO::Platform::ELF_DOL;
- m_blob_type = DiscIO::BlobType::DIRECTORY;
-
- std::string path, name;
- SplitPath(m_file_name, &path, &name, nullptr);
-
- // A bit like the Homebrew Channel icon, except there can be multiple files
- // in a folder with their own icons. Useful for those who don't want to have
- // a Homebrew Channel-style folder structure.
- if (SetWxBannerFromPNGFile(path + name + ".png"))
- return;
-
- // Homebrew Channel icon. The most typical icon format for DOLs and ELFs.
- if (SetWxBannerFromPNGFile(path + "icon.png"))
- return;
- }
- else
- {
- // Volume banner. Typical for everything that isn't a DOL or ELF.
- SetWxBannerFromRaw(m_volume_banner);
- }
-}
-
-bool GameListItem::IsValid() const
-{
- if (!m_valid)
- return false;
-
- if (m_platform == DiscIO::Platform::WII_WAD && !IOS::ES::IsChannel(m_title_id))
- return false;
-
- return true;
-}
-
-bool GameListItem::CustomNameChanged(const Core::TitleDatabase& title_database)
-{
- const auto type = m_platform == DiscIO::Platform::WII_WAD ?
- Core::TitleDatabase::TitleType::Channel :
- Core::TitleDatabase::TitleType::Other;
- m_pending.custom_name = title_database.GetTitleName(m_game_id, type);
- return m_custom_name != m_pending.custom_name;
-}
-
-void GameListItem::CustomNameCommit()
-{
- m_custom_name = std::move(m_pending.custom_name);
-}
-
-bool GameListItem::EmuStateChanged()
-{
- IniFile ini = SConfig::LoadGameIni(m_game_id, m_revision);
- ini.GetIfExists("EmuState", "EmulationStateId", &m_pending.emu_state.rating, 0);
- ini.GetIfExists("EmuState", "EmulationIssues", &m_pending.emu_state.issues, std::string());
- return m_emu_state != m_pending.emu_state;
-}
-
-void GameListItem::EmuStateCommit()
-{
- m_emu_state = std::move(m_pending.emu_state);
-}
-
-void GameListItem::EmuState::DoState(PointerWrap& p)
-{
- p.Do(rating);
- p.Do(issues);
-}
-
-void GameListItem::Banner::DoState(PointerWrap& p)
-{
- p.Do(buffer);
- p.Do(width);
- p.Do(height);
-}
-
-void GameListItem::DoState(PointerWrap& p)
-{
- p.Do(m_valid);
- p.Do(m_file_name);
- p.Do(m_file_size);
- p.Do(m_volume_size);
- p.Do(m_names);
- p.Do(m_descriptions);
- p.Do(m_company);
- p.Do(m_game_id);
- p.Do(m_title_id);
- p.Do(m_region);
- p.Do(m_country);
- p.Do(m_platform);
- p.Do(m_blob_type);
- p.Do(m_revision);
- p.Do(m_disc_number);
- m_volume_banner.DoState(p);
- m_emu_state.DoState(p);
- p.Do(m_custom_name);
- if (p.GetMode() == PointerWrap::MODE_READ)
- {
- SetWxBannerFromRaw(m_volume_banner);
- }
-}
-
-bool GameListItem::IsElfOrDol() const
-{
- if (m_file_name.size() < 4)
- return false;
-
- std::string name_end = m_file_name.substr(m_file_name.size() - 4);
- std::transform(name_end.begin(), name_end.end(), name_end.begin(), ::tolower);
- return name_end == ".elf" || name_end == ".dol";
-}
-
-void GameListItem::ReadVolumeBanner(std::vector<u8>* image, const std::vector<u32>& buffer,
- int width, int height)
-{
- image->resize(width * height * 3);
- for (int i = 0; i < width * height; i++)
- {
- (*image)[i * 3 + 0] = (buffer[i] & 0xFF0000) >> 16;
- (*image)[i * 3 + 1] = (buffer[i] & 0x00FF00) >> 8;
- (*image)[i * 3 + 2] = (buffer[i] & 0x0000FF) >> 0;
- }
-}
-
-bool GameListItem::SetWxBannerFromPNGFile(const std::string& path)
-{
- if (!File::Exists(path))
- return false;
-
- wxImage image(StrToWxStr(path), wxBITMAP_TYPE_PNG);
- if (!image.IsOk())
- return false;
-
- m_banner_wx = image;
- return true;
-}
-
-void GameListItem::SetWxBannerFromRaw(const Banner& banner)
-{
- if (banner.empty())
- return;
-
- // Need to make explicit copy as wxImage uses reference counting for copies combined with only
- // taking a pointer, not the content, when given a buffer to its constructor.
- m_banner_wx.Create(banner.width, banner.height, false);
- std::memcpy(m_banner_wx.GetData(), banner.buffer.data(), banner.buffer.size());
-}
-
-bool GameListItem::BannerChanged()
-{
- // Wii banners can only be read if there is a savefile,
- // so sometimes caches don't contain banners. Let's check
- // if a banner has become available after the cache was made.
-
- if (!m_volume_banner.empty())
- return false;
- if (!DiscIO::IsWii(m_platform))
- return false;
-
- auto& banner = m_pending.volume_banner;
- std::vector<u32> buffer =
- DiscIO::WiiSaveBanner(m_title_id).GetBanner(&banner.width, &banner.height);
- if (buffer.empty())
- return false;
-
- ReadVolumeBanner(&banner.buffer, buffer, banner.width, banner.height);
- // We only reach here if m_volume_banner was empty, so we don't need to explicitly
- // compare to see if they are different
- return true;
-}
-
-void GameListItem::BannerCommit()
-{
- m_volume_banner = std::move(m_pending.volume_banner);
- SetWxBannerFromRaw(m_volume_banner);
-}
-
-std::string GameListItem::GetDescription(DiscIO::Language language) const
-{
- return GetLanguageString(language, m_descriptions);
-}
-
-std::string GameListItem::GetDescription() const
-{
- const bool wii = DiscIO::IsWii(m_platform);
- return GetDescription(SConfig::GetInstance().GetCurrentLanguage(wii));
-}
-
-std::string GameListItem::GetName(DiscIO::Language language) const
-{
- return GetLanguageString(language, m_names);
-}
-
-std::string GameListItem::GetName() const
-{
- if (!m_custom_name.empty())
- return m_custom_name;
-
- const bool wii = DiscIO::IsWii(m_platform);
- std::string name = GetName(SConfig::GetInstance().GetCurrentLanguage(wii));
- if (!name.empty())
- return name;
-
- // No usable name, return filename (better than nothing)
- std::string ext;
- SplitPath(GetFileName(), nullptr, &name, &ext);
- return name + ext;
-}
-
-std::string GameListItem::GetUniqueIdentifier() const
-{
- const DiscIO::Language lang = DiscIO::Language::LANGUAGE_ENGLISH;
- std::vector<std::string> info;
- if (!GetGameID().empty())
- info.push_back(GetGameID());
- if (GetRevision() != 0)
- {
- std::string rev_str = "Revision ";
- info.push_back(rev_str + std::to_string((long long)GetRevision()));
- }
-
- std::string name(GetName(lang));
- if (name.empty())
- name = GetName();
-
- 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(wxString::Format("disc %i", disc_number))) == std::string::npos &&
- lower_name.find(std::string(wxString::Format("disc%i", disc_number))) == std::string::npos)
- {
- std::string disc_text = "Disc ";
- info.push_back(disc_text + std::to_string(disc_number));
- }
- if (info.empty())
- return name;
- std::ostringstream ss;
- std::copy(info.begin(), info.end() - 1, std::ostream_iterator<std::string>(ss, ", "));
- ss << info.back();
- return name + " (" + ss.str() + ")";
-}
-
-std::vector<DiscIO::Language> GameListItem::GetLanguages() const
-{
- std::vector<DiscIO::Language> languages;
- for (std::pair<DiscIO::Language, std::string> name : m_names)
- languages.push_back(name.first);
- return languages;
-}
-
-const std::string GameListItem::GetWiiFSPath() const
-{
- if (!DiscIO::IsWii(m_platform))
- return "";
-
- const std::string path = Common::GetTitleDataPath(m_title_id, Common::FROM_CONFIGURED_ROOT);
-
- if (path[0] == '.')
- return WxStrToStr(wxGetCwd()) + path.substr(strlen(ROOT_DIR));
-
- return path;
-}
diff --git a/Source/Core/DolphinWX/ISOFile.h b/Source/Core/DolphinWX/ISOFile.h
deleted file mode 100644
index aa9893eaf3..0000000000
--- a/Source/Core/DolphinWX/ISOFile.h
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright 2008 Dolphin Emulator Project
-// Licensed under GPLv2+
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "Common/Common.h"
-
-#include <wx/bitmap.h>
-#include <wx/image.h>
-
-namespace Core
-{
-class TitleDatabase;
-}
-
-namespace DiscIO
-{
-enum class BlobType;
-enum class Country;
-enum class Language;
-enum class Region;
-enum class Platform;
-}
-
-class PointerWrap;
-
-class GameListItem
-{
-public:
- GameListItem() = default;
- explicit GameListItem(const std::string& file_name);
- ~GameListItem() = default;
-
- bool IsValid() const;
- const std::string& GetFileName() const { return m_file_name; }
- std::string GetName(DiscIO::Language language) const;
- std::string GetName() const;
- std::string GetUniqueIdentifier() const;
- std::string GetDescription(DiscIO::Language language) const;
- std::string GetDescription() const;
- std::vector<DiscIO::Language> GetLanguages() const;
- std::string GetCompany() const { return m_company; }
- u16 GetRevision() const { return m_revision; }
- const std::string& GetGameID() const { return m_game_id; }
- u64 GetTitleID() const { return m_title_id; }
- const std::string GetWiiFSPath() const;
- DiscIO::Region GetRegion() const { return m_region; }
- DiscIO::Country GetCountry() const { return m_country; }
- DiscIO::Platform GetPlatform() const { return m_platform; }
- DiscIO::BlobType GetBlobType() const { return m_blob_type; }
- const std::string& GetIssues() const { return m_emu_state.issues; }
- int GetEmuState() const { return m_emu_state.rating; }
- u64 GetFileSize() const { return m_file_size; }
- u64 GetVolumeSize() const { return m_volume_size; }
- // 0 is the first disc, 1 is the second disc
- u8 GetDiscNumber() const { return m_disc_number; }
- // NOTE: Banner image is at the original resolution, use WxUtils::ScaleImageToBitmap
- // to display it
- const wxImage& GetBannerImage() const { return m_banner_wx; }
- void DoState(PointerWrap& p);
- bool BannerChanged();
- void BannerCommit();
- bool EmuStateChanged();
- void EmuStateCommit();
- bool CustomNameChanged(const Core::TitleDatabase& title_database);
- void CustomNameCommit();
-
-private:
- struct EmuState
- {
- int rating{};
- std::string issues{};
- bool operator!=(const EmuState& rhs) const
- {
- return rating != rhs.rating || issues != rhs.issues;
- }
- void DoState(PointerWrap& p);
- };
- struct Banner
- {
- std::vector<u8> buffer{};
- int width{};
- int height{};
- bool empty() const { return buffer.empty(); }
- void DoState(PointerWrap& p);
- };
-
- bool IsElfOrDol() const;
- void ReadVolumeBanner(std::vector<u8>* image, const std::vector<u32>& buffer, int width,
- int height);
- // Outputs to m_banner_wx
- bool SetWxBannerFromPNGFile(const std::string& path);
- // Outputs to m_banner_wx
- void SetWxBannerFromRaw(const Banner& banner);
-
- // IMPORTANT: Nearly all data members must be save/restored in DoState.
- // If anything is changed, make sure DoState handles it properly and
- // GameListCtrl::CACHE_REVISION is incremented.
-
- bool m_valid{};
- std::string m_file_name{};
-
- u64 m_file_size{};
- u64 m_volume_size{};
-
- std::map<DiscIO::Language, std::string> m_names{};
- std::map<DiscIO::Language, std::string> m_descriptions{};
- std::string m_company{};
- std::string m_game_id{};
- u64 m_title_id{};
-
- DiscIO::Region m_region{};
- DiscIO::Country m_country{};
- DiscIO::Platform m_platform{};
- DiscIO::BlobType m_blob_type{};
- u16 m_revision{};
- u8 m_disc_number{};
-
- Banner m_volume_banner{};
- EmuState m_emu_state{};
- // Overridden name from TitleDatabase
- std::string m_custom_name{};
-
- // wxImage is not handled in DoState
- wxImage m_banner_wx{};
-
- // The following data members allow GameListCtrl to construct new GameListItems in a threadsafe
- // way. They should not be handled in DoState.
- struct
- {
- EmuState emu_state;
- Banner volume_banner;
- std::string custom_name;
- } m_pending{};
-};
diff --git a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp
index 8b3d815af0..7107bd3c3c 100644
--- a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp
+++ b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp
@@ -28,7 +28,6 @@
#include "DiscIO/Enums.h"
#include "DiscIO/Filesystem.h"
#include "DiscIO/Volume.h"
-#include "DolphinWX/ISOFile.h"
#include "DolphinWX/WxUtils.h"
namespace
diff --git a/Source/Core/DolphinWX/ISOProperties/ISOProperties.cpp b/Source/Core/DolphinWX/ISOProperties/ISOProperties.cpp
index 4ee22bcb38..909d988365 100644
--- a/Source/Core/DolphinWX/ISOProperties/ISOProperties.cpp
+++ b/Source/Core/DolphinWX/ISOProperties/ISOProperties.cpp
@@ -54,12 +54,12 @@
#include "DolphinWX/DolphinSlider.h"
#include "DolphinWX/Frame.h"
#include "DolphinWX/Globals.h"
-#include "DolphinWX/ISOFile.h"
#include "DolphinWX/ISOProperties/FilesystemPanel.h"
#include "DolphinWX/ISOProperties/InfoPanel.h"
#include "DolphinWX/Main.h"
#include "DolphinWX/PatchAddEdit.h"
#include "DolphinWX/WxUtils.h"
+#include "UICommon/GameFile.h"
// A warning message displayed on the ARCodes and GeckoCodes pages when cheats are
// disabled globally to explain why turning cheats on does not work.
@@ -190,15 +190,15 @@ EVT_BUTTON(ID_ADDPATCH, CISOProperties::PatchButtonClicked)
EVT_BUTTON(ID_REMOVEPATCH, CISOProperties::PatchButtonClicked)
END_EVENT_TABLE()
-CISOProperties::CISOProperties(const GameListItem& game_list_item, wxWindow* parent, wxWindowID id,
- const wxString& title, const wxPoint& position, const wxSize& size,
- long style)
+CISOProperties::CISOProperties(const UICommon::GameFile& game_list_item, wxWindow* parent,
+ wxWindowID id, const wxString& title, const wxPoint& position,
+ const wxSize& size, long style)
: wxDialog(parent, id, title, position, size, style), m_open_gamelist_item(game_list_item)
{
Bind(DOLPHIN_EVT_CHANGE_ISO_PROPERTIES_TITLE, &CISOProperties::OnChangeTitle, this);
// Load ISO data
- m_open_iso = DiscIO::CreateVolumeFromFilename(m_open_gamelist_item.GetFileName());
+ m_open_iso = DiscIO::CreateVolumeFromFilename(m_open_gamelist_item.GetFilePath());
m_game_id = m_open_iso->GetGameID();
diff --git a/Source/Core/DolphinWX/ISOProperties/ISOProperties.h b/Source/Core/DolphinWX/ISOProperties/ISOProperties.h
index 92d268aa5d..f8be3bc5be 100644
--- a/Source/Core/DolphinWX/ISOProperties/ISOProperties.h
+++ b/Source/Core/DolphinWX/ISOProperties/ISOProperties.h
@@ -15,8 +15,8 @@
#include <wx/treebase.h>
#include "Common/IniFile.h"
-#include "DolphinWX/ISOFile.h"
#include "DolphinWX/PatchAddEdit.h"
+#include "UICommon/GameFile.h"
class ActionReplayCodesPanel;
class CheatWarningMessage;
@@ -52,9 +52,9 @@ wxDECLARE_EVENT(DOLPHIN_EVT_CHANGE_ISO_PROPERTIES_TITLE, wxCommandEvent);
class CISOProperties : public wxDialog
{
public:
- CISOProperties(const GameListItem& game_list_item, wxWindow* parent, wxWindowID id = wxID_ANY,
- const wxString& title = _("Properties"), const wxPoint& pos = wxDefaultPosition,
- const wxSize& size = wxDefaultSize,
+ CISOProperties(const UICommon::GameFile& game_list_item, wxWindow* parent,
+ wxWindowID id = wxID_ANY, const wxString& title = _("Properties"),
+ const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
long style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
virtual ~CISOProperties();
@@ -141,7 +141,7 @@ private:
void OnCheatCodeToggled(wxCommandEvent& event);
void OnChangeTitle(wxCommandEvent& event);
- const GameListItem m_open_gamelist_item;
+ const UICommon::GameFile m_open_gamelist_item;
IniFile m_gameini_default;
IniFile m_gameini_local;
diff --git a/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp b/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp
index 45a6121813..ffac4846ac 100644
--- a/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp
+++ b/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp
@@ -32,9 +32,9 @@
#include "Core/IOS/ES/Formats.h"
#include "DiscIO/Enums.h"
#include "DiscIO/Volume.h"
-#include "DolphinWX/ISOFile.h"
#include "DolphinWX/ISOProperties/ISOProperties.h"
#include "DolphinWX/WxUtils.h"
+#include "UICommon/GameFile.h"
namespace
{
@@ -142,7 +142,7 @@ int FindPreferredLanguageIndex(DiscIO::Language preferred_language,
}
} // Anonymous namespace
-InfoPanel::InfoPanel(wxWindow* parent, wxWindowID id, const GameListItem& item,
+InfoPanel::InfoPanel(wxWindow* parent, wxWindowID id, const UICommon::GameFile& item,
const std::unique_ptr<DiscIO::Volume>& opened_iso)
: wxPanel{parent, id}, m_game_list_item{item}, m_opened_iso{opened_iso}
{
@@ -209,8 +209,8 @@ void InfoPanel::LoadBannerDetails()
void InfoPanel::LoadBannerImage()
{
- const auto& banner_image = m_game_list_item.GetBannerImage();
- const auto banner_min_size = m_banner->GetMinSize();
+ const wxImage banner_image = WxUtils::ToWxImage(m_game_list_item.GetBannerImage());
+ const wxSize banner_min_size = m_banner->GetMinSize();
if (banner_image.IsOk())
{
@@ -337,7 +337,7 @@ void InfoPanel::OnComputeMD5(wxCommandEvent& WXUNUSED(event))
wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME |
wxPD_REMAINING_TIME | wxPD_SMOOTH);
- const auto result = MD5::MD5Sum(m_game_list_item.GetFileName(), [&progress_dialog](int progress) {
+ const auto result = MD5::MD5Sum(m_game_list_item.GetFilePath(), [&progress_dialog](int progress) {
return progress_dialog.Update(progress);
});
@@ -367,7 +367,7 @@ void InfoPanel::OnSaveBannerImage(wxCommandEvent& WXUNUSED(event))
if (dialog.ShowModal() == wxID_OK)
{
- m_game_list_item.GetBannerImage().SaveFile(dialog.GetPath());
+ WxUtils::ToWxImage(m_game_list_item.GetBannerImage()).SaveFile(dialog.GetPath());
}
Raise();
@@ -375,16 +375,16 @@ void InfoPanel::OnSaveBannerImage(wxCommandEvent& WXUNUSED(event))
void InfoPanel::ChangeBannerDetails(DiscIO::Language language)
{
- const auto name = StrToWxStr(m_game_list_item.GetName(language));
+ const auto name = StrToWxStr(m_game_list_item.GetLongName(language));
const auto comment = StrToWxStr(m_game_list_item.GetDescription(language));
- const auto maker = StrToWxStr(m_game_list_item.GetCompany());
+ const auto maker = StrToWxStr(m_game_list_item.GetLongMaker(language));
m_name->SetValue(name);
m_comment->SetValue(comment);
m_maker->SetValue(maker);
std::string path, filename, extension;
- SplitPath(m_game_list_item.GetFileName(), &path, &filename, &extension);
+ SplitPath(m_game_list_item.GetFilePath(), &path, &filename, &extension);
// Real disk drives don't have filenames on Windows
if (filename.empty() && extension.empty())
diff --git a/Source/Core/DolphinWX/ISOProperties/InfoPanel.h b/Source/Core/DolphinWX/ISOProperties/InfoPanel.h
index 827764bb8d..1ec8c31db5 100644
--- a/Source/Core/DolphinWX/ISOProperties/InfoPanel.h
+++ b/Source/Core/DolphinWX/ISOProperties/InfoPanel.h
@@ -7,7 +7,6 @@
#include <memory>
#include <wx/panel.h>
-class GameListItem;
class wxButton;
class wxChoice;
class wxStaticBitmap;
@@ -16,14 +15,19 @@ class wxTextCtrl;
namespace DiscIO
{
-class Volume;
enum class Language;
+class Volume;
+}
+
+namespace UICommon
+{
+class GameFile;
}
class InfoPanel final : public wxPanel
{
public:
- InfoPanel(wxWindow* parent, wxWindowID id, const GameListItem& item,
+ InfoPanel(wxWindow* parent, wxWindowID id, const UICommon::GameFile& item,
const std::unique_ptr<DiscIO::Volume>& opened_iso);
private:
@@ -37,6 +41,7 @@ private:
void LoadGUIData();
void LoadISODetails();
void LoadBannerDetails();
+ wxImage ConvertBannerImage();
void LoadBannerImage();
wxStaticBoxSizer* CreateISODetailsSizer();
@@ -52,7 +57,7 @@ private:
void EmitTitleChangeEvent(const wxString& new_title);
- const GameListItem& m_game_list_item;
+ const UICommon::GameFile& m_game_list_item;
const std::unique_ptr<DiscIO::Volume>& m_opened_iso;
wxTextCtrl* m_internal_name;
diff --git a/Source/Core/DolphinWX/NetPlay/NetWindow.cpp b/Source/Core/DolphinWX/NetPlay/NetWindow.cpp
index ec6b6bba0b..cd4bba69a6 100644
--- a/Source/Core/DolphinWX/NetPlay/NetWindow.cpp
+++ b/Source/Core/DolphinWX/NetPlay/NetWindow.cpp
@@ -47,11 +47,12 @@
#include "DolphinWX/Frame.h"
#include "DolphinWX/GameListCtrl.h"
-#include "DolphinWX/ISOFile.h"
#include "DolphinWX/NetPlay/ChangeGameDialog.h"
+#include "DolphinWX/NetPlay/MD5Dialog.h"
#include "DolphinWX/NetPlay/PadMapDialog.h"
#include "DolphinWX/WxUtils.h"
-#include "MD5Dialog.h"
+
+#include "UICommon/GameFile.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/VideoConfig.h"
@@ -335,7 +336,7 @@ std::string NetPlayDialog::FindGame(const std::string& target_game)
// find path for selected game, sloppy..
for (u32 i = 0; auto game = m_game_list->GetISO(i); ++i)
if (target_game == game->GetUniqueIdentifier())
- return game->GetFileName();
+ return game->GetFilePath();
return "";
}
diff --git a/Source/Core/DolphinWX/WxUtils.cpp b/Source/Core/DolphinWX/WxUtils.cpp
index 703518da3f..f6d160fe01 100644
--- a/Source/Core/DolphinWX/WxUtils.cpp
+++ b/Source/Core/DolphinWX/WxUtils.cpp
@@ -28,6 +28,8 @@
#include "DolphinWX/WxUtils.h"
+#include "UICommon/GameFile.h"
+
#ifdef _WIN32
#include <Windows.h>
#endif
@@ -285,6 +287,27 @@ wxSize GetTextWidgetMinSize(const wxSpinCtrl* spinner)
return size;
}
+wxImage ToWxImage(const UICommon::GameBanner& banner)
+{
+ return ToWxImage(banner.buffer, banner.width, banner.height);
+}
+
+wxImage ToWxImage(const std::vector<u32>& buffer, int width, int height)
+{
+ wxImage image(width, height, false);
+ if (buffer.empty())
+ return image;
+
+ unsigned char* data = image.GetData();
+ for (int i = 0; i < width * height; i++)
+ {
+ data[i * 3 + 0] = (buffer[i] & 0xFF0000) >> 16;
+ data[i * 3 + 1] = (buffer[i] & 0x00FF00) >> 8;
+ data[i * 3 + 2] = (buffer[i] & 0x0000FF) >> 0;
+ }
+ return image;
+}
+
static wxImage LoadScaledImage(const std::string& file_path, const wxWindow* context,
const wxSize& output_size, const wxRect& usable_rect, LSIFlags flags,
const wxColour& fill_color)
diff --git a/Source/Core/DolphinWX/WxUtils.h b/Source/Core/DolphinWX/WxUtils.h
index 7dc5051751..1c8deff38b 100644
--- a/Source/Core/DolphinWX/WxUtils.h
+++ b/Source/Core/DolphinWX/WxUtils.h
@@ -5,10 +5,14 @@
#pragma once
#include <string>
+#include <vector>
+
#include <wx/colour.h>
#include <wx/gdicmn.h>
#include <wx/string.h>
+#include "Common/CommonTypes.h"
+
class wxControl;
class wxBitmap;
class wxImage;
@@ -18,6 +22,11 @@ class wxToolBar;
class wxTopLevelWindow;
class wxWindow;
+namespace UICommon
+{
+struct GameBanner;
+}
+
namespace WxUtils
{
// Launch a file according to its mime type
@@ -95,6 +104,9 @@ constexpr LSIFlags operator&(LSIFlags left, LSIFlags right)
return static_cast<LSIFlags>(static_cast<unsigned int>(left) & right);
}
+wxImage ToWxImage(const UICommon::GameBanner& banner);
+wxImage ToWxImage(const std::vector<u32>& buffer, int width, int height);
+
// Swiss army knife loader function for preparing a scaled resource image file.
// Only the path and context are mandatory, other parameters can be ignored.
// NOTE: All size parameters are in window pixels, not DIPs or framebuffer pixels.
diff --git a/Source/Core/UICommon/CMakeLists.txt b/Source/Core/UICommon/CMakeLists.txt
index 838dfb8775..bb5439badd 100644
--- a/Source/Core/UICommon/CMakeLists.txt
+++ b/Source/Core/UICommon/CMakeLists.txt
@@ -1,6 +1,8 @@
set(SRCS
CommandLineParse.cpp
Disassembler.cpp
+ GameFile.cpp
+ GameFileCache.cpp
UICommon.cpp
USBUtils.cpp
VideoUtils.cpp
diff --git a/Source/Core/UICommon/GameFile.cpp b/Source/Core/UICommon/GameFile.cpp
new file mode 100644
index 0000000000..1a6b0717c2
--- /dev/null
+++ b/Source/Core/UICommon/GameFile.cpp
@@ -0,0 +1,312 @@
+// Copyright 2008 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "UICommon/GameFile.h"
+
+#include <algorithm>
+#include <cinttypes>
+#include <cstdio>
+#include <cstring>
+#include <iterator>
+#include <map>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "Common/ChunkFile.h"
+#include "Common/CommonPaths.h"
+#include "Common/CommonTypes.h"
+#include "Common/FileUtil.h"
+#include "Common/Hash.h"
+#include "Common/IniFile.h"
+#include "Common/NandPaths.h"
+#include "Common/StringUtil.h"
+
+#include "Core/Boot/Boot.h"
+#include "Core/ConfigManager.h"
+#include "Core/IOS/ES/Formats.h"
+#include "Core/TitleDatabase.h"
+
+#include "DiscIO/Blob.h"
+#include "DiscIO/Enums.h"
+#include "DiscIO/Volume.h"
+#include "DiscIO/WiiSaveBanner.h"
+
+namespace UICommon
+{
+static const std::string EMPTY_STRING;
+
+const std::string& GameFile::Lookup(DiscIO::Language language,
+ const std::map<DiscIO::Language, std::string>& strings)
+{
+ auto end = strings.end();
+ auto it = strings.find(language);
+ if (it != end)
+ return it->second;
+
+ // English tends to be a good fallback when the requested language isn't available
+ if (language != DiscIO::Language::LANGUAGE_ENGLISH)
+ {
+ it = strings.find(DiscIO::Language::LANGUAGE_ENGLISH);
+ if (it != end)
+ return it->second;
+ }
+
+ // If English isn't available either, just pick something
+ if (!strings.empty())
+ return strings.cbegin()->second;
+
+ return EMPTY_STRING;
+}
+
+const std::string&
+GameFile::LookupUsingConfigLanguage(const std::map<DiscIO::Language, std::string>& strings) const
+{
+ const bool wii = DiscIO::IsWii(m_platform);
+ return Lookup(SConfig::GetInstance().GetCurrentLanguage(wii), strings);
+}
+
+GameFile::GameFile(const std::string& path)
+ : m_file_path(path), m_region(DiscIO::Region::UNKNOWN_REGION),
+ m_country(DiscIO::Country::COUNTRY_UNKNOWN)
+{
+ {
+ std::string name, extension;
+ SplitPath(m_file_path, nullptr, &name, &extension);
+ m_file_name = name + extension;
+
+ std::unique_ptr<DiscIO::Volume> volume(DiscIO::CreateVolumeFromFilename(m_file_path));
+ if (volume != nullptr)
+ {
+ m_platform = volume->GetVolumeType();
+
+ m_short_names = volume->GetShortNames();
+ m_long_names = volume->GetLongNames();
+ m_short_makers = volume->GetShortMakers();
+ m_long_makers = volume->GetLongMakers();
+ m_descriptions = volume->GetDescriptions();
+
+ m_region = volume->GetRegion();
+ m_country = volume->GetCountry();
+ m_blob_type = volume->GetBlobType();
+ m_file_size = volume->GetRawSize();
+ m_volume_size = volume->GetSize();
+
+ m_internal_name = volume->GetInternalName();
+ m_game_id = volume->GetGameID();
+ m_title_id = volume->GetTitleID().value_or(0);
+ m_maker_id = volume->GetMakerID();
+ m_revision = volume->GetRevision().value_or(0);
+ m_disc_number = volume->GetDiscNumber().value_or(0);
+ m_apploader_date = volume->GetApploaderDate();
+
+ m_volume_banner.buffer = volume->GetBanner(&m_volume_banner.width, &m_volume_banner.height);
+
+ m_valid = true;
+ }
+ }
+
+ if (!IsValid() && IsElfOrDol())
+ {
+ m_valid = true;
+ m_file_size = File::GetSize(m_file_path);
+ m_platform = DiscIO::Platform::ELF_DOL;
+ m_blob_type = DiscIO::BlobType::DIRECTORY;
+ }
+}
+
+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;
+}
+
+bool GameFile::CustomNameChanged(const Core::TitleDatabase& title_database)
+{
+ const auto type = m_platform == DiscIO::Platform::WII_WAD ?
+ Core::TitleDatabase::TitleType::Channel :
+ Core::TitleDatabase::TitleType::Other;
+ m_pending.custom_name = title_database.GetTitleName(m_game_id, type);
+ return m_custom_name != m_pending.custom_name;
+}
+
+void GameFile::CustomNameCommit()
+{
+ m_custom_name = std::move(m_pending.custom_name);
+}
+
+bool GameFile::EmuStateChanged()
+{
+ IniFile ini = SConfig::LoadGameIni(m_game_id, m_revision);
+ ini.GetIfExists("EmuState", "EmulationStateId", &m_pending.emu_state.rating, 0);
+ ini.GetIfExists("EmuState", "EmulationIssues", &m_pending.emu_state.issues, std::string());
+ return m_emu_state != m_pending.emu_state;
+}
+
+void GameFile::EmuStateCommit()
+{
+ m_emu_state = std::move(m_pending.emu_state);
+}
+
+void GameFile::EmuState::DoState(PointerWrap& p)
+{
+ p.Do(rating);
+ p.Do(issues);
+}
+
+void GameBanner::DoState(PointerWrap& p)
+{
+ p.Do(buffer);
+ p.Do(width);
+ p.Do(height);
+}
+
+void GameFile::DoState(PointerWrap& p)
+{
+ p.Do(m_valid);
+ p.Do(m_file_path);
+ p.Do(m_file_name);
+
+ p.Do(m_file_size);
+ p.Do(m_volume_size);
+
+ p.Do(m_short_names);
+ p.Do(m_long_names);
+ p.Do(m_short_makers);
+ p.Do(m_long_makers);
+ p.Do(m_descriptions);
+ p.Do(m_internal_name);
+ p.Do(m_game_id);
+ p.Do(m_title_id);
+ p.Do(m_maker_id);
+
+ p.Do(m_region);
+ p.Do(m_country);
+ p.Do(m_platform);
+ p.Do(m_blob_type);
+ p.Do(m_revision);
+ p.Do(m_disc_number);
+ p.Do(m_apploader_date);
+
+ m_volume_banner.DoState(p);
+ m_emu_state.DoState(p);
+ p.Do(m_custom_name);
+}
+
+bool GameFile::IsElfOrDol() const
+{
+ if (m_file_path.size() < 4)
+ return false;
+
+ std::string name_end = m_file_path.substr(m_file_path.size() - 4);
+ std::transform(name_end.begin(), name_end.end(), name_end.begin(), ::tolower);
+ return name_end == ".elf" || name_end == ".dol";
+}
+
+bool GameFile::BannerChanged()
+{
+ // Wii banners can only be read if there is a save file.
+ // In case the cache was created without a save file existing,
+ // let's try reading the save file again, because it might exist now.
+
+ if (!m_volume_banner.empty())
+ return false;
+ if (!DiscIO::IsWii(m_platform))
+ return false;
+
+ m_volume_banner.buffer =
+ DiscIO::WiiSaveBanner(m_title_id).GetBanner(&m_volume_banner.width, &m_volume_banner.height);
+ if (m_volume_banner.buffer.empty())
+ return false;
+
+ // We only reach here if m_volume_banner was empty, so we can always return true
+ // without needing any extra check to know whether the banners are different
+ return true;
+}
+
+void GameFile::BannerCommit()
+{
+ m_volume_banner = std::move(m_pending.volume_banner);
+}
+
+const std::string& GameFile::GetName(bool long_name) const
+{
+ if (!m_custom_name.empty())
+ return m_custom_name;
+
+ const std::string& name = long_name ? GetLongName() : GetShortName();
+ if (!name.empty())
+ return name;
+
+ // No usable name, return filename (better than nothing)
+ return m_file_name;
+}
+
+const std::string& GameFile::GetMaker(bool long_maker) const
+{
+ const std::string& maker = long_maker ? GetLongMaker() : GetShortMaker();
+ if (!maker.empty())
+ return maker;
+
+ if (m_game_id.size() >= 6)
+ return DiscIO::GetCompanyFromID(m_maker_id);
+
+ return EMPTY_STRING;
+}
+
+std::vector<DiscIO::Language> GameFile::GetLanguages() const
+{
+ std::vector<DiscIO::Language> languages;
+ // TODO: What if some languages don't have long names but have other strings?
+ for (std::pair<DiscIO::Language, std::string> name : m_long_names)
+ languages.push_back(name.first);
+ return languages;
+}
+
+std::string GameFile::GetUniqueIdentifier() const
+{
+ const DiscIO::Language lang = DiscIO::Language::LANGUAGE_ENGLISH;
+ std::vector<std::string> info;
+ if (!GetGameID().empty())
+ info.push_back(GetGameID());
+ if (GetRevision() != 0)
+ info.push_back("Revision " + std::to_string(GetRevision()));
+
+ std::string name(GetLongName(lang));
+ if (name.empty())
+ name = GetName();
+
+ 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(StringFromFormat("disc %i", disc_number)) == std::string::npos &&
+ lower_name.find(StringFromFormat("disc%i", disc_number)) == std::string::npos)
+ {
+ std::string disc_text = "Disc ";
+ info.push_back(disc_text + std::to_string(disc_number));
+ }
+ if (info.empty())
+ return name;
+ std::ostringstream ss;
+ std::copy(info.begin(), info.end() - 1, std::ostream_iterator<std::string>(ss, ", "));
+ ss << info.back();
+ return name + " (" + ss.str() + ")";
+}
+
+std::string GameFile::GetWiiFSPath() const
+{
+ _assert_(DiscIO::IsWii(m_platform));
+ return Common::GetTitleDataPath(m_title_id, Common::FROM_CONFIGURED_ROOT);
+}
+
+} // namespace UICommon
diff --git a/Source/Core/UICommon/GameFile.h b/Source/Core/UICommon/GameFile.h
new file mode 100644
index 0000000000..76a0be4fee
--- /dev/null
+++ b/Source/Core/UICommon/GameFile.h
@@ -0,0 +1,153 @@
+// Copyright 2008 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "Common/CommonTypes.h"
+
+class PointerWrap;
+
+namespace Core
+{
+class TitleDatabase;
+}
+
+namespace DiscIO
+{
+enum class BlobType;
+enum class Country;
+enum class Language;
+enum class Region;
+enum class Platform;
+}
+
+namespace UICommon
+{
+struct GameBanner
+{
+ std::vector<u32> buffer{};
+ int width{};
+ int height{};
+ bool empty() const { return buffer.empty(); }
+ void DoState(PointerWrap& p);
+};
+
+// This class caches the metadata of a DiscIO::Volume (or a DOL/ELF file).
+class GameFile final
+{
+public:
+ GameFile() = default;
+ explicit GameFile(const std::string& path);
+ ~GameFile() = default;
+
+ bool IsValid() const;
+ const std::string& GetFilePath() const { return m_file_path; }
+ const std::string& GetFileName() const { return m_file_name; }
+ const std::string& GetName(bool long_name = true) const;
+ const std::string& GetMaker(bool long_maker = true) const;
+ const std::string& GetShortName(DiscIO::Language l) const { return Lookup(l, m_short_names); }
+ const std::string& GetShortName() const { return LookupUsingConfigLanguage(m_short_names); }
+ const std::string& GetLongName(DiscIO::Language l) const { return Lookup(l, m_long_names); }
+ const std::string& GetLongName() const { return LookupUsingConfigLanguage(m_long_names); }
+ const std::string& GetShortMaker(DiscIO::Language l) const { return Lookup(l, m_short_makers); }
+ const std::string& GetShortMaker() const { return LookupUsingConfigLanguage(m_short_makers); }
+ const std::string& GetLongMaker(DiscIO::Language l) const { return Lookup(l, m_long_makers); }
+ const std::string& GetLongMaker() const { return LookupUsingConfigLanguage(m_long_makers); }
+ const std::string& GetDescription(DiscIO::Language l) const { return Lookup(l, m_descriptions); }
+ const std::string& GetDescription() const { return LookupUsingConfigLanguage(m_descriptions); }
+ std::vector<DiscIO::Language> GetLanguages() const;
+ const std::string& GetInternalName() const { return m_internal_name; }
+ const std::string& GetGameID() const { return m_game_id; }
+ u64 GetTitleID() const { return m_title_id; }
+ const std::string& GetMakerID() const { return m_maker_id; }
+ u16 GetRevision() const { return m_revision; }
+ // 0 is the first disc, 1 is the second disc
+ u8 GetDiscNumber() const { return m_disc_number; }
+ std::string GetUniqueIdentifier() const;
+ std::string GetWiiFSPath() const;
+ DiscIO::Region GetRegion() const { return m_region; }
+ DiscIO::Country GetCountry() const { return m_country; }
+ DiscIO::Platform GetPlatform() const { return m_platform; }
+ DiscIO::BlobType GetBlobType() const { return m_blob_type; }
+ const std::string& GetApploaderDate() const { return m_apploader_date; }
+ const std::string& GetIssues() const { return m_emu_state.issues; }
+ int GetEmuState() const { return m_emu_state.rating; }
+ u64 GetFileSize() const { return m_file_size; }
+ u64 GetVolumeSize() const { return m_volume_size; }
+ const GameBanner& GetBannerImage() const { return m_volume_banner; }
+ void DoState(PointerWrap& p);
+ bool BannerChanged();
+ void BannerCommit();
+ bool EmuStateChanged();
+ void EmuStateCommit();
+ bool CustomNameChanged(const Core::TitleDatabase& title_database);
+ void CustomNameCommit();
+
+private:
+ struct EmuState
+ {
+ int rating{};
+ std::string issues{};
+ bool operator!=(const EmuState& rhs) const
+ {
+ return rating != rhs.rating || issues != rhs.issues;
+ }
+ void DoState(PointerWrap& p);
+ };
+
+ static const std::string& Lookup(DiscIO::Language language,
+ const std::map<DiscIO::Language, std::string>& strings);
+ const std::string&
+ LookupUsingConfigLanguage(const std::map<DiscIO::Language, std::string>& strings) const;
+ bool IsElfOrDol() const;
+
+ // IMPORTANT: Nearly all data members must be save/restored in DoState.
+ // If anything is changed, make sure DoState handles it properly and
+ // CACHE_REVISION in GameFileCache.cpp is incremented.
+
+ bool m_valid{};
+ std::string m_file_path{};
+ std::string m_file_name{};
+
+ u64 m_file_size{};
+ u64 m_volume_size{};
+
+ std::map<DiscIO::Language, std::string> m_short_names{};
+ std::map<DiscIO::Language, std::string> m_long_names{};
+ std::map<DiscIO::Language, std::string> m_short_makers{};
+ std::map<DiscIO::Language, std::string> m_long_makers{};
+ std::map<DiscIO::Language, std::string> m_descriptions{};
+ std::string m_internal_name{};
+ std::string m_game_id{};
+ u64 m_title_id{};
+ std::string m_maker_id{};
+
+ DiscIO::Region m_region{};
+ DiscIO::Country m_country{};
+ DiscIO::Platform m_platform{};
+ DiscIO::BlobType m_blob_type{};
+ u16 m_revision{};
+ u8 m_disc_number{};
+ std::string m_apploader_date{};
+
+ GameBanner m_volume_banner{};
+ EmuState m_emu_state{};
+ // Overridden name from TitleDatabase
+ std::string m_custom_name{};
+
+ // The following data members allow GameFileCache to construct updated versions
+ // of GameFiles in a threadsafe way. They should not be handled in DoState.
+ struct
+ {
+ EmuState emu_state;
+ GameBanner volume_banner;
+ std::string custom_name;
+ } m_pending{};
+};
+
+} // namespace UICommon
diff --git a/Source/Core/UICommon/GameFileCache.cpp b/Source/Core/UICommon/GameFileCache.cpp
new file mode 100644
index 0000000000..a0e1253c18
--- /dev/null
+++ b/Source/Core/UICommon/GameFileCache.cpp
@@ -0,0 +1,235 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "UICommon/GameFileCache.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <functional>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "Common/ChunkFile.h"
+#include "Common/CommonTypes.h"
+#include "Common/File.h"
+#include "Common/FileSearch.h"
+#include "Common/FileUtil.h"
+
+#include "Core/TitleDatabase.h"
+
+#include "DiscIO/DirectoryBlob.h"
+
+#include "UICommon/GameFile.h"
+
+namespace UICommon
+{
+static constexpr u32 CACHE_REVISION = 7; // Last changed in PR 6281
+
+std::vector<std::string> FindAllGamePaths(const std::vector<std::string>& directories_to_scan,
+ bool recursive_scan)
+{
+ static const std::vector<std::string> search_extensions = {
+ ".gcm", ".tgc", ".iso", ".ciso", ".gcz", ".wbfs", ".wad", ".dol", ".elf"};
+
+ // TODO: We could process paths iteratively as they are found
+ return Common::DoFileSearch(directories_to_scan, search_extensions, recursive_scan);
+}
+
+void GameFileCache::ForEach(std::function<void(const std::shared_ptr<const GameFile>&)> f) const
+{
+ for (const std::shared_ptr<const GameFile>& item : m_cached_files)
+ f(item);
+}
+
+void GameFileCache::Clear()
+{
+ m_cached_files.clear();
+}
+
+std::shared_ptr<const GameFile> GameFileCache::AddOrGet(const std::string& path,
+ bool* cache_changed,
+ const Core::TitleDatabase& title_database)
+{
+ auto it = std::find_if(
+ m_cached_files.begin(), m_cached_files.end(),
+ [&path](const std::shared_ptr<GameFile>& file) { return file->GetFilePath() == path; });
+ const bool found = it != m_cached_files.cend();
+ if (!found)
+ m_cached_files.emplace_back(std::make_shared<GameFile>(path));
+ std::shared_ptr<GameFile>& result = found ? *it : m_cached_files.back();
+ if (UpdateAdditionalMetadata(&result, title_database) || !found)
+ *cache_changed = true;
+
+ return result;
+}
+
+bool GameFileCache::Update(const std::vector<std::string>& all_game_paths)
+{
+ // Copy game paths into a set, except ones that match DiscIO::ShouldHideFromGameList.
+ // TODO: Prevent DoFileSearch from looking inside /files/ directories of DirectoryBlobs at all?
+ // TODO: Make DoFileSearch support filter predicates so we don't have remove things afterwards?
+ std::unordered_set<std::string> game_paths;
+ game_paths.reserve(all_game_paths.size());
+ for (const std::string& path : all_game_paths)
+ {
+ if (!DiscIO::ShouldHideFromGameList(path))
+ game_paths.insert(path);
+ }
+
+ bool cache_changed = false;
+
+ // Delete paths that aren't in game_paths from m_cached_files,
+ // while simultaneously deleting paths that aren't in m_cached_files from game_paths.
+ // For the sake of speed, we don't care about maintaining the order of m_cached_files.
+ {
+ auto it = m_cached_files.begin();
+ auto end = m_cached_files.end();
+ while (it != end)
+ {
+ if (game_paths.erase((*it)->GetFilePath()))
+ {
+ ++it;
+ }
+ else
+ {
+ cache_changed = true;
+ --end;
+ *it = std::move(*end);
+ }
+ }
+ m_cached_files.erase(it, m_cached_files.end());
+ }
+
+ // Now that the previous loop has run, game_paths only contains paths that
+ // aren't in m_cached_files, so we simply add all of them to m_cached_files.
+ for (const auto& path : game_paths)
+ {
+ auto file = std::make_shared<GameFile>(path);
+ if (file->IsValid())
+ {
+ cache_changed = true;
+ m_cached_files.push_back(std::move(file));
+ }
+ }
+
+ return cache_changed;
+}
+
+bool GameFileCache::UpdateAdditionalMetadata(const Core::TitleDatabase& title_database)
+{
+ bool cache_changed = false;
+
+ for (auto& file : m_cached_files)
+ cache_changed |= UpdateAdditionalMetadata(&file, title_database);
+
+ return cache_changed;
+}
+
+bool GameFileCache::UpdateAdditionalMetadata(std::shared_ptr<GameFile>* game_file,
+ const Core::TitleDatabase& title_database)
+{
+ const bool emu_state_changed = (*game_file)->EmuStateChanged();
+ const bool banner_changed = (*game_file)->BannerChanged();
+ const bool custom_title_changed = (*game_file)->CustomNameChanged(title_database);
+ if (!emu_state_changed && !banner_changed && !custom_title_changed)
+ return false;
+
+ // If a cached file needs an update, apply the updates to a copy and delete the original.
+ // This makes the usage of cached files in other threads safe.
+
+ std::shared_ptr<GameFile> copy = std::make_shared<GameFile>(**game_file);
+ if (emu_state_changed)
+ copy->EmuStateCommit();
+ if (banner_changed)
+ copy->BannerCommit();
+ if (custom_title_changed)
+ copy->CustomNameCommit();
+ *game_file = std::move(copy);
+
+ return true;
+}
+
+bool GameFileCache::Load()
+{
+ return SyncCacheFile(false);
+}
+
+bool GameFileCache::Save()
+{
+ return SyncCacheFile(true);
+}
+
+bool GameFileCache::SyncCacheFile(bool save)
+{
+ std::string filename(File::GetUserPath(D_CACHE_IDX) + "gamelist.cache");
+ const char* open_mode = save ? "wb" : "rb";
+ File::IOFile f(filename, open_mode);
+ if (!f)
+ return false;
+ bool success = false;
+ if (save)
+ {
+ // Measure the size of the buffer.
+ u8* ptr = nullptr;
+ PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
+ DoState(&p);
+ const size_t buffer_size = reinterpret_cast<size_t>(ptr);
+
+ // Then actually do the write.
+ std::vector<u8> buffer(buffer_size);
+ ptr = &buffer[0];
+ p.SetMode(PointerWrap::MODE_WRITE);
+ DoState(&p, buffer_size);
+ if (f.WriteBytes(buffer.data(), buffer.size()))
+ success = true;
+ }
+ else
+ {
+ std::vector<u8> buffer(f.GetSize());
+ if (buffer.size() && f.ReadBytes(buffer.data(), buffer.size()))
+ {
+ u8* ptr = buffer.data();
+ PointerWrap p(&ptr, PointerWrap::MODE_READ);
+ DoState(&p, buffer.size());
+ if (p.GetMode() == PointerWrap::MODE_READ)
+ success = true;
+ }
+ }
+ if (!success)
+ {
+ // If some file operation failed, try to delete the probably-corrupted cache
+ f.Close();
+ File::Delete(filename);
+ }
+ return success;
+}
+
+void GameFileCache::DoState(PointerWrap* p, u64 size)
+{
+ struct
+ {
+ u32 revision;
+ u64 expected_size;
+ } header = {CACHE_REVISION, size};
+ p->Do(header);
+ if (p->GetMode() == PointerWrap::MODE_READ)
+ {
+ if (header.revision != CACHE_REVISION || header.expected_size != size)
+ {
+ p->SetMode(PointerWrap::MODE_MEASURE);
+ return;
+ }
+ }
+ p->DoEachElement(m_cached_files, [](PointerWrap& state, std::shared_ptr<GameFile>& elem) {
+ if (state.GetMode() == PointerWrap::MODE_READ)
+ elem = std::make_shared<GameFile>();
+ elem->DoState(state);
+ });
+}
+
+} // namespace DiscIO
diff --git a/Source/Core/UICommon/GameFileCache.h b/Source/Core/UICommon/GameFileCache.h
new file mode 100644
index 0000000000..acfdc3410f
--- /dev/null
+++ b/Source/Core/UICommon/GameFileCache.h
@@ -0,0 +1,58 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <functional>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include "Common/CommonTypes.h"
+
+class PointerWrap;
+
+namespace Core
+{
+class TitleDatabase;
+}
+
+namespace UICommon
+{
+class GameFile;
+
+std::vector<std::string> FindAllGamePaths(const std::vector<std::string>& directories_to_scan,
+ bool recursive_scan);
+
+class GameFileCache
+{
+public:
+ void ForEach(std::function<void(const std::shared_ptr<const GameFile>&)> f) const;
+
+ void Clear();
+
+ std::shared_ptr<const GameFile> AddOrGet(const std::string& path, bool* cache_changed,
+ const Core::TitleDatabase& title_database);
+
+ // These functions return true if the call modified the cache.
+ bool Update(const std::vector<std::string>& all_game_paths);
+ bool UpdateAdditionalMetadata(const Core::TitleDatabase& title_database);
+
+ bool Load();
+ bool Save();
+
+private:
+ bool UpdateAdditionalMetadata(std::shared_ptr<GameFile>* game_file,
+ const Core::TitleDatabase& title_database);
+
+ bool SyncCacheFile(bool save);
+ void DoState(PointerWrap* p, u64 size = 0);
+
+ std::vector<std::shared_ptr<GameFile>> m_cached_files;
+};
+
+} // namespace UICommon
diff --git a/Source/Core/UICommon/UICommon.cpp b/Source/Core/UICommon/UICommon.cpp
index 06d4db5850..eb060b27a3 100644
--- a/Source/Core/UICommon/UICommon.cpp
+++ b/Source/Core/UICommon/UICommon.cpp
@@ -2,16 +2,20 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
+#include <cmath>
#include <memory>
#ifdef _WIN32
#include <shlobj.h> // for SHGetFolderPath
#endif
+#include "Common/Common.h"
#include "Common/CommonPaths.h"
#include "Common/Config/Config.h"
#include "Common/FileUtil.h"
#include "Common/Logging/LogManager.h"
+#include "Common/MathUtil.h"
#include "Common/MsgHandler.h"
+#include "Common/StringUtil.h"
#include "Core/ConfigLoaders/BaseConfigLoader.h"
#include "Core/ConfigManager.h"
@@ -313,4 +317,21 @@ void EnableScreenSaver(bool enable)
#endif
}
+std::string FormatSize(u64 bytes)
+{
+ // i18n: The symbol for the unit "bytes"
+ const char* const unit_symbols[] = {_trans("B"), _trans("KiB"), _trans("MiB"), _trans("GiB"),
+ _trans("TiB"), _trans("PiB"), _trans("EiB")};
+
+ // Find largest power of 2 less than size.
+ // div 10 to get largest named unit less than size
+ // 10 == log2(1024) (number of B in a KiB, KiB in a MiB, etc)
+ // Max value is 63 / 10 = 6
+ const int unit = IntLog2(std::max<u64>(bytes, 1)) / 10;
+
+ // Don't need exact values, only 5 most significant digits
+ const double unit_size = std::pow(2, unit * 10);
+ return StringFromFormat("%.2f %s", bytes / unit_size, GetStringT(unit_symbols[unit]).c_str());
+}
+
} // namespace UICommon
diff --git a/Source/Core/UICommon/UICommon.h b/Source/Core/UICommon/UICommon.h
index 5e11c9bfc2..8bbd864ec0 100644
--- a/Source/Core/UICommon/UICommon.h
+++ b/Source/Core/UICommon/UICommon.h
@@ -25,4 +25,8 @@ void SetUserDirectory(const std::string& custom_path);
bool TriggerSTMPowerEvent();
void SaveWiimoteSources();
+
+// Return a pretty file size string from byte count.
+// e.g. 1134278 -> "1.08 MiB"
+std::string FormatSize(u64 bytes);
} // namespace UICommon
diff --git a/Source/Core/UICommon/UICommon.vcxproj b/Source/Core/UICommon/UICommon.vcxproj
index 643a5e9829..164cb4f884 100644
--- a/Source/Core/UICommon/UICommon.vcxproj
+++ b/Source/Core/UICommon/UICommon.vcxproj
@@ -51,12 +51,16 @@
<DisableSpecificWarnings>4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<ClCompile Include="VideoUtils.cpp" />
+ <ClCompile Include="GameFile.cpp" />
+ <ClCompile Include="GameFileCache.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="CommandLineParse.h" />
<ClInclude Include="UICommon.h" />
<ClInclude Include="Disassembler.h" />
<ClInclude Include="USBUtils.h" />
+ <ClInclude Include="GameFile.h" />
+ <ClInclude Include="GameFileCache.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(ExternalsDir)cpp-optparse\cpp-optparse.vcxproj">