mirror of https://github.com/RPCS3/rpcs3.git
Qt: async game list parsing
This commit is contained in:
parent
6c85d97d6c
commit
a639772dfe
|
@ -3120,6 +3120,8 @@ void Emulator::AddGamesFromDir(const std::string& path)
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
|
process_qt_events();
|
||||||
|
|
||||||
// search direct subdirectories, that way we can drop one folder containing all games
|
// search direct subdirectories, that way we can drop one folder containing all games
|
||||||
for (auto&& dir_entry : fs::dir(path))
|
for (auto&& dir_entry : fs::dir(path))
|
||||||
{
|
{
|
||||||
|
@ -3134,6 +3136,8 @@ void Emulator::AddGamesFromDir(const std::string& path)
|
||||||
{
|
{
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
|
process_qt_events();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_games_config.set_save_on_dirty(true);
|
m_games_config.set_save_on_dirty(true);
|
||||||
|
|
|
@ -73,6 +73,18 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, std
|
||||||
m_gui_settings->SetValue(gui::gl_marginFactor, m_margin_factor);
|
m_gui_settings->SetValue(gui::gl_marginFactor, m_margin_factor);
|
||||||
m_gui_settings->SetValue(gui::gl_textFactor, m_text_factor);
|
m_gui_settings->SetValue(gui::gl_textFactor, m_text_factor);
|
||||||
|
|
||||||
|
// Only show the progress dialog after some time has passed
|
||||||
|
m_progress_dialog_timer = new QTimer(this);
|
||||||
|
m_progress_dialog_timer->setSingleShot(true);
|
||||||
|
m_progress_dialog_timer->setInterval(200);
|
||||||
|
connect(m_progress_dialog_timer, &QTimer::timeout, this, [this]()
|
||||||
|
{
|
||||||
|
if (m_progress_dialog)
|
||||||
|
{
|
||||||
|
m_progress_dialog->show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
m_game_dock = new QMainWindow(this);
|
m_game_dock = new QMainWindow(this);
|
||||||
m_game_dock->setWindowFlags(Qt::Widget);
|
m_game_dock->setWindowFlags(Qt::Widget);
|
||||||
setWidget(m_game_dock);
|
setWidget(m_game_dock);
|
||||||
|
@ -137,6 +149,18 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, std
|
||||||
add_column(gui::column_dir_size, tr("Space On Disk"), tr("Show Space On Disk"));
|
add_column(gui::column_dir_size, tr("Space On Disk"), tr("Show Space On Disk"));
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
|
connect(&m_parsing_watcher, &QFutureWatcher<void>::finished, this, &game_list_frame::OnParsingFinished);
|
||||||
|
connect(&m_parsing_watcher, &QFutureWatcher<void>::canceled, this, [this]()
|
||||||
|
{
|
||||||
|
WaitAndAbortSizeCalcThreads();
|
||||||
|
WaitAndAbortRepaintThreads();
|
||||||
|
|
||||||
|
m_path_entries.clear();
|
||||||
|
m_path_list.clear();
|
||||||
|
m_game_data.clear();
|
||||||
|
m_serials.clear();
|
||||||
|
m_games.pop_all();
|
||||||
|
});
|
||||||
connect(&m_refresh_watcher, &QFutureWatcher<void>::finished, this, &game_list_frame::OnRefreshFinished);
|
connect(&m_refresh_watcher, &QFutureWatcher<void>::finished, this, &game_list_frame::OnRefreshFinished);
|
||||||
connect(&m_refresh_watcher, &QFutureWatcher<void>::canceled, this, [this]()
|
connect(&m_refresh_watcher, &QFutureWatcher<void>::canceled, this, [this]()
|
||||||
{
|
{
|
||||||
|
@ -241,32 +265,19 @@ void game_list_frame::LoadSettings()
|
||||||
m_show_custom_icons = m_gui_settings->GetValue(gui::gl_custom_icon).toBool();
|
m_show_custom_icons = m_gui_settings->GetValue(gui::gl_custom_icon).toBool();
|
||||||
m_play_hover_movies = m_gui_settings->GetValue(gui::gl_hover_gifs).toBool();
|
m_play_hover_movies = m_gui_settings->GetValue(gui::gl_hover_gifs).toBool();
|
||||||
|
|
||||||
Refresh(true);
|
|
||||||
|
|
||||||
const QByteArray state = m_gui_settings->GetValue(gui::gl_state).toByteArray();
|
|
||||||
if (!m_game_list->horizontalHeader()->restoreState(state) && m_game_list->rowCount())
|
|
||||||
{
|
|
||||||
// If no settings exist, resize to contents.
|
|
||||||
ResizeColumnsToContents();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int col = 0; col < m_columnActs.count(); ++col)
|
for (int col = 0; col < m_columnActs.count(); ++col)
|
||||||
{
|
{
|
||||||
const bool vis = m_gui_settings->GetGamelistColVisibility(col);
|
const bool vis = m_gui_settings->GetGamelistColVisibility(col);
|
||||||
m_columnActs[col]->setChecked(vis);
|
m_columnActs[col]->setChecked(vis);
|
||||||
m_game_list->setColumnHidden(col, !vis);
|
m_game_list->setColumnHidden(col, !vis);
|
||||||
}
|
}
|
||||||
|
|
||||||
SortGameList();
|
|
||||||
FixNarrowColumns();
|
|
||||||
|
|
||||||
m_game_list->horizontalHeader()->restoreState(m_game_list->horizontalHeader()->saveState());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
game_list_frame::~game_list_frame()
|
game_list_frame::~game_list_frame()
|
||||||
{
|
{
|
||||||
WaitAndAbortSizeCalcThreads();
|
WaitAndAbortSizeCalcThreads();
|
||||||
WaitAndAbortRepaintThreads();
|
WaitAndAbortRepaintThreads();
|
||||||
|
gui::utils::stop_future_watcher(m_parsing_watcher, true);
|
||||||
gui::utils::stop_future_watcher(m_refresh_watcher, true);
|
gui::utils::stop_future_watcher(m_refresh_watcher, true);
|
||||||
|
|
||||||
SaveSettings();
|
SaveSettings();
|
||||||
|
@ -316,7 +327,7 @@ void game_list_frame::ResizeColumnsToContents(int spacing) const
|
||||||
|
|
||||||
void game_list_frame::OnColClicked(int col)
|
void game_list_frame::OnColClicked(int col)
|
||||||
{
|
{
|
||||||
if (col == 0) return; // Don't "sort" icons.
|
if (col == gui::column_icon) return; // Don't "sort" icons.
|
||||||
|
|
||||||
if (col == m_sort_column)
|
if (col == m_sort_column)
|
||||||
{
|
{
|
||||||
|
@ -352,27 +363,31 @@ bool game_list_frame::IsEntryVisible(const game_info& game, bool search_fallback
|
||||||
return is_visible && matches_category() && SearchMatchesApp(qstr(game->info.name), serial, search_fallback);
|
return is_visible && matches_category() && SearchMatchesApp(qstr(game->info.name), serial, search_fallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_list_frame::SortGameList() const
|
void game_list_frame::SortGameList()
|
||||||
{
|
{
|
||||||
|
gui::utils::stop_future_watcher(m_parsing_watcher, false);
|
||||||
|
gui::utils::stop_future_watcher(m_refresh_watcher, false);
|
||||||
|
WaitAndAbortRepaintThreads();
|
||||||
|
|
||||||
// Back-up old header sizes to handle unwanted column resize in case of zero search results
|
// Back-up old header sizes to handle unwanted column resize in case of zero search results
|
||||||
QList<int> column_widths;
|
|
||||||
const int old_row_count = m_game_list->rowCount();
|
const int old_row_count = m_game_list->rowCount();
|
||||||
const int old_game_count = m_game_data.count();
|
const int old_game_count = m_game_data.count();
|
||||||
|
|
||||||
|
std::vector<int> column_widths(m_game_list->columnCount());
|
||||||
for (int i = 0; i < m_game_list->columnCount(); i++)
|
for (int i = 0; i < m_game_list->columnCount(); i++)
|
||||||
{
|
{
|
||||||
column_widths.append(m_game_list->columnWidth(i));
|
column_widths[i] = m_game_list->columnWidth(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sorting resizes hidden columns, so unhide them as a workaround
|
// Sorting resizes hidden columns, so unhide them as a workaround
|
||||||
QList<int> columns_to_hide;
|
std::vector<int> columns_to_hide;
|
||||||
|
|
||||||
for (int i = 0; i < m_game_list->columnCount(); i++)
|
for (int i = 0; i < m_game_list->columnCount(); i++)
|
||||||
{
|
{
|
||||||
if (m_game_list->isColumnHidden(i))
|
if (m_game_list->isColumnHidden(i))
|
||||||
{
|
{
|
||||||
m_game_list->setColumnHidden(i, false);
|
m_game_list->setColumnHidden(i, false);
|
||||||
columns_to_hide << i;
|
columns_to_hide.push_back(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,9 +395,9 @@ void game_list_frame::SortGameList() const
|
||||||
m_game_list->sortByColumn(m_sort_column, m_col_sort_order);
|
m_game_list->sortByColumn(m_sort_column, m_col_sort_order);
|
||||||
|
|
||||||
// Hide columns again
|
// Hide columns again
|
||||||
for (auto i : columns_to_hide)
|
for (int col : columns_to_hide)
|
||||||
{
|
{
|
||||||
m_game_list->setColumnHidden(i, true);
|
m_game_list->setColumnHidden(col, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't resize the columns if no game is shown to preserve the header settings
|
// Don't resize the columns if no game is shown to preserve the header settings
|
||||||
|
@ -434,6 +449,18 @@ std::string game_list_frame::GetDataDirBySerial(const std::string& serial)
|
||||||
return fs::get_config_dir() + "data/" + serial;
|
return fs::get_config_dir() + "data/" + serial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void game_list_frame::push_path(const std::string& path, std::vector<std::string>& legit_paths)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_path_mutex);
|
||||||
|
if (!m_path_list.insert(path).second)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
legit_paths.push_back(path);
|
||||||
|
}
|
||||||
|
|
||||||
void game_list_frame::Refresh(const bool from_drive, const bool scroll_after)
|
void game_list_frame::Refresh(const bool from_drive, const bool scroll_after)
|
||||||
{
|
{
|
||||||
if (from_drive)
|
if (from_drive)
|
||||||
|
@ -441,12 +468,23 @@ void game_list_frame::Refresh(const bool from_drive, const bool scroll_after)
|
||||||
WaitAndAbortSizeCalcThreads();
|
WaitAndAbortSizeCalcThreads();
|
||||||
}
|
}
|
||||||
WaitAndAbortRepaintThreads();
|
WaitAndAbortRepaintThreads();
|
||||||
|
gui::utils::stop_future_watcher(m_parsing_watcher, from_drive);
|
||||||
gui::utils::stop_future_watcher(m_refresh_watcher, from_drive);
|
gui::utils::stop_future_watcher(m_refresh_watcher, from_drive);
|
||||||
|
|
||||||
|
if (m_progress_dialog_timer)
|
||||||
|
{
|
||||||
|
m_progress_dialog_timer->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_progress_dialog)
|
||||||
|
{
|
||||||
|
m_progress_dialog->accept();
|
||||||
|
m_progress_dialog->deleteLater();
|
||||||
|
m_progress_dialog = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (from_drive)
|
if (from_drive)
|
||||||
{
|
{
|
||||||
const Localized localized;
|
|
||||||
|
|
||||||
m_path_entries.clear();
|
m_path_entries.clear();
|
||||||
m_path_list.clear();
|
m_path_list.clear();
|
||||||
m_serials.clear();
|
m_serials.clear();
|
||||||
|
@ -454,299 +492,87 @@ void game_list_frame::Refresh(const bool from_drive, const bool scroll_after)
|
||||||
m_notes.clear();
|
m_notes.clear();
|
||||||
m_games.pop_all();
|
m_games.pop_all();
|
||||||
|
|
||||||
|
m_progress_dialog = new progress_dialog(tr("Loading games"), tr("Loading games, please wait..."), tr("Cancel"), 0, 0, true, this, Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
|
||||||
|
connect(&m_refresh_watcher, &QFutureWatcher<void>::progressRangeChanged, m_progress_dialog, &QProgressDialog::setRange);
|
||||||
|
connect(&m_refresh_watcher, &QFutureWatcher<void>::progressValueChanged, m_progress_dialog, &QProgressDialog::setValue);
|
||||||
|
connect(m_progress_dialog, &QProgressDialog::canceled, this, [this]()
|
||||||
|
{
|
||||||
|
gui::utils::stop_future_watcher(m_parsing_watcher, true);
|
||||||
|
gui::utils::stop_future_watcher(m_refresh_watcher, true);
|
||||||
|
|
||||||
|
m_path_entries.clear();
|
||||||
|
m_path_list.clear();
|
||||||
|
m_serials.clear();
|
||||||
|
m_game_data.clear();
|
||||||
|
m_notes.clear();
|
||||||
|
m_games.pop_all();
|
||||||
|
|
||||||
|
if (m_progress_dialog_timer)
|
||||||
|
{
|
||||||
|
m_progress_dialog_timer->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_progress_dialog->deleteLater();
|
||||||
|
m_progress_dialog = nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (m_progress_dialog_timer)
|
||||||
|
{
|
||||||
|
m_progress_dialog_timer->start();
|
||||||
|
}
|
||||||
|
|
||||||
if (Emu.IsStopped())
|
if (Emu.IsStopped())
|
||||||
{
|
{
|
||||||
Emu.AddGamesFromDir(g_cfg_vfs.get(g_cfg_vfs.games_dir, rpcs3::utils::get_emu_dir()));
|
Emu.AddGamesFromDir(g_cfg_vfs.get(g_cfg_vfs.games_dir, rpcs3::utils::get_emu_dir()));
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string _hdd = rpcs3::utils::get_hdd0_dir();
|
const std::string _hdd = rpcs3::utils::get_hdd0_dir();
|
||||||
|
|
||||||
const auto push_path = [this](const std::string& path, std::vector<std::string>& legit_paths)
|
m_parsing_watcher.setFuture(QtConcurrent::map(m_parsing_threads, [this, _hdd](int index)
|
||||||
{
|
{
|
||||||
|
if (index > 0)
|
||||||
{
|
{
|
||||||
|
game_list_log.error("Unexpected thread index: %d", index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto add_dir = [this](const std::string& path, bool is_disc)
|
||||||
|
{
|
||||||
|
for (const auto& entry : fs::dir(path))
|
||||||
|
{
|
||||||
|
if (!entry.is_directory || entry.name == "." || entry.name == "..")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QApplication::processEvents();
|
||||||
|
std::lock_guard lock(m_path_mutex);
|
||||||
|
m_path_entries.emplace_back(path_entry{path + entry.name, is_disc, false});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
add_dir(_hdd + "game/", false);
|
||||||
|
add_dir(_hdd + "disc/", true); // Deprecated
|
||||||
|
|
||||||
|
for (const auto& [serial, path] : Emu.GetGamesConfig().get_games())
|
||||||
|
{
|
||||||
|
std::string game_dir = path;
|
||||||
|
game_dir.resize(game_dir.find_last_not_of('/') + 1);
|
||||||
|
|
||||||
|
if (game_dir.empty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't use the C00 subdirectory in our game list
|
||||||
|
if (game_dir.ends_with("/C00") || game_dir.ends_with("\\C00"))
|
||||||
|
{
|
||||||
|
game_dir = game_dir.substr(0, game_dir.size() - 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
QApplication::processEvents();
|
||||||
std::lock_guard lock(m_path_mutex);
|
std::lock_guard lock(m_path_mutex);
|
||||||
if (!m_path_list.insert(path).second)
|
m_path_entries.emplace_back(path_entry{game_dir, false, true});
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
legit_paths.push_back(path);
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto add_disc_dir = [push_path](const std::string& path, std::vector<std::string>& legit_paths)
|
|
||||||
{
|
|
||||||
for (const auto& entry : fs::dir(path))
|
|
||||||
{
|
|
||||||
if (!entry.is_directory || entry.name == "." || entry.name == "..")
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.name == "PS3_GAME" || std::regex_match(entry.name, std::regex("^PS3_GM[[:digit:]]{2}$")))
|
|
||||||
{
|
|
||||||
push_path(path + "/" + entry.name, legit_paths);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto add_dir = [&](const std::string& path, bool is_disc)
|
|
||||||
{
|
|
||||||
for (const auto& entry : fs::dir(path))
|
|
||||||
{
|
|
||||||
if (!entry.is_directory || entry.name == "." || entry.name == "..")
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_path_entries.emplace_back(path_entry{path + entry.name, is_disc, false});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
add_dir(_hdd + "game/", false);
|
|
||||||
add_dir(_hdd + "disc/", true); // Deprecated
|
|
||||||
|
|
||||||
for (const auto& [serial, path] : Emu.GetGamesConfig().get_games())
|
|
||||||
{
|
|
||||||
std::string game_dir = path;
|
|
||||||
|
|
||||||
game_dir.resize(game_dir.find_last_not_of('/') + 1);
|
|
||||||
|
|
||||||
if (game_dir.empty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't use the C00 subdirectory in our game list
|
|
||||||
if (game_dir.ends_with("/C00") || game_dir.ends_with("\\C00"))
|
|
||||||
{
|
|
||||||
game_dir = game_dir.substr(0, game_dir.size() - 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_path_entries.emplace_back(path_entry{game_dir, false, true});
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto dev_flash = g_cfg_vfs.get_dev_flash();
|
|
||||||
|
|
||||||
m_path_entries.emplace_back(path_entry{dev_flash + "vsh/module/vsh.self", false, false});
|
|
||||||
|
|
||||||
// Remove duplicates
|
|
||||||
sort(m_path_entries.begin(), m_path_entries.end(), [](const path_entry& l, const path_entry& r){return l.path < r.path;});
|
|
||||||
m_path_entries.erase(unique(m_path_entries.begin(), m_path_entries.end(), [](const path_entry& l, const path_entry& r){return l.path == r.path;}), m_path_entries.end());
|
|
||||||
|
|
||||||
const std::string game_icon_path = m_play_hover_movies ? fs::get_config_dir() + "/Icons/game_icons/" : "";
|
|
||||||
|
|
||||||
const auto add_game = [this, dev_flash, cat_unknown_localized = sstr(localized.category.unknown), cat_unknown = sstr(cat::cat_unknown), game_icon_path](const std::string& dir_or_elf)
|
|
||||||
{
|
|
||||||
GameInfo game{};
|
|
||||||
game.path = dir_or_elf;
|
|
||||||
|
|
||||||
const Localized thread_localized;
|
|
||||||
|
|
||||||
const std::string sfo_dir = rpcs3::utils::get_sfo_dir_from_game_path(dir_or_elf);
|
|
||||||
const psf::registry psf = psf::load_object(sfo_dir + "/PARAM.SFO");
|
|
||||||
const std::string_view title_id = psf::get_string(psf, "TITLE_ID", "");
|
|
||||||
|
|
||||||
if (title_id.empty())
|
|
||||||
{
|
|
||||||
if (!fs::is_file(dir_or_elf))
|
|
||||||
{
|
|
||||||
// Do not care about invalid entries
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
game.serial = dir_or_elf.substr(dir_or_elf.find_last_of(fs::delim) + 1);
|
|
||||||
game.category = cat::cat_ps3_os.toStdString(); // Key for operating system executables
|
|
||||||
game.version = utils::get_firmware_version();
|
|
||||||
game.fw = game.version;
|
|
||||||
game.bootable = 1;
|
|
||||||
game.icon_path = dev_flash + "vsh/resource/explore/icon/icon_home.png";
|
|
||||||
|
|
||||||
if (dir_or_elf.starts_with(dev_flash))
|
|
||||||
{
|
|
||||||
std::string path_vfs = dir_or_elf.substr(dev_flash.size());
|
|
||||||
|
|
||||||
if (const usz pos = path_vfs.find_first_not_of(fs::delim); pos != umax && pos != 0)
|
|
||||||
{
|
|
||||||
path_vfs = path_vfs.substr(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (const auto it = thread_localized.title.titles.find(path_vfs); it != thread_localized.title.titles.cend())
|
|
||||||
{
|
|
||||||
game.name = it->second.toStdString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (game.name == "Unknown")
|
|
||||||
{
|
|
||||||
game.name = game.serial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
game.serial = std::string(title_id);
|
|
||||||
game.name = std::string(psf::get_string(psf, "TITLE", cat_unknown_localized));
|
|
||||||
game.app_ver = std::string(psf::get_string(psf, "APP_VER", cat_unknown_localized));
|
|
||||||
game.version = std::string(psf::get_string(psf, "VERSION", cat_unknown_localized));
|
|
||||||
game.category = std::string(psf::get_string(psf, "CATEGORY", cat_unknown));
|
|
||||||
game.fw = std::string(psf::get_string(psf, "PS3_SYSTEM_VER", cat_unknown_localized));
|
|
||||||
game.parental_lvl = psf::get_integer(psf, "PARENTAL_LEVEL", 0);
|
|
||||||
game.resolution = psf::get_integer(psf, "RESOLUTION", 0);
|
|
||||||
game.sound_format = psf::get_integer(psf, "SOUND_FORMAT", 0);
|
|
||||||
game.bootable = psf::get_integer(psf, "BOOTABLE", 0);
|
|
||||||
game.attr = psf::get_integer(psf, "ATTRIBUTE", 0);
|
|
||||||
game.icon_path = sfo_dir + "/ICON0.PNG";
|
|
||||||
|
|
||||||
if (game.category == "DG")
|
|
||||||
{
|
|
||||||
std::string latest_icon = rpcs3::utils::get_hdd0_dir() + "game/" + game.serial + "/ICON0.PNG";
|
|
||||||
if (fs::is_file(latest_icon))
|
|
||||||
{
|
|
||||||
game.icon_path = std::move(latest_icon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_show_custom_icons)
|
|
||||||
{
|
|
||||||
if (std::string icon_path = fs::get_config_dir() + "/Icons/game_icons/" + game.serial + "/ICON0.PNG"; fs::is_file(icon_path))
|
|
||||||
{
|
|
||||||
game.icon_path = std::move(icon_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString serial = qstr(game.serial);
|
|
||||||
|
|
||||||
m_games_mutex.lock();
|
|
||||||
|
|
||||||
// Read persistent_settings values
|
|
||||||
const QString note = m_persistent_settings->GetValue(gui::persistent::notes, serial, "").toString();
|
|
||||||
const QString title = m_persistent_settings->GetValue(gui::persistent::titles, serial, "").toString().simplified();
|
|
||||||
QString last_played = m_persistent_settings->GetValue(gui::persistent::last_played, serial, "").toString();
|
|
||||||
quint64 playtime = m_persistent_settings->GetValue(gui::persistent::playtime, serial, 0).toULongLong();
|
|
||||||
|
|
||||||
// Set persistent_settings values if values exist
|
|
||||||
if (!last_played.isEmpty())
|
|
||||||
{
|
|
||||||
m_persistent_settings->SetLastPlayed(serial, last_played);
|
|
||||||
}
|
|
||||||
if (playtime > 0)
|
|
||||||
{
|
|
||||||
m_persistent_settings->SetPlaytime(serial, playtime);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_serials.insert(serial);
|
|
||||||
|
|
||||||
if (!note.isEmpty())
|
|
||||||
{
|
|
||||||
m_notes.insert(serial, note);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!title.isEmpty())
|
|
||||||
{
|
|
||||||
m_titles.insert(serial, title);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_games_mutex.unlock();
|
|
||||||
|
|
||||||
QString qt_cat = qstr(game.category);
|
|
||||||
|
|
||||||
if (const auto boot_cat = thread_localized.category.cat_boot.find(qt_cat); boot_cat != thread_localized.category.cat_boot.cend())
|
|
||||||
{
|
|
||||||
qt_cat = boot_cat->second;
|
|
||||||
}
|
|
||||||
else if (const auto data_cat = thread_localized.category.cat_data.find(qt_cat); data_cat != thread_localized.category.cat_data.cend())
|
|
||||||
{
|
|
||||||
qt_cat = data_cat->second;
|
|
||||||
}
|
|
||||||
else if (game.category == cat_unknown)
|
|
||||||
{
|
|
||||||
qt_cat = thread_localized.category.unknown;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
qt_cat = thread_localized.category.other;
|
|
||||||
}
|
|
||||||
|
|
||||||
gui_game_info info{};
|
|
||||||
info.info = game;
|
|
||||||
info.localized_category = std::move(qt_cat);
|
|
||||||
info.compat = m_game_compat->GetCompatibility(game.serial);
|
|
||||||
info.hasCustomConfig = fs::is_file(rpcs3::utils::get_custom_config_path(game.serial));
|
|
||||||
info.hasCustomPadConfig = fs::is_file(rpcs3::utils::get_custom_input_config_path(game.serial));
|
|
||||||
info.has_hover_gif = fs::is_file(game_icon_path + game.serial + "/hover.gif");
|
|
||||||
|
|
||||||
m_games.push(std::make_shared<gui_game_info>(std::move(info)));
|
|
||||||
};
|
|
||||||
|
|
||||||
m_refresh_watcher.setFuture(QtConcurrent::map(m_path_entries, [this, _hdd, add_disc_dir, push_path, add_game](const path_entry& entry)
|
|
||||||
{
|
|
||||||
std::vector<std::string> legit_paths;
|
|
||||||
|
|
||||||
if (entry.is_from_yml)
|
|
||||||
{
|
|
||||||
if (fs::is_file(entry.path + "/PARAM.SFO"))
|
|
||||||
{
|
|
||||||
push_path(entry.path, legit_paths);
|
|
||||||
}
|
|
||||||
else if (fs::is_file(entry.path + "/PS3_DISC.SFB"))
|
|
||||||
{
|
|
||||||
// Check if a path loaded from games.yml is already registered in add_dir(_hdd + "disc/");
|
|
||||||
if (entry.path.starts_with(_hdd))
|
|
||||||
{
|
|
||||||
std::string_view frag = std::string_view(entry.path).substr(_hdd.size());
|
|
||||||
|
|
||||||
if (frag.starts_with("disc/"))
|
|
||||||
{
|
|
||||||
// Our path starts from _hdd + 'disc/'
|
|
||||||
frag.remove_prefix(5);
|
|
||||||
|
|
||||||
// Check if the remaining part is the only path component
|
|
||||||
if (frag.find_first_of('/') + 1 == 0)
|
|
||||||
{
|
|
||||||
game_list_log.trace("Removed duplicate: %s", entry.path);
|
|
||||||
|
|
||||||
if (static std::unordered_set<std::string> warn_once_list; warn_once_list.emplace(entry.path).second)
|
|
||||||
{
|
|
||||||
game_list_log.todo("Game at '%s' is using deprecated directory '/dev_hdd0/disc/'.\nConsider moving into '%s'.", entry.path, g_cfg_vfs.get(g_cfg_vfs.games_dir, rpcs3::utils::get_emu_dir()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
add_disc_dir(entry.path, legit_paths);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
game_list_log.trace("Invalid game path registered: %s", entry.path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (fs::is_file(entry.path + "/PS3_DISC.SFB"))
|
|
||||||
{
|
|
||||||
if (!entry.is_disc)
|
|
||||||
{
|
|
||||||
game_list_log.error("Invalid game path found in %s", entry.path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
add_disc_dir(entry.path, legit_paths);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (entry.is_disc)
|
|
||||||
{
|
|
||||||
game_list_log.error("Invalid disc path found in %s", entry.path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
push_path(entry.path, legit_paths);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const std::string& path : legit_paths)
|
|
||||||
{
|
|
||||||
add_game(path);
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -777,6 +603,252 @@ void game_list_frame::Refresh(const bool from_drive, const bool scroll_after)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void game_list_frame::OnParsingFinished()
|
||||||
|
{
|
||||||
|
const Localized localized;
|
||||||
|
const std::string dev_flash = g_cfg_vfs.get_dev_flash();
|
||||||
|
const std::string _hdd = rpcs3::utils::get_hdd0_dir();
|
||||||
|
|
||||||
|
m_path_entries.emplace_back(path_entry{dev_flash + "vsh/module/vsh.self", false, false});
|
||||||
|
|
||||||
|
// Remove duplicates
|
||||||
|
sort(m_path_entries.begin(), m_path_entries.end(), [](const path_entry& l, const path_entry& r){return l.path < r.path;});
|
||||||
|
m_path_entries.erase(unique(m_path_entries.begin(), m_path_entries.end(), [](const path_entry& l, const path_entry& r){return l.path == r.path;}), m_path_entries.end());
|
||||||
|
|
||||||
|
const std::string game_icon_path = m_play_hover_movies ? fs::get_config_dir() + "/Icons/game_icons/" : "";
|
||||||
|
|
||||||
|
const auto add_game = [this, dev_flash, cat_unknown_localized = sstr(localized.category.unknown), cat_unknown = sstr(cat::cat_unknown), game_icon_path](const std::string& dir_or_elf)
|
||||||
|
{
|
||||||
|
GameInfo game{};
|
||||||
|
game.path = dir_or_elf;
|
||||||
|
|
||||||
|
const Localized thread_localized;
|
||||||
|
|
||||||
|
const std::string sfo_dir = rpcs3::utils::get_sfo_dir_from_game_path(dir_or_elf);
|
||||||
|
const psf::registry psf = psf::load_object(sfo_dir + "/PARAM.SFO");
|
||||||
|
const std::string_view title_id = psf::get_string(psf, "TITLE_ID", "");
|
||||||
|
|
||||||
|
if (title_id.empty())
|
||||||
|
{
|
||||||
|
if (!fs::is_file(dir_or_elf))
|
||||||
|
{
|
||||||
|
// Do not care about invalid entries
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
game.serial = dir_or_elf.substr(dir_or_elf.find_last_of(fs::delim) + 1);
|
||||||
|
game.category = cat::cat_ps3_os.toStdString(); // Key for operating system executables
|
||||||
|
game.version = utils::get_firmware_version();
|
||||||
|
game.fw = game.version;
|
||||||
|
game.bootable = 1;
|
||||||
|
game.icon_path = dev_flash + "vsh/resource/explore/icon/icon_home.png";
|
||||||
|
|
||||||
|
if (dir_or_elf.starts_with(dev_flash))
|
||||||
|
{
|
||||||
|
std::string path_vfs = dir_or_elf.substr(dev_flash.size());
|
||||||
|
|
||||||
|
if (const usz pos = path_vfs.find_first_not_of(fs::delim); pos != umax && pos != 0)
|
||||||
|
{
|
||||||
|
path_vfs = path_vfs.substr(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto it = thread_localized.title.titles.find(path_vfs); it != thread_localized.title.titles.cend())
|
||||||
|
{
|
||||||
|
game.name = it->second.toStdString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (game.name == "Unknown")
|
||||||
|
{
|
||||||
|
game.name = game.serial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
game.serial = std::string(title_id);
|
||||||
|
game.name = std::string(psf::get_string(psf, "TITLE", cat_unknown_localized));
|
||||||
|
game.app_ver = std::string(psf::get_string(psf, "APP_VER", cat_unknown_localized));
|
||||||
|
game.version = std::string(psf::get_string(psf, "VERSION", cat_unknown_localized));
|
||||||
|
game.category = std::string(psf::get_string(psf, "CATEGORY", cat_unknown));
|
||||||
|
game.fw = std::string(psf::get_string(psf, "PS3_SYSTEM_VER", cat_unknown_localized));
|
||||||
|
game.parental_lvl = psf::get_integer(psf, "PARENTAL_LEVEL", 0);
|
||||||
|
game.resolution = psf::get_integer(psf, "RESOLUTION", 0);
|
||||||
|
game.sound_format = psf::get_integer(psf, "SOUND_FORMAT", 0);
|
||||||
|
game.bootable = psf::get_integer(psf, "BOOTABLE", 0);
|
||||||
|
game.attr = psf::get_integer(psf, "ATTRIBUTE", 0);
|
||||||
|
game.icon_path = sfo_dir + "/ICON0.PNG";
|
||||||
|
|
||||||
|
if (game.category == "DG")
|
||||||
|
{
|
||||||
|
std::string latest_icon = rpcs3::utils::get_hdd0_dir() + "game/" + game.serial + "/ICON0.PNG";
|
||||||
|
if (fs::is_file(latest_icon))
|
||||||
|
{
|
||||||
|
game.icon_path = std::move(latest_icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_show_custom_icons)
|
||||||
|
{
|
||||||
|
if (std::string icon_path = fs::get_config_dir() + "/Icons/game_icons/" + game.serial + "/ICON0.PNG"; fs::is_file(icon_path))
|
||||||
|
{
|
||||||
|
game.icon_path = std::move(icon_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString serial = qstr(game.serial);
|
||||||
|
|
||||||
|
m_games_mutex.lock();
|
||||||
|
|
||||||
|
// Read persistent_settings values
|
||||||
|
const QString note = m_persistent_settings->GetValue(gui::persistent::notes, serial, "").toString();
|
||||||
|
const QString title = m_persistent_settings->GetValue(gui::persistent::titles, serial, "").toString().simplified();
|
||||||
|
QString last_played = m_persistent_settings->GetValue(gui::persistent::last_played, serial, "").toString();
|
||||||
|
quint64 playtime = m_persistent_settings->GetValue(gui::persistent::playtime, serial, 0).toULongLong();
|
||||||
|
|
||||||
|
// Set persistent_settings values if values exist
|
||||||
|
if (!last_played.isEmpty())
|
||||||
|
{
|
||||||
|
m_persistent_settings->SetLastPlayed(serial, last_played);
|
||||||
|
}
|
||||||
|
if (playtime > 0)
|
||||||
|
{
|
||||||
|
m_persistent_settings->SetPlaytime(serial, playtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_serials.insert(serial);
|
||||||
|
|
||||||
|
if (!note.isEmpty())
|
||||||
|
{
|
||||||
|
m_notes.insert(serial, note);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!title.isEmpty())
|
||||||
|
{
|
||||||
|
m_titles.insert(serial, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_games_mutex.unlock();
|
||||||
|
|
||||||
|
QString qt_cat = qstr(game.category);
|
||||||
|
|
||||||
|
if (const auto boot_cat = thread_localized.category.cat_boot.find(qt_cat); boot_cat != thread_localized.category.cat_boot.cend())
|
||||||
|
{
|
||||||
|
qt_cat = boot_cat->second;
|
||||||
|
}
|
||||||
|
else if (const auto data_cat = thread_localized.category.cat_data.find(qt_cat); data_cat != thread_localized.category.cat_data.cend())
|
||||||
|
{
|
||||||
|
qt_cat = data_cat->second;
|
||||||
|
}
|
||||||
|
else if (game.category == cat_unknown)
|
||||||
|
{
|
||||||
|
qt_cat = thread_localized.category.unknown;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qt_cat = thread_localized.category.other;
|
||||||
|
}
|
||||||
|
|
||||||
|
gui_game_info info{};
|
||||||
|
info.info = std::move(game);
|
||||||
|
info.localized_category = std::move(qt_cat);
|
||||||
|
info.compat = m_game_compat->GetCompatibility(info.info.serial);
|
||||||
|
info.hasCustomConfig = fs::is_file(rpcs3::utils::get_custom_config_path(info.info.serial));
|
||||||
|
info.hasCustomPadConfig = fs::is_file(rpcs3::utils::get_custom_input_config_path(info.info.serial));
|
||||||
|
info.has_hover_gif = fs::is_file(game_icon_path + info.info.serial + "/hover.gif");
|
||||||
|
|
||||||
|
m_games.push(std::make_shared<gui_game_info>(std::move(info)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto add_disc_dir = [this](const std::string& path, std::vector<std::string>& legit_paths)
|
||||||
|
{
|
||||||
|
for (const auto& entry : fs::dir(path))
|
||||||
|
{
|
||||||
|
if (!entry.is_directory || entry.name == "." || entry.name == "..")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.name == "PS3_GAME" || std::regex_match(entry.name, std::regex("^PS3_GM[[:digit:]]{2}$")))
|
||||||
|
{
|
||||||
|
push_path(path + "/" + entry.name, legit_paths);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
m_refresh_watcher.setFuture(QtConcurrent::map(m_path_entries, [this, _hdd, add_disc_dir, add_game](const path_entry& entry)
|
||||||
|
{
|
||||||
|
std::vector<std::string> legit_paths;
|
||||||
|
|
||||||
|
if (entry.is_from_yml)
|
||||||
|
{
|
||||||
|
if (fs::is_file(entry.path + "/PARAM.SFO"))
|
||||||
|
{
|
||||||
|
push_path(entry.path, legit_paths);
|
||||||
|
}
|
||||||
|
else if (fs::is_file(entry.path + "/PS3_DISC.SFB"))
|
||||||
|
{
|
||||||
|
// Check if a path loaded from games.yml is already registered in add_dir(_hdd + "disc/");
|
||||||
|
if (entry.path.starts_with(_hdd))
|
||||||
|
{
|
||||||
|
std::string_view frag = std::string_view(entry.path).substr(_hdd.size());
|
||||||
|
|
||||||
|
if (frag.starts_with("disc/"))
|
||||||
|
{
|
||||||
|
// Our path starts from _hdd + 'disc/'
|
||||||
|
frag.remove_prefix(5);
|
||||||
|
|
||||||
|
// Check if the remaining part is the only path component
|
||||||
|
if (frag.find_first_of('/') + 1 == 0)
|
||||||
|
{
|
||||||
|
game_list_log.trace("Removed duplicate: %s", entry.path);
|
||||||
|
|
||||||
|
if (static std::unordered_set<std::string> warn_once_list; warn_once_list.emplace(entry.path).second)
|
||||||
|
{
|
||||||
|
game_list_log.todo("Game at '%s' is using deprecated directory '/dev_hdd0/disc/'.\nConsider moving into '%s'.", entry.path, g_cfg_vfs.get(g_cfg_vfs.games_dir, rpcs3::utils::get_emu_dir()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add_disc_dir(entry.path, legit_paths);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
game_list_log.trace("Invalid game path registered: %s", entry.path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (fs::is_file(entry.path + "/PS3_DISC.SFB"))
|
||||||
|
{
|
||||||
|
if (!entry.is_disc)
|
||||||
|
{
|
||||||
|
game_list_log.error("Invalid game path found in %s", entry.path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_disc_dir(entry.path, legit_paths);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (entry.is_disc)
|
||||||
|
{
|
||||||
|
game_list_log.error("Invalid disc path found in %s", entry.path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
push_path(entry.path, legit_paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const std::string& path : legit_paths)
|
||||||
|
{
|
||||||
|
add_game(path);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
void game_list_frame::OnRefreshFinished()
|
void game_list_frame::OnRefreshFinished()
|
||||||
{
|
{
|
||||||
WaitAndAbortSizeCalcThreads();
|
WaitAndAbortSizeCalcThreads();
|
||||||
|
@ -851,6 +923,32 @@ void game_list_frame::OnRefreshFinished()
|
||||||
m_path_entries.clear();
|
m_path_entries.clear();
|
||||||
|
|
||||||
Refresh();
|
Refresh();
|
||||||
|
|
||||||
|
if (!std::exchange(m_initial_refresh_done, true))
|
||||||
|
{
|
||||||
|
const QByteArray state = m_gui_settings->GetValue(gui::gl_state).toByteArray();
|
||||||
|
if (!m_game_list->horizontalHeader()->restoreState(state) && m_game_list->rowCount())
|
||||||
|
{
|
||||||
|
// If no settings exist, resize to contents.
|
||||||
|
ResizeColumnsToContents();
|
||||||
|
}
|
||||||
|
|
||||||
|
FixNarrowColumns();
|
||||||
|
|
||||||
|
m_game_list->horizontalHeader()->restoreState(m_game_list->horizontalHeader()->saveState());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_progress_dialog_timer)
|
||||||
|
{
|
||||||
|
m_progress_dialog_timer->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_progress_dialog)
|
||||||
|
{
|
||||||
|
m_progress_dialog->accept();
|
||||||
|
m_progress_dialog->deleteLater();
|
||||||
|
m_progress_dialog = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_list_frame::OnCompatFinished()
|
void game_list_frame::OnCompatFinished()
|
||||||
|
@ -2338,6 +2436,8 @@ void game_list_frame::ResizeIcons(const int& slider_pos)
|
||||||
|
|
||||||
void game_list_frame::RepaintIcons(const bool& from_settings)
|
void game_list_frame::RepaintIcons(const bool& from_settings)
|
||||||
{
|
{
|
||||||
|
gui::utils::stop_future_watcher(m_parsing_watcher, false);
|
||||||
|
gui::utils::stop_future_watcher(m_refresh_watcher, false);
|
||||||
WaitAndAbortRepaintThreads();
|
WaitAndAbortRepaintThreads();
|
||||||
|
|
||||||
if (from_settings)
|
if (from_settings)
|
||||||
|
@ -2744,7 +2844,7 @@ void game_list_frame::PopulateGameGrid(int maxCols, const QSize& image_size, con
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get list of matching apps
|
// Get list of matching apps
|
||||||
QList<game_info> matching_apps;
|
std::vector<game_info> matching_apps;
|
||||||
|
|
||||||
for (const auto& app : m_game_data)
|
for (const auto& app : m_game_data)
|
||||||
{
|
{
|
||||||
|
@ -2757,7 +2857,7 @@ void game_list_frame::PopulateGameGrid(int maxCols, const QSize& image_size, con
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback is not needed when at least one entry is visible
|
// Fallback is not needed when at least one entry is visible
|
||||||
if (matching_apps.isEmpty())
|
if (matching_apps.empty())
|
||||||
{
|
{
|
||||||
for (const auto& app : m_game_data)
|
for (const auto& app : m_game_data)
|
||||||
{
|
{
|
||||||
|
@ -2768,7 +2868,7 @@ void game_list_frame::PopulateGameGrid(int maxCols, const QSize& image_size, con
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const int entries = matching_apps.count();
|
const int entries = static_cast<int>(matching_apps.size());
|
||||||
|
|
||||||
// Edge cases!
|
// Edge cases!
|
||||||
if (entries == 0)
|
if (entries == 0)
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QTableWidgetItem>
|
#include <QTableWidgetItem>
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
@ -21,6 +22,7 @@ class game_list_grid;
|
||||||
class gui_settings;
|
class gui_settings;
|
||||||
class emu_settings;
|
class emu_settings;
|
||||||
class persistent_settings;
|
class persistent_settings;
|
||||||
|
class progress_dialog;
|
||||||
|
|
||||||
class game_list_frame : public custom_dock_widget
|
class game_list_frame : public custom_dock_widget
|
||||||
{
|
{
|
||||||
|
@ -80,6 +82,7 @@ public Q_SLOTS:
|
||||||
void FocusAndSelectFirstEntryIfNoneIs();
|
void FocusAndSelectFirstEntryIfNoneIs();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
|
void OnParsingFinished();
|
||||||
void OnRefreshFinished();
|
void OnRefreshFinished();
|
||||||
void OnCompatFinished();
|
void OnCompatFinished();
|
||||||
void OnColClicked(int col);
|
void OnColClicked(int col);
|
||||||
|
@ -101,6 +104,8 @@ protected:
|
||||||
void resizeEvent(QResizeEvent *event) override;
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
bool eventFilter(QObject *object, QEvent *event) override;
|
bool eventFilter(QObject *object, QEvent *event) override;
|
||||||
private:
|
private:
|
||||||
|
void push_path(const std::string& path, std::vector<std::string>& legit_paths);
|
||||||
|
|
||||||
QPixmap PaintedPixmap(const QPixmap& icon, bool paint_config_icon = false, bool paint_pad_config_icon = false, const QColor& color = QColor()) const;
|
QPixmap PaintedPixmap(const QPixmap& icon, bool paint_config_icon = false, bool paint_pad_config_icon = false, const QColor& color = QColor()) const;
|
||||||
QColor getGridCompatibilityColor(const QString& string) const;
|
QColor getGridCompatibilityColor(const QString& string) const;
|
||||||
void IconLoadFunction(game_info game, std::shared_ptr<atomic_t<bool>> cancel);
|
void IconLoadFunction(game_info game, std::shared_ptr<atomic_t<bool>> cancel);
|
||||||
|
@ -111,7 +116,7 @@ private:
|
||||||
void PopulateGameList();
|
void PopulateGameList();
|
||||||
void PopulateGameGrid(int maxCols, const QSize& image_size, const QColor& image_color);
|
void PopulateGameGrid(int maxCols, const QSize& image_size, const QColor& image_color);
|
||||||
bool IsEntryVisible(const game_info& game, bool search_fallback = false);
|
bool IsEntryVisible(const game_info& game, bool search_fallback = false);
|
||||||
void SortGameList() const;
|
void SortGameList();
|
||||||
bool SearchMatchesApp(const QString& name, const QString& serial, bool fallback = false) const;
|
bool SearchMatchesApp(const QString& name, const QString& serial, bool fallback = false) const;
|
||||||
|
|
||||||
bool RemoveCustomConfiguration(const std::string& title_id, const game_info& game = nullptr, bool is_interactive = false);
|
bool RemoveCustomConfiguration(const std::string& title_id, const game_info& game = nullptr, bool is_interactive = false);
|
||||||
|
@ -144,9 +149,12 @@ private:
|
||||||
// Game List
|
// Game List
|
||||||
game_list* m_game_list = nullptr;
|
game_list* m_game_list = nullptr;
|
||||||
game_compatibility* m_game_compat = nullptr;
|
game_compatibility* m_game_compat = nullptr;
|
||||||
|
progress_dialog* m_progress_dialog = nullptr;
|
||||||
|
QTimer* m_progress_dialog_timer = nullptr;
|
||||||
QList<QAction*> m_columnActs;
|
QList<QAction*> m_columnActs;
|
||||||
Qt::SortOrder m_col_sort_order;
|
Qt::SortOrder m_col_sort_order;
|
||||||
int m_sort_column;
|
int m_sort_column;
|
||||||
|
bool m_initial_refresh_done = false;
|
||||||
QMap<QString, QString> m_notes;
|
QMap<QString, QString> m_notes;
|
||||||
QMap<QString, QString> m_titles;
|
QMap<QString, QString> m_titles;
|
||||||
|
|
||||||
|
@ -175,6 +183,8 @@ private:
|
||||||
QSet<QString> m_serials;
|
QSet<QString> m_serials;
|
||||||
QMutex m_games_mutex;
|
QMutex m_games_mutex;
|
||||||
lf_queue<game_info> m_games;
|
lf_queue<game_info> m_games;
|
||||||
|
const std::array<int, 1> m_parsing_threads{0};
|
||||||
|
QFutureWatcher<void> m_parsing_watcher;
|
||||||
QFutureWatcher<void> m_refresh_watcher;
|
QFutureWatcher<void> m_refresh_watcher;
|
||||||
QSet<QString> m_hidden_list;
|
QSet<QString> m_hidden_list;
|
||||||
bool m_show_hidden{false};
|
bool m_show_hidden{false};
|
||||||
|
|
|
@ -192,9 +192,6 @@ bool main_window::Init([[maybe_unused]] bool with_cli_boot)
|
||||||
connect(m_thumb_playPause, &QWinThumbnailToolButton::clicked, this, &main_window::OnPlayOrPause);
|
connect(m_thumb_playPause, &QWinThumbnailToolButton::clicked, this, &main_window::OnPlayOrPause);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Fix possible hidden game list columns. The game list has to be visible already. Use this after show()
|
|
||||||
m_game_list_frame->FixNarrowColumns();
|
|
||||||
|
|
||||||
// RPCS3 Updater
|
// RPCS3 Updater
|
||||||
|
|
||||||
QMenu* download_menu = new QMenu(tr("Update Available!"));
|
QMenu* download_menu = new QMenu(tr("Update Available!"));
|
||||||
|
@ -249,6 +246,9 @@ bool main_window::Init([[maybe_unused]] bool with_cli_boot)
|
||||||
// Focus to search bar by default
|
// Focus to search bar by default
|
||||||
ui->mw_searchbar->setFocus();
|
ui->mw_searchbar->setFocus();
|
||||||
|
|
||||||
|
// Refresh gamelist last
|
||||||
|
m_game_list_frame->Refresh(true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,7 +120,6 @@ private Q_SLOTS:
|
||||||
static void show_boot_error(game_boot_result status);
|
static void show_boot_error(game_boot_result status);
|
||||||
|
|
||||||
void SaveWindowState() const;
|
void SaveWindowState() const;
|
||||||
void ConfigureGuiFromSettings();
|
|
||||||
void SetIconSizeActions(int idx) const;
|
void SetIconSizeActions(int idx) const;
|
||||||
void ResizeIcons(int index);
|
void ResizeIcons(int index);
|
||||||
|
|
||||||
|
@ -139,6 +138,7 @@ protected:
|
||||||
void dragLeaveEvent(QDragLeaveEvent* event) override;
|
void dragLeaveEvent(QDragLeaveEvent* event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void ConfigureGuiFromSettings();
|
||||||
void RepaintToolBarIcons();
|
void RepaintToolBarIcons();
|
||||||
void RepaintThumbnailIcons();
|
void RepaintThumbnailIcons();
|
||||||
void CreateActions();
|
void CreateActions();
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "gui_settings.h"
|
#include "gui_settings.h"
|
||||||
#include "persistent_settings.h"
|
#include "persistent_settings.h"
|
||||||
#include "game_list_delegate.h"
|
#include "game_list_delegate.h"
|
||||||
|
#include "progress_dialog.h"
|
||||||
|
|
||||||
#include "Emu/System.h"
|
#include "Emu/System.h"
|
||||||
#include "Emu/system_utils.hpp"
|
#include "Emu/system_utils.hpp"
|
||||||
|
@ -22,7 +23,6 @@
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
#include <QProgressDialog>
|
|
||||||
|
|
||||||
#include "Utilities/File.h"
|
#include "Utilities/File.h"
|
||||||
#include "Utilities/mutex.h"
|
#include "Utilities/mutex.h"
|
||||||
|
@ -237,8 +237,7 @@ std::vector<SaveDataEntry> save_manager_dialog::GetSaveEntries(const std::string
|
||||||
|
|
||||||
QFutureWatcher<void> future_watcher;
|
QFutureWatcher<void> future_watcher;
|
||||||
|
|
||||||
QProgressDialog progress_dialog(tr("Loading save data, please wait..."), tr("Cancel"), 0, 1, this, Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
|
progress_dialog progress_dialog(tr("Loading save data"), tr("Loading save data, please wait..."), tr("Cancel"), 0, 1, false, this, Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
|
||||||
progress_dialog.setWindowTitle(tr("Loading save data"));
|
|
||||||
|
|
||||||
connect(&future_watcher, &QFutureWatcher<void>::progressRangeChanged, &progress_dialog, &QProgressDialog::setRange);
|
connect(&future_watcher, &QFutureWatcher<void>::progressRangeChanged, &progress_dialog, &QProgressDialog::setRange);
|
||||||
connect(&future_watcher, &QFutureWatcher<void>::progressValueChanged, &progress_dialog, &QProgressDialog::setValue);
|
connect(&future_watcher, &QFutureWatcher<void>::progressValueChanged, &progress_dialog, &QProgressDialog::setValue);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "qt_utils.h"
|
#include "qt_utils.h"
|
||||||
#include "game_list.h"
|
#include "game_list.h"
|
||||||
#include "gui_settings.h"
|
#include "gui_settings.h"
|
||||||
|
#include "progress_dialog.h"
|
||||||
|
|
||||||
#include "util/logs.hpp"
|
#include "util/logs.hpp"
|
||||||
#include "Utilities/StrUtil.h"
|
#include "Utilities/StrUtil.h"
|
||||||
|
@ -29,7 +30,6 @@
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
#include <QWheelEvent>
|
#include <QWheelEvent>
|
||||||
#include <QProgressDialog>
|
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
|
|
||||||
|
@ -919,8 +919,7 @@ void trophy_manager_dialog::StartTrophyLoadThreads()
|
||||||
|
|
||||||
QFutureWatcher<void> futureWatcher;
|
QFutureWatcher<void> futureWatcher;
|
||||||
|
|
||||||
QProgressDialog progressDialog(tr("Loading trophy data, please wait..."), tr("Cancel"), 0, 1, this, Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
|
progress_dialog progressDialog(tr("Loading trophies"), tr("Loading trophy data, please wait..."), tr("Cancel"), 0, 1, false, this, Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
|
||||||
progressDialog.setWindowTitle(tr("Loading trophies"));
|
|
||||||
|
|
||||||
connect(&futureWatcher, &QFutureWatcher<void>::progressRangeChanged, &progressDialog, &QProgressDialog::setRange);
|
connect(&futureWatcher, &QFutureWatcher<void>::progressRangeChanged, &progressDialog, &QProgressDialog::setRange);
|
||||||
connect(&futureWatcher, &QFutureWatcher<void>::progressValueChanged, &progressDialog, &QProgressDialog::setValue);
|
connect(&futureWatcher, &QFutureWatcher<void>::progressValueChanged, &progressDialog, &QProgressDialog::setValue);
|
||||||
|
|
Loading…
Reference in New Issue