summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Common/ChunkFile.h122
-rw-r--r--Source/Core/Common/FileSearch.cpp85
-rw-r--r--Source/Core/Common/FileSearch.h8
-rw-r--r--Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp2
-rw-r--r--Source/Core/DolphinQt2/Settings.cpp2
-rw-r--r--Source/Core/DolphinQt2/Settings/InterfacePane.cpp5
-rw-r--r--Source/Core/DolphinWX/Config/ConfigMain.cpp21
-rw-r--r--Source/Core/DolphinWX/Config/ConfigMain.h7
-rw-r--r--Source/Core/DolphinWX/Config/InterfaceConfigPane.cpp5
-rw-r--r--Source/Core/DolphinWX/Config/PathConfigPane.cpp6
-rw-r--r--Source/Core/DolphinWX/Frame.cpp15
-rw-r--r--Source/Core/DolphinWX/Frame.h12
-rw-r--r--Source/Core/DolphinWX/FrameAui.cpp3
-rw-r--r--Source/Core/DolphinWX/FrameTools.cpp54
-rw-r--r--Source/Core/DolphinWX/GameListCtrl.cpp630
-rw-r--r--Source/Core/DolphinWX/GameListCtrl.h66
-rw-r--r--Source/Core/DolphinWX/ISOFile.cpp143
-rw-r--r--Source/Core/DolphinWX/ISOFile.h26
-rw-r--r--Source/Core/DolphinWX/Input/InputConfigDiag.cpp2
-rw-r--r--Source/Core/DolphinWX/Main.cpp5
-rw-r--r--Source/Core/DolphinWX/NetPlay/ChangeGameDialog.cpp2
-rw-r--r--Source/Core/DolphinWX/NetPlay/ChangeGameDialog.h4
-rw-r--r--Source/Core/DolphinWX/NetPlay/NetPlayLauncher.h4
-rw-r--r--Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.cpp2
-rw-r--r--Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.h6
-rw-r--r--Source/Core/DolphinWX/NetPlay/NetWindow.cpp4
-rw-r--r--Source/Core/DolphinWX/NetPlay/NetWindow.h8
-rw-r--r--Source/Core/VideoCommon/HiresTextures.cpp2
-rw-r--r--Source/Core/VideoCommon/PostProcessing.cpp5
29 files changed, 657 insertions, 599 deletions
diff --git a/Source/Core/Common/ChunkFile.h b/Source/Core/Common/ChunkFile.h
index 72832c5e97..497a6d439a 100644
--- a/Source/Core/Common/ChunkFile.h
+++ b/Source/Core/Common/ChunkFile.h
@@ -296,125 +296,3 @@ private:
*ptr += size;
}
};
-
-// NOTE: this class is only used in DolphinWX/ISOFile.cpp for caching loaded
-// ISO data. It will be removed when DolphinWX is, so please don't use it.
-class CChunkFileReader
-{
-public:
- // Load file template
- template <class T>
- static bool Load(const std::string& _rFilename, u32 _Revision, T& _class)
- {
- INFO_LOG(COMMON, "ChunkReader: Loading %s", _rFilename.c_str());
-
- if (!File::Exists(_rFilename))
- return false;
-
- // Check file size
- const u64 fileSize = File::GetSize(_rFilename);
- static const u64 headerSize = sizeof(SChunkHeader);
- if (fileSize < headerSize)
- {
- ERROR_LOG(COMMON, "ChunkReader: File too small");
- return false;
- }
-
- File::IOFile pFile(_rFilename, "rb");
- if (!pFile)
- {
- ERROR_LOG(COMMON, "ChunkReader: Can't open file for reading");
- return false;
- }
-
- // read the header
- SChunkHeader header;
- if (!pFile.ReadArray(&header, 1))
- {
- ERROR_LOG(COMMON, "ChunkReader: Bad header size");
- return false;
- }
-
- // Check revision
- if (header.Revision != _Revision)
- {
- ERROR_LOG(COMMON, "ChunkReader: Wrong file revision, got %d expected %d", header.Revision,
- _Revision);
- return false;
- }
-
- // get size
- const u32 sz = (u32)(fileSize - headerSize);
- if (header.ExpectedSize != sz)
- {
- ERROR_LOG(COMMON, "ChunkReader: Bad file size, got %d expected %d", sz, header.ExpectedSize);
- return false;
- }
-
- // read the state
- std::vector<u8> buffer(sz);
- if (!pFile.ReadArray(&buffer[0], sz))
- {
- ERROR_LOG(COMMON, "ChunkReader: Error reading file");
- return false;
- }
-
- u8* ptr = &buffer[0];
- PointerWrap p(&ptr, PointerWrap::MODE_READ);
- _class.DoState(p);
-
- INFO_LOG(COMMON, "ChunkReader: Done loading %s", _rFilename.c_str());
- return true;
- }
-
- // Save file template
- template <class T>
- static bool Save(const std::string& _rFilename, u32 _Revision, T& _class)
- {
- INFO_LOG(COMMON, "ChunkReader: Writing %s", _rFilename.c_str());
- File::IOFile pFile(_rFilename, "wb");
- if (!pFile)
- {
- ERROR_LOG(COMMON, "ChunkReader: Error opening file for write");
- return false;
- }
-
- // Get data
- u8* ptr = nullptr;
- PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
- _class.DoState(p);
- size_t const sz = (size_t)ptr;
- std::vector<u8> buffer(sz);
- ptr = &buffer[0];
- p.SetMode(PointerWrap::MODE_WRITE);
- _class.DoState(p);
-
- // Create header
- SChunkHeader header;
- header.Revision = _Revision;
- header.ExpectedSize = (u32)sz;
-
- // Write to file
- if (!pFile.WriteArray(&header, 1))
- {
- ERROR_LOG(COMMON, "ChunkReader: Failed writing header");
- return false;
- }
-
- if (!pFile.WriteArray(&buffer[0], sz))
- {
- ERROR_LOG(COMMON, "ChunkReader: Failed writing data");
- return false;
- }
-
- INFO_LOG(COMMON, "ChunkReader: Done writing %s", _rFilename.c_str());
- return true;
- }
-
-private:
- struct SChunkHeader
- {
- u32 Revision;
- u32 ExpectedSize;
- };
-};
diff --git a/Source/Core/Common/FileSearch.cpp b/Source/Core/Common/FileSearch.cpp
index 29b8624b15..783c22d864 100644
--- a/Source/Core/Common/FileSearch.cpp
+++ b/Source/Core/Common/FileSearch.cpp
@@ -7,10 +7,19 @@
#include "Common/CommonPaths.h"
#include "Common/FileSearch.h"
+
+#ifdef _MSC_VER
+#include <experimental/filesystem>
+namespace fs = std::experimental::filesystem;
+#define HAS_STD_FILESYSTEM
+#else
#include "Common/FileUtil.h"
+#endif
namespace Common
{
+#ifndef HAS_STD_FILESYSTEM
+
static std::vector<std::string>
FileSearchWithTest(const std::vector<std::string>& directories, bool recursive,
std::function<bool(const File::FSTEntry&)> callback)
@@ -36,10 +45,10 @@ FileSearchWithTest(const std::vector<std::string>& directories, bool recursive,
return result;
}
-std::vector<std::string> DoFileSearch(const std::vector<std::string>& exts,
- const std::vector<std::string>& directories, bool recursive)
+std::vector<std::string> DoFileSearchNoSTL(const std::vector<std::string>& directories,
+ const std::vector<std::string>& exts, bool recursive)
{
- bool accept_all = std::find(exts.begin(), exts.end(), "") != exts.end();
+ bool accept_all = exts.empty();
return FileSearchWithTest(directories, recursive, [&](const File::FSTEntry& entry) {
if (accept_all)
return true;
@@ -52,11 +61,71 @@ std::vector<std::string> DoFileSearch(const std::vector<std::string>& exts,
});
}
-// Result includes the passed directories themselves as well as their subdirectories.
-std::vector<std::string> FindSubdirectories(const std::vector<std::string>& directories,
- bool recursive)
+std::vector<std::string> DoFileSearch(const std::vector<std::string>& directories,
+ const std::vector<std::string>& exts, bool recursive)
{
- return FileSearchWithTest(directories, true,
- [&](const File::FSTEntry& entry) { return entry.isDirectory; });
+ return DoFileSearchNoSTL(directories, exts, recursive);
}
+
+#else
+
+std::vector<std::string> DoFileSearch(const std::vector<std::string>& directories,
+ const std::vector<std::string>& exts, bool recursive)
+{
+ bool accept_all = exts.empty();
+
+ std::vector<fs::path> native_exts;
+ for (const auto& ext : exts)
+ native_exts.push_back(ext);
+
+ // N.B. This avoids doing any copies
+ auto ext_matches = [&native_exts](const fs::path& path) {
+ const auto& native_path = path.native();
+ return std::any_of(native_exts.cbegin(), native_exts.cend(), [&native_path](const auto& ext) {
+ // TODO provide cross-platform compat for the comparison function, once more platforms
+ // support std::filesystem
+ return native_path.length() >= ext.native().length() &&
+ _wcsicmp(&native_path.c_str()[native_path.length() - ext.native().length()],
+ ext.c_str()) == 0;
+ });
+ };
+
+ std::vector<std::string> result;
+ auto add_filtered = [&](const fs::directory_entry& entry) {
+ auto& path = entry.path();
+ if (accept_all || (ext_matches(path) && !fs::is_directory(path)))
+ result.emplace_back(path.u8string());
+ };
+ for (const auto& directory : directories)
+ {
+ if (recursive)
+ {
+ // TODO use fs::directory_options::follow_directory_symlink ?
+ for (auto& entry : fs::recursive_directory_iterator(fs::path(directory.c_str())))
+ add_filtered(entry);
+ }
+ else
+ {
+ for (auto& entry : fs::directory_iterator(fs::path(directory.c_str())))
+ add_filtered(entry);
+ }
+ }
+
+ // Remove duplicates (occurring because caller gave e.g. duplicate or overlapping directories -
+ // not because std::filesystem returns duplicates). Also note that this pathname-based uniqueness
+ // isn't as thorough as std::filesystem::equivalent.
+ std::sort(result.begin(), result.end());
+ result.erase(std::unique(result.begin(), result.end()), result.end());
+
+ // Dolphin expects to be able to use "/" (DIR_SEP) everywhere. std::filesystem uses the OS
+ // separator.
+ if (fs::path::preferred_separator != DIR_SEP_CHR)
+ for (auto& path : result)
+ std::replace(path.begin(), path.end(), '\\', DIR_SEP_CHR);
+
+ return result;
+}
+
+#endif
+
} // namespace Common
diff --git a/Source/Core/Common/FileSearch.h b/Source/Core/Common/FileSearch.h
index dc2f7f6b62..dc38db945a 100644
--- a/Source/Core/Common/FileSearch.h
+++ b/Source/Core/Common/FileSearch.h
@@ -9,9 +9,9 @@
namespace Common
{
-std::vector<std::string> DoFileSearch(const std::vector<std::string>& exts,
- const std::vector<std::string>& directories,
+// Callers can pass empty "exts" to indicate they want all files + directories in results
+// Otherwise, only files matching the extensions are returned
+std::vector<std::string> DoFileSearch(const std::vector<std::string>& directories,
+ const std::vector<std::string>& exts = {},
bool recursive = false);
-std::vector<std::string> FindSubdirectories(const std::vector<std::string>& directories,
- bool recursive);
} // namespace Common
diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp
index f12751c8be..3dcdb64d11 100644
--- a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp
+++ b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp
@@ -148,7 +148,7 @@ GCMemcardDirectory::GCMemcardDirectory(const std::string& directory, int slot, u
hdr_file.ReadBytes(&m_hdr, BLOCK_SIZE);
}
- std::vector<std::string> filenames = Common::DoFileSearch({".gci"}, {m_save_directory});
+ std::vector<std::string> filenames = Common::DoFileSearch({m_save_directory}, {".gci"});
if (filenames.size() > 112)
{
diff --git a/Source/Core/DolphinQt2/Settings.cpp b/Source/Core/DolphinQt2/Settings.cpp
index 346b802237..bac3a4143f 100644
--- a/Source/Core/DolphinQt2/Settings.cpp
+++ b/Source/Core/DolphinQt2/Settings.cpp
@@ -365,7 +365,7 @@ QVector<QString> Settings::GetProfiles(const InputConfig* config) const
const std::string path = GetProfilesDir().toStdString() + config->GetProfileName();
QVector<QString> vec;
- for (const auto& file : Common::DoFileSearch({".ini"}, {path}))
+ for (const auto& file : Common::DoFileSearch({path}, {".ini"}))
{
std::string basename;
SplitPath(file, nullptr, &basename, nullptr);
diff --git a/Source/Core/DolphinQt2/Settings/InterfacePane.cpp b/Source/Core/DolphinQt2/Settings/InterfacePane.cpp
index b34f66f1f6..84998ea13d 100644
--- a/Source/Core/DolphinQt2/Settings/InterfacePane.cpp
+++ b/Source/Core/DolphinQt2/Settings/InterfacePane.cpp
@@ -59,9 +59,8 @@ void InterfacePane::CreateUI()
combobox_layout->addRow(tr("&Theme:"), m_combobox_theme);
// List avalable themes
- auto file_search_results = Common::DoFileSearch(
- {""}, {File::GetUserPath(D_THEMES_IDX), File::GetSysDirectory() + THEMES_DIR},
- /*recursive*/ false);
+ auto file_search_results =
+ Common::DoFileSearch({File::GetUserPath(D_THEMES_IDX), File::GetSysDirectory() + THEMES_DIR});
for (const std::string& filename : file_search_results)
{
std::string name, ext;
diff --git a/Source/Core/DolphinWX/Config/ConfigMain.cpp b/Source/Core/DolphinWX/Config/ConfigMain.cpp
index 48fd35ab96..b04f2cc706 100644
--- a/Source/Core/DolphinWX/Config/ConfigMain.cpp
+++ b/Source/Core/DolphinWX/Config/ConfigMain.cpp
@@ -24,21 +24,21 @@
#include "DolphinWX/GameListCtrl.h"
#include "DolphinWX/WxUtils.h"
-// Sent by child panes to signify that the game list should
-// be updated when this modal dialog closes.
wxDEFINE_EVENT(wxDOLPHIN_CFG_REFRESH_LIST, wxCommandEvent);
+wxDEFINE_EVENT(wxDOLPHIN_CFG_RESCAN_LIST, wxCommandEvent);
CConfigMain::CConfigMain(wxWindow* parent, wxWindowID id, const wxString& title,
const wxPoint& position, const wxSize& size, long style)
: wxDialog(parent, id, title, position, size, style)
{
- // Control refreshing of the ISOs list
- m_refresh_game_list_on_close = false;
+ // Control refreshing of the GameListCtrl
+ m_event_on_close = wxEVT_NULL;
Bind(wxEVT_CLOSE_WINDOW, &CConfigMain::OnClose, this);
Bind(wxEVT_BUTTON, &CConfigMain::OnCloseButton, this, wxID_CLOSE);
Bind(wxEVT_SHOW, &CConfigMain::OnShow, this);
Bind(wxDOLPHIN_CFG_REFRESH_LIST, &CConfigMain::OnSetRefreshGameListOnClose, this);
+ Bind(wxDOLPHIN_CFG_RESCAN_LIST, &CConfigMain::OnSetRescanGameListOnClose, this);
wxDialog::SetExtraStyle(GetExtraStyle() & ~wxWS_EX_BLOCK_EVENTS);
@@ -115,8 +115,8 @@ void CConfigMain::OnClose(wxCloseEvent& WXUNUSED(event))
SConfig::GetInstance().SaveSettings();
- if (m_refresh_game_list_on_close)
- AddPendingEvent(wxCommandEvent{DOLPHIN_EVT_RELOAD_GAMELIST});
+ if (m_event_on_close != wxEVT_NULL)
+ AddPendingEvent(wxCommandEvent{m_event_on_close});
}
void CConfigMain::OnShow(wxShowEvent& event)
@@ -132,5 +132,12 @@ void CConfigMain::OnCloseButton(wxCommandEvent& WXUNUSED(event))
void CConfigMain::OnSetRefreshGameListOnClose(wxCommandEvent& WXUNUSED(event))
{
- m_refresh_game_list_on_close = true;
+ // Don't override a rescan
+ if (m_event_on_close == wxEVT_NULL)
+ m_event_on_close = DOLPHIN_EVT_REFRESH_GAMELIST;
+}
+
+void CConfigMain::OnSetRescanGameListOnClose(wxCommandEvent& WXUNUSED(event))
+{
+ m_event_on_close = DOLPHIN_EVT_RESCAN_GAMELIST;
}
diff --git a/Source/Core/DolphinWX/Config/ConfigMain.h b/Source/Core/DolphinWX/Config/ConfigMain.h
index 84c2f9121f..005797308b 100644
--- a/Source/Core/DolphinWX/Config/ConfigMain.h
+++ b/Source/Core/DolphinWX/Config/ConfigMain.h
@@ -10,7 +10,10 @@
class wxNotebook;
class wxPanel;
+// Fast refresh - can be fulfilled from cache
wxDECLARE_EVENT(wxDOLPHIN_CFG_REFRESH_LIST, wxCommandEvent);
+// Rescan and refresh - modifies cache
+wxDECLARE_EVENT(wxDOLPHIN_CFG_RESCAN_LIST, wxCommandEvent);
class CConfigMain : public wxDialog
{
@@ -41,8 +44,8 @@ private:
void OnCloseButton(wxCommandEvent& event);
void OnShow(wxShowEvent& event);
void OnSetRefreshGameListOnClose(wxCommandEvent& event);
+ void OnSetRescanGameListOnClose(wxCommandEvent& event);
wxNotebook* Notebook;
-
- bool m_refresh_game_list_on_close;
+ wxEventType m_event_on_close;
};
diff --git a/Source/Core/DolphinWX/Config/InterfaceConfigPane.cpp b/Source/Core/DolphinWX/Config/InterfaceConfigPane.cpp
index 278e1697c3..d3308bc22e 100644
--- a/Source/Core/DolphinWX/Config/InterfaceConfigPane.cpp
+++ b/Source/Core/DolphinWX/Config/InterfaceConfigPane.cpp
@@ -196,9 +196,8 @@ void InterfaceConfigPane::LoadGUIValues()
void InterfaceConfigPane::LoadThemes()
{
- auto sv = Common::DoFileSearch(
- {""}, {File::GetUserPath(D_THEMES_IDX), File::GetSysDirectory() + THEMES_DIR},
- /*recursive*/ false);
+ auto sv =
+ Common::DoFileSearch({File::GetUserPath(D_THEMES_IDX), File::GetSysDirectory() + THEMES_DIR});
for (const std::string& filename : sv)
{
std::string name, ext;
diff --git a/Source/Core/DolphinWX/Config/PathConfigPane.cpp b/Source/Core/DolphinWX/Config/PathConfigPane.cpp
index 5a0a1c1c13..ec3376acc2 100644
--- a/Source/Core/DolphinWX/Config/PathConfigPane.cpp
+++ b/Source/Core/DolphinWX/Config/PathConfigPane.cpp
@@ -163,7 +163,7 @@ void PathConfigPane::OnRecursiveISOCheckBoxChanged(wxCommandEvent& event)
{
SConfig::GetInstance().m_RecursiveISOFolder = m_recursive_iso_paths_checkbox->IsChecked();
- AddPendingEvent(wxCommandEvent(wxDOLPHIN_CFG_REFRESH_LIST));
+ AddPendingEvent(wxCommandEvent(wxDOLPHIN_CFG_RESCAN_LIST));
}
void PathConfigPane::OnAddISOPath(wxCommandEvent& event)
@@ -179,7 +179,7 @@ void PathConfigPane::OnAddISOPath(wxCommandEvent& event)
}
else
{
- AddPendingEvent(wxCommandEvent(wxDOLPHIN_CFG_REFRESH_LIST));
+ AddPendingEvent(wxCommandEvent(wxDOLPHIN_CFG_RESCAN_LIST));
m_iso_paths_listbox->Append(dialog.GetPath());
}
}
@@ -189,7 +189,7 @@ void PathConfigPane::OnAddISOPath(wxCommandEvent& event)
void PathConfigPane::OnRemoveISOPath(wxCommandEvent& event)
{
- AddPendingEvent(wxCommandEvent(wxDOLPHIN_CFG_REFRESH_LIST));
+ AddPendingEvent(wxCommandEvent(wxDOLPHIN_CFG_RESCAN_LIST));
m_iso_paths_listbox->Delete(m_iso_paths_listbox->GetSelection());
// This seems to not be activated on Windows when it should be. wxw bug?
diff --git a/Source/Core/DolphinWX/Frame.cpp b/Source/Core/DolphinWX/Frame.cpp
index b4b4dd46ea..29a7b526c3 100644
--- a/Source/Core/DolphinWX/Frame.cpp
+++ b/Source/Core/DolphinWX/Frame.cpp
@@ -337,7 +337,8 @@ CFrame::CFrame(wxFrame* parent, wxWindowID id, const wxString& title, wxRect geo
wxFrame::CreateToolBar(wxTB_DEFAULT_STYLE | wxTB_TEXT | wxTB_FLAT)->Realize();
// Give it a status bar
- SetStatusBar(CreateStatusBar(2, wxST_SIZEGRIP, ID_STATUSBAR));
+ SetStatusBar(
+ CreateStatusBar(2, wxSTB_SIZEGRIP | wxSTB_ELLIPSIZE_END | wxSTB_SHOW_TIPS, ID_STATUSBAR));
if (!SConfig::GetInstance().m_InterfaceStatusbar)
GetStatusBar()->Hide();
@@ -351,8 +352,9 @@ CFrame::CFrame(wxFrame* parent, wxWindowID id, const wxString& title, wxRect geo
// This panel is the parent for rendering and it holds the gamelistctrl
m_panel = new wxPanel(this, IDM_MPANEL, wxDefaultPosition, wxDefaultSize, 0);
- m_game_list_ctrl = new CGameListCtrl(m_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize,
- wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT);
+ m_game_list_ctrl =
+ new GameListCtrl(m_batch_mode, m_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize,
+ wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT);
m_game_list_ctrl->Bind(wxEVT_LIST_ITEM_ACTIVATED, &CFrame::OnGameListCtrlItemActivated, this);
wxBoxSizer* sizerPanel = new wxBoxSizer(wxHORIZONTAL);
@@ -490,7 +492,8 @@ void CFrame::BindEvents()
BindMenuBarEvents();
Bind(DOLPHIN_EVT_RELOAD_THEME_BITMAPS, &CFrame::OnReloadThemeBitmaps, this);
- Bind(DOLPHIN_EVT_RELOAD_GAMELIST, &CFrame::OnReloadGameList, this);
+ Bind(DOLPHIN_EVT_REFRESH_GAMELIST, &CFrame::OnRefreshGameList, this);
+ Bind(DOLPHIN_EVT_RESCAN_GAMELIST, &CFrame::OnRescanGameList, this);
Bind(DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM, &CFrame::OnUpdateLoadWiiMenuItem, this);
Bind(DOLPHIN_EVT_BOOT_SOFTWARE, &CFrame::OnPlay, this);
Bind(DOLPHIN_EVT_STOP_SOFTWARE, &CFrame::OnStop, this);
@@ -876,7 +879,7 @@ void CFrame::OnGameListCtrlItemActivated(wxListEvent& WXUNUSED(event))
// 1. Boot the selected iso
// 2. Boot the default or last loaded iso.
// 3. Call BrowseForDirectory if the gamelist is empty
- if (!m_game_list_ctrl->GetISO(0) && CGameListCtrl::IsHidingItems())
+ if (!m_game_list_ctrl->GetISO(0) && GameListCtrl::IsHidingItems())
{
SConfig::GetInstance().m_ListGC = SConfig::GetInstance().m_ListWii =
SConfig::GetInstance().m_ListWad = SConfig::GetInstance().m_ListElfDol =
@@ -909,7 +912,7 @@ void CFrame::OnGameListCtrlItemActivated(wxListEvent& WXUNUSED(event))
GetMenuBar()->FindItem(IDM_LIST_WORLD)->Check(true);
GetMenuBar()->FindItem(IDM_LIST_UNKNOWN)->Check(true);
- UpdateGameList();
+ GameListRefresh();
}
else if (!m_game_list_ctrl->GetISO(0))
{
diff --git a/Source/Core/DolphinWX/Frame.h b/Source/Core/DolphinWX/Frame.h
index 432cd7496f..094df16173 100644
--- a/Source/Core/DolphinWX/Frame.h
+++ b/Source/Core/DolphinWX/Frame.h
@@ -33,7 +33,7 @@
struct BootParameters;
// Class declarations
-class CGameListCtrl;
+class GameListCtrl;
class CCodeWindow;
class CConfigMain;
class CLogWindow;
@@ -98,10 +98,11 @@ public:
void DoStop();
void UpdateGUI();
- void UpdateGameList();
+ void GameListRefresh();
+ void GameListRescan();
void ToggleLogWindow(bool bShow);
void ToggleLogConfigWindow(bool bShow);
- void StatusBarMessage(const char* Text, ...);
+ void StatusBarMessage(const char* format, ...);
void ClearStatusBar();
void BootGame(const std::string& filename);
bool RendererHasFocus();
@@ -143,7 +144,7 @@ private:
ADD_PANE_CENTER
};
- CGameListCtrl* m_game_list_ctrl = nullptr;
+ GameListCtrl* m_game_list_ctrl = nullptr;
CConfigMain* m_main_config_dialog = nullptr;
wxPanel* m_panel = nullptr;
CRenderFrame* m_render_frame = nullptr;
@@ -267,7 +268,8 @@ private:
void OnHelp(wxCommandEvent& event);
void OnReloadThemeBitmaps(wxCommandEvent& event);
- void OnReloadGameList(wxCommandEvent& event);
+ void OnRefreshGameList(wxCommandEvent& event);
+ void OnRescanGameList(wxCommandEvent& event);
void OnUpdateInterpreterMenuItem(wxUpdateUIEvent& event);
diff --git a/Source/Core/DolphinWX/FrameAui.cpp b/Source/Core/DolphinWX/FrameAui.cpp
index bfc2328be9..9ab5a035e7 100644
--- a/Source/Core/DolphinWX/FrameAui.cpp
+++ b/Source/Core/DolphinWX/FrameAui.cpp
@@ -511,8 +511,7 @@ void CFrame::OnPerspectiveMenu(wxCommandEvent& event)
return;
}
SaveIniPerspectives();
- GetStatusBar()->SetStatusText(
- StrToWxStr(std::string("Saved " + m_perspectives[m_active_perspective].name)), 0);
+ StatusBarMessage("Saved %s", m_perspectives[m_active_perspective].name.c_str());
break;
case IDM_PERSPECTIVES_ADD_PANE_TOP:
AddPane(ADD_PANE_TOP);
diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp
index 8ac6fd7e08..007c6595db 100644
--- a/Source/Core/DolphinWX/FrameTools.cpp
+++ b/Source/Core/DolphinWX/FrameTools.cpp
@@ -774,7 +774,7 @@ void CFrame::OnBootDrive(wxCommandEvent& event)
void CFrame::OnRefresh(wxCommandEvent& WXUNUSED(event))
{
- UpdateGameList();
+ GameListRescan();
}
void CFrame::OnScreenshot(wxCommandEvent& WXUNUSED(event))
@@ -1097,12 +1097,17 @@ void CFrame::OnReloadThemeBitmaps(wxCommandEvent& WXUNUSED(event))
reload_event.SetEventObject(this);
wxPostEvent(GetToolBar(), reload_event);
- UpdateGameList();
+ GameListRefresh();
}
-void CFrame::OnReloadGameList(wxCommandEvent& WXUNUSED(event))
+void CFrame::OnRefreshGameList(wxCommandEvent& WXUNUSED(event))
{
- UpdateGameList();
+ GameListRefresh();
+}
+
+void CFrame::OnRescanGameList(wxCommandEvent& WXUNUSED(event))
+{
+ GameListRescan();
}
void CFrame::OnUpdateInterpreterMenuItem(wxUpdateUIEvent& event)
@@ -1128,18 +1133,16 @@ void CFrame::ClearStatusBar()
}
}
-void CFrame::StatusBarMessage(const char* Text, ...)
+void CFrame::StatusBarMessage(const char* format, ...)
{
- const int MAX_BYTES = 1024 * 10;
- char Str[MAX_BYTES];
- va_list ArgPtr;
- va_start(ArgPtr, Text);
- vsnprintf(Str, MAX_BYTES, Text, ArgPtr);
- va_end(ArgPtr);
+ va_list args;
+ va_start(args, format);
+ std::string msg = StringFromFormatV(format, args);
+ va_end(args);
if (this->GetStatusBar()->IsEnabled())
{
- this->GetStatusBar()->SetStatusText(StrToWxStr(Str), 0);
+ this->GetStatusBar()->SetStatusText(StrToWxStr(msg), 0);
}
}
@@ -1605,9 +1608,16 @@ void CFrame::UpdateGUI()
}
}
-void CFrame::UpdateGameList()
+void CFrame::GameListRefresh()
+{
+ wxCommandEvent event{DOLPHIN_EVT_REFRESH_GAMELIST, GetId()};
+ event.SetEventObject(this);
+ wxPostEvent(m_game_list_ctrl, event);
+}
+
+void CFrame::GameListRescan()
{
- wxCommandEvent event{DOLPHIN_EVT_RELOAD_GAMELIST, GetId()};
+ wxCommandEvent event{DOLPHIN_EVT_RESCAN_GAMELIST, GetId()};
event.SetEventObject(this);
wxPostEvent(m_game_list_ctrl, event);
}
@@ -1674,17 +1684,19 @@ void CFrame::GameListChanged(wxCommandEvent& event)
SConfig::GetInstance().m_ListDrives = event.IsChecked();
break;
case IDM_PURGE_GAME_LIST_CACHE:
- std::vector<std::string> rFilenames =
- Common::DoFileSearch({".cache"}, {File::GetUserPath(D_CACHE_IDX)});
+ std::vector<std::string> filenames =
+ Common::DoFileSearch({File::GetUserPath(D_CACHE_IDX)}, {".cache"});
- for (const std::string& rFilename : rFilenames)
+ for (const std::string& filename : filenames)
{
- File::Delete(rFilename);
+ File::Delete(filename);
}
- break;
+ // Do rescan after cache has been cleared
+ GameListRescan();
+ return;
}
- UpdateGameList();
+ GameListRefresh();
}
// Enable and disable the toolbar
@@ -1743,6 +1755,6 @@ void CFrame::OnChangeColumnsVisible(wxCommandEvent& event)
default:
return;
}
- UpdateGameList();
+ GameListRefresh();
SConfig::GetInstance().SaveSettings();
}
diff --git a/Source/Core/DolphinWX/GameListCtrl.cpp b/Source/Core/DolphinWX/GameListCtrl.cpp
index 36d334fcbf..4e89a7051c 100644
--- a/Source/Core/DolphinWX/GameListCtrl.cpp
+++ b/Source/Core/DolphinWX/GameListCtrl.cpp
@@ -44,6 +44,7 @@
#include "Common/MathUtil.h"
#include "Common/StringUtil.h"
#include "Common/SysConf.h"
+#include "Common/Thread.h"
#include "Core/Boot/Boot.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
@@ -82,7 +83,7 @@ public:
static bool sorted = false;
static int CompareGameListItems(const GameListItem* iso1, const GameListItem* iso2,
- long sortData = CGameListCtrl::COLUMN_TITLE)
+ long sortData = GameListCtrl::COLUMN_TITLE)
{
int t = 1;
@@ -94,7 +95,7 @@ static int CompareGameListItems(const GameListItem* iso1, const GameListItem* is
switch (sortData)
{
- case CGameListCtrl::COLUMN_TITLE:
+ case GameListCtrl::COLUMN_TITLE:
if (!strcasecmp(iso1->GetName().c_str(), iso2->GetName().c_str()))
{
if (iso1->GetGameID() != iso2->GetGameID())
@@ -111,34 +112,34 @@ static int CompareGameListItems(const GameListItem* iso1, const GameListItem* is
return t * wxStricmp(iso1_filename, iso2_filename);
}
return strcasecmp(iso1->GetName().c_str(), iso2->GetName().c_str()) * t;
- case CGameListCtrl::COLUMN_MAKER:
+ case GameListCtrl::COLUMN_MAKER:
return strcasecmp(iso1->GetCompany().c_str(), iso2->GetCompany().c_str()) * t;
- case CGameListCtrl::COLUMN_FILENAME:
+ case GameListCtrl::COLUMN_FILENAME:
return wxStricmp(wxFileNameFromPath(iso1->GetFileName()),
wxFileNameFromPath(iso2->GetFileName())) *
t;
- case CGameListCtrl::COLUMN_ID:
+ case GameListCtrl::COLUMN_ID:
return strcasecmp(iso1->GetGameID().c_str(), iso2->GetGameID().c_str()) * t;
- case CGameListCtrl::COLUMN_COUNTRY:
+ case GameListCtrl::COLUMN_COUNTRY:
if (iso1->GetCountry() > iso2->GetCountry())
return 1 * t;
if (iso1->GetCountry() < iso2->GetCountry())
return -1 * t;
return 0;
- case CGameListCtrl::COLUMN_SIZE:
+ case GameListCtrl::COLUMN_SIZE:
if (iso1->GetFileSize() > iso2->GetFileSize())
return 1 * t;
if (iso1->GetFileSize() < iso2->GetFileSize())
return -1 * t;
return 0;
- case CGameListCtrl::COLUMN_PLATFORM:
+ case GameListCtrl::COLUMN_PLATFORM:
if (iso1->GetPlatform() > iso2->GetPlatform())
return 1 * t;
if (iso1->GetPlatform() < iso2->GetPlatform())
return -1 * t;
return 0;
- case CGameListCtrl::COLUMN_EMULATION_STATE:
+ case GameListCtrl::COLUMN_EMULATION_STATE:
{
const int nState1 = iso1->GetEmuState(), nState2 = iso2->GetEmuState();
@@ -155,36 +156,6 @@ static int CompareGameListItems(const GameListItem* iso1, const GameListItem* is
return 0;
}
-static std::vector<std::string> GetFileSearchExtensions()
-{
- std::vector<std::string> extensions;
-
- if (SConfig::GetInstance().m_ListGC)
- {
- extensions.push_back(".gcm");
- extensions.push_back(".tgc");
- }
-
- if (SConfig::GetInstance().m_ListWii || SConfig::GetInstance().m_ListGC)
- {
- extensions.push_back(".iso");
- extensions.push_back(".ciso");
- extensions.push_back(".gcz");
- extensions.push_back(".wbfs");
- }
-
- if (SConfig::GetInstance().m_ListWad)
- extensions.push_back(".wad");
-
- if (SConfig::GetInstance().m_ListElfDol)
- {
- extensions.push_back(".dol");
- extensions.push_back(".elf");
- }
-
- return extensions;
-}
-
static bool ShouldDisplayGameListItem(const GameListItem& item)
{
const bool show_platform = [&item] {
@@ -240,9 +211,10 @@ static bool ShouldDisplayGameListItem(const GameListItem& item)
}
}
-wxDEFINE_EVENT(DOLPHIN_EVT_RELOAD_GAMELIST, wxCommandEvent);
+wxDEFINE_EVENT(DOLPHIN_EVT_REFRESH_GAMELIST, wxCommandEvent);
+wxDEFINE_EVENT(DOLPHIN_EVT_RESCAN_GAMELIST, wxCommandEvent);
-struct CGameListCtrl::ColumnInfo
+struct GameListCtrl::ColumnInfo
{
const int id;
const int default_width;
@@ -250,9 +222,9 @@ struct CGameListCtrl::ColumnInfo
bool& visible;
};
-CGameListCtrl::CGameListCtrl(wxWindow* parent, const wxWindowID id, const wxPoint& pos,
- const wxSize& size, long style)
- : wxListCtrl(parent, id, pos, size, style), toolTip(nullptr),
+GameListCtrl::GameListCtrl(bool disable_scanning, wxWindow* parent, const wxWindowID id,
+ const wxPoint& pos, const wxSize& size, long style)
+ : wxListCtrl(parent, id, pos, size, style), m_tooltip(nullptr),
m_columns({// {COLUMN, {default_width (without platform padding), resizability, visibility}}
{COLUMN_PLATFORM, 32 + 1 /* icon padding */, false,
SConfig::GetInstance().m_showSystemColumn},
@@ -265,34 +237,78 @@ CGameListCtrl::CGameListCtrl(wxWindow* parent, const wxWindowID id, const wxPoin
{COLUMN_EMULATION_STATE, 48, false, SConfig::GetInstance().m_showStateColumn},
{COLUMN_SIZE, wxLIST_AUTOSIZE, false, SConfig::GetInstance().m_showSizeColumn}})
{
- Bind(wxEVT_SIZE, &CGameListCtrl::OnSize, this);
- Bind(wxEVT_RIGHT_DOWN, &CGameListCtrl::OnRightClick, this);
- Bind(wxEVT_LEFT_DOWN, &CGameListCtrl::OnLeftClick, this);
- Bind(wxEVT_MOTION, &CGameListCtrl::OnMouseMotion, this);
- Bind(wxEVT_LIST_KEY_DOWN, &CGameListCtrl::OnKeyPress, this);
- Bind(wxEVT_LIST_COL_BEGIN_DRAG, &CGameListCtrl::OnColBeginDrag, this);
- Bind(wxEVT_LIST_COL_CLICK, &CGameListCtrl::OnColumnClick, this);
-
- Bind(wxEVT_MENU, &CGameListCtrl::OnProperties, this, IDM_PROPERTIES);
- Bind(wxEVT_MENU, &CGameListCtrl::OnWiki, this, IDM_GAME_WIKI);
- Bind(wxEVT_MENU, &CGameListCtrl::OnOpenContainingFolder, this, IDM_OPEN_CONTAINING_FOLDER);
- Bind(wxEVT_MENU, &CGameListCtrl::OnOpenSaveFolder, this, IDM_OPEN_SAVE_FOLDER);
- Bind(wxEVT_MENU, &CGameListCtrl::OnExportSave, this, IDM_EXPORT_SAVE);
- Bind(wxEVT_MENU, &CGameListCtrl::OnSetDefaultISO, this, IDM_SET_DEFAULT_ISO);
- Bind(wxEVT_MENU, &CGameListCtrl::OnCompressISO, this, IDM_COMPRESS_ISO);
- Bind(wxEVT_MENU, &CGameListCtrl::OnMultiCompressISO, this, IDM_MULTI_COMPRESS_ISO);
- Bind(wxEVT_MENU, &CGameListCtrl::OnMultiDecompressISO, this, IDM_MULTI_DECOMPRESS_ISO);
- Bind(wxEVT_MENU, &CGameListCtrl::OnDeleteISO, this, IDM_DELETE_ISO);
- Bind(wxEVT_MENU, &CGameListCtrl::OnChangeDisc, this, IDM_LIST_CHANGE_DISC);
- Bind(wxEVT_MENU, &CGameListCtrl::OnNetPlayHost, this, IDM_START_NETPLAY);
-
- Bind(DOLPHIN_EVT_RELOAD_GAMELIST, &CGameListCtrl::OnReloadGameList, this);
-
- wxTheApp->Bind(DOLPHIN_EVT_LOCAL_INI_CHANGED, &CGameListCtrl::OnLocalIniModified, this);
+ Bind(wxEVT_SIZE, &GameListCtrl::OnSize, this);
+ Bind(wxEVT_RIGHT_DOWN, &GameListCtrl::OnRightClick, this);
+ Bind(wxEVT_LEFT_DOWN, &GameListCtrl::OnLeftClick, this);
+ Bind(wxEVT_MOTION, &GameListCtrl::OnMouseMotion, this);
+ Bind(wxEVT_LIST_KEY_DOWN, &GameListCtrl::OnKeyPress, this);
+ Bind(wxEVT_LIST_COL_BEGIN_DRAG, &GameListCtrl::OnColBeginDrag, this);
+ Bind(wxEVT_LIST_COL_CLICK, &GameListCtrl::OnColumnClick, this);
+
+ Bind(wxEVT_MENU, &GameListCtrl::OnProperties, this, IDM_PROPERTIES);
+ Bind(wxEVT_MENU, &GameListCtrl::OnWiki, this, IDM_GAME_WIKI);
+ Bind(wxEVT_MENU, &GameListCtrl::OnOpenContainingFolder, this, IDM_OPEN_CONTAINING_FOLDER);
+ Bind(wxEVT_MENU, &GameListCtrl::OnOpenSaveFolder, this, IDM_OPEN_SAVE_FOLDER);
+ Bind(wxEVT_MENU, &GameListCtrl::OnExportSave, this, IDM_EXPORT_SAVE);
+ Bind(wxEVT_MENU, &GameListCtrl::OnSetDefaultISO, this, IDM_SET_DEFAULT_ISO);
+ Bind(wxEVT_MENU, &GameListCtrl::OnCompressISO, this, IDM_COMPRESS_ISO);
+ Bind(wxEVT_MENU, &GameListCtrl::OnMultiCompressISO, this, IDM_MULTI_COMPRESS_ISO);
+ Bind(wxEVT_MENU, &GameListCtrl::OnMultiDecompressISO, this, IDM_MULTI_DECOMPRESS_ISO);
+ Bind(wxEVT_MENU, &GameListCtrl::OnDeleteISO, this, IDM_DELETE_ISO);
+ Bind(wxEVT_MENU, &GameListCtrl::OnChangeDisc, this, IDM_LIST_CHANGE_DISC);
+ Bind(wxEVT_MENU, &GameListCtrl::OnNetPlayHost, this, IDM_START_NETPLAY);
+
+ Bind(DOLPHIN_EVT_REFRESH_GAMELIST, &GameListCtrl::OnRefreshGameList, this);
+ Bind(DOLPHIN_EVT_RESCAN_GAMELIST, &GameListCtrl::OnRescanGameList, this);
+
+ wxTheApp->Bind(DOLPHIN_EVT_LOCAL_INI_CHANGED, &GameListCtrl::OnLocalIniModified, this);
+
+ if (!disable_scanning)
+ {
+ m_scan_thread = std::thread([&] {
+ Common::SetCurrentThreadName("gamelist scanner");
+
+ if (SyncCacheFile(false))
+ {
+ // Account for changes outside the cache (just wii banners atm; could scan Ini here)
+ // Note this is on the initial load path. Further improvements could be made if updates
+ // from scan->ui threads described small changelists instead of updating the entire list
+ // at once.
+ bool cache_modified = false;
+ for (auto& file : m_cached_files)
+ {
+ if (file->ReloadBannerIfNeeded())
+ cache_modified = true;
+ }
+
+ QueueEvent(new wxCommandEvent(DOLPHIN_EVT_REFRESH_GAMELIST));
+
+ if (cache_modified)
+ SyncCacheFile(true);
+ }
+ else
+ {
+ RescanList();
+ }
+
+ m_scan_trigger.Wait();
+ while (!m_scan_exiting.IsSet())
+ {
+ RescanList();
+ m_scan_trigger.Wait();
+ }
+ });
+ }
}
-CGameListCtrl::~CGameListCtrl()
+GameListCtrl::~GameListCtrl()
{
+ if (m_scan_thread.joinable())
+ {
+ m_scan_exiting.Set();
+ m_scan_trigger.Set();
+ m_scan_thread.join();
+ }
}
template <typename T>
@@ -307,7 +323,7 @@ static void InitBitmap(wxImageList* img_list, std::vector<int>* vector, wxWindow
wxTransparentColour));
}
-void CGameListCtrl::InitBitmaps()
+void GameListCtrl::InitBitmaps()
{
const wxSize size = FromDIP(wxSize(96, 32));
const wxSize flag_bmp_size = FromDIP(wxSize(32, 32));
@@ -316,59 +332,63 @@ void CGameListCtrl::InitBitmaps()
wxImageList* img_list = new wxImageList(size.GetWidth(), size.GetHeight());
AssignImageList(img_list, wxIMAGE_LIST_SMALL);
- m_FlagImageIndex.resize(static_cast<size_t>(DiscIO::Country::NUMBER_OF_COUNTRIES));
- InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_JAPAN,
+ auto& flag_indexes = m_image_indexes.flag;
+ flag_indexes.resize(static_cast<size_t>(DiscIO::Country::NUMBER_OF_COUNTRIES));
+ InitBitmap(img_list, &flag_indexes, this, flag_bmp_size, DiscIO::Country::COUNTRY_JAPAN,
"Flag_Japan");
- InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_EUROPE,
+ InitBitmap(img_list, &flag_indexes, this, flag_bmp_size, DiscIO::Country::COUNTRY_EUROPE,
"Flag_Europe");
- InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_USA,
+ InitBitmap(img_list, &flag_indexes, this, flag_bmp_size, DiscIO::Country::COUNTRY_USA,
"Flag_USA");
- InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_AUSTRALIA,
+ InitBitmap(img_list, &flag_indexes, this, flag_bmp_size, DiscIO::Country::COUNTRY_AUSTRALIA,
"Flag_Australia");
- InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_FRANCE,
+ InitBitmap(img_list, &flag_indexes, this, flag_bmp_size, DiscIO::Country::COUNTRY_FRANCE,
"Flag_France");
- InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_GERMANY,
+ InitBitmap(img_list, &flag_indexes, this, flag_bmp_size, DiscIO::Country::COUNTRY_GERMANY,
"Flag_Germany");
- InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_ITALY,
+ InitBitmap(img_list, &flag_indexes, this, flag_bmp_size, DiscIO::Country::COUNTRY_ITALY,
"Flag_Italy");
- InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_KOREA,
+ InitBitmap(img_list, &flag_indexes, this, flag_bmp_size, DiscIO::Country::COUNTRY_KOREA,
"Flag_Korea");
- InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_NETHERLANDS,
+ InitBitmap(img_list, &flag_indexes, this, flag_bmp_size, DiscIO::Country::COUNTRY_NETHERLANDS,
"Flag_Netherlands");
- InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_RUSSIA,
+ InitBitmap(img_list, &flag_indexes, this, flag_bmp_size, DiscIO::Country::COUNTRY_RUSSIA,
"Flag_Russia");
- InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_SPAIN,
+ InitBitmap(img_list, &flag_indexes, this, flag_bmp_size, DiscIO::Country::COUNTRY_SPAIN,
"Flag_Spain");
- InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_TAIWAN,
+ InitBitmap(img_list, &flag_indexes, this, flag_bmp_size, DiscIO::Country::COUNTRY_TAIWAN,
"Flag_Taiwan");
- InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_WORLD,
+ InitBitmap(img_list, &flag_indexes, this, flag_bmp_size, DiscIO::Country::COUNTRY_WORLD,
"Flag_International");
- InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_UNKNOWN,
+ InitBitmap(img_list, &flag_indexes, this, flag_bmp_size, DiscIO::Country::COUNTRY_UNKNOWN,
"Flag_Unknown");
- m_PlatformImageIndex.resize(static_cast<size_t>(DiscIO::Platform::NUMBER_OF_PLATFORMS));
- InitBitmap(img_list, &m_PlatformImageIndex, this, platform_bmp_size,
- DiscIO::Platform::GAMECUBE_DISC, "Platform_Gamecube");
- InitBitmap(img_list, &m_PlatformImageIndex, this, platform_bmp_size, DiscIO::Platform::WII_DISC,
+ auto& platform_indexes = m_image_indexes.platform;
+ platform_indexes.resize(static_cast<size_t>(DiscIO::Platform::NUMBER_OF_PLATFORMS));
+ InitBitmap(img_list, &platform_indexes, this, platform_bmp_size, DiscIO::Platform::GAMECUBE_DISC,
+ "Platform_Gamecube");
+ InitBitmap(img_list, &platform_indexes, this, platform_bmp_size, DiscIO::Platform::WII_DISC,
"Platform_Wii");
- InitBitmap(img_list, &m_PlatformImageIndex, this, platform_bmp_size, DiscIO::Platform::WII_WAD,
+ InitBitmap(img_list, &platform_indexes, this, platform_bmp_size, DiscIO::Platform::WII_WAD,
"Platform_Wad");
- InitBitmap(img_list, &m_PlatformImageIndex, this, platform_bmp_size, DiscIO::Platform::ELF_DOL,
+ InitBitmap(img_list, &platform_indexes, this, platform_bmp_size, DiscIO::Platform::ELF_DOL,
"Platform_File");
- m_EmuStateImageIndex.resize(6);
- InitBitmap(img_list, &m_EmuStateImageIndex, this, rating_bmp_size, 0, "rating0", true);
- InitBitmap(img_list, &m_EmuStateImageIndex, this, rating_bmp_size, 1, "rating1", true);
- InitBitmap(img_list, &m_EmuStateImageIndex, this, rating_bmp_size, 2, "rating2", true);
- InitBitmap(img_list, &m_EmuStateImageIndex, this, rating_bmp_size, 3, "rating3", true);
- InitBitmap(img_list, &m_EmuStateImageIndex, this, rating_bmp_size, 4, "rating4", true);
- InitBitmap(img_list, &m_EmuStateImageIndex, this, rating_bmp_size, 5, "rating5", true);
-
- m_utility_game_banners.resize(1);
- InitBitmap(img_list, &m_utility_game_banners, this, size, 0, "nobanner");
+ auto& emu_state_indexes = m_image_indexes.emu_state;
+ emu_state_indexes.resize(6);
+ InitBitmap(img_list, &emu_state_indexes, this, rating_bmp_size, 0, "rating0", true);
+ InitBitmap(img_list, &emu_state_indexes, this, rating_bmp_size, 1, "rating1", true);
+ InitBitmap(img_list, &emu_state_indexes, this, rating_bmp_size, 2, "rating2", true);
+ InitBitmap(img_list, &emu_state_indexes, this, rating_bmp_size, 3, "rating3", true);
+ InitBitmap(img_list, &emu_state_indexes, this, rating_bmp_size, 4, "rating4", true);
+ InitBitmap(img_list, &emu_state_indexes, this, rating_bmp_size, 5, "rating5", true);
+
+ auto& utility_banner_indexes = m_image_indexes.utility_banner;
+ utility_banner_indexes.resize(1);
+ InitBitmap(img_list, &utility_banner_indexes, this, size, 0, "nobanner");
}
-void CGameListCtrl::BrowseForDirectory()
+void GameListCtrl::BrowseForDirectory()
{
wxString dirHome;
wxGetHomeDir(&dirHome);
@@ -388,25 +408,42 @@ void CGameListCtrl::BrowseForDirectory()
{
SConfig::GetInstance().m_ISOFolder.push_back(sPath);
SConfig::GetInstance().SaveSettings();
+ m_scan_trigger.Set();
}
-
- ReloadList();
}
}
-void CGameListCtrl::ReloadList()
+void GameListCtrl::RefreshList()
{
int scrollPos = wxWindow::GetScrollPos(wxVERTICAL);
// Don't let the user refresh it while a game is running
if (Core::GetState() != Core::State::Uninitialized)
return;
- ScanForISOs();
+ m_shown_files.clear();
+ for (auto& item : m_cached_files)
+ {
+ if (ShouldDisplayGameListItem(*item))
+ m_shown_files.push_back(item);
+ }
+
+ // Drives are not cached. Not sure if this is required, but better to err on the
+ // side of caution if cross-platform issues could come into play.
+ if (SConfig::GetInstance().m_ListDrives)
+ {
+ const Core::TitleDatabase title_database;
+ for (const auto& drive : cdio_get_devices())
+ {
+ auto file = std::make_shared<GameListItem>(drive, title_database);
+ if (file->IsValid())
+ m_shown_files.push_back(file);
+ }
+ }
Freeze();
ClearAll();
- if (m_ISOFiles.size() != 0)
+ if (!m_shown_files.empty())
{
// Don't load bitmaps unless there are games to list
InitBitmaps();
@@ -431,20 +468,19 @@ void CGameListCtrl::ReloadList()
#endif
// set initial sizes for columns
SetColumnWidth(COLUMN_DUMMY, 0);
-
for (const auto& c : m_columns)
{
SetColumnWidth(c.id, c.visible ? FromDIP(c.default_width + platform_padding) : 0);
}
// add all items
- for (int i = 0; i < (int)m_ISOFiles.size(); i++)
+ for (int i = 0; i < (int)m_shown_files.size(); i++)
InsertItemInReportView(i);
SetColors();
// Sort items by Title
if (!sorted)
- last_column = 0;
+ m_last_column = 0;
sorted = false;
wxListEvent event;
event.m_col = SConfig::GetInstance().m_ListSort2;
@@ -478,7 +514,7 @@ void CGameListCtrl::ReloadList()
errorString = _("Dolphin could not find any GameCube/Wii ISOs or WADs. Double-click here to "
"set a games directory...");
}
- InsertColumn(0, "");
+ InsertColumn(COLUMN_DUMMY, "");
long index = InsertItem(0, errorString);
SetItemFont(index, *wxITALIC_FONT);
SetColumnWidth(0, wxLIST_AUTOSIZE);
@@ -512,37 +548,37 @@ static wxString NiceSizeFormat(u64 size)
return wxString::Format("%.2f %s", size / unit_size, unit_symbols[unit]);
}
-// Update the column content of the item at _Index
-void CGameListCtrl::UpdateItemAtColumn(long _Index, int column)
+// Update the column content of the item at index
+void GameListCtrl::UpdateItemAtColumn(long index, int column)
{
- GameListItem& rISOFile = *m_ISOFiles[GetItemData(_Index)];
+ const auto& iso_file = *GetISO(GetItemData(index));
switch (column)
{
case COLUMN_PLATFORM:
{
- SetItemColumnImage(_Index, COLUMN_PLATFORM,
- m_PlatformImageIndex[static_cast<size_t>(rISOFile.GetPlatform())]);
+ SetItemColumnImage(index, COLUMN_PLATFORM,
+ m_image_indexes.platform[static_cast<size_t>(iso_file.GetPlatform())]);
break;
}
case COLUMN_BANNER:
{
- int ImageIndex = m_utility_game_banners[0]; // nobanner
+ int image_index = m_image_indexes.utility_banner[0]; // nobanner
- if (rISOFile.GetBannerImage().IsOk())
+ if (iso_file.GetBannerImage().IsOk())
{
wxImageList* img_list = GetImageList(wxIMAGE_LIST_SMALL);
- ImageIndex = img_list->Add(
- WxUtils::ScaleImageToBitmap(rISOFile.GetBannerImage(), this, img_list->GetSize()));
+ image_index = img_list->Add(
+ WxUtils::ScaleImageToBitmap(iso_file.GetBannerImage(), this, img_list->GetSize()));
}
- SetItemColumnImage(_Index, COLUMN_BANNER, ImageIndex);
+ SetItemColumnImage(index, COLUMN_BANNER, image_index);
break;
}
case COLUMN_TITLE:
{
- wxString name = StrToWxStr(rISOFile.GetName());
- int disc_number = rISOFile.GetDiscNumber() + 1;
+ wxString name = StrToWxStr(iso_file.GetName());
+ int disc_number = iso_file.GetDiscNumber() + 1;
if (disc_number > 1 &&
name.Lower().find(wxString::Format("disc %i", disc_number)) == std::string::npos &&
@@ -551,40 +587,40 @@ void CGameListCtrl::UpdateItemAtColumn(long _Index, int column)
name = wxString::Format(_("%s (Disc %i)"), name.c_str(), disc_number);
}
- SetItem(_Index, COLUMN_TITLE, name, -1);
+ SetItem(index, COLUMN_TITLE, name, -1);
break;
}
case COLUMN_MAKER:
- SetItem(_Index, COLUMN_MAKER, StrToWxStr(rISOFile.GetCompany()), -1);
+ SetItem(index, COLUMN_MAKER, StrToWxStr(iso_file.GetCompany()), -1);
break;
case COLUMN_FILENAME:
- SetItem(_Index, COLUMN_FILENAME, wxFileNameFromPath(StrToWxStr(rISOFile.GetFileName())), -1);
+ SetItem(index, COLUMN_FILENAME, wxFileNameFromPath(StrToWxStr(iso_file.GetFileName())), -1);
break;
case COLUMN_EMULATION_STATE:
- SetItemColumnImage(_Index, COLUMN_EMULATION_STATE,
- m_EmuStateImageIndex[rISOFile.GetEmuState()]);
+ SetItemColumnImage(index, COLUMN_EMULATION_STATE,
+ m_image_indexes.emu_state[iso_file.GetEmuState()]);
break;
case COLUMN_COUNTRY:
- SetItemColumnImage(_Index, COLUMN_COUNTRY,
- m_FlagImageIndex[static_cast<size_t>(rISOFile.GetCountry())]);
+ SetItemColumnImage(index, COLUMN_COUNTRY,
+ m_image_indexes.flag[static_cast<size_t>(iso_file.GetCountry())]);
break;
case COLUMN_SIZE:
- SetItem(_Index, COLUMN_SIZE, NiceSizeFormat(rISOFile.GetFileSize()), -1);
+ SetItem(index, COLUMN_SIZE, NiceSizeFormat(iso_file.GetFileSize()), -1);
break;
case COLUMN_ID:
- SetItem(_Index, COLUMN_ID, rISOFile.GetGameID(), -1);
+ SetItem(index, COLUMN_ID, iso_file.GetGameID(), -1);
break;
}
}
-void CGameListCtrl::InsertItemInReportView(long index)
+void GameListCtrl::InsertItemInReportView(long index)
{
// When using wxListCtrl, there is no hope of per-column text colors.
// But for reference, here are the old colors that were used: (BGR)
// title: 0xFF0000
// company: 0x007030
- // Insert a first column with nothing in it, that will be used as the Index
+ // Insert a first column (COLUMN_DUMMY) with nothing in it to use as the Index
long item_index;
{
wxListItem li;
@@ -595,7 +631,7 @@ void CGameListCtrl::InsertItemInReportView(long index)
}
// Iterate over all columns and fill them with content if they are visible
- for (int i = 1; i < NUMBER_OF_COLUMN; i++)
+ for (int i = FIRST_COLUMN_WITH_CONTENT; i < NUMBER_OF_COLUMN; i++)
{
if (GetColumnWidth(i) != 0)
UpdateItemAtColumn(item_index, i);
@@ -620,7 +656,7 @@ static wxColour ContrastText(const wxColour& bgc)
return (lum > LUM_THRESHOLD) ? *wxBLACK : *wxWHITE;
}
-void CGameListCtrl::SetColors()
+void GameListCtrl::SetColors()
{
for (long i = 0; i < GetItemCount(); i++)
{
@@ -632,99 +668,155 @@ void CGameListCtrl::SetColors()
}
}
-void CGameListCtrl::ScanForISOs()
+void GameListCtrl::DoState(PointerWrap* p, u32 size)
{
- m_ISOFiles.clear();
-
- const Core::TitleDatabase title_database;
- auto rFilenames =
- Common::DoFileSearch(GetFileSearchExtensions(), SConfig::GetInstance().m_ISOFolder,
- SConfig::GetInstance().m_RecursiveISOFolder);
-
- if (rFilenames.size() > 0)
+ struct
{
- wxProgressDialog dialog(
- _("Scanning for ISOs"), _("Scanning..."), (int)rFilenames.size() - 1, this,
- wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT | wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME |
- wxPD_REMAINING_TIME | wxPD_SMOOTH // - makes updates as small as possible (down to 1px)
- );
-
- for (u32 i = 0; i < rFilenames.size(); i++)
+ 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)
{
- std::string FileName;
- SplitPath(rFilenames[i], nullptr, &FileName, nullptr);
-
- // Update with the progress (i) and the message
- dialog.Update(i, wxString::Format(_("Scanning %s"), StrToWxStr(FileName)));
- if (dialog.WasCancelled())
- break;
-
- auto iso_file = std::make_unique<GameListItem>(rFilenames[i], title_database);
-
- if (iso_file->IsValid() && ShouldDisplayGameListItem(*iso_file))
- {
- m_ISOFiles.push_back(std::move(iso_file));
- }
+ 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);
+ });
+}
- if (SConfig::GetInstance().m_ListDrives)
+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)
{
- const std::vector<std::string> drives = cdio_get_devices();
-
- for (const auto& drive : drives)
+ // 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()))
{
- auto gli = std::make_unique<GameListItem>(drive, title_database);
-
- if (gli->IsValid())
- m_ISOFiles.push_back(std::move(gli));
+ u8* ptr = buffer.data();
+ PointerWrap p(&ptr, PointerWrap::MODE_READ);
+ DoState(&p, buffer.size());
+ if (p.GetMode() == PointerWrap::MODE_READ)
+ success = true;
}
}
-
- std::sort(m_ISOFiles.begin(), m_ISOFiles.end());
+ if (!success)
+ {
+ // If some file operation failed, try to delete the probably-corrupted cache
+ f.Close();
+ File::Delete(filename);
+ }
+ return success;
}
-void CGameListCtrl::OnReloadGameList(wxCommandEvent& WXUNUSED(event))
+void GameListCtrl::RescanList()
{
- ReloadList();
+ auto post_status = [&](const wxString& status) {
+ auto event = new wxCommandEvent(wxEVT_HOST_COMMAND, IDM_UPDATE_STATUS_BAR);
+ event->SetInt(0);
+ event->SetString(status);
+ QueueEvent(event);
+ };
+
+ 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
+ auto search_results = Common::DoFileSearch(SConfig::GetInstance().m_ISOFolder, search_extensions,
+ SConfig::GetInstance().m_RecursiveISOFolder);
+
+ std::vector<std::string> cached_paths;
+ for (const auto& file : m_cached_files)
+ cached_paths.emplace_back(file->GetFileName());
+ std::sort(cached_paths.begin(), cached_paths.end());
+
+ std::list<std::string> removed_paths;
+ std::set_difference(cached_paths.cbegin(), cached_paths.cend(), search_results.cbegin(),
+ search_results.cend(), std::back_inserter(removed_paths));
+
+ std::vector<std::string> new_paths;
+ std::set_difference(search_results.cbegin(), search_results.cend(), cached_paths.cbegin(),
+ cached_paths.cend(), std::back_inserter(new_paths));
+
+ const Core::TitleDatabase title_database;
+ // For now, only scan new_paths. This could cause false negatives (file actively being written),
+ // but otherwise should be fine.
+ for (const auto& path : removed_paths)
+ {
+ auto it = std::find_if(
+ m_cached_files.cbegin(), m_cached_files.cend(),
+ [&path](const std::shared_ptr<GameListItem>& file) { return file->GetFileName() == path; });
+ if (it != m_cached_files.cend())
+ m_cached_files.erase(it);
+ }
+ for (const auto& path : new_paths)
+ {
+ auto file = std::make_shared<GameListItem>(path, title_database);
+ if (file->IsValid())
+ m_cached_files.push_back(std::move(file));
+ }
+
+ for (auto& file : m_cached_files)
+ file->ReloadINI();
+
+ // Post UI event to update the displayed list
+ QueueEvent(new wxCommandEvent(DOLPHIN_EVT_REFRESH_GAMELIST));
+
+ post_status(_("Scan complete!"));
+
+ SyncCacheFile(true);
}
-void CGameListCtrl::OnLocalIniModified(wxCommandEvent& ev)
+void GameListCtrl::OnRefreshGameList(wxCommandEvent& WXUNUSED(event))
{
- ev.Skip();
- std::string game_id = WxStrToStr(ev.GetString());
- // NOTE: The same game may occur multiple times if there are multiple
- // physical copies in the search paths.
- for (std::size_t i = 0; i < m_ISOFiles.size(); ++i)
- {
- if (m_ISOFiles[i]->GetGameID() != game_id)
- continue;
- m_ISOFiles[i]->ReloadINI();
+ RefreshList();
+}
- // The indexes in m_ISOFiles and the list do not line up.
- // We need to find the corresponding item in the list (if it exists)
- long item_id = 0;
- for (; item_id < GetItemCount(); ++item_id)
- {
- if (i == static_cast<std::size_t>(GetItemData(item_id)))
- break;
- }
- // If the item is not currently being displayed then we're done.
- if (item_id == GetItemCount())
- continue;
+void GameListCtrl::OnRescanGameList(wxCommandEvent& WXUNUSED(event))
+{
+ m_scan_trigger.Set();
+}
- // Update all the columns
- for (int j = 1; j < NUMBER_OF_COLUMN; ++j)
- {
- // NOTE: Banner is not modified by the INI and updating it will
- // duplicate it in memory which is not wanted.
- if (j != COLUMN_BANNER && GetColumnWidth(j) != 0)
- UpdateItemAtColumn(item_id, j);
- }
- }
+void GameListCtrl::OnLocalIniModified(wxCommandEvent& ev)
+{
+ ev.Skip();
+ // We need show any changes to the ini which could impact our columns. Currently only the
+ // EmuState/Issues settings can do that. We also need to persist the changes to the cache - so
+ // just trigger a rescan which will sync the cache and then display the new values.
+ m_scan_trigger.Set();
}
-void CGameListCtrl::OnColBeginDrag(wxListEvent& event)
+void GameListCtrl::OnColBeginDrag(wxListEvent& event)
{
const int column_id = event.GetColumn();
@@ -732,15 +824,15 @@ void CGameListCtrl::OnColBeginDrag(wxListEvent& event)
event.Veto();
}
-const GameListItem* CGameListCtrl::GetISO(size_t index) const
+const GameListItem* GameListCtrl::GetISO(size_t index) const
{
- if (index < m_ISOFiles.size())
- return m_ISOFiles[index].get();
+ if (index < m_shown_files.size())
+ return m_shown_files[index].get();
return nullptr;
}
-static CGameListCtrl* caller;
+static GameListCtrl* caller;
static int wxCALLBACK wxListCompare(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
{
// return 1 if item1 > item2
@@ -752,32 +844,32 @@ static int wxCALLBACK wxListCompare(wxIntPtr item1, wxIntPtr item2, wxIntPtr sor
return CompareGameListItems(iso1, iso2, sortData);
}
-void CGameListCtrl::OnColumnClick(wxListEvent& event)
+void GameListCtrl::OnColumnClick(wxListEvent& event)
{
if (event.GetColumn() != COLUMN_BANNER)
{
int current_column = event.GetColumn();
if (sorted)
{
- if (last_column == current_column)
+ if (m_last_column == current_column)
{
- last_sort = -last_sort;
+ m_last_sort = -m_last_sort;
}
else
{
- SConfig::GetInstance().m_ListSort2 = last_sort;
- last_column = current_column;
- last_sort = current_column;
+ SConfig::GetInstance().m_ListSort2 = m_last_sort;
+ m_last_column = current_column;
+ m_last_sort = current_column;
}
- SConfig::GetInstance().m_ListSort = last_sort;
+ SConfig::GetInstance().m_ListSort = m_last_sort;
}
else
{
- last_sort = current_column;
- last_column = current_column;
+ m_last_sort = current_column;
+ m_last_column = current_column;
}
caller = this;
- SortItems(wxListCompare, last_sort);
+ SortItems(wxListCompare, m_last_sort);
}
SetColors();
@@ -786,12 +878,12 @@ void CGameListCtrl::OnColumnClick(wxListEvent& event)
}
// This is used by keyboard gamelist search
-void CGameListCtrl::OnKeyPress(wxListEvent& event)
+void GameListCtrl::OnKeyPress(wxListEvent& event)
{
static int lastKey = 0, sLoop = 0;
int Loop = 0;
- for (int i = 0; i < (int)m_ISOFiles.size(); i++)
+ for (int i = 0; i < (int)m_shown_files.size(); i++)
{
// Easy way to get game string
wxListItem bleh;
@@ -807,7 +899,7 @@ void CGameListCtrl::OnKeyPress(wxListEvent& event)
if (lastKey == event.GetKeyCode() && Loop < sLoop)
{
Loop++;
- if (i + 1 == (int)m_ISOFiles.size())
+ if (i + 1 == (int)m_shown_files.size())
i = -1;
continue;
}
@@ -828,7 +920,7 @@ void CGameListCtrl::OnKeyPress(wxListEvent& event)
// If we get past the last game in the list,
// we'll have to go back to the first one.
- if (i + 1 == (int)m_ISOFiles.size() && sLoop > 0 && Loop > 0)
+ if (i + 1 == (int)m_shown_files.size() && sLoop > 0 && Loop > 0)
i = -1;
}
@@ -836,7 +928,7 @@ void CGameListCtrl::OnKeyPress(wxListEvent& event)
}
// This shows a little tooltip with the current Game's emulation state
-void CGameListCtrl::OnMouseMotion(wxMouseEvent& event)
+void GameListCtrl::OnMouseMotion(wxMouseEvent& event)
{
int flags;
long subitem = 0;
@@ -859,7 +951,7 @@ void CGameListCtrl::OnMouseMotion(wxMouseEvent& event)
if (Rect.GetX() + Rect.GetWidth() - GetColumnWidth(COLUMN_EMULATION_STATE) < event.GetX())
#endif
{
- if (toolTip || lastItem == item || this != FindFocus())
+ if (m_tooltip || lastItem == item || this != FindFocus())
{
if (lastItem != item)
lastItem = -1;
@@ -870,7 +962,7 @@ void CGameListCtrl::OnMouseMotion(wxMouseEvent& event)
// Emulation status
static const char* const emuState[] = {"Broken", "Intro", "In-Game", "Playable", "Perfect"};
- const GameListItem* iso = m_ISOFiles[GetItemData(item)].get();
+ const GameListItem* iso = GetISO(GetItemData(item));
const int emu_state = iso->GetEmuState();
const std::string& issues = iso->GetIssues();
@@ -881,11 +973,11 @@ void CGameListCtrl::OnMouseMotion(wxMouseEvent& event)
char temp[2048];
sprintf(temp, "^ %s%s%s", emuState[emu_state - 1], issues.size() > 0 ? " :\n" : "",
issues.c_str());
- toolTip = new wxEmuStateTip(this, StrToWxStr(temp), &toolTip);
+ m_tooltip = new wxEmuStateTip(this, StrToWxStr(temp), &m_tooltip);
}
else
{
- toolTip = new wxEmuStateTip(this, _("Not Set"), &toolTip);
+ m_tooltip = new wxEmuStateTip(this, _("Not Set"), &m_tooltip);
}
// Get item Coords
@@ -900,20 +992,20 @@ void CGameListCtrl::OnMouseMotion(wxMouseEvent& event)
#endif
// Convert to screen coordinates
ClientToScreen(&mx, &my);
- toolTip->SetBoundingRect(wxRect(mx - GetColumnWidth(COLUMN_EMULATION_STATE), my,
- GetColumnWidth(COLUMN_EMULATION_STATE), Rect.GetHeight()));
- toolTip->SetPosition(
+ m_tooltip->SetBoundingRect(wxRect(mx - GetColumnWidth(COLUMN_EMULATION_STATE), my,
+ GetColumnWidth(COLUMN_EMULATION_STATE), Rect.GetHeight()));
+ m_tooltip->SetPosition(
wxPoint(mx - GetColumnWidth(COLUMN_EMULATION_STATE), my - 5 + Rect.GetHeight()));
lastItem = item;
}
}
- if (!toolTip)
+ if (!m_tooltip)
lastItem = -1;
event.Skip();
}
-void CGameListCtrl::OnLeftClick(wxMouseEvent& event)
+void GameListCtrl::OnLeftClick(wxMouseEvent& event)
{
// Focus the clicked item.
int flags;
@@ -945,7 +1037,7 @@ static bool IsWADInstalled(const GameListItem& wad)
[](const auto& file) { return file.virtualName != "title.tmd"; });
}
-void CGameListCtrl::OnRightClick(wxMouseEvent& event)
+void GameListCtrl::OnRightClick(wxMouseEvent& event)
{
// Focus the clicked item.
int flags;
@@ -1043,9 +1135,9 @@ void CGameListCtrl::OnRightClick(wxMouseEvent& event)
}
}
-const GameListItem* CGameListCtrl::GetSelectedISO() const
+const GameListItem* GameListCtrl::GetSelectedISO() const
{
- if (m_ISOFiles.empty())
+ if (m_shown_files.empty())
return nullptr;
if (GetSelectedItemCount() == 0)
@@ -1055,10 +1147,10 @@ const GameListItem* CGameListCtrl::GetSelectedISO() const
if (item == wxNOT_FOUND)
return nullptr;
- return m_ISOFiles[GetItemData(item)].get();
+ return GetISO(GetItemData(item));
}
-std::vector<const GameListItem*> CGameListCtrl::GetAllSelectedISOs() const
+std::vector<const GameListItem*> GameListCtrl::GetAllSelectedISOs() const
{
std::vector<const GameListItem*> result;
long item = -1;
@@ -1067,11 +1159,11 @@ std::vector<const GameListItem*> CGameListCtrl::GetAllSelectedISOs() const
item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item == wxNOT_FOUND)
return result;
- result.push_back(m_ISOFiles[GetItemData(item)].get());
+ result.push_back(GetISO(GetItemData(item)));
}
}
-bool CGameListCtrl::IsHidingItems()
+bool GameListCtrl::IsHidingItems()
{
return !(SConfig::GetInstance().m_ListGC && SConfig::GetInstance().m_ListWii &&
SConfig::GetInstance().m_ListWad && SConfig::GetInstance().m_ListElfDol &&
@@ -1084,7 +1176,7 @@ bool CGameListCtrl::IsHidingItems()
SConfig::GetInstance().m_ListWorld && SConfig::GetInstance().m_ListUnknown);
}
-void CGameListCtrl::OnOpenContainingFolder(wxCommandEvent& WXUNUSED(event))
+void GameListCtrl::OnOpenContainingFolder(wxCommandEvent& WXUNUSED(event))
{
const GameListItem* iso = GetSelectedISO();
if (!iso)
@@ -1095,7 +1187,7 @@ void CGameListCtrl::OnOpenContainingFolder(wxCommandEvent& WXUNUSED(event))
WxUtils::Explore(WxStrToStr(path.GetPath()));
}
-void CGameListCtrl::OnOpenSaveFolder(wxCommandEvent& WXUNUSED(event))
+void GameListCtrl::OnOpenSaveFolder(wxCommandEvent& WXUNUSED(event))
{
const GameListItem* iso = GetSelectedISO();
if (!iso)
@@ -1105,7 +1197,7 @@ void CGameListCtrl::OnOpenSaveFolder(wxCommandEvent& WXUNUSED(event))
WxUtils::Explore(path);
}
-void CGameListCtrl::OnExportSave(wxCommandEvent& WXUNUSED(event))
+void GameListCtrl::OnExportSave(wxCommandEvent& WXUNUSED(event))
{
const GameListItem* iso = GetSelectedISO();
if (iso)
@@ -1113,7 +1205,7 @@ void CGameListCtrl::OnExportSave(wxCommandEvent& WXUNUSED(event))
}
// Save this file as the default file
-void CGameListCtrl::OnSetDefaultISO(wxCommandEvent& event)
+void GameListCtrl::OnSetDefaultISO(wxCommandEvent& event)
{
const GameListItem* iso = GetSelectedISO();
if (!iso)
@@ -1133,7 +1225,7 @@ void CGameListCtrl::OnSetDefaultISO(wxCommandEvent& event)
}
}
-void CGameListCtrl::OnDeleteISO(wxCommandEvent& WXUNUSED(event))
+void GameListCtrl::OnDeleteISO(wxCommandEvent& WXUNUSED(event))
{
const wxString message =
GetSelectedItemCount() == 1 ?
@@ -1144,11 +1236,11 @@ void CGameListCtrl::OnDeleteISO(wxCommandEvent& WXUNUSED(event))
{
for (const GameListItem* iso : GetAllSelectedISOs())
File::Delete(iso->GetFileName());
- ReloadList();
+ m_scan_trigger.Set();
}
}
-void CGameListCtrl::OnProperties(wxCommandEvent& WXUNUSED(event))
+void GameListCtrl::OnProperties(wxCommandEvent& WXUNUSED(event))
{
const GameListItem* iso = GetSelectedISO();
if (!iso)
@@ -1158,7 +1250,7 @@ void CGameListCtrl::OnProperties(wxCommandEvent& WXUNUSED(event))
ISOProperties->Show();
}
-void CGameListCtrl::OnWiki(wxCommandEvent& WXUNUSED(event))
+void GameListCtrl::OnWiki(wxCommandEvent& WXUNUSED(event))
{
const GameListItem* iso = GetSelectedISO();
if (!iso)
@@ -1169,7 +1261,7 @@ void CGameListCtrl::OnWiki(wxCommandEvent& WXUNUSED(event))
WxUtils::Launch(wikiUrl);
}
-void CGameListCtrl::OnNetPlayHost(wxCommandEvent& WXUNUSED(event))
+void GameListCtrl::OnNetPlayHost(wxCommandEvent& WXUNUSED(event))
{
const GameListItem* iso = GetSelectedISO();
if (!iso)
@@ -1192,7 +1284,7 @@ void CGameListCtrl::OnNetPlayHost(wxCommandEvent& WXUNUSED(event))
NetPlayLauncher::Host(config);
}
-bool CGameListCtrl::MultiCompressCB(const std::string& text, float percent, void* arg)
+bool GameListCtrl::MultiCompressCB(const std::string& text, float percent, void* arg)
{
CompressionProgress* progress = static_cast<CompressionProgress*>(arg);
@@ -1204,17 +1296,17 @@ bool CGameListCtrl::MultiCompressCB(const std::string& text, float percent, void
return progress->dialog->Update(total_percent * progress->dialog->GetRange(), text_string);
}
-void CGameListCtrl::OnMultiCompressISO(wxCommandEvent& /*event*/)
+void GameListCtrl::OnMultiCompressISO(wxCommandEvent& /*event*/)
{
CompressSelection(true);
}
-void CGameListCtrl::OnMultiDecompressISO(wxCommandEvent& /*event*/)
+void GameListCtrl::OnMultiDecompressISO(wxCommandEvent& /*event*/)
{
CompressSelection(false);
}
-void CGameListCtrl::CompressSelection(bool _compress)
+void GameListCtrl::CompressSelection(bool _compress)
{
std::vector<const GameListItem*> items_to_compress;
bool wii_compression_warning_accepted = false;
@@ -1316,15 +1408,15 @@ void CGameListCtrl::CompressSelection(bool _compress)
if (!all_good)
WxUtils::ShowErrorDialog(_("Dolphin was unable to complete the requested action."));
- ReloadList();
+ m_scan_trigger.Set();
}
-bool CGameListCtrl::CompressCB(const std::string& text, float percent, void* arg)
+bool GameListCtrl::CompressCB(const std::string& text, float percent, void* arg)
{
return ((wxProgressDialog*)arg)->Update((int)(percent * 1000), StrToWxStr(text));
}
-void CGameListCtrl::OnCompressISO(wxCommandEvent& WXUNUSED(event))
+void GameListCtrl::OnCompressISO(wxCommandEvent& WXUNUSED(event))
{
const GameListItem* iso = GetSelectedISO();
if (!iso)
@@ -1389,10 +1481,10 @@ void CGameListCtrl::OnCompressISO(wxCommandEvent& WXUNUSED(event))
if (!all_good)
WxUtils::ShowErrorDialog(_("Dolphin was unable to complete the requested action."));
- ReloadList();
+ m_scan_trigger.Set();
}
-void CGameListCtrl::OnChangeDisc(wxCommandEvent& WXUNUSED(event))
+void GameListCtrl::OnChangeDisc(wxCommandEvent& WXUNUSED(event))
{
const GameListItem* iso = GetSelectedISO();
if (!iso || !Core::IsRunning())
@@ -1400,17 +1492,17 @@ void CGameListCtrl::OnChangeDisc(wxCommandEvent& WXUNUSED(event))
DVDInterface::ChangeDiscAsHost(WxStrToStr(iso->GetFileName()));
}
-void CGameListCtrl::OnSize(wxSizeEvent& event)
+void GameListCtrl::OnSize(wxSizeEvent& event)
{
event.Skip();
- if (lastpos == event.GetSize())
+ if (m_lastpos == event.GetSize())
return;
- lastpos = event.GetSize();
+ m_lastpos = event.GetSize();
AutomaticColumnWidth();
}
-void CGameListCtrl::AutomaticColumnWidth()
+void GameListCtrl::AutomaticColumnWidth()
{
wxRect rc(GetClientRect());
@@ -1444,14 +1536,14 @@ void CGameListCtrl::AutomaticColumnWidth()
Thaw();
}
-void CGameListCtrl::UnselectAll()
+void GameListCtrl::UnselectAll()
{
for (int i = 0; i < GetItemCount(); i++)
{
SetItemState(i, 0, wxLIST_STATE_SELECTED);
}
}
-bool CGameListCtrl::WiiCompressWarning()
+bool GameListCtrl::WiiCompressWarning()
{
return wxMessageBox(_("Compressing a Wii disc image will irreversibly change the compressed copy "
"by removing padding data. Your disc image will still work. Continue?"),
@@ -1462,7 +1554,7 @@ bool CGameListCtrl::WiiCompressWarning()
// Windows draws vertical rules between columns when using UXTheme (e.g. Aero, Win10)
// This function paints over those lines which removes them.
// [The repaint background idea is ripped off from Eclipse SWT which does the same thing]
-bool CGameListCtrl::MSWOnNotify(int id, WXLPARAM lparam, WXLPARAM* result)
+bool GameListCtrl::MSWOnNotify(int id, WXLPARAM lparam, WXLPARAM* result)
{
NMLVCUSTOMDRAW* nmlv = reinterpret_cast<NMLVCUSTOMDRAW*>(lparam);
// Intercept the NM_CUSTOMDRAW[CDDS_PREPAINT]
diff --git a/Source/Core/DolphinWX/GameListCtrl.h b/Source/Core/DolphinWX/GameListCtrl.h
index a7995768c8..f8bdfce19f 100644
--- a/Source/Core/DolphinWX/GameListCtrl.h
+++ b/Source/Core/DolphinWX/GameListCtrl.h
@@ -7,11 +7,15 @@
#include <cstddef>
#include <memory>
#include <string>
+#include <thread>
#include <vector>
#include <wx/listctrl.h>
#include <wx/tipwin.h>
+#include "Common/ChunkFile.h"
+#include "Common/Event.h"
+#include "Common/Flag.h"
#include "DolphinWX/ISOFile.h"
class wxEmuStateTip : public wxTipWindow
@@ -31,26 +35,27 @@ public:
}
};
-wxDECLARE_EVENT(DOLPHIN_EVT_RELOAD_GAMELIST, wxCommandEvent);
+wxDECLARE_EVENT(DOLPHIN_EVT_REFRESH_GAMELIST, wxCommandEvent);
+wxDECLARE_EVENT(DOLPHIN_EVT_RESCAN_GAMELIST, wxCommandEvent);
-class CGameListCtrl : public wxListCtrl
+class GameListCtrl : public wxListCtrl
{
public:
- CGameListCtrl(wxWindow* parent, const wxWindowID id, const wxPoint& pos, const wxSize& size,
- long style);
- ~CGameListCtrl();
+ GameListCtrl(bool disable_scanning, wxWindow* parent, const wxWindowID id, const wxPoint& pos,
+ const wxSize& size, long style);
+ ~GameListCtrl();
void BrowseForDirectory();
const GameListItem* GetISO(size_t index) const;
const GameListItem* GetSelectedISO() const;
- std::vector<const GameListItem*> GetAllSelectedISOs() const;
static bool IsHidingItems();
enum
{
COLUMN_DUMMY = 0,
- COLUMN_PLATFORM,
+ FIRST_COLUMN_WITH_CONTENT,
+ COLUMN_PLATFORM = FIRST_COLUMN_WITH_CONTENT,
COLUMN_BANNER,
COLUMN_TITLE,
COLUMN_MAKER,
@@ -69,17 +74,19 @@ public:
private:
struct ColumnInfo;
- void ReloadList();
-
- void ClearIsoFiles() { m_ISOFiles.clear(); }
void InitBitmaps();
- void UpdateItemAtColumn(long _Index, int column);
- void InsertItemInReportView(long _Index);
+ void UpdateItemAtColumn(long index, int column);
+ void InsertItemInReportView(long index);
void SetColors();
- void ScanForISOs();
+ void RefreshList();
+ void RescanList();
+ void DoState(PointerWrap* p, u32 size = 0);
+ bool SyncCacheFile(bool write);
+ std::vector<const GameListItem*> GetAllSelectedISOs() const;
// events
- void OnReloadGameList(wxCommandEvent& event);
+ void OnRefreshGameList(wxCommandEvent& event);
+ void OnRescanGameList(wxCommandEvent& event);
void OnLeftClick(wxMouseEvent& event);
void OnRightClick(wxMouseEvent& event);
void OnMouseMotion(wxMouseEvent& event);
@@ -109,16 +116,27 @@ private:
static bool MultiCompressCB(const std::string& text, float percent, void* arg);
static bool WiiCompressWarning();
- std::vector<int> m_FlagImageIndex;
- std::vector<int> m_PlatformImageIndex;
- std::vector<int> m_EmuStateImageIndex;
- std::vector<int> m_utility_game_banners;
- std::vector<std::unique_ptr<GameListItem>> m_ISOFiles;
-
- int last_column;
- int last_sort;
- wxSize lastpos;
- wxEmuStateTip* toolTip;
+ struct
+ {
+ std::vector<int> flag;
+ std::vector<int> platform;
+ std::vector<int> utility_banner;
+ std::vector<int> emu_state;
+ } m_image_indexes;
+
+ // Actual backing GameListItems are maintained in a background thread and cached to file
+ static constexpr u32 CACHE_REVISION = 0;
+ std::list<std::shared_ptr<GameListItem>> m_cached_files;
+ std::thread m_scan_thread;
+ 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;
+
+ int m_last_column;
+ int m_last_sort;
+ wxSize m_lastpos;
+ wxEmuStateTip* m_tooltip;
std::vector<ColumnInfo> m_columns;
};
diff --git a/Source/Core/DolphinWX/ISOFile.cpp b/Source/Core/DolphinWX/ISOFile.cpp
index b3faa0a7e8..6d6b0a7d94 100644
--- a/Source/Core/DolphinWX/ISOFile.cpp
+++ b/Source/Core/DolphinWX/ISOFile.cpp
@@ -38,8 +38,6 @@
#include "DolphinWX/ISOFile.h"
#include "DolphinWX/WxUtils.h"
-static const u32 CACHE_REVISION = 0x129; // Last changed in PR 5102
-
static std::string GetLanguageString(DiscIO::Language language,
std::map<DiscIO::Language, std::string> strings)
{
@@ -69,26 +67,8 @@ GameListItem::GameListItem(const std::string& _rFileName, const Core::TitleDatab
m_Revision(0), m_Valid(false), m_ImageWidth(0), m_ImageHeight(0), m_disc_number(0),
m_has_custom_name(false)
{
- if (LoadFromCache())
- {
- m_Valid = true;
-
- // 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_pImage.empty())
- {
- std::vector<u32> buffer =
- DiscIO::Volume::GetWiiBanner(&m_ImageWidth, &m_ImageHeight, m_title_id);
- ReadVolumeBanner(buffer, m_ImageWidth, m_ImageHeight);
- if (!m_pImage.empty())
- SaveToCache();
- }
- }
- else
{
std::unique_ptr<DiscIO::Volume> volume(DiscIO::CreateVolumeFromFilename(_rFileName));
-
if (volume != nullptr)
{
m_Platform = volume->GetVolumeType();
@@ -116,7 +96,6 @@ GameListItem::GameListItem(const std::string& _rFileName, const Core::TitleDatab
ReadVolumeBanner(buffer, m_ImageWidth, m_ImageHeight);
m_Valid = true;
- SaveToCache();
}
}
@@ -138,37 +117,28 @@ GameListItem::GameListItem(const std::string& _rFileName, const Core::TitleDatab
m_FileSize = File::GetSize(_rFileName);
m_Platform = DiscIO::Platform::ELF_DOL;
m_blob_type = DiscIO::BlobType::DIRECTORY;
- }
- std::string path, name;
- SplitPath(m_FileName, &path, &name, nullptr);
+ std::string path, name;
+ SplitPath(m_FileName, &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 (ReadPNGBanner(path + name + ".png"))
- return;
-
- // Homebrew Channel icon. Typical for DOLs and ELFs,
- // but can be also used with volumes.
- if (ReadPNGBanner(path + "icon.png"))
- return;
+ // 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;
- // Volume banner. Typical for everything that isn't a DOL or ELF.
- if (!m_pImage.empty())
+ // Homebrew Channel icon. Typical for DOLs and ELFs,
+ // but can be also used with volumes.
+ if (SetWxBannerFromPngFile(path + "icon.png"))
+ return;
+ }
+ else
{
- // 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_image.Create(m_ImageWidth, m_ImageHeight, false);
- std::memcpy(m_image.GetData(), m_pImage.data(), m_pImage.size());
- return;
+ // Volume banner. Typical for everything that isn't a DOL or ELF.
+ SetWxBannerFromRaw();
}
}
-GameListItem::~GameListItem()
-{
-}
-
bool GameListItem::IsValid() const
{
if (!m_Valid)
@@ -198,37 +168,35 @@ void GameListItem::ReloadINI()
}
}
-bool GameListItem::LoadFromCache()
-{
- return CChunkFileReader::Load<GameListItem>(CreateCacheFilename(), CACHE_REVISION, *this);
-}
-
-void GameListItem::SaveToCache()
-{
- if (!File::IsDirectory(File::GetUserPath(D_CACHE_IDX)))
- File::CreateDir(File::GetUserPath(D_CACHE_IDX));
-
- CChunkFileReader::Save<GameListItem>(CreateCacheFilename(), CACHE_REVISION, *this);
-}
-
void GameListItem::DoState(PointerWrap& p)
{
+ p.Do(m_FileName);
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_issues);
+ p.Do(m_emu_state);
p.Do(m_FileSize);
p.Do(m_VolumeSize);
p.Do(m_region);
p.Do(m_Country);
+ p.Do(m_Platform);
p.Do(m_blob_type);
+ p.Do(m_Revision);
p.Do(m_pImage);
+ p.Do(m_Valid);
p.Do(m_ImageWidth);
p.Do(m_ImageHeight);
- p.Do(m_Platform);
p.Do(m_disc_number);
- p.Do(m_Revision);
+ p.Do(m_custom_name_titles_txt);
+ p.Do(m_custom_name);
+ p.Do(m_has_custom_name);
+ if (p.GetMode() == PointerWrap::MODE_READ)
+ {
+ SetWxBannerFromRaw();
+ }
}
bool GameListItem::IsElfOrDol() const
@@ -241,27 +209,6 @@ bool GameListItem::IsElfOrDol() const
return name_end == ".elf" || name_end == ".dol";
}
-std::string GameListItem::CreateCacheFilename() const
-{
- std::string Filename, LegalPathname, extension;
- SplitPath(m_FileName, &LegalPathname, &Filename, &extension);
-
- if (Filename.empty())
- return Filename; // Disc Drive
-
- // Filename.extension_HashOfFolderPath_Size.cache
- // Append hash to prevent ISO name-clashing in different folders.
- Filename.append(
- StringFromFormat("%s_%x_%" PRIx64 ".cache", extension.c_str(),
- HashFletcher((const u8*)LegalPathname.c_str(), LegalPathname.size()),
- File::GetSize(m_FileName)));
-
- std::string fullname(File::GetUserPath(D_CACHE_IDX));
- fullname += Filename;
- return fullname;
-}
-
-// Outputs to m_pImage
void GameListItem::ReadVolumeBanner(const std::vector<u32>& buffer, int width, int height)
{
m_pImage.resize(width * height * 3);
@@ -273,8 +220,7 @@ void GameListItem::ReadVolumeBanner(const std::vector<u32>& buffer, int width, i
}
}
-// Outputs to m_Bitmap
-bool GameListItem::ReadPNGBanner(const std::string& path)
+bool GameListItem::SetWxBannerFromPngFile(const std::string& path)
{
if (!File::Exists(path))
return false;
@@ -287,6 +233,37 @@ bool GameListItem::ReadPNGBanner(const std::string& path)
return true;
}
+void GameListItem::SetWxBannerFromRaw()
+{
+ // 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.
+ if (!m_pImage.empty())
+ {
+ m_image.Create(m_ImageWidth, m_ImageHeight, false);
+ std::memcpy(m_image.GetData(), m_pImage.data(), m_pImage.size());
+ }
+}
+
+bool GameListItem::ReloadBannerIfNeeded()
+{
+ // 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_Platform == DiscIO::Platform::WII_DISC || m_Platform == DiscIO::Platform::WII_WAD) &&
+ m_pImage.empty())
+ {
+ std::vector<u32> buffer =
+ DiscIO::Volume::GetWiiBanner(&m_ImageWidth, &m_ImageHeight, m_title_id);
+ if (buffer.size())
+ {
+ ReadVolumeBanner(buffer, m_ImageWidth, m_ImageHeight);
+ SetWxBannerFromRaw();
+ return true;
+ }
+ }
+ return false;
+}
+
std::string GameListItem::GetDescription(DiscIO::Language language) const
{
return GetLanguageString(language, m_descriptions);
diff --git a/Source/Core/DolphinWX/ISOFile.h b/Source/Core/DolphinWX/ISOFile.h
index 479ab142bc..84f70c050b 100644
--- a/Source/Core/DolphinWX/ISOFile.h
+++ b/Source/Core/DolphinWX/ISOFile.h
@@ -32,8 +32,9 @@ class PointerWrap;
class GameListItem
{
public:
+ GameListItem() = default;
GameListItem(const std::string& file_name, const Core::TitleDatabase& title_database);
- ~GameListItem();
+ ~GameListItem() = default;
// Reload settings after INI changes
void ReloadINI();
@@ -65,8 +66,20 @@ public:
// to display it
const wxImage& GetBannerImage() const { return m_image; }
void DoState(PointerWrap& p);
+ bool ReloadBannerIfNeeded();
private:
+ bool IsElfOrDol() const;
+ // Outputs to m_pImage
+ void ReadVolumeBanner(const std::vector<u32>& buffer, int width, int height);
+ // Outputs to m_image
+ bool SetWxBannerFromPngFile(const std::string& path);
+ void SetWxBannerFromRaw();
+
+ // IMPORTANT: 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.
+
std::string m_FileName;
std::map<DiscIO::Language, std::string> m_names;
@@ -97,15 +110,4 @@ private:
std::string m_custom_name_titles_txt; // Custom title from titles.txt
std::string m_custom_name; // Custom title from INI or titles.txt
bool m_has_custom_name;
-
- bool LoadFromCache();
- void SaveToCache();
-
- bool IsElfOrDol() const;
- std::string CreateCacheFilename() const;
-
- // Outputs to m_pImage
- void ReadVolumeBanner(const std::vector<u32>& buffer, int width, int height);
- // Outputs to m_Bitmap
- bool ReadPNGBanner(const std::string& path);
};
diff --git a/Source/Core/DolphinWX/Input/InputConfigDiag.cpp b/Source/Core/DolphinWX/Input/InputConfigDiag.cpp
index f1e4e7e2f9..8aaa51ccb0 100644
--- a/Source/Core/DolphinWX/Input/InputConfigDiag.cpp
+++ b/Source/Core/DolphinWX/Input/InputConfigDiag.cpp
@@ -264,7 +264,7 @@ void InputConfigDialog::UpdateProfileComboBox()
pname += PROFILES_PATH;
pname += m_config.GetProfileName();
- std::vector<std::string> sv = Common::DoFileSearch({".ini"}, {pname});
+ std::vector<std::string> sv = Common::DoFileSearch({pname}, {".ini"});
wxArrayString strs;
for (const std::string& filename : sv)
diff --git a/Source/Core/DolphinWX/Main.cpp b/Source/Core/DolphinWX/Main.cpp
index a73729253f..70f34ab482 100644
--- a/Source/Core/DolphinWX/Main.cpp
+++ b/Source/Core/DolphinWX/Main.cpp
@@ -208,9 +208,6 @@ void DolphinApp::MacOpenFile(const wxString& fileName)
void DolphinApp::AfterInit()
{
- if (!m_batch_mode)
- main_frame->UpdateGameList();
-
#if defined(USE_ANALYTICS) && USE_ANALYTICS
if (!SConfig::GetInstance().m_analytics_permission_asked)
{
@@ -481,7 +478,7 @@ void Host_SetWiiMoteConnectionState(int _State)
event.SetString(_("Wii Remote Connected"));
break;
}
- // Update field 1 or 2
+ // The second field is used for auxiliary info such as this
event.SetInt(1);
NOTICE_LOG(WIIMOTE, "%s", static_cast<const char*>(event.GetString().c_str()));
diff --git a/Source/Core/DolphinWX/NetPlay/ChangeGameDialog.cpp b/Source/Core/DolphinWX/NetPlay/ChangeGameDialog.cpp
index b685e89b08..53ada89a9b 100644
--- a/Source/Core/DolphinWX/NetPlay/ChangeGameDialog.cpp
+++ b/Source/Core/DolphinWX/NetPlay/ChangeGameDialog.cpp
@@ -9,7 +9,7 @@
#include "DolphinWX/NetPlay/ChangeGameDialog.h"
#include "DolphinWX/NetPlay/NetWindow.h"
-ChangeGameDialog::ChangeGameDialog(wxWindow* parent, const CGameListCtrl* const game_list)
+ChangeGameDialog::ChangeGameDialog(wxWindow* parent, const GameListCtrl* const game_list)
: wxDialog(parent, wxID_ANY, _("Select Game"))
{
const int space5 = FromDIP(5);
diff --git a/Source/Core/DolphinWX/NetPlay/ChangeGameDialog.h b/Source/Core/DolphinWX/NetPlay/ChangeGameDialog.h
index 53c2200fa6..d0cee54597 100644
--- a/Source/Core/DolphinWX/NetPlay/ChangeGameDialog.h
+++ b/Source/Core/DolphinWX/NetPlay/ChangeGameDialog.h
@@ -6,13 +6,13 @@
#include <wx/dialog.h>
-class CGameListCtrl;
+class GameListCtrl;
class wxListBox;
class ChangeGameDialog final : public wxDialog
{
public:
- ChangeGameDialog(wxWindow* parent, const CGameListCtrl* const game_list);
+ ChangeGameDialog(wxWindow* parent, const GameListCtrl* const game_list);
wxString GetChosenGameName() const;
diff --git a/Source/Core/DolphinWX/NetPlay/NetPlayLauncher.h b/Source/Core/DolphinWX/NetPlay/NetPlayLauncher.h
index 64297885ba..fa245dadc7 100644
--- a/Source/Core/DolphinWX/NetPlay/NetPlayLauncher.h
+++ b/Source/Core/DolphinWX/NetPlay/NetPlayLauncher.h
@@ -8,7 +8,7 @@
#include "Common/CommonTypes.h"
#include "Common/IniFile.h"
-class CGameListCtrl;
+class GameListCtrl;
class wxRect;
class wxWindow;
@@ -24,7 +24,7 @@ public:
const wxRect window_defaults{wxDefaultCoord, wxDefaultCoord, 768, 768 - 128};
std::string player_name;
- const CGameListCtrl* game_list_ctrl;
+ const GameListCtrl* game_list_ctrl;
wxWindow* parent_window;
bool use_traversal;
std::string traversal_host;
diff --git a/Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.cpp b/Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.cpp
index 14967fad76..1fd401ea37 100644
--- a/Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.cpp
+++ b/Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.cpp
@@ -36,7 +36,7 @@ wxString GetTraversalLabelText(IniFile::Section& section)
}
} // Anonymous namespace
-NetPlaySetupFrame::NetPlaySetupFrame(wxWindow* const parent, const CGameListCtrl* const game_list)
+NetPlaySetupFrame::NetPlaySetupFrame(wxWindow* const parent, const GameListCtrl* const game_list)
: wxFrame(parent, wxID_ANY, _("Dolphin NetPlay Setup")), m_game_list(game_list)
{
IniFile inifile;
diff --git a/Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.h b/Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.h
index a91e26989b..1e8cd170ab 100644
--- a/Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.h
+++ b/Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.h
@@ -8,7 +8,7 @@
#include <string>
#include <wx/frame.h>
-class CGameListCtrl;
+class GameListCtrl;
class wxCheckBox;
class wxChoice;
class wxListBox;
@@ -20,7 +20,7 @@ class wxTextCtrl;
class NetPlaySetupFrame final : public wxFrame
{
public:
- NetPlaySetupFrame(wxWindow* const parent, const CGameListCtrl* const game_list);
+ NetPlaySetupFrame(wxWindow* const parent, const GameListCtrl* const game_list);
~NetPlaySetupFrame();
private:
@@ -66,5 +66,5 @@ private:
#endif
wxString m_traversal_string;
- const CGameListCtrl* const m_game_list;
+ const GameListCtrl* const m_game_list;
};
diff --git a/Source/Core/DolphinWX/NetPlay/NetWindow.cpp b/Source/Core/DolphinWX/NetPlay/NetWindow.cpp
index 25c015877c..98aefef953 100644
--- a/Source/Core/DolphinWX/NetPlay/NetWindow.cpp
+++ b/Source/Core/DolphinWX/NetPlay/NetWindow.cpp
@@ -58,13 +58,13 @@ NetPlayServer* NetPlayDialog::netplay_server = nullptr;
NetPlayClient* NetPlayDialog::netplay_client = nullptr;
NetPlayDialog* NetPlayDialog::npd = nullptr;
-void NetPlayDialog::FillWithGameNames(wxListBox* game_lbox, const CGameListCtrl& game_list)
+void NetPlayDialog::FillWithGameNames(wxListBox* game_lbox, const GameListCtrl& game_list)
{
for (u32 i = 0; auto game = game_list.GetISO(i); ++i)
game_lbox->Append(StrToWxStr(game->GetUniqueIdentifier()));
}
-NetPlayDialog::NetPlayDialog(wxWindow* const parent, const CGameListCtrl* const game_list,
+NetPlayDialog::NetPlayDialog(wxWindow* const parent, const GameListCtrl* const game_list,
const std::string& game, const bool is_hosting)
: wxFrame(parent, wxID_ANY, _("Dolphin NetPlay")), m_selected_game(game), m_start_btn(nullptr),
m_host_label(nullptr), m_host_type_choice(nullptr), m_host_copy_btn(nullptr),
diff --git a/Source/Core/DolphinWX/NetPlay/NetWindow.h b/Source/Core/DolphinWX/NetPlay/NetWindow.h
index 84b60ad40f..ce77a6dfd7 100644
--- a/Source/Core/DolphinWX/NetPlay/NetWindow.h
+++ b/Source/Core/DolphinWX/NetPlay/NetWindow.h
@@ -25,7 +25,7 @@
#include <wx/msw/winundef.h>
#endif
-class CGameListCtrl;
+class GameListCtrl;
class MD5Dialog;
class wxButton;
class wxCheckBox;
@@ -78,7 +78,7 @@ enum class MD5Target
class NetPlayDialog : public wxFrame, public NetPlayUI
{
public:
- NetPlayDialog(wxWindow* parent, const CGameListCtrl* const game_list, const std::string& game,
+ NetPlayDialog(wxWindow* parent, const GameListCtrl* const game_list, const std::string& game,
const bool is_hosting = false);
~NetPlayDialog();
@@ -107,7 +107,7 @@ public:
static NetPlayDialog*& GetInstance() { return npd; }
static NetPlayClient*& GetNetPlayClient() { return netplay_client; }
static NetPlayServer*& GetNetPlayServer() { return netplay_server; }
- static void FillWithGameNames(wxListBox* game_lbox, const CGameListCtrl& game_list);
+ static void FillWithGameNames(wxListBox* game_lbox, const GameListCtrl& game_list);
bool IsRecording() override;
@@ -163,7 +163,7 @@ private:
std::vector<int> m_playerids;
Common::FifoQueue<std::string> m_chat_msgs;
- const CGameListCtrl* const m_game_list;
+ const GameListCtrl* const m_game_list;
static NetPlayDialog* npd;
static NetPlayServer* netplay_server;
diff --git a/Source/Core/VideoCommon/HiresTextures.cpp b/Source/Core/VideoCommon/HiresTextures.cpp
index 7b4a1d387c..36b170ef10 100644
--- a/Source/Core/VideoCommon/HiresTextures.cpp
+++ b/Source/Core/VideoCommon/HiresTextures.cpp
@@ -97,7 +97,7 @@ void HiresTexture::Update()
};
std::vector<std::string> filenames =
- Common::DoFileSearch(extensions, {texture_directory}, /*recursive*/ true);
+ Common::DoFileSearch({texture_directory}, extensions, /*recursive*/ true);
const std::string code = game_id + "_";
diff --git a/Source/Core/VideoCommon/PostProcessing.cpp b/Source/Core/VideoCommon/PostProcessing.cpp
index 71f38732e7..0575402dd1 100644
--- a/Source/Core/VideoCommon/PostProcessing.cpp
+++ b/Source/Core/VideoCommon/PostProcessing.cpp
@@ -31,8 +31,9 @@ PostProcessingShaderImplementation::~PostProcessingShaderImplementation()
static std::vector<std::string> GetShaders(const std::string& sub_dir = "")
{
std::vector<std::string> paths =
- Common::DoFileSearch({".glsl"}, {File::GetUserPath(D_SHADERS_IDX) + sub_dir,
- File::GetSysDirectory() + SHADERS_DIR DIR_SEP + sub_dir});
+ Common::DoFileSearch({File::GetUserPath(D_SHADERS_IDX) + sub_dir,
+ File::GetSysDirectory() + SHADERS_DIR DIR_SEP + sub_dir},
+ {".glsl"});
std::vector<std::string> result;
for (std::string path : paths)
{