Add savestate buttons to home menu

This commit is contained in:
Eladash 2023-03-18 21:35:51 +02:00 committed by Megamouse
parent a6313fa46c
commit 79d09d02ed
10 changed files with 142 additions and 10 deletions

View File

@ -42,6 +42,8 @@ public:
// Underlying type // Underlying type
using under = std::underlying_type_t<T>; using under = std::underlying_type_t<T>;
ENABLE_BITWISE_SERIALIZATION;
private: private:
// Underlying value // Underlying value
under m_data; under m_data;
@ -49,7 +51,7 @@ private:
friend class atomic_bs_t<T>; friend class atomic_bs_t<T>;
// Value constructor // Value constructor
constexpr explicit bs_t(int, under data) constexpr explicit bs_t(int, under data) noexcept
: m_data(data) : m_data(data)
{ {
} }
@ -71,19 +73,19 @@ public:
bs_t() = default; bs_t() = default;
// Construct from a single bit // Construct from a single bit
constexpr bs_t(T bit) constexpr bs_t(T bit) noexcept
: m_data(shift(bit)) : m_data(shift(bit))
{ {
} }
// Test for empty bitset // Test for empty bitset
constexpr explicit operator bool() const constexpr explicit operator bool() const noexcept
{ {
return m_data != 0; return m_data != 0;
} }
// Extract underlying data // Extract underlying data
constexpr explicit operator under() const constexpr explicit operator under() const noexcept
{ {
return m_data; return m_data;
} }
@ -138,7 +140,7 @@ public:
return bs_t(0, lhs.m_data ^ rhs.m_data); 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; return m_data == rhs.m_data;
} }

View File

@ -163,6 +163,13 @@ extern u64 get_sysutil_cb_manager_read_count()
extern bool send_open_home_menu_cmds() extern bool send_open_home_menu_cmds()
{ {
auto status = g_fxo->try_get<SysutilMenuOpenStatus>();
if (!status || status->active)
{
return false;
}
// TODO: handle CELL_SYSUTIL_BGMPLAYBACK_STATUS_DISABLE // TODO: handle CELL_SYSUTIL_BGMPLAYBACK_STATUS_DISABLE
if (sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_BEGIN, 0) < 0 || if (sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_BEGIN, 0) < 0 ||
sysutil_send_system_cmd(CELL_SYSUTIL_SYSTEM_MENU_OPEN, 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; return false;
} }
status->active = true;
return true; return true;
} }
extern void send_close_home_menu_cmds() extern void send_close_home_menu_cmds()
{ {
auto status = g_fxo->try_get<SysutilMenuOpenStatus>();
if (!status || !status->active)
{
return;
}
// TODO: handle CELL_SYSUTIL_BGMPLAYBACK_STATUS_DISABLE // TODO: handle CELL_SYSUTIL_BGMPLAYBACK_STATUS_DISABLE
sysutil_send_system_cmd(CELL_SYSUTIL_BGMPLAYBACK_STOP, 0); 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_SYSTEM_MENU_CLOSE, 0);
sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_END, 0); sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_END, 0);
status->active = false;
} }
template <> template <>

View File

@ -317,6 +317,14 @@ struct CellSysCacheParam
vm::bptr<void> reserved; vm::bptr<void> reserved;
}; };
template <u32 BaseEvent>
struct SysutilEventStatus
{
atomic_t<bool> active = false;
};
using SysutilMenuOpenStatus = SysutilEventStatus<CELL_SYSUTIL_SYSTEM_MENU_OPEN>;
extern void sysutil_register_cb(std::function<s32(ppu_thread&)>&&); extern void sysutil_register_cb(std::function<s32(ppu_thread&)>&&);
extern s32 sysutil_send_system_cmd(u64 status, u64 param); extern s32 sysutil_send_system_cmd(u64 status, u64 param);
s32 sysutil_check_name_string(const char* src, s32 minlen, s32 maxlen); s32 sysutil_check_name_string(const char* src, s32 minlen, s32 maxlen);

