From 79d09d02ed4b4e7b5dc7984f1febf555f0615108 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 18 Mar 2023 21:35:51 +0200 Subject: [PATCH] Add savestate buttons to home menu --- Utilities/bit_set.h | 12 +++--- rpcs3/Emu/Cell/Modules/cellSysutil.cpp | 17 ++++++++ rpcs3/Emu/Cell/Modules/cellSysutil.h | 8 ++++ .../HomeMenu/overlay_home_menu_main_menu.cpp | 41 +++++++++++++++++++ rpcs3/Emu/System.cpp | 41 ++++++++++++++++++- rpcs3/Emu/System.h | 11 +++++ rpcs3/Emu/localized_string_id.h | 3 ++ rpcs3/Emu/savestate_utils.cpp | 12 +++++- rpcs3/rpcs3qt/gs_frame.cpp | 4 +- rpcs3/rpcs3qt/localized_emu.h | 3 ++ 10 files changed, 142 insertions(+), 10 deletions(-) diff --git a/Utilities/bit_set.h b/Utilities/bit_set.h index 6bcbc768c0..8b81929b7c 100644 --- a/Utilities/bit_set.h +++ b/Utilities/bit_set.h @@ -42,6 +42,8 @@ public: // Underlying type using under = std::underlying_type_t; + ENABLE_BITWISE_SERIALIZATION; + private: // Underlying value under m_data; @@ -49,7 +51,7 @@ private: friend class atomic_bs_t; // Value constructor - constexpr explicit bs_t(int, under data) + constexpr explicit bs_t(int, under data) noexcept : m_data(data) { } @@ -71,19 +73,19 @@ public: bs_t() = default; // Construct from a single bit - constexpr bs_t(T bit) + constexpr bs_t(T bit) noexcept : m_data(shift(bit)) { } // Test for empty bitset - constexpr explicit operator bool() const + constexpr explicit operator bool() const noexcept { return m_data != 0; } // Extract underlying data - constexpr explicit operator under() const + constexpr explicit operator under() const noexcept { return m_data; } @@ -138,7 +140,7 @@ public: return bs_t(0, lhs.m_data ^ rhs.m_data); } - constexpr bool operator ==(bs_t rhs) const + constexpr bool operator ==(bs_t rhs) const noexcept { return m_data == rhs.m_data; } diff --git a/rpcs3/Emu/Cell/Modules/cellSysutil.cpp b/rpcs3/Emu/Cell/Modules/cellSysutil.cpp index b2d5eda902..cd3c662d1c 100644 --- a/rpcs3/Emu/Cell/Modules/cellSysutil.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSysutil.cpp @@ -163,6 +163,13 @@ extern u64 get_sysutil_cb_manager_read_count() extern bool send_open_home_menu_cmds() { + auto status = g_fxo->try_get(); + + if (!status || status->active) + { + return false; + } + // TODO: handle CELL_SYSUTIL_BGMPLAYBACK_STATUS_DISABLE if (sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_BEGIN, 0) < 0 || sysutil_send_system_cmd(CELL_SYSUTIL_SYSTEM_MENU_OPEN, 0) < 0 || @@ -171,15 +178,25 @@ extern bool send_open_home_menu_cmds() return false; } + status->active = true; return true; } extern void send_close_home_menu_cmds() { + auto status = g_fxo->try_get(); + + if (!status || !status->active) + { + return; + } + // TODO: handle CELL_SYSUTIL_BGMPLAYBACK_STATUS_DISABLE sysutil_send_system_cmd(CELL_SYSUTIL_BGMPLAYBACK_STOP, 0); sysutil_send_system_cmd(CELL_SYSUTIL_SYSTEM_MENU_CLOSE, 0); sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_END, 0); + + status->active = false; } template <> diff --git a/rpcs3/Emu/Cell/Modules/cellSysutil.h b/rpcs3/Emu/Cell/Modules/cellSysutil.h index 2a28d07f5e..557fef9202 100644 --- a/rpcs3/Emu/Cell/Modules/cellSysutil.h +++ b/rpcs3/Emu/Cell/Modules/cellSysutil.h @@ -317,6 +317,14 @@ struct CellSysCacheParam vm::bptr reserved; }; +template +struct SysutilEventStatus +{ + atomic_t active = false; +}; + +using SysutilMenuOpenStatus = SysutilEventStatus; + extern void sysutil_register_cb(std::function&&); extern s32 sysutil_send_system_cmd(u64 status, u64 param); s32 sysutil_check_name_string(const char* src, s32 minlen, s32 maxlen); diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp index 78407b5bc0..ba260c216c 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp @@ -5,6 +5,7 @@ extern atomic_t g_user_asked_for_recording; extern atomic_t g_user_asked_for_screenshot; +extern bool boot_last_savestate(bool testing); namespace rsx { @@ -39,6 +40,46 @@ namespace rsx return page_navigation::exit; }); + const bool suspend_mode = g_cfg.savestate.suspend_emu.get(); + + std::unique_ptr save_state = std::make_unique(get_localized_string(suspend_mode ? localized_string_id::HOME_MENU_SAVESTATE_AND_EXIT : localized_string_id::HOME_MENU_SAVESTATE)); + add_item(save_state, [suspend_mode](pad_button btn) -> page_navigation + { + if (btn != pad_button::cross) return page_navigation::stay; + + rsx_log.notice("User selected savestate in home menu"); + + Emu.CallFromMainThread([suspend_mode]() + { + Emu.Kill(false, true); + + if (!suspend_mode) + { + Emu.Restart(); + } + }); + + return page_navigation::exit; + }); + + if (!suspend_mode && boot_last_savestate(true)) + { + std::unique_ptr reload_state = std::make_unique(get_localized_string(localized_string_id::HOME_MENU_RELOAD_SAVESTATE)); + add_item(reload_state, [](pad_button btn) -> page_navigation + { + if (btn != pad_button::cross) return page_navigation::stay; + + rsx_log.notice("User selected reload savestate in home menu"); + + Emu.CallFromMainThread([]() + { + boot_last_savestate(false); + }); + + return page_navigation::exit; + }); + } + std::unique_ptr recording = std::make_unique(get_localized_string(localized_string_id::HOME_MENU_RECORDING)); add_item(recording, [](pad_button btn) -> page_navigation { diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 98a846aa9a..32202e5efe 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -22,6 +22,7 @@ #include "Emu/Cell/lv2/sys_overlay.h" #include "Emu/Cell/lv2/sys_spu.h" #include "Emu/Cell/Modules/cellGame.h" +#include "Emu/Cell/Modules/cellSysutil.h" #include "Emu/title.h" #include "Emu/IdManager.h" @@ -84,6 +85,8 @@ extern bool is_savestate_version_compatible(const std::vector> read_used_savestate_versions(); std::string get_savestate_path(std::string_view title_id, std::string_view boot_path); +extern void send_close_home_menu_cmds(); + fs::file g_tty; atomic_t g_tty_size{0}; std::array, 16> g_tty_input; @@ -206,7 +209,12 @@ void init_fxo_for_exec(utils::serial* ar, bool full = false) if (ar) { Emu.ExecDeserializationRemnants(); - ar->pos += 32; // Reserved area + + auto flags = (*ar)(Emu.m_savestate_extension_flags1); + + const usz advance = (Emu.m_savestate_extension_flags1 & Emulator::SaveStateExtentionFlags1::SupportsMenuOpenResume ? 32 : 31); + + ar->pos += advance; // Reserved area } } @@ -888,6 +896,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool } m_state_inspection_savestate = g_cfg.savestate.state_inspection_mode.get(); + m_savestate_extension_flags1 = {}; bool resolve_path_as_vfs_path = false; @@ -2248,11 +2257,29 @@ void Emulator::FinalizeRunRequest() spu.state.notify_one(cpu_flag::stop); }; + if (m_savestate_extension_flags1 & SaveStateExtentionFlags1::ShouldCloseMenu) + { + g_fxo->get().active = true; + } + idm::select>(on_select); lv2_obj::make_scheduler_ready(); m_state.compare_and_swap_test(system_state::starting, system_state::running); + + if (m_savestate_extension_flags1 & SaveStateExtentionFlags1::ShouldCloseMenu) + { + std::thread([this, info = ProcureCurrentEmulationCourseInformation()]() + { + std::this_thread::sleep_for(2s); + + CallFromMainThread([this]() + { + send_close_home_menu_cmds(); + }, info); + }).detach(); + } } bool Emulator::Pause(bool freeze_emulation, bool show_resume_message) @@ -2579,6 +2606,7 @@ std::shared_ptr Emulator::Kill(bool allow_autoexit, bool savestat m_config_path.clear(); m_config_mode = cfg_mode::custom; read_used_savestate_versions(); + m_savestate_extension_flags1 = {}; return to_ar; } @@ -2755,6 +2783,16 @@ std::shared_ptr Emulator::Kill(bool allow_autoexit, bool savestat ar(std::array{}); // Reserved for future use vm::save(ar); g_fxo->save(ar); + + bs_t extension_flags{SaveStateExtentionFlags1::SupportsMenuOpenResume}; + + if (g_fxo->get().active) + { + extension_flags += SaveStateExtentionFlags1::ShouldCloseMenu; + } + + ar(extension_flags); + ar(std::array{}); // Reserved for future use ar(timestamp); }); @@ -2874,6 +2912,7 @@ std::shared_ptr Emulator::Kill(bool allow_autoexit, bool savestat m_config_mode = cfg_mode::custom; m_ar.reset(); read_used_savestate_versions(); + m_savestate_extension_flags1 = {}; // Always Enable display sleep, not only if it was prevented. enable_display_sleep(); diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 6ce1e772ce..546207a6e2 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -2,6 +2,7 @@ #include "util/types.hpp" #include "util/atomic.hpp" +#include "Utilities/bit_set.h" #include #include #include @@ -158,6 +159,16 @@ class Emulator final } } + enum class SaveStateExtentionFlags1 : u8 + { + SupportsMenuOpenResume, + ShouldCloseMenu, + + __bitset_enum_max, + }; + + bs_t m_savestate_extension_flags1{}; + public: static constexpr std::string_view game_id_boot_prefix = "%RPCS3_GAMEID%:"; static constexpr std::string_view vfs_boot_prefix = "%RPCS3_VFS%:"; diff --git a/rpcs3/Emu/localized_string_id.h b/rpcs3/Emu/localized_string_id.h index fb81f0642f..e07fcd6b07 100644 --- a/rpcs3/Emu/localized_string_id.h +++ b/rpcs3/Emu/localized_string_id.h @@ -167,6 +167,9 @@ enum class localized_string_id HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY, HOME_MENU_SETTINGS_DEBUG, HOME_MENU_SCREENSHOT, + HOME_MENU_SAVESTATE, + HOME_MENU_SAVESTATE_AND_EXIT, + HOME_MENU_RELOAD_SAVESTATE, HOME_MENU_RECORDING, EMULATION_PAUSED_RESUME_WITH_START, diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index 7a4249c92e..567337f4d0 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -174,7 +174,7 @@ std::vector> read_used_savestate_versions() return used_serial; } -bool boot_last_savestate() +bool boot_last_savestate(bool testing) { if (!g_cfg.savestate.suspend_emu && !Emu.GetTitleID().empty() && (Emu.IsRunning() || Emu.GetStatus() == system_state::paused)) { @@ -203,7 +203,15 @@ bool boot_last_savestate() } } - if (fs::is_file(savestate_path)) + const bool result = fs::is_file(savestate_path); + + if (testing) + { + sys_log.trace("boot_last_savestate(true) returned %s.", result); + return result; + } + + if (result) { sys_log.success("Booting the most recent savestate \'%s\' using the Reload shortcut.", savestate_path); Emu.GracefulShutdown(false); diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 6abf71b72e..3df93dac4a 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -372,8 +372,8 @@ void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKey return; } - extern bool boot_last_savestate(); - boot_last_savestate(); + extern bool boot_last_savestate(bool testing); + boot_last_savestate(false); } break; } diff --git a/rpcs3/rpcs3qt/localized_emu.h b/rpcs3/rpcs3qt/localized_emu.h index d34603574e..1f80e530ff 100644 --- a/rpcs3/rpcs3qt/localized_emu.h +++ b/rpcs3/rpcs3qt/localized_emu.h @@ -186,6 +186,9 @@ private: case localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY: return tr("Performance Overlay"); case localized_string_id::HOME_MENU_SETTINGS_DEBUG: return tr("Debug"); case localized_string_id::HOME_MENU_SCREENSHOT: return tr("Take Screenshot"); + case localized_string_id::HOME_MENU_SAVESTATE: return tr("Save Emulation State"); + case localized_string_id::HOME_MENU_SAVESTATE_AND_EXIT: return tr("Save Emulation State And Exit"); + case localized_string_id::HOME_MENU_RELOAD_SAVESTATE: return tr("Reload Last Emulation State"); case localized_string_id::HOME_MENU_RECORDING: return tr("Start/Stop Recording"); case localized_string_id::EMULATION_PAUSED_RESUME_WITH_START: return tr("Press and hold the START button to resume"); case localized_string_id::EMULATION_RESUMING: return tr("Resuming...!");