From 6d766ccbb59111da17976f1fbea61c3f6cba4872 Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Mon, 4 Nov 2024 23:53:34 +0300 Subject: [PATCH] cellSaveData: Add autosave indicator (#15720) --- rpcs3/Emu/Cell/Modules/cellSaveData.cpp | 51 +++++++++++++++ .../HomeMenu/overlay_home_menu_settings.cpp | 1 + .../Overlays/overlay_compile_notification.cpp | 4 +- rpcs3/Emu/RSX/Overlays/overlay_message.cpp | 62 ++++++++++++++----- rpcs3/Emu/RSX/Overlays/overlay_message.h | 48 +++++++++----- rpcs3/Emu/localized_string_id.h | 2 + rpcs3/Emu/system_config.h | 1 + rpcs3/rpcs3qt/emu_settings_type.h | 2 + rpcs3/rpcs3qt/localized_emu.h | 2 + rpcs3/rpcs3qt/settings_dialog.cpp | 3 + rpcs3/rpcs3qt/settings_dialog.ui | 7 +++ rpcs3/rpcs3qt/tooltips.h | 1 + 12 files changed, 151 insertions(+), 33 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp index fe062bede0..4ce8be031c 100644 --- a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp @@ -10,6 +10,8 @@ #include "Emu/Cell/PPUModule.h" #include "Emu/Cell/Modules/cellSysutil.h" #include "Emu/Cell/Modules/cellUserInfo.h" +#include "Emu/RSX/Overlays/overlay_message.h" +#include "Emu/system_config.h" #include "cellSaveData.h" #include "cellMsgDialog.h" @@ -1750,6 +1752,50 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v fileGet->excSize = 0; + // show indicator for automatic save or auto load interactions if the game requests it (statSet->indicator) + const bool show_auto_indicator = operation <= SAVEDATA_OP_LIST_AUTO_LOAD && statSet && statSet->indicator && g_cfg.misc.show_autosave_autoload_hint; + + if (show_auto_indicator) + { + auto msg_text = localized_string_id::INVALID; + + if (operation == SAVEDATA_OP_AUTO_SAVE || operation == SAVEDATA_OP_LIST_AUTO_SAVE) + { + msg_text = localized_string_id::CELL_SAVEDATA_AUTOSAVE; + } + else if (operation == SAVEDATA_OP_AUTO_LOAD || operation == SAVEDATA_OP_LIST_AUTO_LOAD) + { + msg_text = localized_string_id::CELL_SAVEDATA_AUTOLOAD; + } + + auto msg_location = rsx::overlays::message_pin_location::top_left; + + switch (statSet->indicator->dispPosition & 0x0F) + { + case CELL_SAVEDATA_INDICATORPOS_LOWER_RIGHT: + msg_location = rsx::overlays::message_pin_location::bottom_right; + break; + case CELL_SAVEDATA_INDICATORPOS_LOWER_LEFT: + msg_location = rsx::overlays::message_pin_location::bottom_left; + break; + case CELL_SAVEDATA_INDICATORPOS_UPPER_RIGHT: + msg_location = rsx::overlays::message_pin_location::top_right; + break; + case CELL_SAVEDATA_INDICATORPOS_UPPER_LEFT: + case CELL_SAVEDATA_INDICATORPOS_CENTER: + default: + msg_location = rsx::overlays::message_pin_location::top_left; + break; + } + + // TODO: Blinking variants + + // RPCS3 saves basically instantaneously so there's not much point in showing auto indicator + // WHILE saving is in progress. Instead we show the indicator for 3 seconds to let the user + // know when the game autosaves. + rsx::overlays::queue_message(msg_text, 3'000'000, {}, msg_location); + } + error_code savedata_result = CELL_OK; u64 delay_save_until = 0; @@ -2098,6 +2144,11 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v fs::remove_all(old_path); } + if (show_auto_indicator) + { + // auto indicator should be hidden here if save/load throttling is added + } + if (savedata_result + 0u == CELL_SAVEDATA_ERROR_CBRESULT) { return display_callback_result_error_message(ppu, *result, errDialog); diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp index fa63aedf17..c63802e366 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp @@ -97,6 +97,7 @@ namespace rsx add_checkbox(&g_cfg.misc.show_rpcn_popups, "Show RPCN Popups"); add_checkbox(&g_cfg.misc.show_shader_compilation_hint, "Show Shader Compilation Hint"); add_checkbox(&g_cfg.misc.show_ppu_compilation_hint, "Show PPU Compilation Hint"); + add_checkbox(&g_cfg.misc.show_autosave_autoload_hint, "Show Autosave/Autoload Hint"); apply_layout(); } diff --git a/rpcs3/Emu/RSX/Overlays/overlay_compile_notification.cpp b/rpcs3/Emu/RSX/Overlays/overlay_compile_notification.cpp index f7827c9155..da7f58e6d7 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_compile_notification.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_compile_notification.cpp @@ -22,7 +22,7 @@ namespace rsx localized_string_id::RSX_OVERLAYS_COMPILING_SHADERS, 5'000'000, {}, - message_pin_location::bottom, + message_pin_location::bottom_left, s_shader_loading_icon24, true); } @@ -41,7 +41,7 @@ namespace rsx localized_string_id::RSX_OVERLAYS_COMPILING_PPU_MODULES, 20'000'000, refs, - message_pin_location::bottom, + message_pin_location::bottom_left, s_ppu_loading_icon24, true); diff --git a/rpcs3/Emu/RSX/Overlays/overlay_message.cpp b/rpcs3/Emu/RSX/Overlays/overlay_message.cpp index 5f794797df..fd49fc0cfd 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_message.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_message.cpp @@ -117,12 +117,12 @@ namespace rsx return compiled_resources; } - void message_item::update(usz index, u64 timestamp_us, s16 y_offset) + void message_item::update(usz index, u64 timestamp_us, s16 x_offset, s16 y_offset) { if (m_cur_pos != index) { m_cur_pos = index; - set_pos(10, y_offset); + set_pos(x_offset, y_offset); } if (!m_processed) @@ -197,19 +197,30 @@ namespace rsx // Render reversed list. Oldest entries are furthest from the border constexpr u16 spacing = 4; + s16 x_offset = 10; s16 y_offset = 8; usz index = 0; + for (auto it = vis_set.rbegin(); it != vis_set.rend(); ++it, ++index) { - if (origin == message_pin_location::top) [[ likely ]] + switch (origin) { - it->update(index, cur_time, y_offset); + case message_pin_location::bottom_right: y_offset += (spacing + it->h); - } - else - { + it->update(index, cur_time, virtual_width - x_offset - it->w, virtual_height - y_offset); + break; + case message_pin_location::bottom_left: y_offset += (spacing + it->h); - it->update(index, cur_time, virtual_height - y_offset); + it->update(index, cur_time, x_offset, virtual_height - y_offset); + break; + case message_pin_location::top_right: + it->update(index, cur_time, virtual_width - x_offset - it->w, y_offset); + y_offset += (spacing + it->h); + break; + case message_pin_location::top_left: + it->update(index, cur_time, x_offset, y_offset); + y_offset += (spacing + it->h); + break; } } } @@ -223,10 +234,13 @@ namespace rsx std::lock_guard lock(m_mutex_queue); - update_queue(m_visible_items_top, m_ready_queue_top, message_pin_location::top); - update_queue(m_visible_items_bottom, m_ready_queue_bottom, message_pin_location::bottom); + update_queue(m_visible_items_bottom_right, m_ready_queue_bottom_right, message_pin_location::bottom_right); + update_queue(m_visible_items_bottom_left, m_ready_queue_bottom_left, message_pin_location::bottom_left); + update_queue(m_visible_items_top_right, m_ready_queue_top_right, message_pin_location::top_right); + update_queue(m_visible_items_top_left, m_ready_queue_top_left, message_pin_location::top_left); - visible = !m_visible_items_top.empty() || !m_visible_items_bottom.empty(); + visible = !m_visible_items_bottom_right.empty() || !m_visible_items_bottom_left.empty() || + !m_visible_items_top_right.empty() || !m_visible_items_top_left.empty(); } compiled_resource message::get_compiled() @@ -240,12 +254,22 @@ namespace rsx compiled_resource cr{}; - for (auto& item : m_visible_items_top) + for (auto& item : m_visible_items_bottom_right) { cr.add(item.get_compiled()); } - for (auto& item : m_visible_items_bottom) + for (auto& item : m_visible_items_bottom_left) + { + cr.add(item.get_compiled()); + } + + for (auto& item : m_visible_items_top_right) + { + cr.add(item.get_compiled()); + } + + for (auto& item : m_visible_items_top_left) { cr.add(item.get_compiled()); } @@ -283,10 +307,14 @@ namespace rsx switch (location) { - case message_pin_location::top: - return check_list(m_ready_queue_top) || check_list(m_visible_items_top); - case message_pin_location::bottom: - return check_list(m_ready_queue_bottom) || check_list(m_visible_items_bottom); + case message_pin_location::bottom_right: + return check_list(m_ready_queue_bottom_right) || check_list(m_visible_items_bottom_right); + case message_pin_location::bottom_left: + return check_list(m_ready_queue_bottom_left) || check_list(m_visible_items_bottom_left); + case message_pin_location::top_right: + return check_list(m_ready_queue_top_right) || check_list(m_visible_items_top_right); + case message_pin_location::top_left: + return check_list(m_ready_queue_top_left) || check_list(m_visible_items_top_left); } return false; diff --git a/rpcs3/Emu/RSX/Overlays/overlay_message.h b/rpcs3/Emu/RSX/Overlays/overlay_message.h index cd3474aa0f..17d03788b8 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_message.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_message.h @@ -11,8 +11,10 @@ namespace rsx { enum class message_pin_location { - top, - bottom + bottom_right, + bottom_left, + top_right, + top_left }; class message_item : public rounded_rect @@ -20,7 +22,7 @@ namespace rsx public: template message_item(T msg_id, u64 expiration, std::shared_ptr> refs, std::shared_ptr icon = {}); - void update(usz index, u64 timestamp_us, s16 y_offset); + void update(usz index, u64 timestamp_us, s16 x_offset, s16 y_offset); void set_pos(s16 _x, s16 _y) override; void reset_expiration(); @@ -55,15 +57,29 @@ namespace rsx T msg_id, u64 expiration, std::shared_ptr> refs, - message_pin_location location = message_pin_location::top, + message_pin_location location = message_pin_location::top_left, std::shared_ptr icon = {}, bool allow_refresh = false) { std::lock_guard lock(m_mutex_queue); - auto& queue = location == message_pin_location::top - ? m_ready_queue_top - : m_ready_queue_bottom; + auto* queue = &m_ready_queue_top_left; + + switch (location) + { + case message_pin_location::bottom_right: + queue = &m_ready_queue_bottom_right; + break; + case message_pin_location::bottom_left: + queue = &m_ready_queue_bottom_left; + break; + case message_pin_location::top_right: + queue = &m_ready_queue_top_right; + break; + case message_pin_location::top_left: + queue = &m_ready_queue_top_left; + break; + } if constexpr (std::is_same_v>) { @@ -71,13 +87,13 @@ namespace rsx { if (!message_exists(location, id, allow_refresh)) { - queue.emplace_back(id, expiration, refs, icon); + queue->emplace_back(id, expiration, refs, icon); } } } else if (!message_exists(location, msg_id, allow_refresh)) { - queue.emplace_back(msg_id, expiration, std::move(refs), icon); + queue->emplace_back(msg_id, expiration, std::move(refs), icon); } visible = true; @@ -89,12 +105,16 @@ namespace rsx shared_mutex m_mutex_queue; // Top and bottom enqueued sets - std::deque m_ready_queue_top; - std::deque m_ready_queue_bottom; + std::deque m_ready_queue_bottom_right; + std::deque m_ready_queue_bottom_left; + std::deque m_ready_queue_top_right; + std::deque m_ready_queue_top_left; // Top and bottom visible sets - std::deque m_visible_items_top; - std::deque m_visible_items_bottom; + std::deque m_visible_items_bottom_right; + std::deque m_visible_items_bottom_left; + std::deque m_visible_items_top_right; + std::deque m_visible_items_top_left; void update_queue(std::deque& vis_set, std::deque& ready_set, message_pin_location origin); @@ -109,7 +129,7 @@ namespace rsx T msg_id, u64 expiration = 5'000'000, std::shared_ptr> refs = {}, - message_pin_location location = message_pin_location::top, + message_pin_location location = message_pin_location::top_left, std::shared_ptr icon = {}, bool allow_refresh = false) { diff --git a/rpcs3/Emu/localized_string_id.h b/rpcs3/Emu/localized_string_id.h index b5d6d82271..b549efce65 100644 --- a/rpcs3/Emu/localized_string_id.h +++ b/rpcs3/Emu/localized_string_id.h @@ -138,6 +138,8 @@ enum class localized_string_id CELL_SAVEDATA_SAVE, CELL_SAVEDATA_LOAD, CELL_SAVEDATA_OVERWRITE, + CELL_SAVEDATA_AUTOSAVE, + CELL_SAVEDATA_AUTOLOAD, CELL_CROSS_CONTROLLER_MSG, CELL_CROSS_CONTROLLER_FW_MSG, diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index 5cae66d7fc..8a0e7737ca 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -345,6 +345,7 @@ struct cfg_root : cfg::node cfg::_bool show_pressure_intensity_toggle_hint{ this, "Show pressure intensity toggle hint", true, true }; cfg::_bool show_analog_limiter_toggle_hint{ this, "Show analog limiter toggle hint", true, true }; cfg::_bool show_mouse_and_keyboard_toggle_hint{ this, "Show mouse and keyboard toggle hint", true, true }; + cfg::_bool show_autosave_autoload_hint{ this, "Show autosave/autoload hint", false, true }; cfg::_bool use_native_interface{ this, "Use native user interface", true }; cfg::string gdb_server{ this, "GDB Server", "127.0.0.1:2345" }; cfg::_bool silence_all_logs{ this, "Silence All Logs", false, true }; diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index b3aa1b590a..670b873405 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -178,6 +178,7 @@ enum class emu_settings_type ShowPressureIntensityToggleHint, ShowAnalogLimiterToggleHint, ShowMouseAndKeyboardToggleHint, + ShowAutosaveAutoloadHint, WindowTitleFormat, PauseDuringHomeMenu, @@ -375,6 +376,7 @@ inline static const QMap settings_location = { emu_settings_type::SilenceAllLogs, { "Miscellaneous", "Silence All Logs" }}, { emu_settings_type::WindowTitleFormat, { "Miscellaneous", "Window Title Format" }}, { emu_settings_type::PauseDuringHomeMenu, { "Miscellaneous", "Pause Emulation During Home Menu" }}, + { emu_settings_type::ShowAutosaveAutoloadHint, { "Miscellaneous", "Show autosave/autoload hint" }}, // Networking { emu_settings_type::InternetStatus, { "Net", "Internet enabled"}}, diff --git a/rpcs3/rpcs3qt/localized_emu.h b/rpcs3/rpcs3qt/localized_emu.h index a85a604552..e791360597 100644 --- a/rpcs3/rpcs3qt/localized_emu.h +++ b/rpcs3/rpcs3qt/localized_emu.h @@ -165,6 +165,8 @@ private: case localized_string_id::CELL_SAVEDATA_DELETE: return tr("Delete this data?\n\n%0", "Savedata entry info").arg(std::forward(args)...); case localized_string_id::CELL_SAVEDATA_LOAD: return tr("Load this data?\n\n%0", "Savedata entry info").arg(std::forward(args)...); case localized_string_id::CELL_SAVEDATA_OVERWRITE: return tr("Do you want to overwrite the saved data?\n\n%0", "Savedata entry info").arg(std::forward(args)...); + case localized_string_id::CELL_SAVEDATA_AUTOSAVE: return tr("Saving"); + case localized_string_id::CELL_SAVEDATA_AUTOLOAD: return tr("Loading"); case localized_string_id::CELL_CROSS_CONTROLLER_MSG: return tr("Start [%0] on the PS Vita system.\nIf you have not installed [%0], go to [Remote Play] on the PS Vita system and start [Cross-Controller] from the LiveArea™ screen.", "Cross-Controller message").arg(std::forward(args)...); case localized_string_id::CELL_CROSS_CONTROLLER_FW_MSG: return tr("If your system software version on the PS Vita system is earlier than 1.80, you must update the system software to the latest version.", "Cross-Controller firmware message"); case localized_string_id::CELL_NP_RECVMESSAGE_DIALOG_TITLE: return tr("Select Message", "RECVMESSAGE_DIALOG"); diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 00aebbabf9..da862377e1 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1850,6 +1850,9 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceCheckBox(ui->showMouseAndKeyboardToggleHint, emu_settings_type::ShowMouseAndKeyboardToggleHint); SubscribeTooltip(ui->showMouseAndKeyboardToggleHint, tooltips.settings.show_mouse_and_keyboard_toggle_hint); + m_emu_settings->EnhanceCheckBox(ui->showAutosaveAutoloadHint, emu_settings_type::ShowAutosaveAutoloadHint); + SubscribeTooltip(ui->showAutosaveAutoloadHint, tooltips.settings.show_autosave_autoload_hint); + m_emu_settings->EnhanceCheckBox(ui->pauseDuringHomeMenu, emu_settings_type::PauseDuringHomeMenu); SubscribeTooltip(ui->pauseDuringHomeMenu, tooltips.settings.pause_during_home_menu); diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index 1f6738d9d9..74c5629092 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -3007,6 +3007,13 @@ + + + + Show autosave/autoload hint + + + diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index faa67f21c7..8549ad0137 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -145,6 +145,7 @@ public: const QString show_pressure_intensity_toggle_hint = tr("Shows pressure intensity toggle hint using the native overlay."); const QString show_analog_limiter_toggle_hint = tr("Shows analog limiter toggle hint using the native overlay."); const QString show_mouse_and_keyboard_toggle_hint = tr("Shows mouse and keyboard toggle hint using the native overlay."); + const QString show_autosave_autoload_hint = tr("Shows autosave/autoload hint using the native overlay."); const QString use_native_interface = tr("Enables use of native HUD within the game window that can interact with game controllers.\nWhen disabled, regular Qt dialogs are used instead.\nCurrently, the on-screen keyboard only supports the English key layout."); const QString pause_during_home_menu = tr("When enabled, opening the home menu will also pause emulation.\nWhile most games pause themselves while the home menu is shown, some do not.\nIn that case it can be helpful to pause the emulation whenever the home menu is open.");