View File

@ -5,6 +5,7 @@
extern atomic_t<bool> g_user_asked_for_recording; extern atomic_t<bool> g_user_asked_for_recording;
extern atomic_t<bool> g_user_asked_for_screenshot; extern atomic_t<bool> g_user_asked_for_screenshot;
extern bool boot_last_savestate(bool testing);
namespace rsx namespace rsx
{ {
@ -39,6 +40,46 @@ namespace rsx
return page_navigation::exit; return page_navigation::exit;
}); });
const bool suspend_mode = g_cfg.savestate.suspend_emu.get();
std::unique_ptr<overlay_element> save_state = std::make_unique<home_menu_entry>(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<overlay_element> reload_state = std::make_unique<home_menu_entry>(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<overlay_element> recording = std::make_unique<home_menu_entry>(get_localized_string(localized_string_id::HOME_MENU_RECORDING)); std::unique_ptr<overlay_element> recording = std::make_unique<home_menu_entry>(get_localized_string(localized_string_id::HOME_MENU_RECORDING));
add_item(recording, [](pad_button btn) -> page_navigation add_item(recording, [](pad_button btn) -> page_navigation
{ {

View File

@ -22,6 +22,7 @@
#include "Emu/Cell/lv2/sys_overlay.h" #include "Emu/Cell/lv2/sys_overlay.h"
#include "Emu/Cell/lv2/sys_spu.h" #include "Emu/Cell/lv2/sys_spu.h"
#include "Emu/Cell/Modules/cellGame.h" #include "Emu/Cell/Modules/cellGame.h"
#include "Emu/Cell/Modules/cellSysutil.h"
#include "Emu/title.h" #include "Emu/title.h"
#include "Emu/IdManager.h" #include "Emu/IdManager.h"
@ -84,6 +85,8 @@ extern bool is_savestate_version_compatible(const std::vector<std::pair<u16, u16
extern std::vector<std::pair<u16, u16>> read_used_savestate_versions(); extern std::vector<std::pair<u16, u16>> read_used_savestate_versions();
std::string get_savestate_path(std::string_view title_id, std::string_view boot_path); 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; fs::file g_tty;
atomic_t<s64> g_tty_size{0}; atomic_t<s64> g_tty_size{0};
std::array<std::deque<std::string>, 16> g_tty_input; std::array<std::deque<std::string>, 16> g_tty_input;
@ -206,7 +209,12 @@ void init_fxo_for_exec(utils::serial* ar, bool full = false)
if (ar) if (ar)
{ {
Emu.ExecDeserializationRemnants(); 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_state_inspection_savestate = g_cfg.savestate.state_inspection_mode.get();
m_savestate_extension_flags1 = {};
bool resolve_path_as_vfs_path = false; bool resolve_path_as_vfs_path = false;
@ -2248,11 +2257,29 @@ void Emulator::FinalizeRunRequest()
spu.state.notify_one(cpu_flag::stop); spu.state.notify_one(cpu_flag::stop);
}; };
if (m_savestate_extension_flags1 & SaveStateExtentionFlags1::ShouldCloseMenu)
{
g_fxo->get<SysutilMenuOpenStatus>().active = true;
}
idm::select<named_thread<spu_thread>>(on_select); idm::select<named_thread<spu_thread>>(on_select);
lv2_obj::make_scheduler_ready(); lv2_obj::make_scheduler_ready();
m_state.compare_and_swap_test(system_state::starting, system_state::running); 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) bool Emulator::Pause(bool freeze_emulation, bool show_resume_message)
@ -2579,6 +2606,7 @@ std::shared_ptr<utils::serial> Emulator::Kill(bool allow_autoexit, bool savestat
m_config_path.clear(); m_config_path.clear();
m_config_mode = cfg_mode::custom; m_config_mode = cfg_mode::custom;
read_used_savestate_versions(); read_used_savestate_versions();
m_savestate_extension_flags1 = {};
return to_ar; return to_ar;
} }
@ -2755,6 +2783,16 @@ std::shared_ptr<utils::serial> Emulator::Kill(bool allow_autoexit, bool savestat
ar(std::array<u8, 32>{}); // Reserved for future use ar(std::array<u8, 32>{}); // Reserved for future use
vm::save(ar); vm::save(ar);
g_fxo->save(ar); g_fxo->save(ar);
bs_t<SaveStateExtentionFlags1> extension_flags{SaveStateExtentionFlags1::SupportsMenuOpenResume};
if (g_fxo->get<SysutilMenuOpenStatus>().active)
{
extension_flags += SaveStateExtentionFlags1::ShouldCloseMenu;
}
ar(extension_flags);
ar(std::array<u8, 32>{}); // Reserved for future use ar(std::array<u8, 32>{}); // Reserved for future use
ar(timestamp); ar(timestamp);
}); });
@ -2874,6 +2912,7 @@ std::shared_ptr<utils::serial> Emulator::Kill(bool allow_autoexit, bool savestat
m_config_mode = cfg_mode::custom; m_config_mode = cfg_mode::custom;
m_ar.reset(); m_ar.reset();
read_used_savestate_versions(); read_used_savestate_versions();
m_savestate_extension_flags1 = {};
// Always Enable display sleep, not only if it was prevented. // Always Enable display sleep, not only if it was prevented.
enable_display_sleep(); enable_display_sleep();

View File

@ -2,6 +2,7 @@
#include "util/types.hpp" #include "util/types.hpp"
#include "util/atomic.hpp" #include "util/atomic.hpp"
#include "Utilities/bit_set.h"
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <string> #include <string>
@ -158,6 +159,16 @@ class Emulator final
} }
} }
enum class SaveStateExtentionFlags1 : u8
{
SupportsMenuOpenResume,
ShouldCloseMenu,
__bitset_enum_max,
};
bs_t<SaveStateExtentionFlags1> m_savestate_extension_flags1{};
public: public:
static constexpr std::string_view game_id_boot_prefix = "%RPCS3_GAMEID%:"; static constexpr std::string_view game_id_boot_prefix = "%RPCS3_GAMEID%:";
static constexpr std::string_view vfs_boot_prefix = "%RPCS3_VFS%:"; static constexpr std::string_view vfs_boot_prefix = "%RPCS3_VFS%:";

