diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 5674623bcc..c005e67371 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -82,7 +82,7 @@ extern std::pair, CellError> ppu_load_overlay(const extern bool ppu_load_rel_exec(const ppu_rel_object&); extern bool is_savestate_version_compatible(const std::vector>& data, bool is_boot_check); extern std::vector> read_used_savestate_versions(); -std::string get_savestate_path(std::string_view title_id, std::string_view boot_path); +std::string get_savestate_file(std::string_view title_id, std::string_view boot_path, s64 abs_id, s64 rel_id); extern void send_close_home_menu_cmds(); @@ -492,7 +492,7 @@ void Emulator::Init() make_path_verbose(fs::get_cache_dir() + "shaderlog/", false); make_path_verbose(fs::get_cache_dir() + "spu_progs/", false); - make_path_verbose(fs::get_cache_dir() + "/savestates/", false); + make_path_verbose(fs::get_parent_dir(get_savestate_file("NO_ID", "/NO_FILE", -1, -1)), false); make_path_verbose(fs::get_config_dir() + "captures/", false); make_path_verbose(fs::get_config_dir() + "sounds/", false); make_path_verbose(patch_engine::get_patches_path(), false); @@ -2180,7 +2180,7 @@ void Emulator::FixGuestTime() // Mark a known savestate location and the one we try to boot (in case we boot a moved/copied savestate) if (g_cfg.savestate.suspend_emu) { - for (std::string old_path : std::initializer_list{m_ar ? m_path_old : "", m_title_id.empty() ? "" : get_savestate_path(m_title_id, m_path_old)}) + for (std::string old_path : std::initializer_list{m_ar ? m_path_old : "", m_title_id.empty() ? "" : get_savestate_file(m_title_id, m_path_old, 0, 0)}) { if (old_path.empty()) { @@ -2841,7 +2841,12 @@ void Emulator::Kill(bool allow_autoexit, bool savestate) if (savestate) { - const std::string path = get_savestate_path(m_title_id, m_path); + const std::string path = get_savestate_file(m_title_id, m_path, 0, 0); + + if (!fs::create_path(fs::get_parent_dir(path))) + { + sys_log.error("Failed to create savestate directory! (path='%s', %s)", fs::get_parent_dir(path), fs::g_tls_error); + } fs::pending_file file(path); diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index 370fa57bf9..e76bff203f 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -146,9 +146,25 @@ bool is_savestate_version_compatible(const std::vector>& dat return ok; } -std::string get_savestate_path(std::string_view title_id, std::string_view boot_path) +std::string get_savestate_file(std::string_view title_id, std::string_view boot_path, s64 abs_id, s64 rel_id) { - return fs::get_cache_dir() + "/savestates/" + std::string{title_id.empty() ? boot_path.substr(boot_path.find_last_of(fs::delim) + 1) : title_id} + ".SAVESTAT"; + const std::string title = std::string{title_id.empty() ? boot_path.substr(boot_path.find_last_of(fs::delim) + 1) : title_id}; + + if (abs_id == -1 && rel_id == -1) + { + // Return directory + return fs::get_cache_dir() + "/savestates/" + title + "/"; + } + + ensure(rel_id < 0 || abs_id >= 0, "Unimplemented!"); + + const std::string save_id = fmt::format("%d", abs_id); + + // Make sure that savestate file with higher IDs are placed at the bottom of "by name" file ordering in directory view by adding a single character prefix which tells the ID length + // While not needing to keep a 59 chars long suffix at all times for this purpose + const char prefix = ::at32("0123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"sv, save_id.size()); + + return fs::get_cache_dir() + "/savestates/" + title + "/" + title + '_' + prefix + '_' + save_id + ".SAVESTAT"; } bool is_savestate_compatible(const fs::file& file) diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index b6e7fd2ffe..4acf9649da 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -50,6 +50,8 @@ LOG_CHANNEL(sys_log, "SYS"); extern atomic_t g_system_progress_canceled; +std::string get_savestate_file(std::string_view title_id, std::string_view boot_pat, s64 abs_id, s64 rel_id); + inline std::string sstr(const QString& _in) { return _in.toStdString(); } game_list_frame::game_list_frame(std::shared_ptr gui_settings, std::shared_ptr emu_settings, std::shared_ptr persistent_settings, QWidget* parent) @@ -1078,7 +1080,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) extern bool is_savestate_compatible(const fs::file& file); - if (const std::string sstate = fs::get_cache_dir() + "/savestates/" + current_game.serial + ".SAVESTAT"; is_savestate_compatible(fs::file(sstate))) + if (const std::string sstate = get_savestate_file(current_game.serial, current_game.path, 0, 0); is_savestate_compatible(fs::file(sstate))) { QAction* boot_state = menu.addAction(is_current_running_game ? tr("&Reboot with savestate")