diff --git a/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp b/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp index 2b433e999c..b908480d52 100644 --- a/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp @@ -158,13 +158,64 @@ struct msg_dlg_thread_info using msg_dlg_thread = named_thread; +// Forward decleration for opened_dialog_context +error_code open_msg_dialog(bool is_blocking, u32 type, std::string msg_string, msg_dialog_source source, vm::ptr callback, vm::ptr userData, vm::ptr extParam, s32* return_code); + +struct opened_dialog_context +{ + bool is_opened = false; + u32 type{}; + std::string msg_string; + msg_dialog_source source{}; + vm::ptr callback{}; + vm::ptr userData{}; + vm::ptr extParam{}; + + SAVESTATE_INIT_POS(54); + + opened_dialog_context(const opened_dialog_context&) = delete; + opened_dialog_context& operator=(const opened_dialog_context&) = delete; + + opened_dialog_context() noexcept + { + g_fxo->need(); + } + + opened_dialog_context(utils::serial& ar) noexcept + : opened_dialog_context() + { + save(ar); + + if (is_opened) + { + g_fxo->get().wait_until = ar.pop(); + + Emu.PostponeInitCode([=]() + { + ensure(open_msg_dialog(false, type, msg_string, source, callback, userData, extParam, nullptr) == CELL_OK); + }); + } + } + + void save(utils::serial& ar) + { + ar(is_opened); + + if (!is_opened) + { + return; + } + + ar(type, msg_string, source, callback, userData, extParam, +g_fxo->get().wait_until); + } +}; + // forward declaration for open_msg_dialog error_code cellMsgDialogOpen2(u32 type, vm::cptr msgString, vm::ptr callback, vm::ptr userData, vm::ptr extParam); -// wrapper to call for other hle dialogs -error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr msgString, msg_dialog_source source, vm::ptr callback, vm::ptr userData, vm::ptr extParam, s32* return_code) +error_code open_msg_dialog(bool is_blocking, u32 type, std::string msg_string, msg_dialog_source source, vm::ptr callback, vm::ptr userData, vm::ptr extParam, s32* return_code) { - cellSysutil.notice("open_msg_dialog(is_blocking=%d, type=0x%x, msgString=%s, source=%s, callback=*0x%x, userData=*0x%x, extParam=*0x%x, return_code=*0x%x)", is_blocking, type, msgString, source, callback, userData, extParam, return_code); + cellSysutil.notice("open_msg_dialog(is_blocking=%d, type=0x%x, msg_string=%s, source=%s, callback=*0x%x, userData=*0x%x, extParam=*0x%x, return_code=*0x%x)", is_blocking, type, msg_string, source, callback, userData, extParam, return_code); const MsgDialogType _type{ type }; @@ -173,6 +224,9 @@ error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr msgString, *return_code = CELL_MSGDIALOG_BUTTON_NONE; } + const auto ppu = cpu_thread::get_current(); + const bool loaded_from_savestate = ppu && ppu->loaded_from_savestate; + if (auto manager = g_fxo->try_get()) { if (manager->get()) @@ -180,14 +234,29 @@ error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr msgString, return CELL_SYSUTIL_ERROR_BUSY; } - if (s32 ret = sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_BEGIN, 0); ret < 0) + if (s32 ret = loaded_from_savestate ? 0 : sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_BEGIN, 0); ret < 0) { return CellSysutilError{ret + 0u}; } const auto notify = std::make_shared>(0); - const auto res = manager->create()->show(is_blocking, msgString.get_ptr(), _type, source, [callback, userData, &return_code, is_blocking, notify](s32 status) + auto dlg = manager->create(); + + if (!is_blocking) + { + auto& ctxt = g_fxo->get(); + ctxt.is_opened = true; + + ctxt.type = type; + ctxt.msg_string = msg_string; + ctxt.source = source; + ctxt.callback = callback; + ctxt.userData = userData; + ctxt.extParam = extParam; + } + + const auto res = dlg->show(is_blocking, msg_string, _type, source, [callback, userData, &return_code, is_blocking, notify](s32 status) { if (is_blocking && return_code) { @@ -210,12 +279,28 @@ error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr msgString, *notify = 1; notify->notify_one(); } + + auto& ctxt = g_fxo->get(); + ctxt.is_opened = false; }); // Wait for on_close - while (is_blocking && !Emu.IsStopped() && !*notify) + while (!*notify) { - notify->wait(false, atomic_wait_timeout{1'000'000}); + if (ppu) + { + if (ppu->is_stopped()) + { + ppu->state += cpu_flag::again; + return {}; + } + } + else if (Emu.IsStopped()) + { + return {}; + } + + thread_ctrl::wait_on(*notify, 0); } return res; @@ -228,15 +313,30 @@ error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr msgString, return CELL_SYSUTIL_ERROR_BUSY; } - if (s32 ret = sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_BEGIN, 0); ret < 0) + if (s32 ret = loaded_from_savestate ? 0 : sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_BEGIN, 0); ret < 0) { return CellSysutilError{ret + 0u}; } + if (!is_blocking) + { + auto& ctxt = g_fxo->get(); + ctxt.is_opened = true; + + ctxt.type = type; + ctxt.msg_string = msg_string; + ctxt.source = source; + ctxt.callback = callback; + ctxt.userData = userData; + ctxt.extParam = extParam; + } + dlg->type = _type; dlg->source = source; - dlg->on_close = [callback, userData, is_blocking, &return_code, wptr = std::weak_ptr(dlg)](s32 status) + const auto notify = std::make_shared>(0); + + dlg->on_close = [notify, callback, userData, is_blocking, &return_code, wptr = std::weak_ptr(dlg)](s32 status) { if (is_blocking && return_code) { @@ -245,7 +345,9 @@ error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr msgString, const auto dlg = wptr.lock(); - if (dlg && dlg->state.compare_and_swap_test(MsgDialogState::Open, MsgDialogState::Close)) + const bool closed = dlg && dlg->state.compare_and_swap_test(MsgDialogState::Open, MsgDialogState::Close); + + if (closed) { sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_END, 0); @@ -263,53 +365,67 @@ error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr msgString, } input::SetIntercepted(false); + + auto& ctxt = g_fxo->get(); + ctxt.is_opened = false; + + if (closed) + { + *notify = 1; + notify->notify_one(); + } }; input::SetIntercepted(true); - auto& ppu = *get_current_cpu_thread(); - lv2_obj::sleep(ppu); - - // PS3 memory must not be accessed by Main thread - std::string msg_string = msgString.get_ptr(); + if (ppu) + { + lv2_obj::sleep(*ppu); + } // Run asynchronously in GUI thread - Emu.CallFromMainThread([&, msg_string = std::move(msg_string)]() + Emu.CallFromMainThread([&, is_blocking, notify, msg_string = std::move(msg_string)]() { dlg->Create(msg_string); - lv2_obj::awake(&ppu); + + if (!is_blocking) + { + *notify = 1; + notify->notify_one(); + } }); - while (auto state = ppu.state.fetch_sub(cpu_flag::signal)) + while (!*notify) { - if (is_stopped(state)) + if (ppu) + { + if (ppu->is_stopped()) + { + ppu->state += cpu_flag::again; + return {}; + } + } + else if (Emu.IsStopped()) { return {}; } - if (state & cpu_flag::signal) - { - break; - } - - ppu.state.wait(state); - } - - if (is_blocking) - { - while (auto dlg = g_fxo->get().get()) - { - if (Emu.IsStopped() || dlg->state != MsgDialogState::Open) - { - break; - } - std::this_thread::yield(); - } + thread_ctrl::wait_on(*notify, 0); } return CELL_OK; } +// wrapper to call for other hle dialogs +error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr msgString, msg_dialog_source source, vm::ptr callback, vm::ptr userData, vm::ptr extParam, s32* return_code) +{ + // PS3 memory must not be accessed by Main thread + std::string msg_string; + ensure(vm::read_string(msgString.addr(), CELL_MSGDIALOG_STRING_SIZE, msg_string, true), "Secret access violation"); + + return open_msg_dialog(is_blocking, type, std::move(msg_string), source, callback, userData, extParam, return_code); +} + void close_msg_dialog() { cellSysutil.notice("close_msg_dialog()"); @@ -376,7 +492,7 @@ error_code cellMsgDialogOpen2(u32 type, vm::cptr msgString, vm::ptr= CELL_MSGDIALOG_STRING_SIZE || type & -0x33f8) + if (!msgString || !std::memchr(msgString.get_ptr(), '\0', CELL_MSGDIALOG_STRING_SIZE - 1) || type & -0x33f8) { return CELL_MSGDIALOG_ERROR_PARAM; } diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 65ab0fe18d..e83f415c34 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -114,26 +114,34 @@ void ppu_module_manager::register_module(ppu_static_module* _module) ppu_module_manager::get().emplace(_module->name, _module); } -ppu_static_function& ppu_module_manager::access_static_function(const char* _module, u32 fnid) +ppu_static_function& ppu_module_manager::access_static_function(const char* _module, u32 fnid, bool for_creation) { auto& res = ::at32(ppu_module_manager::get(), _module)->functions[fnid]; - if (res.name) + if (for_creation && res.name) { fmt::throw_exception("PPU FNID duplication in module %s (%s, 0x%x)", _module, res.name, fnid); } + else if (!for_creation && !res.name) + { + fmt::throw_exception("PPU FNID unregistered in module %s (%s, 0x%x)", _module, res.name, fnid); + } return res; } -ppu_static_variable& ppu_module_manager::access_static_variable(const char* _module, u32 vnid) +ppu_static_variable& ppu_module_manager::access_static_variable(const char* _module, u32 vnid, bool for_creation) { auto& res = ::at32(ppu_module_manager::get(), _module)->variables[vnid]; - if (res.name) + if (for_creation && res.name) { fmt::throw_exception("PPU VNID duplication in module %s (%s, 0x%x)", _module, res.name, vnid); } + else if (!for_creation && !res.name) + { + fmt::throw_exception("PPU VNID unregistered in module %s (%s, 0x%x)", _module, res.name, vnid); + } return res; } @@ -153,6 +161,11 @@ void ppu_module_manager::initialize_modules() } } +u32 ppu_symbol_addr(std::string_view _module, std::string_view nid) noexcept +{ + return *ppu_module_manager::find_static_function(_module.data(), ppu_generate_id(nid)).export_addr; +} + // Global linkage information struct ppu_linkage_info { diff --git a/rpcs3/Emu/Cell/PPUModule.h b/rpcs3/Emu/Cell/PPUModule.h index fdcd736f38..99e2daedfb 100644 --- a/rpcs3/Emu/Cell/PPUModule.h +++ b/rpcs3/Emu/Cell/PPUModule.h @@ -106,9 +106,9 @@ class ppu_module_manager final static void register_module(ppu_static_module*); - static ppu_static_function& access_static_function(const char* _module, u32 fnid); + static ppu_static_function& access_static_function(const char* _module, u32 fnid, bool for_creation); - static ppu_static_variable& access_static_variable(const char* _module, u32 vnid); + static ppu_static_variable& access_static_variable(const char* _module, u32 vnid, bool for_creation); // Global variable for each registered function template @@ -125,7 +125,7 @@ public: template static auto& register_static_function(const char* _module, const char* name, ppu_intrp_func_t func, u32 fnid) { - auto& info = access_static_function(_module, fnid); + auto& info = access_static_function(_module, fnid, true); info.name = name; info.index = ppu_function_manager::register_function(func); @@ -142,6 +142,11 @@ public: return *registered::info; } + static auto& find_static_function(const char* _module, u32 fnid) + { + return access_static_function(_module, fnid, false); + } + template static auto& register_static_variable(const char* _module, const char* name, u32 vnid) { @@ -149,7 +154,7 @@ public: static_assert(std::is_same_v, "Static variable registration: vm::gvar expected"); - auto& info = access_static_variable(_module, vnid); + auto& info = access_static_variable(_module, vnid, true); info.name = name; info.var = &Var->raw(); @@ -313,6 +318,8 @@ inline RT ppu_execute(ppu_thread& ppu, Args... args) return func(ppu, args...); } +u32 ppu_symbol_addr(std::string_view _module, std::string_view name) noexcept; + #define BIND_FUNC_WITH_BLR(func, _module) BIND_FUNC(func, if (cpu_flag::again - ppu.state) ppu.cia = static_cast(ppu.lr) & ~3; else ppu.current_module = _module) #define REG_FNID(_module, nid, func) ppu_module_manager::register_static_function<&func>(#_module, ppu_select_name(#func, nid), BIND_FUNC_WITH_BLR(func, #_module), ppu_generate_id(nid)) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 784cf27829..b0284aa48c 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -2514,12 +2514,18 @@ ppu_thread::ppu_thread(utils::serial& ar) ar(lv2_obj::g_priority_order_tag); } + u32 hle_cia = umax; + if (version >= 3) { // Function and module for HLE function relocation - // TODO: Use it - ar.pop(); - ar.pop(); + ar(last_module_storage); + ar(last_function_storage); + + if (!last_module_storage.empty() && !last_function_storage.empty()) + { + hle_cia = ppu_symbol_addr(last_module_storage, last_function_storage); + } } serialize_common(ar); @@ -2674,6 +2680,12 @@ ppu_thread::ppu_thread(utils::serial& ar) ppu_tname = make_single(ar.pop()); + if (hle_cia != cia) + { + ppu_log.success("PPU HLE function has been relocated: OG-CIA: 0x%x, NEW-CIA=0x%x (function: %s)", cia, hle_cia, last_function_storage); + cia = hle_cia; + } + ppu_log.notice("Loading PPU Thread [0x%x: %s]: cia=0x%x, state=%s, status=%s", id, *ppu_tname.load(), cia, +state, ppu_thread_status{status}); } diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 322cc13ebe..311cedc0de 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -300,6 +300,8 @@ public: const char* current_function{}; // Current function name for diagnosis, optimized for speed. const char* last_function{}; // Sticky copy of current_function, is not cleared on function return const char* current_module{}; // Current module name, for savestates. + std::string last_module_storage{}; // Possible storage for current_module + std::string last_function_storage{}; // Possible storage for last_function const bool is_interrupt_thread; // True for interrupts-handler threads diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index 31b24eba5f..02ae2bf928 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -40,7 +40,7 @@ static std::array s_serial_versions; return ::s_serial_versions[identifier].current_version;\ } -SERIALIZATION_VER(global_version, 0, 19) // For stuff not listed here +SERIALIZATION_VER(global_version, 0, 20) // For stuff not listed here SERIALIZATION_VER(ppu, 1, 1, 2/*PPU sleep order*/, 3/*PPU FNID and module*/) SERIALIZATION_VER(spu, 2, 1) SERIALIZATION_VER(lv2_sync, 3, 1)