diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index e95c39acf9..f0a4a0f0a2 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -855,12 +855,14 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool bool resolve_path_as_vfs_path = false; + const bool from_dev_flash = IsPathInsideDir(m_path, g_cfg_vfs.get_dev_flash()); + if (m_ar) { struct file_header { ENABLE_BITWISE_SERIALIZATION; - + nse_t magic; bool LE_format; bool state_inspection_support; @@ -874,14 +876,14 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool { return game_boot_result::savestate_corrupted; } - + if (header.LE_format != (std::endian::native == std::endian::little) || header.offset >= m_ar->data.size()) { return game_boot_result::savestate_corrupted; } g_cfg.savestate.state_inspection_mode.set(header.state_inspection_support); - + // Emulate seek operation (please avoid using in other places) m_ar->pos = header.offset; @@ -894,7 +896,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool argv.clear(); klic.clear(); - + std::string disc_info; (*m_ar)(argv.emplace_back(), disc_info, klic.emplace_back(), m_game_dir, hdd1); @@ -907,7 +909,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool { // Restore disc path for disc games (must exist in games.yml i.e. your game library) m_title_id = disc_info; - + // Load /dev_bdvd/ from game list if available if (auto node = games[m_title_id]) { @@ -1189,39 +1191,32 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool } else if (m_config_mode == cfg_mode::custom) { - const std::string config_path = rpcs3::utils::get_custom_config_path(m_title_id); - - // Load custom config-1 - if (fs::file cfg_file{ config_path }) + // Load custom configs + for (std::string config_path : { - sys_log.notice("Applying custom config: %s", config_path); - - if (g_cfg.from_string(cfg_file.to_string())) + m_path + ".yml", + rpcs3::utils::get_custom_config_path(from_dev_flash ? m_path.substr(m_path.find_last_of(fs::delim) + 1) : m_title_id), + }) + { + if (config_path.empty()) { - g_cfg.name = config_path; - m_config_path = config_path; + continue; } - else + + if (fs::file cfg_file{config_path}) { + sys_log.notice("Applying custom config: %s", config_path); + + if (g_cfg.from_string(cfg_file.to_string())) + { + g_cfg.name = config_path; + m_config_path = config_path; + break; + } + sys_log.fatal("Failed to apply custom config: %s", config_path); } } - - // Load custom config-2 - if (fs::file cfg_file{ m_path + ".yml" }) - { - sys_log.notice("Applying custom config: %s.yml", m_path); - - if (g_cfg.from_string(cfg_file.to_string())) - { - g_cfg.name = m_path + ".yml"; - m_config_path = g_cfg.name; - } - else - { - sys_log.fatal("Failed to apply custom config: %s.yml", m_path); - } - } } // Disable incompatible settings @@ -1441,7 +1436,6 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool // Detect boot location const std::string hdd0_game = vfs::get("/dev_hdd0/game/"); const bool from_hdd0_game = IsPathInsideDir(m_path, hdd0_game); - const bool from_dev_flash = IsPathInsideDir(m_path, g_cfg_vfs.get_dev_flash()); #ifdef _WIN32 // m_path might be passed from command line with differences in uppercase/lowercase on windows. diff --git a/rpcs3/Emu/system_utils.cpp b/rpcs3/Emu/system_utils.cpp index a702d8464e..1cd7d62aac 100644 --- a/rpcs3/Emu/system_utils.cpp +++ b/rpcs3/Emu/system_utils.cpp @@ -352,9 +352,14 @@ namespace rpcs3::utils #endif } - std::string get_custom_config_path(const std::string& title_id) + std::string get_custom_config_path(const std::string& identifier) { - return get_custom_config_dir() + "config_" + title_id + ".yml"; + if (identifier.empty()) + { + return {}; + } + + return get_custom_config_dir() + "config_" + identifier + ".yml"; } std::string get_input_config_root() diff --git a/rpcs3/Emu/system_utils.hpp b/rpcs3/Emu/system_utils.hpp index e403d17b88..59510cb994 100644 --- a/rpcs3/Emu/system_utils.hpp +++ b/rpcs3/Emu/system_utils.hpp @@ -31,7 +31,7 @@ namespace rpcs3::utils std::string get_sfo_dir_from_game_path(const std::string& game_path, const std::string& title_id = ""); std::string get_custom_config_dir(); - std::string get_custom_config_path(const std::string& title_id); + std::string get_custom_config_path(const std::string& identifier); std::string get_input_config_root(); std::string get_input_config_dir(const std::string& title_id = ""); diff --git a/rpcs3/rpcs3qt/category.h b/rpcs3/rpcs3qt/category.h index ad4c82d55b..641af991b8 100644 --- a/rpcs3/rpcs3qt/category.h +++ b/rpcs3/rpcs3qt/category.h @@ -42,11 +42,13 @@ namespace cat const QString cat_ps3_save = "SD"; const QString cat_psp_save = "MS"; + const QString cat_ps3_os = "/OS"; + const QString cat_unknown = "Unknown"; const QStringList ps2_games = { cat_ps2_game, cat_ps2_inst }; const QStringList psp_games = { cat_psp_game, cat_psp_mini, cat_psp_rema }; const QStringList media = { cat_app_photo, cat_app_video, cat_bc_video, cat_app_music, cat_app_store, cat_app_tv, cat_web_tv }; const QStringList data = { cat_ps3_data, cat_ps2_data, cat_ps3_save, cat_psp_save }; - const QStringList others = { cat_network, cat_store_fe }; + const QStringList others = { cat_network, cat_store_fe, cat_ps3_os }; } diff --git a/rpcs3/rpcs3qt/emu_settings.cpp b/rpcs3/rpcs3qt/emu_settings.cpp index 6d50f19db8..471344f0fd 100644 --- a/rpcs3/rpcs3qt/emu_settings.cpp +++ b/rpcs3/rpcs3qt/emu_settings.cpp @@ -8,6 +8,7 @@ #include "Emu/System.h" #include "Emu/system_config.h" +#include "Emu/vfs_config.h" #include "Emu/system_utils.hpp" #include "Emu/Cell/Modules/cellSysutil.h" #include "Emu/Io/Keyboard.h" @@ -136,10 +137,9 @@ void emu_settings::LoadSettings(const std::string& title_id) // Otherwise we'll always trigger the "obsolete settings dialog" when editing custom configs. ValidateSettings(true); - const std::string config_path = rpcs3::utils::get_custom_config_path(m_title_id); std::string custom_config_path; - if (fs::is_file(config_path)) + if (std::string config_path = rpcs3::utils::get_custom_config_path(m_title_id); fs::is_file(config_path)) { custom_config_path = config_path; } diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 87849bc83f..88d95b4a13 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -23,6 +23,7 @@ #include "Utilities/File.h" #include "Utilities/mutex.h" #include "util/yaml.hpp" +#include "util/sysinfo.hpp" #include "Input/pad_thread.h" #include @@ -580,48 +581,84 @@ void game_list_frame::Refresh(const bool from_drive, const bool scroll_after) } } + const auto dev_flash = g_cfg_vfs.get_dev_flash(); + + m_path_list.emplace_back(dev_flash + "vsh/module/vsh.self"); + // Remove duplicates sort(m_path_list.begin(), m_path_list.end()); m_path_list.erase(unique(m_path_list.begin(), m_path_list.end()), m_path_list.end()); const std::string game_icon_path = m_play_hover_movies ? fs::get_config_dir() + "/Icons/game_icons/" : ""; - m_refresh_watcher.setFuture(QtConcurrent::map(m_path_list, [this, cat_unknown_localized = sstr(localized.category.unknown), cat_unknown = sstr(cat::cat_unknown), game_icon_path](const std::string& dir) + m_refresh_watcher.setFuture(QtConcurrent::map(m_path_list, [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); + 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()) { - // Do not care about invalid entries - return; - } + if (!fs::is_file(dir_or_elf)) + { + // Do not care about invalid entries + return; + } - GameInfo game{}; - game.path = dir; - 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.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 (usz pos = path_vfs.find_first_not_of(fs::delim); pos != umax && pos != 0) + { + path_vfs = path_vfs.substr(pos); + } + + if (Localized().title.titles.contains(path_vfs)) + { + game.name = Localized().title.titles.at(path_vfs).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 (m_show_custom_icons) { - game.icon_path = fs::get_config_dir() + "/Icons/game_icons/" + game.serial + "/ICON0.PNG"; - } - - if (!m_show_custom_icons || !fs::is_file(game.icon_path)) - { - game.icon_path = sfo_dir + "/ICON0.PNG"; + 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); + } } m_mutex_cat.lock(); @@ -916,6 +953,8 @@ void game_list_frame::CreateShortcuts(const game_info& gameinfo, const std::set< std::string gameid_token_value; + const std::string dev_flash = g_cfg_vfs.get_dev_flash(); + if (gameinfo->info.category == "DG" && !fs::is_file(rpcs3::utils::get_hdd0_dir() + "/game/" + gameinfo->info.serial + "/USRDIR/EBOOT.BIN")) { const usz ps3_game_dir_pos = fs::get_parent_dir(gameinfo->info.path).size(); @@ -948,9 +987,11 @@ void game_list_frame::CreateShortcuts(const game_info& gameinfo, const std::set< } #ifdef __linux__ - const std::string target_cli_args = fmt::format("--no-gui \"%%%%RPCS3_GAMEID%%%%:%s\"", gameid_token_value); + const std::string target_cli_args = gameinfo->info.path.starts_with(dev_flash) ? fmt::format("--no-gui \"%%%%RPCS3_VFS%%%%:dev_flash/%s\"", gameinfo->info.path.substr(dev_flash.size())) + : fmt::format("--no-gui \"%%%%RPCS3_GAMEID%%%%:%s\"", gameid_token_value); #else - const std::string target_cli_args = fmt::format("--no-gui \"%%RPCS3_GAMEID%%:%s\"", gameid_token_value); + const std::string target_cli_args = gameinfo->info.path.starts_with(dev_flash) ? fmt::format("--no-gui \"%%RPCS3_VFS%%:dev_flash/%s\"", gameinfo->info.path.substr(dev_flash.size())) + : fmt::format("--no-gui \"%%RPCS3_GAMEID%%:%s\"", gameid_token_value); #endif const std::string target_icon_dir = fmt::format("%sIcons/game_icons/%s/", fs::get_config_dir(), gameinfo->info.serial); @@ -1566,7 +1607,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) // Disable options depending on software category const QString category = qstr(current_game.category); - if (category == cat::cat_disc_game) + if (category == cat::cat_disc_game || category == cat::cat_ps3_os) { remove_game->setEnabled(false); } diff --git a/rpcs3/rpcs3qt/localized.cpp b/rpcs3/rpcs3qt/localized.cpp index 37822ec851..f816c4b746 100644 --- a/rpcs3/rpcs3qt/localized.cpp +++ b/rpcs3/rpcs3qt/localized.cpp @@ -69,3 +69,10 @@ Localized::sound::sound() }) { } + +Localized::title_t::title_t() + : titles({ + { "vsh/module/vsh.self", tr("XMB (VSH)") }, + }) +{ +} diff --git a/rpcs3/rpcs3qt/localized.h b/rpcs3/rpcs3qt/localized.h index ce4a9b193f..6245d586cd 100644 --- a/rpcs3/rpcs3qt/localized.h +++ b/rpcs3/rpcs3qt/localized.h @@ -33,6 +33,7 @@ public: const QString network = tr("Network"); const QString store_fe = tr("Store"); const QString web_tv = tr("Web TV"); + const QString os_app = tr("Operating System"); // PS2 bootable const QString ps2_game = tr("PS2 Classics"); @@ -79,6 +80,7 @@ public: { cat::cat_psp_game , psp_game }, // psp_games { cat::cat_psp_mini , psp_mini }, // psp_games { cat::cat_psp_rema , psp_rema }, // psp_games + { cat::cat_ps3_os , os_app }, // other }; const localized_cat cat_data = @@ -122,4 +124,10 @@ public: sound(); const std::map format; } sound; + + const struct title_t + { + title_t(); + const std::map titles; + } title; };