mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-03 21:41:26 +12:00
[TESTERS NEEDED] Improved contextual menu (#16038)
This commit is contained in:
parent
7f2534819e
commit
d1648dd707
9 changed files with 528 additions and 133 deletions
|
@ -1757,7 +1757,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
||||||
// Booting disc game from wrong location
|
// Booting disc game from wrong location
|
||||||
sys_log.error("Disc game %s found at invalid location /dev_hdd0/game/", m_title_id);
|
sys_log.error("Disc game %s found at invalid location /dev_hdd0/game/", m_title_id);
|
||||||
|
|
||||||
const std::string games_common = g_cfg_vfs.get(g_cfg_vfs.games_dir, rpcs3::utils::get_emu_dir());
|
const std::string games_common = rpcs3::utils::get_games_dir();
|
||||||
const std::string dst_dir = games_common + sfb_dir.substr(hdd0_game.size());
|
const std::string dst_dir = games_common + sfb_dir.substr(hdd0_game.size());
|
||||||
|
|
||||||
// Move and retry from correct location
|
// Move and retry from correct location
|
||||||
|
@ -4082,6 +4082,56 @@ game_boot_result Emulator::AddGameToYml(const std::string& path)
|
||||||
return game_boot_result::invalid_file_or_folder;
|
return game_boot_result::invalid_file_or_folder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 Emulator::RemoveGames(const std::vector<std::string>& title_id_list)
|
||||||
|
{
|
||||||
|
if (title_id_list.empty())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 games_removed = 0;
|
||||||
|
|
||||||
|
m_games_config.set_save_on_dirty(false);
|
||||||
|
|
||||||
|
for (const std::string& title_id : title_id_list)
|
||||||
|
{
|
||||||
|
if (RemoveGameFromYml(title_id) == game_boot_result::no_errors)
|
||||||
|
{
|
||||||
|
games_removed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_games_config.set_save_on_dirty(true);
|
||||||
|
|
||||||
|
if (m_games_config.is_dirty() && !m_games_config.save())
|
||||||
|
{
|
||||||
|
sys_log.error("Failed to save games.yml after removing games");
|
||||||
|
}
|
||||||
|
|
||||||
|
return games_removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
game_boot_result Emulator::RemoveGameFromYml(const std::string& title_id)
|
||||||
|
{
|
||||||
|
// Remove title from games.yml
|
||||||
|
switch (m_games_config.remove_game(title_id))
|
||||||
|
{
|
||||||
|
case games_config::result::failure:
|
||||||
|
{
|
||||||
|
sys_log.error("Failed to remove title '%s' (error=%s)", title_id, fs::g_tls_error);
|
||||||
|
return game_boot_result::generic_error;
|
||||||
|
}
|
||||||
|
case games_config::result::success:
|
||||||
|
case games_config::result::exists: // not applicable for m_games_config.remove_game(). Added just to avoid compilation warnings!
|
||||||
|
{
|
||||||
|
sys_log.notice("Removed title '%s'", title_id);
|
||||||
|
return game_boot_result::no_errors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return game_boot_result::generic_error;
|
||||||
|
}
|
||||||
|
|
||||||
bool Emulator::IsPathInsideDir(std::string_view path, std::string_view dir) const
|
bool Emulator::IsPathInsideDir(std::string_view path, std::string_view dir) const
|
||||||
{
|
{
|
||||||
const std::string dir_path = GetCallbacks().resolve_path(dir);
|
const std::string dir_path = GetCallbacks().resolve_path(dir);
|
||||||
|
|
|
@ -379,6 +379,8 @@ public:
|
||||||
u32 AddGamesFromDir(const std::string& path);
|
u32 AddGamesFromDir(const std::string& path);
|
||||||
game_boot_result AddGame(const std::string& path);
|
game_boot_result AddGame(const std::string& path);
|
||||||
game_boot_result AddGameToYml(const std::string& path);
|
game_boot_result AddGameToYml(const std::string& path);
|
||||||
|
u32 RemoveGames(const std::vector<std::string>& title_id_list);
|
||||||
|
game_boot_result RemoveGameFromYml(const std::string& title_id);
|
||||||
|
|
||||||
// Check if path is inside the specified directory
|
// Check if path is inside the specified directory
|
||||||
bool IsPathInsideDir(std::string_view path, std::string_view dir) const;
|
bool IsPathInsideDir(std::string_view path, std::string_view dir) const;
|
||||||
|
|
|
@ -97,6 +97,27 @@ games_config::result games_config::add_external_hdd_game(const std::string& key,
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
games_config::result games_config::remove_game(const std::string& key)
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_mutex);
|
||||||
|
|
||||||
|
// Remove node
|
||||||
|
if (m_games.erase(key) == 0) // If node not found
|
||||||
|
{
|
||||||
|
// Nothing to do
|
||||||
|
return result::success;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dirty = true;
|
||||||
|
|
||||||
|
if (m_save_on_dirty && !save_nl())
|
||||||
|
{
|
||||||
|
return result::failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result::success;
|
||||||
|
}
|
||||||
|
|
||||||
bool games_config::save_nl()
|
bool games_config::save_nl()
|
||||||
{
|
{
|
||||||
YAML::Emitter out;
|
YAML::Emitter out;
|
||||||
|
|
|
@ -24,6 +24,7 @@ public:
|
||||||
};
|
};
|
||||||
result add_game(const std::string& key, const std::string& path);
|
result add_game(const std::string& key, const std::string& path);
|
||||||
result add_external_hdd_game(const std::string& key, std::string& path);
|
result add_external_hdd_game(const std::string& key, std::string& path);
|
||||||
|
result remove_game(const std::string& key);
|
||||||
bool save();
|
bool save();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -107,6 +107,11 @@ namespace rpcs3::utils
|
||||||
return emu_dir_.empty() ? fs::get_config_dir() : emu_dir_;
|
return emu_dir_.empty() ? fs::get_config_dir() : emu_dir_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string get_games_dir()
|
||||||
|
{
|
||||||
|
return g_cfg_vfs.get(g_cfg_vfs.games_dir, get_emu_dir());
|
||||||
|
}
|
||||||
|
|
||||||
std::string get_hdd0_dir()
|
std::string get_hdd0_dir()
|
||||||
{
|
{
|
||||||
return g_cfg_vfs.get(g_cfg_vfs.dev_hdd0, get_emu_dir());
|
return g_cfg_vfs.get(g_cfg_vfs.dev_hdd0, get_emu_dir());
|
||||||
|
|
|
@ -14,6 +14,7 @@ namespace rpcs3::utils
|
||||||
bool install_pkg(const std::string& path);
|
bool install_pkg(const std::string& path);
|
||||||
|
|
||||||
std::string get_emu_dir();
|
std::string get_emu_dir();
|
||||||
|
std::string get_games_dir();
|
||||||
std::string get_hdd0_dir();
|
std::string get_hdd0_dir();
|
||||||
std::string get_hdd1_dir();
|
std::string get_hdd1_dir();
|
||||||
std::string get_cache_dir();
|
std::string get_cache_dir();
|
||||||
|
|
|
@ -284,6 +284,95 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool game_list_frame::RemoveContentPath(const std::string& path, const std::string& desc)
|
||||||
|
{
|
||||||
|
if (!fs::exists(path))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs::is_dir(path))
|
||||||
|
{
|
||||||
|
if (fs::remove_all(path))
|
||||||
|
{
|
||||||
|
game_list_log.notice("Removed '%s' directory: '%s'", desc, path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
game_list_log.error("Could not remove '%s' directory: '%s' (%s)", desc, path, fs::g_tls_error);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // If file
|
||||||
|
{
|
||||||
|
if (fs::remove_file(path))
|
||||||
|
{
|
||||||
|
game_list_log.notice("Removed '%s' file: '%s'", desc, path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
game_list_log.error("Could not remove '%s' file: '%s' (%s)", desc, path, fs::g_tls_error);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 game_list_frame::RemoveContentPathList(const std::vector<std::string>& path_list, const std::string& desc)
|
||||||
|
{
|
||||||
|
u32 paths_removed = 0;
|
||||||
|
|
||||||
|
for (const std::string& path : path_list)
|
||||||
|
{
|
||||||
|
if (RemoveContentPath(path, desc))
|
||||||
|
{
|
||||||
|
paths_removed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths_removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool game_list_frame::RemoveContentBySerial(const std::string& base_dir, const std::string& serial, const std::string& desc)
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
for (const auto& entry : fs::dir(base_dir))
|
||||||
|
{
|
||||||
|
// Search for any path starting with serial (e.g. BCES01118_BCES01118)
|
||||||
|
if (!entry.name.starts_with(serial))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!RemoveContentPath(base_dir + entry.name, desc))
|
||||||
|
{
|
||||||
|
success = false; // Mark as failed if there is at least one failure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> game_list_frame::GetDirListBySerial(const std::string& base_dir, const std::string& serial)
|
||||||
|
{
|
||||||
|
std::vector<std::string> dir_list;
|
||||||
|
|
||||||
|
for (const auto& entry : fs::dir(base_dir))
|
||||||
|
{
|
||||||
|
// Check for sub folder starting with serial (e.g. BCES01118_BCES01118)
|
||||||
|
if (entry.is_directory && entry.name.starts_with(serial))
|
||||||
|
{
|
||||||
|
dir_list.push_back(base_dir + entry.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dir_list;
|
||||||
|
}
|
||||||
|
|
||||||
std::string game_list_frame::GetCacheDirBySerial(const std::string& serial)
|
std::string game_list_frame::GetCacheDirBySerial(const std::string& serial)
|
||||||
{
|
{
|
||||||
return rpcs3::utils::get_cache_dir() + (serial == "vsh.self" ? "vsh" : serial);
|
return rpcs3::utils::get_cache_dir() + (serial == "vsh.self" ? "vsh" : serial);
|
||||||
|
@ -306,7 +395,7 @@ void game_list_frame::push_path(const std::string& path, std::vector<std::string
|
||||||
legit_paths.push_back(path);
|
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 std::vector<std::string>& serials_to_remove_from_yml, const bool scroll_after)
|
||||||
{
|
{
|
||||||
if (from_drive)
|
if (from_drive)
|
||||||
{
|
{
|
||||||
|
@ -336,7 +425,10 @@ void game_list_frame::Refresh(const bool from_drive, const bool scroll_after)
|
||||||
m_progress_dialog->SetValue(0);
|
m_progress_dialog->SetValue(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string games_dir = g_cfg_vfs.get(g_cfg_vfs.games_dir, rpcs3::utils::get_emu_dir());
|
// Remove the specified serials (title id) in "games.yml" file (if any)
|
||||||
|
Emu.RemoveGames(serials_to_remove_from_yml);
|
||||||
|
|
||||||
|
const std::string games_dir = rpcs3::utils::get_games_dir();
|
||||||
const u32 games_added = Emu.AddGamesFromDir(games_dir);
|
const u32 games_added = Emu.AddGamesFromDir(games_dir);
|
||||||
|
|
||||||
if (games_added)
|
if (games_added)
|
||||||
|
@ -687,7 +779,7 @@ void game_list_frame::OnParsingFinished()
|
||||||
|
|
||||||
if (static std::unordered_set<std::string> warn_once_list; warn_once_list.emplace(entry.path).second)
|
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()));
|
game_list_log.todo("Game at '%s' is using deprecated directory '/dev_hdd0/disc/'.\nConsider moving into '%s'.", entry.path, rpcs3::utils::get_games_dir());
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -1024,7 +1116,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
|
||||||
const QString name = qstr(current_game.name).simplified();
|
const QString name = qstr(current_game.name).simplified();
|
||||||
|
|
||||||
const std::string cache_base_dir = GetCacheDirBySerial(current_game.serial);
|
const std::string cache_base_dir = GetCacheDirBySerial(current_game.serial);
|
||||||
const std::string data_base_dir = GetDataDirBySerial(current_game.serial);
|
const std::string config_data_base_dir = GetDataDirBySerial(current_game.serial);
|
||||||
|
|
||||||
// Make Actions
|
// Make Actions
|
||||||
QMenu menu;
|
QMenu menu;
|
||||||
|
@ -1120,32 +1212,14 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
|
||||||
? tr("&Change Custom Gamepad Configuration")
|
? tr("&Change Custom Gamepad Configuration")
|
||||||
: tr("&Create Custom Gamepad Configuration"));
|
: tr("&Create Custom Gamepad Configuration"));
|
||||||
QAction* configure_patches = menu.addAction(tr("&Manage Game Patches"));
|
QAction* configure_patches = menu.addAction(tr("&Manage Game Patches"));
|
||||||
|
|
||||||
|
menu.addSeparator();
|
||||||
|
|
||||||
QAction* create_cpu_cache = menu.addAction(tr("&Create LLVM Cache"));
|
QAction* create_cpu_cache = menu.addAction(tr("&Create LLVM Cache"));
|
||||||
|
|
||||||
menu.addSeparator();
|
// Remove menu
|
||||||
|
|
||||||
QMenu* shortcut_menu = menu.addMenu(tr("&Create Shortcut"));
|
|
||||||
QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("&Create Desktop Shortcut"));
|
|
||||||
connect(create_desktop_shortcut, &QAction::triggered, this, [this, gameinfo](){ CreateShortcuts(gameinfo, { gui::utils::shortcut_location::desktop }); });
|
|
||||||
#ifdef _WIN32
|
|
||||||
QAction* create_start_menu_shortcut = shortcut_menu->addAction(tr("&Create Start Menu Shortcut"));
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
QAction* create_start_menu_shortcut = shortcut_menu->addAction(tr("&Create Launchpad Shortcut"));
|
|
||||||
#else
|
|
||||||
QAction* create_start_menu_shortcut = shortcut_menu->addAction(tr("&Create Application Menu Shortcut"));
|
|
||||||
#endif
|
|
||||||
connect(create_start_menu_shortcut, &QAction::triggered, this, [this, gameinfo](){ CreateShortcuts(gameinfo, { gui::utils::shortcut_location::applications }); });
|
|
||||||
|
|
||||||
menu.addSeparator();
|
|
||||||
|
|
||||||
QAction* rename_title = menu.addAction(tr("&Rename In Game List"));
|
|
||||||
QAction* hide_serial = menu.addAction(tr("&Hide From Game List"));
|
|
||||||
hide_serial->setCheckable(true);
|
|
||||||
hide_serial->setChecked(m_hidden_list.contains(serial));
|
|
||||||
menu.addSeparator();
|
|
||||||
QMenu* remove_menu = menu.addMenu(tr("&Remove"));
|
QMenu* remove_menu = menu.addMenu(tr("&Remove"));
|
||||||
QAction* remove_game = remove_menu->addAction(tr("&Remove %1").arg(gameinfo->localized_category));
|
|
||||||
remove_game->setEnabled(!is_current_running_game);
|
|
||||||
if (gameinfo->hasCustomConfig)
|
if (gameinfo->hasCustomConfig)
|
||||||
{
|
{
|
||||||
QAction* remove_custom_config = remove_menu->addAction(tr("&Remove Custom Configuration"));
|
QAction* remove_custom_config = remove_menu->addAction(tr("&Remove Custom Configuration"));
|
||||||
|
@ -1174,6 +1248,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
|
||||||
if (has_cache_dir)
|
if (has_cache_dir)
|
||||||
{
|
{
|
||||||
remove_menu->addSeparator();
|
remove_menu->addSeparator();
|
||||||
|
|
||||||
QAction* remove_shaders_cache = remove_menu->addAction(tr("&Remove Shaders Cache"));
|
QAction* remove_shaders_cache = remove_menu->addAction(tr("&Remove Shaders Cache"));
|
||||||
remove_shaders_cache->setEnabled(!is_current_running_game);
|
remove_shaders_cache->setEnabled(!is_current_running_game);
|
||||||
connect(remove_shaders_cache, &QAction::triggered, [this, cache_base_dir]()
|
connect(remove_shaders_cache, &QAction::triggered, [this, cache_base_dir]()
|
||||||
|
@ -1194,33 +1269,24 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_hdd1_cache = false;
|
const std::string hdd1_cache_base_dir = rpcs3::utils::get_hdd1_dir() + "caches/";
|
||||||
const std::string hdd1 = rpcs3::utils::get_hdd1_dir() + "/caches/";
|
const bool has_hdd1_cache_dir = !GetDirListBySerial(hdd1_cache_base_dir, current_game.serial).empty();
|
||||||
|
|
||||||
for (const auto& entry : fs::dir(hdd1))
|
if (has_hdd1_cache_dir)
|
||||||
{
|
|
||||||
if (entry.is_directory && entry.name.starts_with(current_game.serial))
|
|
||||||
{
|
|
||||||
has_hdd1_cache = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (has_hdd1_cache)
|
|
||||||
{
|
{
|
||||||
QAction* remove_hdd1_cache = remove_menu->addAction(tr("&Remove HDD1 Cache"));
|
QAction* remove_hdd1_cache = remove_menu->addAction(tr("&Remove HDD1 Cache"));
|
||||||
remove_hdd1_cache->setEnabled(!is_current_running_game);
|
remove_hdd1_cache->setEnabled(!is_current_running_game);
|
||||||
connect(remove_hdd1_cache, &QAction::triggered, [this, hdd1, serial = current_game.serial]()
|
connect(remove_hdd1_cache, &QAction::triggered, [this, hdd1_cache_base_dir, serial = current_game.serial]()
|
||||||
{
|
{
|
||||||
RemoveHDD1Cache(hdd1, serial, true);
|
RemoveHDD1Cache(hdd1_cache_base_dir, serial, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_cache_dir || has_hdd1_cache)
|
if (has_cache_dir || has_hdd1_cache_dir)
|
||||||
{
|
{
|
||||||
QAction* remove_all_caches = remove_menu->addAction(tr("&Remove All Caches"));
|
QAction* remove_all_caches = remove_menu->addAction(tr("&Remove All Caches"));
|
||||||
remove_all_caches->setEnabled(!is_current_running_game);
|
remove_all_caches->setEnabled(!is_current_running_game);
|
||||||
connect(remove_all_caches, &QAction::triggered, [this, current_game, cache_base_dir, hdd1]()
|
connect(remove_all_caches, &QAction::triggered, [this, current_game, cache_base_dir, hdd1_cache_base_dir]()
|
||||||
{
|
{
|
||||||
if (is_game_running(current_game.serial))
|
if (is_game_running(current_game.serial))
|
||||||
return;
|
return;
|
||||||
|
@ -1228,59 +1294,76 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
|
||||||
if (QMessageBox::question(this, tr("Confirm Removal"), tr("Remove all caches?")) != QMessageBox::Yes)
|
if (QMessageBox::question(this, tr("Confirm Removal"), tr("Remove all caches?")) != QMessageBox::Yes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (fs::remove_all(cache_base_dir))
|
RemoveContentPath(cache_base_dir, "cache");
|
||||||
game_list_log.success("Removed cache directory: '%s'", cache_base_dir);
|
RemoveHDD1Cache(hdd1_cache_base_dir, current_game.serial);
|
||||||
else
|
|
||||||
game_list_log.error("Could not remove cache directory: '%s' (%s)", cache_base_dir, fs::g_tls_error);
|
|
||||||
|
|
||||||
RemoveHDD1Cache(hdd1, current_game.serial);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string savestate_dir = fs::get_config_dir() + "savestates/" + current_game.serial;
|
||||||
|
|
||||||
|
if (fs::is_dir(savestate_dir))
|
||||||
|
{
|
||||||
|
remove_menu->addSeparator();
|
||||||
|
|
||||||
|
QAction* remove_savestate = remove_menu->addAction(tr("&Remove Savestate"));
|
||||||
|
remove_savestate->setEnabled(!is_current_running_game);
|
||||||
|
connect(remove_savestate, &QAction::triggered, [this, current_game, savestate_dir]()
|
||||||
|
{
|
||||||
|
if (is_game_running(current_game.serial))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (QMessageBox::question(this, tr("Confirm Removal"), tr("Remove savestate?")) != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
RemoveContentPath(savestate_dir, "savestate");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable the Remove menu if empty
|
||||||
|
remove_menu->setEnabled(!remove_menu->isEmpty());
|
||||||
|
|
||||||
menu.addSeparator();
|
menu.addSeparator();
|
||||||
QAction* open_game_folder = menu.addAction(tr("&Open Install Folder"));
|
|
||||||
if (gameinfo->hasCustomConfig)
|
|
||||||
{
|
|
||||||
QAction* open_config_dir = menu.addAction(tr("&Open Custom Config Folder"));
|
|
||||||
connect(open_config_dir, &QAction::triggered, [current_game]()
|
|
||||||
{
|
|
||||||
const std::string config_path = rpcs3::utils::get_custom_config_path(current_game.serial);
|
|
||||||
|
|
||||||
if (fs::is_file(config_path))
|
// Manage Game menu
|
||||||
gui::utils::open_dir(config_path);
|
QMenu* manage_game_menu = menu.addMenu(tr("&Manage Game"));
|
||||||
|
|
||||||
|
// Create game shortcuts
|
||||||
|
QAction* create_desktop_shortcut = manage_game_menu->addAction(tr("&Create Desktop Shortcut"));
|
||||||
|
connect(create_desktop_shortcut, &QAction::triggered, this, [this, gameinfo]()
|
||||||
|
{
|
||||||
|
CreateShortcuts(gameinfo, {gui::utils::shortcut_location::desktop});
|
||||||
});
|
});
|
||||||
}
|
#ifdef _WIN32
|
||||||
|
QAction* create_start_menu_shortcut = manage_game_menu->addAction(tr("&Create Start Menu Shortcut"));
|
||||||
// This is a debug feature, let's hide it by reusing debug tab protection
|
#elif defined(__APPLE__)
|
||||||
if (m_gui_settings->GetValue(gui::m_showDebugTab).toBool())
|
QAction* create_start_menu_shortcut = manage_game_menu->addAction(tr("&Create Launchpad Shortcut"));
|
||||||
|
#else
|
||||||
|
QAction* create_start_menu_shortcut = manage_game_menu->addAction(tr("&Create Application Menu Shortcut"));
|
||||||
|
#endif
|
||||||
|
connect(create_start_menu_shortcut, &QAction::triggered, this, [this, gameinfo]()
|
||||||
{
|
{
|
||||||
QAction* open_cache_folder = menu.addAction(tr("&Open Cache Folder"));
|
CreateShortcuts(gameinfo, {gui::utils::shortcut_location::applications});
|
||||||
open_cache_folder->setEnabled(fs::is_dir(cache_base_dir));
|
|
||||||
|
|
||||||
if (open_cache_folder->isEnabled())
|
|
||||||
{
|
|
||||||
connect(open_cache_folder, &QAction::triggered, this, [cache_base_dir]()
|
|
||||||
{
|
|
||||||
gui::utils::open_dir(cache_base_dir);
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fs::is_dir(data_base_dir))
|
manage_game_menu->addSeparator();
|
||||||
{
|
|
||||||
QAction* open_data_dir = menu.addAction(tr("&Open Data Folder"));
|
|
||||||
connect(open_data_dir, &QAction::triggered, [data_base_dir]()
|
|
||||||
{
|
|
||||||
gui::utils::open_dir(data_base_dir);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
menu.addSeparator();
|
|
||||||
QAction* check_compat = menu.addAction(tr("&Check Game Compatibility"));
|
|
||||||
QAction* download_compat = menu.addAction(tr("&Download Compatibility Database"));
|
|
||||||
menu.addSeparator();
|
|
||||||
QAction* edit_notes = menu.addAction(tr("&Edit Tooltip Notes"));
|
|
||||||
QAction* reset_time_played = menu.addAction(tr("&Reset Time Played"));
|
|
||||||
|
|
||||||
|
// Hide/rename game in game list
|
||||||
|
QAction* hide_serial = manage_game_menu->addAction(tr("&Hide From Game List"));
|
||||||
|
hide_serial->setCheckable(true);
|
||||||
|
hide_serial->setChecked(m_hidden_list.contains(serial));
|
||||||
|
QAction* rename_title = manage_game_menu->addAction(tr("&Rename In Game List"));
|
||||||
|
|
||||||
|
// Edit tooltip notes/reset time played
|
||||||
|
QAction* edit_notes = manage_game_menu->addAction(tr("&Edit Tooltip Notes"));
|
||||||
|
QAction* reset_time_played = manage_game_menu->addAction(tr("&Reset Time Played"));
|
||||||
|
|
||||||
|
manage_game_menu->addSeparator();
|
||||||
|
|
||||||
|
// Remove game
|
||||||
|
QAction* remove_game = manage_game_menu->addAction(tr("&Remove %1").arg(gameinfo->localized_category));
|
||||||
|
remove_game->setEnabled(!is_current_running_game);
|
||||||
|
|
||||||
|
// Custom Images menu
|
||||||
QMenu* icon_menu = menu.addMenu(tr("&Custom Images"));
|
QMenu* icon_menu = menu.addMenu(tr("&Custom Images"));
|
||||||
const std::array<QAction*, 3> custom_icon_actions =
|
const std::array<QAction*, 3> custom_icon_actions =
|
||||||
{
|
{
|
||||||
|
@ -1427,11 +1510,119 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
|
||||||
icon_menu->setEnabled(false);
|
icon_menu->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
menu.addSeparator();
|
||||||
|
|
||||||
|
// Open Folder menu
|
||||||
|
QMenu* open_folder_menu = menu.addMenu(tr("&Open Folder"));
|
||||||
|
|
||||||
|
const bool is_disc_game = qstr(current_game.category) == cat::cat_disc_game;
|
||||||
|
const std::string captures_dir = fs::get_config_dir() + "/captures/";
|
||||||
|
const std::string recordings_dir = fs::get_config_dir() + "/recordings/" + current_game.serial;
|
||||||
|
const std::string screenshots_dir = fs::get_config_dir() + "/screenshots/" + current_game.serial;
|
||||||
|
std::vector<std::string> data_dir_list;
|
||||||
|
|
||||||
|
if (is_disc_game)
|
||||||
|
{
|
||||||
|
QAction* open_disc_game_folder = open_folder_menu->addAction(tr("&Open Disc Game Folder"));
|
||||||
|
connect(open_disc_game_folder, &QAction::triggered, [current_game]()
|
||||||
|
{
|
||||||
|
gui::utils::open_dir(current_game.path);
|
||||||
|
});
|
||||||
|
|
||||||
|
data_dir_list = GetDirListBySerial(rpcs3::utils::get_hdd0_dir() + "game/", current_game.serial); // It could be absent for a disc game
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data_dir_list.push_back(current_game.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data_dir_list.empty()) // "true" if data path is present (it could be absent for a disc game)
|
||||||
|
{
|
||||||
|
QAction* open_data_folder = open_folder_menu->addAction(tr("&Open %0 Folder").arg(is_disc_game ? tr("Game Data") : gameinfo->localized_category));
|
||||||
|
connect(open_data_folder, &QAction::triggered, [data_dir_list]()
|
||||||
|
{
|
||||||
|
for (const std::string& data_dir : data_dir_list)
|
||||||
|
{
|
||||||
|
gui::utils::open_dir(data_dir);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gameinfo->hasCustomConfig)
|
||||||
|
{
|
||||||
|
QAction* open_config_dir = open_folder_menu->addAction(tr("&Open Custom Config Folder"));
|
||||||
|
connect(open_config_dir, &QAction::triggered, [current_game]()
|
||||||
|
{
|
||||||
|
const std::string config_path = rpcs3::utils::get_custom_config_path(current_game.serial);
|
||||||
|
|
||||||
|
if (fs::is_file(config_path))
|
||||||
|
gui::utils::open_dir(config_path);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a debug feature, let's hide it by reusing debug tab protection
|
||||||
|
if (m_gui_settings->GetValue(gui::m_showDebugTab).toBool() && has_cache_dir)
|
||||||
|
{
|
||||||
|
QAction* open_cache_folder = open_folder_menu->addAction(tr("&Open Cache Folder"));
|
||||||
|
connect(open_cache_folder, &QAction::triggered, [cache_base_dir]()
|
||||||
|
{
|
||||||
|
gui::utils::open_dir(cache_base_dir);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs::is_dir(config_data_base_dir))
|
||||||
|
{
|
||||||
|
QAction* open_config_data_dir = open_folder_menu->addAction(tr("&Open Config Data Folder"));
|
||||||
|
connect(open_config_data_dir, &QAction::triggered, [config_data_base_dir]()
|
||||||
|
{
|
||||||
|
gui::utils::open_dir(config_data_base_dir);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs::is_dir(savestate_dir))
|
||||||
|
{
|
||||||
|
QAction* open_savestate_dir = open_folder_menu->addAction(tr("&Open Savestate Folder"));
|
||||||
|
connect(open_savestate_dir, &QAction::triggered, [savestate_dir]()
|
||||||
|
{
|
||||||
|
gui::utils::open_dir(savestate_dir);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QAction* open_captures_dir = open_folder_menu->addAction(tr("&Open Captures Folder"));
|
||||||
|
connect(open_captures_dir, &QAction::triggered, [captures_dir]()
|
||||||
|
{
|
||||||
|
gui::utils::open_dir(captures_dir);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (fs::is_dir(recordings_dir))
|
||||||
|
{
|
||||||
|
QAction* open_recordings_dir = open_folder_menu->addAction(tr("&Open Recordings Folder"));
|
||||||
|
connect(open_recordings_dir, &QAction::triggered, [recordings_dir]()
|
||||||
|
{
|
||||||
|
gui::utils::open_dir(recordings_dir);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs::is_dir(screenshots_dir))
|
||||||
|
{
|
||||||
|
QAction* open_screenshots_dir = open_folder_menu->addAction(tr("&Open Screenshots Folder"));
|
||||||
|
connect(open_screenshots_dir, &QAction::triggered, [screenshots_dir]()
|
||||||
|
{
|
||||||
|
gui::utils::open_dir(screenshots_dir);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy Info menu
|
||||||
QMenu* info_menu = menu.addMenu(tr("&Copy Info"));
|
QMenu* info_menu = menu.addMenu(tr("&Copy Info"));
|
||||||
QAction* copy_info = info_menu->addAction(tr("&Copy Name + Serial"));
|
QAction* copy_info = info_menu->addAction(tr("&Copy Name + Serial"));
|
||||||
QAction* copy_name = info_menu->addAction(tr("&Copy Name"));
|
QAction* copy_name = info_menu->addAction(tr("&Copy Name"));
|
||||||
QAction* copy_serial = info_menu->addAction(tr("&Copy Serial"));
|
QAction* copy_serial = info_menu->addAction(tr("&Copy Serial"));
|
||||||
|
|
||||||
|
menu.addSeparator();
|
||||||
|
|
||||||
|
QAction* check_compat = menu.addAction(tr("&Check Game Compatibility"));
|
||||||
|
QAction* download_compat = menu.addAction(tr("&Download Compatibility Database"));
|
||||||
|
|
||||||
connect(boot, &QAction::triggered, this, [this, gameinfo]()
|
connect(boot, &QAction::triggered, this, [this, gameinfo]()
|
||||||
{
|
{
|
||||||
sys_log.notice("Booting from gamelist per context menu...");
|
sys_log.notice("Booting from gamelist per context menu...");
|
||||||
|
@ -1492,63 +1683,187 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
|
||||||
CreateCPUCaches(gameinfo);
|
CreateCPUCaches(gameinfo);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connect(remove_game, &QAction::triggered, this, [this, current_game, gameinfo, cache_base_dir, name]
|
connect(remove_game, &QAction::triggered, this, [this, current_game, gameinfo, cache_base_dir, hdd1_cache_base_dir, name]
|
||||||
{
|
{
|
||||||
if (current_game.path.empty())
|
|
||||||
{
|
|
||||||
game_list_log.fatal("Cannot remove game. Path is empty");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_game_running(current_game.serial))
|
if (is_game_running(current_game.serial))
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, tr("Cannot Remove Game"), tr("The PS3 application is still running, it cannot be removed!"));
|
QMessageBox::critical(this, tr("Cannot Remove Game"), tr("The PS3 application is still running, it cannot be removed!"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString size_information;
|
const bool is_disc_game = qstr(current_game.category) == cat::cat_disc_game;
|
||||||
|
const bool is_in_games_dir = is_disc_game && Emu.IsPathInsideDir(current_game.path, rpcs3::utils::get_games_dir());
|
||||||
|
std::vector<std::string> data_dir_list;
|
||||||
|
|
||||||
if (current_game.size_on_disk != umax)
|
if (is_disc_game)
|
||||||
{
|
{
|
||||||
fs::device_stat stat{};
|
data_dir_list = GetDirListBySerial(rpcs3::utils::get_hdd0_dir() + "game/", current_game.serial);
|
||||||
if (fs::statfs(current_game.path, stat))
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
size_information = tr("Game Directory Size: %0\nCurrent Free Disk Space: %1\n\n").arg(gui::utils::format_byte_size(current_game.size_on_disk)).arg(gui::utils::format_byte_size(stat.avail_free));
|
data_dir_list.push_back(current_game.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool has_data_dir = !data_dir_list.empty(); // "true" if data path is present (it could be absent for a disc game)
|
||||||
|
QString text = tr("%0 - %1\n").arg(qstr(current_game.serial)).arg(name);
|
||||||
|
|
||||||
|
if (is_disc_game)
|
||||||
|
{
|
||||||
|
text += tr("\nDisc Game Info:\nPath: %0\n").arg(qstr(current_game.path));
|
||||||
|
|
||||||
|
if (current_game.size_on_disk != umax) // If size was properly detected
|
||||||
|
{
|
||||||
|
text += tr("Size: %0\n").arg(gui::utils::format_byte_size(current_game.size_on_disk));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QMessageBox mb(QMessageBox::Question, tr("Confirm %1 Removal").arg(gameinfo->localized_category), tr("Permanently remove %0 from drive?\n%1Path: %2").arg(name).arg(size_information).arg(qstr(current_game.path)), QMessageBox::Yes | QMessageBox::No, this);
|
if (has_data_dir)
|
||||||
mb.setCheckBox(new QCheckBox(tr("Remove caches and custom configs")));
|
{
|
||||||
|
u64 total_data_size = 0;
|
||||||
|
|
||||||
|
text += tr("\nData Info:\n");
|
||||||
|
|
||||||
|
for (const std::string& data_dir : data_dir_list)
|
||||||
|
{
|
||||||
|
text += tr("Path: %0\n").arg(qstr(data_dir));
|
||||||
|
|
||||||
|
if (const u64 data_size = fs::get_dir_size(data_dir, 1); data_size != umax) // If size was properly detected
|
||||||
|
{
|
||||||
|
total_data_size += data_size;
|
||||||
|
text += tr("Size: %0\n").arg(gui::utils::format_byte_size(data_size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_dir_list.size() > 1)
|
||||||
|
{
|
||||||
|
text += tr("Total size: %0\n").arg(gui::utils::format_byte_size(total_data_size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs::device_stat stat{}; fs::statfs(rpcs3::utils::get_hdd0_dir(), stat)) // retrieve disk space info on data path's drive
|
||||||
|
{
|
||||||
|
text += tr("\nCurrent free disk space: %0\n").arg(gui::utils::format_byte_size(stat.avail_free));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_data_dir)
|
||||||
|
{
|
||||||
|
text += tr("\nPermanently remove %0 and selected (optional) contents from drive?\n").arg(is_disc_game ? tr("Game Data") : gameinfo->localized_category);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
text += tr("\nPermanently remove selected (optional) contents from drive?\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
QMessageBox mb(QMessageBox::Question, tr("Confirm %0 Removal").arg(gameinfo->localized_category), text, QMessageBox::Yes | QMessageBox::No, this);
|
||||||
|
QCheckBox* disc = new QCheckBox(tr("Remove title from game list (Disc Game path is not removed!)"));
|
||||||
|
QCheckBox* caches = new QCheckBox(tr("Remove caches and custom configs"));
|
||||||
|
QCheckBox* icons = new QCheckBox(tr("Remove icons and shortcuts"));
|
||||||
|
QCheckBox* savestate = new QCheckBox(tr("Remove savestate"));
|
||||||
|
QCheckBox* captures = new QCheckBox(tr("Remove captures"));
|
||||||
|
QCheckBox* recordings = new QCheckBox(tr("Remove recordings"));
|
||||||
|
QCheckBox* screenshots = new QCheckBox(tr("Remove screenshots"));
|
||||||
|
|
||||||
|
if (is_disc_game)
|
||||||
|
{
|
||||||
|
if (is_in_games_dir)
|
||||||
|
{
|
||||||
|
disc->setToolTip(tr("Title located under auto-detection \"games\" folder cannot be removed"));
|
||||||
|
disc->setDisabled(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
disc->setChecked(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
disc->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
caches->setChecked(true);
|
||||||
|
icons->setChecked(true);
|
||||||
|
mb.setCheckBox(disc);
|
||||||
|
|
||||||
|
QGridLayout* grid = qobject_cast<QGridLayout*>(mb.layout());
|
||||||
|
int row, column, rowSpan, columnSpan;
|
||||||
|
|
||||||
|
grid->getItemPosition(grid->indexOf(disc), &row, &column, &rowSpan, &columnSpan);
|
||||||
|
grid->addWidget(caches, row + 3, column, rowSpan, columnSpan);
|
||||||
|
grid->addWidget(icons, row + 4, column, rowSpan, columnSpan);
|
||||||
|
grid->addWidget(savestate, row + 5, column, rowSpan, columnSpan);
|
||||||
|
grid->addWidget(captures, row + 6, column, rowSpan, columnSpan);
|
||||||
|
grid->addWidget(recordings, row + 7, column, rowSpan, columnSpan);
|
||||||
|
grid->addWidget(screenshots, row + 8, column, rowSpan, columnSpan);
|
||||||
|
|
||||||
if (mb.exec() == QMessageBox::Yes)
|
if (mb.exec() == QMessageBox::Yes)
|
||||||
{
|
{
|
||||||
const bool remove_caches = mb.checkBox()->isChecked();
|
const bool remove_caches = caches->isChecked();
|
||||||
if (fs::remove_all(current_game.path))
|
|
||||||
|
// Remove data path in "dev_hdd0/game" folder (if any)
|
||||||
|
if (has_data_dir && RemoveContentPathList(data_dir_list, gameinfo->localized_category.toStdString()) != data_dir_list.size())
|
||||||
{
|
{
|
||||||
|
QMessageBox::critical(this, tr("Failure!"), remove_caches
|
||||||
|
? tr("Failed to remove %0 from drive!\nPath: %1\nCaches and custom configs have been left intact.").arg(name).arg(qstr(data_dir_list[0]))
|
||||||
|
: tr("Failed to remove %0 from drive!\nPath: %1").arg(name).arg(qstr(data_dir_list[0])));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove lock file in "dev_hdd0/game/$locks" folder (if any)
|
||||||
|
RemoveContentBySerial(rpcs3::utils::get_hdd0_dir() + "game/$locks/", current_game.serial, "lock");
|
||||||
|
|
||||||
|
// Remove caches in "cache" and "dev_hdd1/caches" folders (if any) and custom configs in "config/custom_config" folder (if any)
|
||||||
if (remove_caches)
|
if (remove_caches)
|
||||||
{
|
{
|
||||||
if (fs::is_dir(cache_base_dir))
|
RemoveContentPath(cache_base_dir, "cache");
|
||||||
{
|
RemoveHDD1Cache(hdd1_cache_base_dir, current_game.serial);
|
||||||
if (fs::remove_all(cache_base_dir))
|
|
||||||
game_list_log.notice("Removed cache directory: '%s'", cache_base_dir);
|
|
||||||
else
|
|
||||||
game_list_log.error("Could not remove cache directory: '%s' (%s)", cache_base_dir, fs::g_tls_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
RemoveCustomConfiguration(current_game.serial);
|
RemoveCustomConfiguration(current_game.serial);
|
||||||
RemoveCustomPadConfiguration(current_game.serial);
|
RemoveCustomPadConfiguration(current_game.serial);
|
||||||
}
|
}
|
||||||
m_game_data.erase(std::remove(m_game_data.begin(), m_game_data.end(), gameinfo), m_game_data.end());
|
|
||||||
game_list_log.success("Removed %s %s in %s", gameinfo->localized_category, current_game.name, current_game.path);
|
// Remove icons in "Icons/game_icons" folder, shortcuts in "games/shortcuts" folder and from desktop/start menu
|
||||||
Refresh(true);
|
if (icons->isChecked())
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
game_list_log.error("Failed to remove %s %s in %s (%s)", gameinfo->localized_category, current_game.name, current_game.path, fs::g_tls_error);
|
RemoveContentBySerial(fs::get_config_dir() + "Icons/game_icons/", current_game.serial, "icons");
|
||||||
QMessageBox::critical(this, tr("Failure!"), remove_caches
|
RemoveContentBySerial(fs::get_config_dir() + "games/shortcuts/", name.toStdString() + ".lnk", "link");
|
||||||
? tr("Failed to remove %0 from drive!\nPath: %1\nCaches and custom configs have been left intact.").arg(name).arg(qstr(current_game.path))
|
// TODO: Remove shortcuts from desktop/start menu
|
||||||
: tr("Failed to remove %0 from drive!\nPath: %1").arg(name).arg(qstr(current_game.path)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (savestate->isChecked())
|
||||||
|
{
|
||||||
|
RemoveContentBySerial(fs::get_config_dir() + "savestates/", current_game.serial, "savestate");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (captures->isChecked())
|
||||||
|
{
|
||||||
|
RemoveContentBySerial(fs::get_config_dir() + "captures/", current_game.serial, "captures");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recordings->isChecked())
|
||||||
|
{
|
||||||
|
RemoveContentBySerial(fs::get_config_dir() + "recordings/", current_game.serial, "recordings");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (screenshots->isChecked())
|
||||||
|
{
|
||||||
|
RemoveContentBySerial(fs::get_config_dir() + "screenshots/", current_game.serial, "screenshots");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_game_data.erase(std::remove(m_game_data.begin(), m_game_data.end(), gameinfo), m_game_data.end());
|
||||||
|
game_list_log.success("Removed %s - %s", gameinfo->localized_category, current_game.name);
|
||||||
|
|
||||||
|
std::vector<std::string> serials_to_remove_from_yml{};
|
||||||
|
|
||||||
|
// Prepare list of serials (title id) to remove in "games.yml" file (if any)
|
||||||
|
if (is_disc_game && disc->isChecked())
|
||||||
|
{
|
||||||
|
serials_to_remove_from_yml.push_back(current_game.serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, refresh the game list.
|
||||||
|
// Hidden list in "GuiConfigs/CurrentSettings.ini" file is also properly updated (title removed) if needed
|
||||||
|
Refresh(true, serials_to_remove_from_yml);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connect(configure_patches, &QAction::triggered, this, [this, gameinfo]()
|
connect(configure_patches, &QAction::triggered, this, [this, gameinfo]()
|
||||||
|
@ -1564,10 +1879,6 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
|
||||||
patch_manager_dialog patch_manager(m_gui_settings, games, gameinfo->info.serial, game_list::GetGameVersion(gameinfo), this);
|
patch_manager_dialog patch_manager(m_gui_settings, games, gameinfo->info.serial, game_list::GetGameVersion(gameinfo), this);
|
||||||
patch_manager.exec();
|
patch_manager.exec();
|
||||||
});
|
});
|
||||||
connect(open_game_folder, &QAction::triggered, this, [current_game]()
|
|
||||||
{
|
|
||||||
gui::utils::open_dir(current_game.path);
|
|
||||||
});
|
|
||||||
connect(check_compat, &QAction::triggered, this, [serial]
|
connect(check_compat, &QAction::triggered, this, [serial]
|
||||||
{
|
{
|
||||||
const QString link = "https://rpcs3.net/compatibility?g=" + serial;
|
const QString link = "https://rpcs3.net/compatibility?g=" + serial;
|
||||||
|
@ -1648,11 +1959,11 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
|
||||||
// Disable options depending on software category
|
// Disable options depending on software category
|
||||||
const QString category = qstr(current_game.category);
|
const QString category = qstr(current_game.category);
|
||||||
|
|
||||||
if (category == cat::cat_disc_game || category == cat::cat_ps3_os)
|
if (category == cat::cat_ps3_os)
|
||||||
{
|
{
|
||||||
remove_game->setEnabled(false);
|
remove_game->setEnabled(false);
|
||||||
}
|
}
|
||||||
else if (category != cat::cat_hdd_game)
|
else if (category != cat::cat_disc_game && category != cat::cat_hdd_game)
|
||||||
{
|
{
|
||||||
check_compat->setEnabled(false);
|
check_compat->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ public:
|
||||||
~game_list_frame();
|
~game_list_frame();
|
||||||
|
|
||||||
/** Refresh the gamelist with/without loading game data from files. Public so that main frame can refresh after vfs or install */
|
/** Refresh the gamelist with/without loading game data from files. Public so that main frame can refresh after vfs or install */
|
||||||
void Refresh(const bool from_drive = false, const bool scroll_after = true);
|
void Refresh(const bool from_drive = false, const std::vector<std::string>& serials_to_remove_from_yml = {}, const bool scroll_after = true);
|
||||||
|
|
||||||
/** Adds/removes categories that should be shown on gamelist. Public so that main frame menu actions can apply them */
|
/** Adds/removes categories that should be shown on gamelist. Public so that main frame menu actions can apply them */
|
||||||
void ToggleCategoryFilter(const QStringList& categories, bool show);
|
void ToggleCategoryFilter(const QStringList& categories, bool show);
|
||||||
|
@ -137,6 +137,10 @@ private:
|
||||||
static bool CreateCPUCaches(const std::string& path, const std::string& serial = {});
|
static bool CreateCPUCaches(const std::string& path, const std::string& serial = {});
|
||||||
static bool CreateCPUCaches(const game_info& game);
|
static bool CreateCPUCaches(const game_info& game);
|
||||||
|
|
||||||
|
static bool RemoveContentPath(const std::string& path, const std::string& desc);
|
||||||
|
static u32 RemoveContentPathList(const std::vector<std::string>& path_list, const std::string& desc);
|
||||||
|
static bool RemoveContentBySerial(const std::string& base_dir, const std::string& serial, const std::string& desc);
|
||||||
|
static std::vector<std::string> GetDirListBySerial(const std::string& base_dir, const std::string& serial);
|
||||||
static std::string GetCacheDirBySerial(const std::string& serial);
|
static std::string GetCacheDirBySerial(const std::string& serial);
|
||||||
static std::string GetDataDirBySerial(const std::string& serial);
|
static std::string GetDataDirBySerial(const std::string& serial);
|
||||||
std::string CurrentSelectionPath();
|
std::string CurrentSelectionPath();
|
||||||
|
|
|
@ -99,7 +99,7 @@ namespace gui::utils
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
else if (location == shortcut_location::rpcs3_shortcuts)
|
else if (location == shortcut_location::rpcs3_shortcuts)
|
||||||
{
|
{
|
||||||
link_path = g_cfg_vfs.get(g_cfg_vfs.games_dir, rpcs3::utils::get_emu_dir()) + "/shortcuts/";
|
link_path = rpcs3::utils::get_games_dir() + "/shortcuts/";
|
||||||
fs::create_dir(link_path);
|
fs::create_dir(link_path);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue