diff --git a/rpcs3/Emu/GameInfo.h b/rpcs3/Emu/GameInfo.h index da8b2638ba..a99e708521 100644 --- a/rpcs3/Emu/GameInfo.h +++ b/rpcs3/Emu/GameInfo.h @@ -9,6 +9,7 @@ struct GameInfo std::string icon_path; std::string movie_path; std::string audio_path; + std::string game_dir; std::string name; std::string serial; diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 492aed799a..d0b0d0d959 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -1492,9 +1492,9 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, // ISOs that are install discs will error if set to EBOOT.BIN // so this should cover both of them - if (fs::exists(path + "PS3_GAME/USRDIR/EBOOT.BIN")) + if (fs::exists(path + m_game_dir + "/USRDIR/EBOOT.BIN")) { - path = path + "PS3_GAME/USRDIR/EBOOT.BIN"; + path = path + m_game_dir + "/USRDIR/EBOOT.BIN"; } m_path_real = m_path; diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 8bee2dc7a5..99c570b809 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -215,6 +215,8 @@ public: m_cb = std::move(cb); } + void SetGameDir(const std::string& game_dir) { m_game_dir = game_dir; } + const auto& GetCallbacks() const { return m_cb; diff --git a/rpcs3/Loader/ISO.h b/rpcs3/Loader/ISO.h index 8a88e876bf..c67239a3ed 100644 --- a/rpcs3/Loader/ISO.h +++ b/rpcs3/Loader/ISO.h @@ -142,6 +142,7 @@ public: iso_archive(const std::string& path); const std::string& path() const { return m_path; } + const iso_fs_node& root() const { return m_root; } const std::shared_ptr get_dec() { return m_dec; } iso_fs_node* retrieve(const std::string& path); diff --git a/rpcs3/Loader/iso_cache.cpp b/rpcs3/Loader/iso_cache.cpp index 17453489e8..768948e421 100644 --- a/rpcs3/Loader/iso_cache.cpp +++ b/rpcs3/Loader/iso_cache.cpp @@ -34,7 +34,7 @@ namespace namespace iso_cache { - bool load(const std::string& iso_path, iso_metadata_cache_entry& out_entry) + bool load(const std::string& iso_path, const std::string& cache_key, iso_metadata_cache_entry& out_entry) { fs::stat_t iso_stat{}; if (!fs::get_stat(iso_path, iso_stat) || iso_stat.is_directory) @@ -42,7 +42,7 @@ namespace iso_cache return false; } - const std::string stem = get_cache_stem(iso_path); + const std::string stem = get_cache_stem(cache_key); const std::string dir = get_cache_dir(); const std::string yml_path = dir + stem + ".yml"; const std::string sfo_path = dir + stem + ".sfo"; @@ -89,11 +89,9 @@ namespace iso_cache return true; } - void save(const std::string& iso_path, const iso_metadata_cache_entry& entry) + void save(const std::string& iso_path, const std::string& cache_key, const iso_metadata_cache_entry& entry) { - iso_cache_log.notice("Saving cache for '%s'", iso_path); - - const std::string stem = get_cache_stem(iso_path); + const std::string stem = get_cache_stem(cache_key); const std::string dir = get_cache_dir(); const std::string yml_path = dir + stem + ".yml"; const std::string sfo_path = dir + stem + ".sfo"; diff --git a/rpcs3/Loader/iso_cache.h b/rpcs3/Loader/iso_cache.h index 8f6594b76c..a6c66797e6 100644 --- a/rpcs3/Loader/iso_cache.h +++ b/rpcs3/Loader/iso_cache.h @@ -22,10 +22,10 @@ struct iso_metadata_cache_entry namespace iso_cache { // Returns false if no valid cache entry exists or mtime has changed. - bool load(const std::string& iso_path, iso_metadata_cache_entry& out_entry); + bool load(const std::string& iso_path, const std::string& cache_key, iso_metadata_cache_entry& out_entry); // Persists a populated cache entry to disk. - void save(const std::string& iso_path, const iso_metadata_cache_entry& entry); + void save(const std::string& iso_path, const std::string& cache_key, const iso_metadata_cache_entry& entry); // Remove cache entries for ISOs that are no longer in the scanned set. void cleanup(const std::unordered_set& valid_iso_paths); diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 050589240d..e3d2ece920 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -546,17 +546,19 @@ void game_list_frame::OnParsingFinished() const auto add_game = [this, localized_title, localized_icon, localized_movie, dev_flash, game_icon_path, _hdd, cat_unknown_localized = localized.category.unknown.toStdString(), cat_unknown = cat::cat_unknown.toStdString(), play_hover_movies = m_play_hover_movies, play_hover_music = m_play_hover_music, show_custom_icons = m_show_custom_icons] - (const std::string& dir_or_elf) + (const std::string& dir_or_elf, const std::string& game_dir = "PS3_GAME") { std::unique_ptr archive; iso_metadata_cache_entry cache_entry{}; const bool is_iso = is_file_iso(dir_or_elf); + std::string iso_cache_key; if (is_iso) { + const std::string iso_cache_key = (game_dir == "PS3_GAME") ? dir_or_elf : dir_or_elf + "|" + game_dir; // Only construct iso_archive (which walks the full directory tree) // when no valid cache entry exists for this ISO path + mtime. - if (!iso_cache::load(dir_or_elf, cache_entry)) + if (!iso_cache::load(dir_or_elf, iso_cache_key, cache_entry)) { archive = std::make_unique(dir_or_elf); } @@ -576,10 +578,11 @@ void game_list_frame::OnParsingFinished() gui_game_info game{}; game.info.path = dir_or_elf; + game.info.game_dir = (game_dir == "PS3_GAME") ? "" : game_dir; const Localized thread_localized; - const std::string sfo_dir = (archive || !cache_entry.psf_data.empty()) ? "PS3_GAME" : rpcs3::utils::get_sfo_dir_from_game_path(dir_or_elf); + const std::string sfo_dir = (archive || !cache_entry.psf_data.empty()) ? game_dir : rpcs3::utils::get_sfo_dir_from_game_path(dir_or_elf); const std::string sfo_path = sfo_dir + "/PARAM.SFO"; // Load PSF: from archive on cache miss, rehydrate from cached SFO bytes on hit. @@ -762,7 +765,7 @@ void game_list_frame::OnParsingFinished() } } - iso_cache::save(dir_or_elf, cache_entry); + iso_cache::save(dir_or_elf, (game_dir == "PS3_GAME") ? dir_or_elf : dir_or_elf + "|" + game_dir, cache_entry); } } @@ -888,7 +891,38 @@ void game_list_frame::OnParsingFinished() } else if (is_file_iso(entry.path)) { - push_path(entry.path, legit_paths); + iso_archive archive(entry.path); + const iso_fs_node& root = archive.root(); + const std::regex ps3_gm_regex("^PS3_GM[[:digit:]]{2}$"); + bool found = false; + + for (const auto& child : root.children) + { + if (m_refresh_watcher.isCanceled()) + { + break; + } + + if (!child->metadata.is_directory) + { + continue; + } + + const std::string& name = child->metadata.name; + + if (name == "PS3_GAME" || std::regex_match(name, ps3_gm_regex)) + { + add_game(entry.path, name); + found = true; + } + } + + if (!found) + { + add_game(entry.path); + } + + return; } else { diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 9dd437b535..2c86daaefa 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -3685,6 +3685,10 @@ void main_window::CreateDockWindows() connect(m_game_list_frame, &game_list_frame::RequestBoot, this, [this](const game_info& game, cfg_mode config_mode, const std::string& config_path, const std::string& savestate) { + if (!game->info.game_dir.empty()) + { + Emu.SetGameDir(game->info.game_dir); + } Boot(savestate.empty() ? game->info.path : savestate, game->info.serial, false, false, config_mode, config_path); }); diff --git a/rpcs3/rpcs3qt/qt_utils.cpp b/rpcs3/rpcs3qt/qt_utils.cpp index fe7bb6f8d6..b81fb52944 100644 --- a/rpcs3/rpcs3qt/qt_utils.cpp +++ b/rpcs3/rpcs3qt/qt_utils.cpp @@ -712,7 +712,7 @@ namespace gui // Check cache first — avoids constructing a full iso_archive just for the icon. iso_metadata_cache_entry cache_entry{}; - if (iso_cache::load(archive_path, cache_entry) && !cache_entry.icon_data.empty()) + if (iso_cache::load(archive_path, archive_path, cache_entry) && !cache_entry.icon_data.empty()) { const QByteArray data(reinterpret_cast(cache_entry.icon_data.data()), static_cast(cache_entry.icon_data.size()));