Merge pull request #5659 from shuffle2/gamelist-speedup-with-emustate

Gamelist speedup with emustate
This commit is contained in:
shuffle2 2017-06-23 17:32:15 -07:00 committed by GitHub
commit ced53e2920
29 changed files with 656 additions and 598 deletions

View File

@ -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;
};
};

View File

@ -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

View File

@ -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

View File

@ -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)
{

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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;

View File

@ -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?

View File

@ -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))
{

View File

@ -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);

View File

@ -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);

View File

@ -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_RELOAD_GAMELIST, GetId()};
wxCommandEvent event{DOLPHIN_EVT_REFRESH_GAMELIST, GetId()};
event.SetEventObject(this);
wxPostEvent(m_game_list_ctrl, event);
}
void CFrame::GameListRescan()
{
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();
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
struct
{
std::vector<int> flag;
std::vector<int> platform;
std::vector<int> utility_banner;
std::vector<int> emu_state;
} m_image_indexes;
int last_column;
int last_sort;
wxSize lastpos;
wxEmuStateTip* toolTip;
// 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;
};

View File

@ -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);
// 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. Typical for DOLs and ELFs,
// but can be also used with volumes.
if (SetWxBannerFromPngFile(path + "icon.png"))
return;
}
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;
// Volume banner. Typical for everything that isn't a DOL or ELF.
if (!m_pImage.empty())
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);

View File

@ -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);
};

View File

@ -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)

View File

@ -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()));

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
};

View File

@ -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),

View File

@ -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;

View File

@ -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 + "_";

View File

@ -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)
{