View File

@ -167,6 +167,9 @@ enum class localized_string_id
HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY, HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY,
HOME_MENU_SETTINGS_DEBUG, HOME_MENU_SETTINGS_DEBUG,
HOME_MENU_SCREENSHOT, HOME_MENU_SCREENSHOT,
HOME_MENU_SAVESTATE,
HOME_MENU_SAVESTATE_AND_EXIT,
HOME_MENU_RELOAD_SAVESTATE,
HOME_MENU_RECORDING, HOME_MENU_RECORDING,
EMULATION_PAUSED_RESUME_WITH_START, EMULATION_PAUSED_RESUME_WITH_START,

View File

@ -174,7 +174,7 @@ std::vector<std::pair<u16, u16>> read_used_savestate_versions()
return used_serial; 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)) 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); sys_log.success("Booting the most recent savestate \'%s\' using the Reload shortcut.", savestate_path);
Emu.GracefulShutdown(false); Emu.GracefulShutdown(false);

View File

@ -372,8 +372,8 @@ void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKey
return; return;
} }
extern bool boot_last_savestate(); extern bool boot_last_savestate(bool testing);
boot_last_savestate(); boot_last_savestate(false);
} }
break; break;
} }

View File

@ -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_PERFORMANCE_OVERLAY: return tr("Performance Overlay");
case localized_string_id::HOME_MENU_SETTINGS_DEBUG: return tr("Debug"); 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_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::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_PAUSED_RESUME_WITH_START: return tr("Press and hold the START button to resume");
case localized_string_id::EMULATION_RESUMING: return tr("Resuming...!"); case localized_string_id::EMULATION_RESUMING: return tr("Resuming...!");