diff --git a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp index dee57ac893..a431243327 100644 --- a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp @@ -10,6 +10,7 @@ #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 "cellSaveData.h" #include "cellMsgDialog.h" @@ -1750,6 +1751,48 @@ 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; + + 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; + } + + u32 indicator_pos = (statSet->indicator->dispPosition & 0x0F); + + auto msg_location = rsx::overlays::message_pin_location::top; + switch (indicator_pos) + { + case CELL_SAVEDATA_INDICATORPOS_UPPER_LEFT: + msg_location = rsx::overlays::message_pin_location::top; + break; + case CELL_SAVEDATA_INDICATORPOS_LOWER_LEFT: + msg_location = rsx::overlays::message_pin_location::bottom; + break; + case CELL_SAVEDATA_INDICATORPOS_UPPER_RIGHT: + msg_location = rsx::overlays::message_pin_location::top_right; + break; + case CELL_SAVEDATA_INDICATORPOS_LOWER_RIGHT: + msg_location = rsx::overlays::message_pin_location::bottom_right; + 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 +2141,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/overlay_message.cpp b/rpcs3/Emu/RSX/Overlays/overlay_message.cpp index 25a331d0df..1933a98b18 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) @@ -198,18 +198,29 @@ namespace rsx // Render reversed list. Oldest entries are furthest from the border constexpr u16 spacing = 4; s16 y_offset = 8; + s16 x_offset = 10; usz index = 0; for (auto it = vis_set.rbegin(); it != vis_set.rend(); ++it, ++index) { if (origin == message_pin_location::top) [[ likely ]] { - it->update(index, cur_time, y_offset); + it->update(index, cur_time, x_offset, y_offset); y_offset += (spacing + it->h); } - else + else if (origin == message_pin_location::bottom) { 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); + } + else if (origin == message_pin_location::top_right) + { + it->update(index, cur_time, virtual_width - x_offset - it->w, y_offset); + y_offset += (spacing + it->h); + } + else if (origin == message_pin_location::bottom_right) + { + y_offset += (spacing + it->h); + it->update(index, cur_time, virtual_width - x_offset - it->w, virtual_height - y_offset); } } } @@ -225,8 +236,11 @@ namespace rsx 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_top_right, m_ready_queue_top_right, message_pin_location::top_right); + update_queue(m_visible_items_bottom_right, m_ready_queue_bottom_right, message_pin_location::bottom_right); - visible = !m_visible_items_top.empty() || !m_visible_items_bottom.empty(); + visible = !m_visible_items_top.empty() || !m_visible_items_bottom.empty() || + !m_visible_items_top_right.empty() || !m_visible_items_bottom_right.empty(); } compiled_resource message::get_compiled() @@ -250,6 +264,16 @@ namespace rsx cr.add(item.get_compiled()); } + for (auto& item : m_visible_items_top_right) + { + cr.add(item.get_compiled()); + } + + for (auto& item : m_visible_items_bottom_right) + { + cr.add(item.get_compiled()); + } + return cr; } @@ -287,6 +311,10 @@ namespace rsx 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::top_right: + return check_list(m_ready_queue_top_right) || check_list(m_visible_items_top_right); + case message_pin_location::bottom_right: + return check_list(m_ready_queue_bottom_right) || check_list(m_visible_items_bottom_right); } return false; diff --git a/rpcs3/Emu/RSX/Overlays/overlay_message.h b/rpcs3/Emu/RSX/Overlays/overlay_message.h index cd3474aa0f..6282e949e4 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_message.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_message.h @@ -12,7 +12,9 @@ namespace rsx enum class message_pin_location { top, - bottom + bottom, + top_right, + bottom_right }; 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(); @@ -61,9 +63,23 @@ namespace rsx { 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; + + switch (location) + { + case message_pin_location::top: + queue = &m_ready_queue_top; + break; + case message_pin_location::bottom: + queue = &m_ready_queue_bottom; + break; + case message_pin_location::top_right: + queue = &m_ready_queue_top_right; + break; + case message_pin_location::bottom_right: + queue = &m_ready_queue_bottom_right; + 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; @@ -91,10 +107,14 @@ namespace rsx // Top and bottom enqueued sets std::deque m_ready_queue_top; std::deque m_ready_queue_bottom; + std::deque m_ready_queue_top_right; + std::deque m_ready_queue_bottom_right; // Top and bottom visible sets std::deque m_visible_items_top; std::deque m_visible_items_bottom; + std::deque m_visible_items_top_right; + std::deque m_visible_items_bottom_right; void update_queue(std::deque& vis_set, std::deque& ready_set, message_pin_location origin); diff --git a/rpcs3/Emu/localized_string_id.h b/rpcs3/Emu/localized_string_id.h index 6e2bb3c2dc..991f270961 100644 --- a/rpcs3/Emu/localized_string_id.h +++ b/rpcs3/Emu/localized_string_id.h @@ -132,6 +132,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/rpcs3qt/localized_emu.h b/rpcs3/rpcs3qt/localized_emu.h index eff8602ff8..194f32e5a0 100644 --- a/rpcs3/rpcs3qt/localized_emu.h +++ b/rpcs3/rpcs3qt/localized_emu.h @@ -159,6 +159,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");