From 5ca19eabd548d517b8834e2aa3e08ab90e8d9ebf Mon Sep 17 00:00:00 2001 From: RipleyTom Date: Sat, 28 Feb 2026 18:55:31 +0100 Subject: [PATCH] NP code review sceNpTrophy fixes - sceNpTrophyGetTrophyUnlockState: signed 1 was potentially shifted by 31(UB) - sceNpTrophyUnlockTrophy: used a reader_lock and was missing check for read only context cellGame fixes - cellHddGameCheck: missing log parameters - cellGameDataCheckCreate2: missing log parameter - cellGameThemeInstall: condition inverted checking for extension + added tolower just in case - cellGameThemeInstallFromBuffer: OOB access, buf is always filled from the start and then used as a parameter to the CB sceNp fixes - sceNpManagerGetTicket: Made accurate from RE - sceNpDrmGetTimelimit: Fix msec calculation sceNp2 fixes -sceNpMatching2ContextStartAsync: avoid capturing ctx -sceNpMatching2ContextStop: Minor error value swap rpcn_client fixes -add_friend: missing log parameter -handle_friend_notification: misc validation issue np_handler fixes -ticket: Missing move in move constructor -ticket::parse: Misc validation fix -get_player_history_entry: potential UB fix np_requests fixes -Wrong CB event_type set for get_room_member_data_external_list! -reply_tus_get_data was not copying status data! -Order of error check in reply_get_room_member_data_external_list was wrong -Improved logging np_requests_gui: -Added missing guards for gui_notifications upnp_handler fixes -Highest density of bugs per line of code in the west, let's pretend I never wrote this signaling_handler fixes -Swapped to multimap to avoid collisions on timestamps Misc fixes --- Utilities/bin_patch.cpp | 2 +- rpcs3/Emu/Cell/Modules/cellGame.cpp | 12 ++--- rpcs3/Emu/Cell/Modules/sceNp.cpp | 15 +++--- rpcs3/Emu/Cell/Modules/sceNp2.cpp | 4 +- rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp | 8 ++-- rpcs3/Emu/NP/np_handler.cpp | 15 +++--- rpcs3/Emu/NP/np_helpers.cpp | 4 +- rpcs3/Emu/NP/np_helpers.h | 2 +- rpcs3/Emu/NP/np_notifications.cpp | 3 ++ rpcs3/Emu/NP/np_requests.cpp | 64 ++++++++++++++------------ rpcs3/Emu/NP/np_requests_gui.cpp | 27 ++++++++--- rpcs3/Emu/NP/rpcn_client.cpp | 4 +- rpcs3/Emu/NP/signaling_handler.cpp | 1 + rpcs3/Emu/NP/signaling_handler.h | 2 +- rpcs3/Emu/NP/upnp_handler.cpp | 35 +++++++------- rpcs3/Emu/NP/upnp_handler.h | 2 +- 16 files changed, 111 insertions(+), 89 deletions(-) diff --git a/Utilities/bin_patch.cpp b/Utilities/bin_patch.cpp index 9449d808c0..fd94b830e0 100644 --- a/Utilities/bin_patch.cpp +++ b/Utilities/bin_patch.cpp @@ -329,7 +329,7 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st is_valid = false; continue; } - else if (serial.size() != 9 || !std::all_of(serial.begin(), serial.end(), [](char c) { return std::isalnum(c); })) + else if (serial.size() != 9 || !std::all_of(serial.begin(), serial.end(), [](char c) { return std::isalnum(static_cast(c)); })) { append_log_message(log_messages, fmt::format("Error: Serial '%s' invalid (patch: %s, key: %s, location: %s, file: %s)", serial, description, main_key, get_yaml_node_location(serial_node), path), &patch_log.error); is_valid = false; diff --git a/rpcs3/Emu/Cell/Modules/cellGame.cpp b/rpcs3/Emu/Cell/Modules/cellGame.cpp index 371aa2a7b3..51e5ed6a33 100644 --- a/rpcs3/Emu/Cell/Modules/cellGame.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGame.cpp @@ -492,8 +492,8 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName strcpy_trunc(get->getParam.titleLang[i], psf::get_string(psf, fmt::format("TITLE_%02d", i))); } - cellGame.warning("cellHddGameCheck(): Data exists:\nATTRIBUTE: 0x%x, RESOLUTION: 0x%x, RESOLUTION: 0x%x, SOUND_FORMAT: 0x%x, dataVersion: %s" - , get->getParam.attribute, get->getParam.resolution, get->getParam.soundFormat, get->getParam.soundFormat, std::span(reinterpret_cast(get->getParam.dataVersion), 6)); + cellGame.warning("cellHddGameCheck(): Data exists:\nATTRIBUTE: 0x%x, RESOLUTION: 0x%x, SOUND_FORMAT: 0x%x, dataVersion: %s" + , get->getParam.attribute, get->getParam.resolution, get->getParam.soundFormat, std::span(reinterpret_cast(get->getParam.dataVersion), 6)); } // TODO ? @@ -580,7 +580,7 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName break; default: - cellGame.error("cellHddGameCheck(): callback returned unknown error (code=0x%x). Error message: %s", result->invalidMsg); + cellGame.error("cellHddGameCheck(): callback returned unknown error (code=0x%x). Error message: %s", result->result, result->invalidMsg); error_msg = get_localized_string(localized_string_id::CELL_HDD_GAME_CHECK_INVALID, "%s", result->invalidMsg); break; } @@ -1199,7 +1199,7 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr break; default: - cellGame.error("cellGameDataCheckCreate2(): callback returned unknown error (code=0x%x). Error message: %s", cbResult->invalidMsg); + cellGame.error("cellGameDataCheckCreate2(): callback returned unknown error (code=0x%x). Error message: %s", cbResult->result, cbResult->invalidMsg); error_msg = get_localized_string(localized_string_id::CELL_GAMEDATA_CHECK_INVALID, "%s", cbResult->invalidMsg); break; } @@ -1747,7 +1747,7 @@ error_code cellGameThemeInstall(vm::cptr usrdirPath, vm::cptr fileNa { u32 magic{}; - if (src_path.ends_with(".p3t") || !theme.read(magic) || magic != "P3TF"_u32) + if (!fmt::to_lower(src_path).ends_with(".p3t") || !theme.read(magic) || magic != "P3TF"_u32) { return CELL_GAME_ERROR_INVALID_THEME_FILE; } @@ -1819,7 +1819,7 @@ error_code cellGameThemeInstallFromBuffer(ppu_thread& ppu, u32 fileSize, u32 buf const u32 read_size = std::min(bufSize, fileSize - file_offset); cellGame.notice("cellGameThemeInstallFromBuffer: writing %d bytes at pos %d", read_size, file_offset); - if (theme.write(reinterpret_cast(buf.get_ptr()) + file_offset, read_size) != read_size) + if (theme.write(reinterpret_cast(buf.get_ptr()), read_size) != read_size) { cellGame.error("cellGameThemeInstallFromBuffer: failed to write to destination file '%s' (error=%s)", dst_path, fs::g_tls_error); diff --git a/rpcs3/Emu/Cell/Modules/sceNp.cpp b/rpcs3/Emu/Cell/Modules/sceNp.cpp index 3eee01cf9a..e82491eac1 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp.cpp @@ -871,7 +871,7 @@ error_code sceNpDrmGetTimelimit(vm::cptr path, vm::ptr time_remain) } // Convert time to milliseconds - s64 msec = *sec * 1000ll + *nsec / 1000ll; + s64 msec = *sec * 1000ll + *nsec / 1'000'000ll; // Return the remaining time in microseconds if (npd.activate_time != 0 && msec < npd.activate_time) @@ -4242,19 +4242,16 @@ error_code sceNpManagerGetTicket(vm::ptr buffer, vm::ptr bufferSize) } const auto& ticket = nph.get_ticket(); - *bufferSize = static_cast(ticket.size()); if (!buffer) { + *bufferSize = static_cast(ticket.size()); return CELL_OK; } - if (*bufferSize < ticket.size()) - { - return SCE_NP_ERROR_INVALID_ARGUMENT; - } - - memcpy(buffer.get_ptr(), ticket.data(), ticket.size()); + const u32 size_read = std::min(::size32(ticket), static_cast(*bufferSize)); + std::memcpy(buffer.get_ptr(), ticket.data(), size_read); + *bufferSize = size_read; return CELL_OK; } @@ -5676,7 +5673,7 @@ error_code scenp_score_record_score(s32 transId, SceNpScoreBoardId boardId, SceN else { data = &gameInfo->nativeData[0]; - data_size = 64; + data_size = sizeof(gameInfo->nativeData); } nph.record_score(trans_ctx, boardId, score, scoreComment, data, data_size, tmpRank, async); diff --git a/rpcs3/Emu/Cell/Modules/sceNp2.cpp b/rpcs3/Emu/Cell/Modules/sceNp2.cpp index c9816b60f5..7809676078 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp2.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp2.cpp @@ -1135,7 +1135,7 @@ error_code sceNpMatching2ContextStartAsync(SceNpMatching2ContextId ctxId, u32 ti { sysutil_register_cb([=, context_callback = ctx->context_callback, context_callback_param = ctx->context_callback_param](ppu_thread& cb_ppu) -> s32 { - context_callback(cb_ppu, ctxId, SCE_NP_MATCHING2_CONTEXT_EVENT_Start, SCE_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ACTION, 0, ctx->context_callback_param); + context_callback(cb_ppu, ctxId, SCE_NP_MATCHING2_CONTEXT_EVENT_Start, SCE_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ACTION, 0, context_callback_param); return 0; }); } @@ -1760,7 +1760,7 @@ error_code sceNpMatching2ContextStop(SceNpMatching2ContextId ctxId) const auto ctx = get_match2_context(ctxId); if (!ctx) - return SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID; + return SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND; if (!ctx->started.compare_and_swap_test(1, 0)) return SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_STARTED; diff --git a/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp b/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp index 803d174549..866db860ec 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp @@ -1026,14 +1026,14 @@ error_code sceNpTrophyUnlockTrophy(ppu_thread& ppu, u32 context, u32 handle, s32 auto& trophy_manager = g_fxo->get(); - reader_lock lock(trophy_manager.mtx); + std::scoped_lock lock(trophy_manager.mtx); if (!trophy_manager.is_initialized) { return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED; } - const auto [ctxt, error] = trophy_manager.get_context_ex(context, handle); + const auto [ctxt, error] = trophy_manager.get_context_ex(context, handle, true); if (error) { @@ -1184,9 +1184,9 @@ error_code sceNpTrophyGetTrophyUnlockState(u32 context, u32 handle, vm::ptrGetTrophyUnlockState(id)) - flags->flag_bits[id / 32] |= 1 << (id % 32); + flags->flag_bits[id / 32] |= 1u << (id % 32); else - flags->flag_bits[id / 32] &= ~(1 << (id % 32)); + flags->flag_bits[id / 32] &= ~(1u << (id % 32)); } return CELL_OK; diff --git a/rpcs3/Emu/NP/np_handler.cpp b/rpcs3/Emu/NP/np_handler.cpp index f0cdc55ee2..f1fda0752c 100644 --- a/rpcs3/Emu/NP/np_handler.cpp +++ b/rpcs3/Emu/NP/np_handler.cpp @@ -111,7 +111,7 @@ namespace np } ticket::ticket(std::vector&& raw_data) - : raw_data(raw_data) + : raw_data(std::move(raw_data)) { parse(); } @@ -387,7 +387,7 @@ namespace np return; } - if (nodes[0].id != 0x3000 && nodes[1].id != 0x3002) + if (nodes[0].id != 0x3000 || nodes[1].id != 0x3002) { ticket_log.error("The 2 blobs ids are incorrect"); return; @@ -1467,14 +1467,13 @@ namespace np if (all_history) { + if (index >= players_history.size()) + return false; + auto it = players_history.begin(); std::advance(it, index); - - if (it != players_history.end()) - { - string_to_npid(it->first, *npid); - return true; - } + string_to_npid(it->first, *npid); + return true; } else { diff --git a/rpcs3/Emu/NP/np_helpers.cpp b/rpcs3/Emu/NP/np_helpers.cpp index 39c59afdb0..9d651693d9 100644 --- a/rpcs3/Emu/NP/np_helpers.cpp +++ b/rpcs3/Emu/NP/np_helpers.cpp @@ -20,7 +20,7 @@ namespace np return std::string(ip_str); } - std::string ether_to_string(std::array& ether) + std::string ether_to_string(const std::array& ether) { return fmt::format("%02X:%02X:%02X:%02X:%02X:%02X", ether[0], ether[1], ether[2], ether[3], ether[4], ether[5]); } @@ -110,7 +110,7 @@ namespace np bool is_valid_npid(const SceNpId& npid) { - if (!std::all_of(npid.handle.data, npid.handle.data + 16, [](char c) { return std::isalnum(c) || c == '-' || c == '_' || c == 0; } ) + if (!std::all_of(npid.handle.data, npid.handle.data + 16, [](char c) { return std::isalnum(static_cast(c)) || c == '-' || c == '_' || c == 0; } ) || npid.handle.data[16] != 0 || !std::all_of(npid.handle.dummy, npid.handle.dummy + 3, [](char val) { return val == 0; }) ) { diff --git a/rpcs3/Emu/NP/np_helpers.h b/rpcs3/Emu/NP/np_helpers.h index a51499ca64..d9e2a9d076 100644 --- a/rpcs3/Emu/NP/np_helpers.h +++ b/rpcs3/Emu/NP/np_helpers.h @@ -7,7 +7,7 @@ namespace np { std::string ip_to_string(u32 addr); - std::string ether_to_string(std::array& ether); + std::string ether_to_string(const std::array& ether); bool validate_communication_id(const SceNpCommunicationId& com_id); std::string communication_id_to_string(const SceNpCommunicationId& communicationId); std::optional string_to_communication_id(std::string_view str); diff --git a/rpcs3/Emu/NP/np_notifications.cpp b/rpcs3/Emu/NP/np_notifications.cpp index 6d6d511ee0..9b64882f0f 100644 --- a/rpcs3/Emu/NP/np_notifications.cpp +++ b/rpcs3/Emu/NP/np_notifications.cpp @@ -381,7 +381,10 @@ namespace np auto ctx = get_matching_context(ctx_id); if (!ctx) + { + np_memory.free(edata.addr()); return; + } gui_cache.add_room(room_info->room_status.id); diff --git a/rpcs3/Emu/NP/np_requests.cpp b/rpcs3/Emu/NP/np_requests.cpp index cd18892277..d9dd6dc337 100644 --- a/rpcs3/Emu/NP/np_requests.cpp +++ b/rpcs3/Emu/NP/np_requests.cpp @@ -191,7 +191,7 @@ namespace np case rpcn::ErrorType::RoomGroupMaxSlotMismatch: error_code = SCE_NP_MATCHING2_SERVER_ERROR_MAX_OVER_SLOT_GROUP; break; case rpcn::ErrorType::RoomPasswordMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_PASSWORD; break; case rpcn::ErrorType::RoomGroupNoJoinLabel: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_JOIN_GROUP_LABEL; break; - default: fmt::throw_exception("Unexpected error in reply to CreateRoom: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in reply to CreateRoom: %s", error); } if (error_code != CELL_OK) @@ -262,7 +262,7 @@ namespace np case rpcn::ErrorType::RoomPasswordMismatch: error_code = SCE_NP_MATCHING2_SERVER_ERROR_PASSWORD_MISMATCH; break; case rpcn::ErrorType::RoomGroupFull: error_code = SCE_NP_MATCHING2_SERVER_ERROR_GROUP_FULL; break; case rpcn::ErrorType::RoomGroupJoinLabelNotFound: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_GROUP; break; - default: fmt::throw_exception("Unexpected error in reply to JoinRoom: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in reply to JoinRoom: %s", error); } if (error_code != 0) @@ -348,7 +348,7 @@ namespace np case rpcn::ErrorType::NoError: break; case rpcn::ErrorType::NotFound: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; // Unsure if this should return another error(missing user in room has no appropriate error code) case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; - default: fmt::throw_exception("Unexpected error in reply to LeaveRoom: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in reply to LeaveRoom: %s", error); } if (error_code != CELL_OK) @@ -447,7 +447,7 @@ namespace np u32 np_handler::get_room_member_data_external_list(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2GetRoomMemberDataExternalListRequest* req) { - const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomDataExternalList, true); + const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomMemberDataExternalList, true); if (!get_rpcn()->get_room_member_data_external_list(req_id, get_match2_context(ctx_id)->communicationId, req->roomId)) { @@ -465,13 +465,18 @@ namespace np if (!cb_info_opt) return; - ensure(error == rpcn::ErrorType::NoError, "Unexpected error in GetRoomMemberDataExternalList reply"); - - if (error == rpcn::ErrorType::RoomMissing) + switch (error) + { + case rpcn::ErrorType::NoError: + break; + case rpcn::ErrorType::RoomMissing: { cb_info_opt->queue_callback(req_id, 0, SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM, 0); return; } + default: + fmt::throw_exception("Unexpected error in GetRoomMemberDataExternalList reply: %s", error); + } const auto resp = reply.get_protobuf(); ensure(!reply.is_error(), "Malformed reply to GetRoomMemberDataExternalList command"); @@ -518,7 +523,7 @@ namespace np case rpcn::ErrorType::NoError: break; case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; case rpcn::ErrorType::Unauthorized: error_code = SCE_NP_MATCHING2_SERVER_ERROR_FORBIDDEN; break; - default: fmt::throw_exception("Unexpected error in reply to SetRoomDataExternal: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in reply to SetRoomDataExternal: %s", error); } cb_info_opt->queue_callback(req_id, 0, error_code, 0); @@ -550,7 +555,7 @@ namespace np { case rpcn::ErrorType::NoError: break; case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; - default: fmt::throw_exception("Unexpected error in reply to GetRoomDataInternal: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in reply to GetRoomDataInternal: %s", error); } if (error_code != CELL_OK) @@ -606,7 +611,7 @@ namespace np { case rpcn::ErrorType::NoError: break; case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; - default: fmt::throw_exception("Unexpected error in reply to GetRoomDataInternal: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in reply to SetRoomDataInternal: %s", error); } cb_info_opt->queue_callback(req_id, 0, error_code, 0); @@ -640,7 +645,7 @@ namespace np case rpcn::ErrorType::NoError: break; case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; case rpcn::ErrorType::NotFound: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_USER; break; - default: fmt::throw_exception("Unexpected error in reply to GetRoomMemberDataInternal: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in reply to GetRoomMemberDataInternal: %s", error); } if (error_code != CELL_OK) @@ -694,7 +699,7 @@ namespace np case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; case rpcn::ErrorType::NotFound: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_USER; break; case rpcn::ErrorType::Unauthorized: error_code = SCE_NP_MATCHING2_SERVER_ERROR_FORBIDDEN; break; - default: fmt::throw_exception("Unexpected error in reply to SetRoomMemberDataInternal: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in reply to SetRoomMemberDataInternal: %s", error); } cb_info_opt->queue_callback(req_id, 0, error_code, 0); @@ -723,7 +728,7 @@ namespace np switch (error) { case rpcn::ErrorType::NoError: break; - default: fmt::throw_exception("Unexpected error in reply to SetUserInfo: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in reply to SetUserInfo: %s", error); } cb_info_opt->queue_callback(req_id, 0, 0, 0); @@ -755,7 +760,7 @@ namespace np { case rpcn::ErrorType::NoError: break; case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; - default: fmt::throw_exception("Unexpected error in reply to PingRoomOwner: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in reply to PingRoomOwner: %s", error); } if (error_code != CELL_OK) @@ -803,7 +808,7 @@ namespace np case rpcn::ErrorType::NoError: break; case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; case rpcn::ErrorType::Unauthorized: error_code = SCE_NP_MATCHING2_SERVER_ERROR_FORBIDDEN; break; - default: fmt::throw_exception("Unexpected error in reply to SendRoomMessage: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in reply to SendRoomMessage: %s", error); } cb_info_opt->queue_callback(req_id, 0, error_code, 0); @@ -841,7 +846,7 @@ namespace np rpcn_log.error("Signaling information was requested for a user that doesn't exist or is not online"); return; } - default: fmt::throw_exception("Unexpected error in reply to RequestSignalingInfos: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in reply to RequestSignalingInfos: %s", error); } const auto resp = reply.get_protobuf(); @@ -861,9 +866,7 @@ namespace np const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetLobbyInfoList, false); auto cb_info_opt = take_pending_request(req_id); - - if (!cb_info_opt) - return true; + ensure (cb_info_opt); const u32 event_key = get_event_key(); @@ -1017,7 +1020,7 @@ namespace np { case rpcn::ErrorType::NoError: break; case rpcn::ErrorType::NotFound: error_code = SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_BOARD_MASTER_NOT_FOUND; break; - default: fmt::throw_exception("Unexpected error in reply to GetBoardInfos: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in reply to GetBoardInfos: %s", error); } if (error_code != CELL_OK) @@ -1091,7 +1094,7 @@ namespace np score_trans->wake_cond.notify_one(); return; } - default: fmt::throw_exception("Unexpected error in reply_record_score: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in reply_record_score: %s", error); } auto tmp_rank = reply.get(); @@ -1154,7 +1157,7 @@ namespace np case rpcn::ErrorType::NotFound: trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_STORE_NOT_FOUND); break; case rpcn::ErrorType::ScoreInvalid: trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_INVALID_SCORE); break; case rpcn::ErrorType::ScoreHasData: trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_GAME_DATA_ALREADY_EXISTS); break; - default: fmt::throw_exception("Unexpected error in reply to RecordScoreData: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in reply to RecordScoreData: %s", error); } } @@ -1207,7 +1210,7 @@ namespace np { case rpcn::ErrorType::NoError: break; case rpcn::ErrorType::NotFound: score_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_GAME_DATA_MASTER_NOT_FOUND); return; - default: fmt::throw_exception("Unexpected error in reply to GetScoreData: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in reply to GetScoreData: %s", error); } auto* tdata = std::get_if(&score_trans->tdata); @@ -1287,7 +1290,7 @@ namespace np switch (error) { case rpcn::ErrorType::NoError: break; - default: fmt::throw_exception("Unexpected error in GetScoreResponse: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in GetScoreResponse: %s", error); } const auto resp = reply.get_protobuf(); @@ -1476,7 +1479,7 @@ namespace np case rpcn::ErrorType::NotFound: trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_USER_NOT_ASSIGNED); break; case rpcn::ErrorType::Unauthorized: trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_FORBIDDEN); break; case rpcn::ErrorType::CondFail: trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_CONDITIONS_NOT_SATISFIED); break; - default: fmt::throw_exception("Unexpected error in handle_tus_no_data: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in handle_tus_no_data: %s", error); } } @@ -1499,7 +1502,7 @@ namespace np case rpcn::ErrorType::NotFound: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_USER_NOT_ASSIGNED); case rpcn::ErrorType::Unauthorized: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_FORBIDDEN); case rpcn::ErrorType::CondFail: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_CONDITIONS_NOT_SATISFIED); - default: fmt::throw_exception("Unexpected error in handle_TusVarResponse: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in handle_TusVarResponse: %s", error); } const auto resp = reply.get_protobuf(); @@ -1555,7 +1558,7 @@ namespace np case rpcn::ErrorType::NotFound: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_USER_NOT_ASSIGNED); case rpcn::ErrorType::Unauthorized: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_FORBIDDEN); case rpcn::ErrorType::CondFail: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_CONDITIONS_NOT_SATISFIED); - default: fmt::throw_exception("Unexpected error in handle_TusVariable: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in handle_TusVariable: %s", error); } auto pb_var = reply.get_protobuf(); @@ -1603,7 +1606,7 @@ namespace np case rpcn::ErrorType::NotFound: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_USER_NOT_ASSIGNED); case rpcn::ErrorType::Unauthorized: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_FORBIDDEN); case rpcn::ErrorType::CondFail: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_CONDITIONS_NOT_SATISFIED); - default: fmt::throw_exception("Unexpected error in handle_TusDataStatusResponse: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in handle_TusDataStatusResponse: %s", error); } const auto resp = reply.get_protobuf(); @@ -1803,7 +1806,7 @@ namespace np if (!tdata) { trans_ctx->tdata = tdata_tus_get_data{.recvSize = recvSize, .dataStatus = dataStatus, .data = data}; - const u32 req_id = get_req_id(REQUEST_ID_HIGH::SCORE); + const u32 req_id = get_req_id(REQUEST_ID_HIGH::TUS); get_rpcn()->tus_get_data(req_id, trans_ctx->communicationId, targetNpId, slotId, vuser); transaction_async_handler(std::move(lock), trans_ctx, req_id, async); return; @@ -1844,7 +1847,7 @@ namespace np case rpcn::ErrorType::NotFound: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_USER_NOT_ASSIGNED); case rpcn::ErrorType::Unauthorized: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_FORBIDDEN); case rpcn::ErrorType::CondFail: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_CONDITIONS_NOT_SATISFIED); - default: fmt::throw_exception("Unexpected error in reply to TusGetData: %d", static_cast(error)); + default: fmt::throw_exception("Unexpected error in reply to TusGetData: %s", error); } auto pb_data = reply.get_protobuf(); @@ -1870,6 +1873,7 @@ namespace np data_status->data = tdata->data; data_status->dataSize = ::narrow(pb_data->data().size()); data_status->info.infoSize = ::narrow(pb_status.info().size()); + memcpy(data_status->info.data, pb_data->status().info().data(), std::min(pb_data->status().info().size(), sizeof(data_status->info.data))); const u32 to_copy = std::min(data_status->dataSize, tdata->recvSize); memcpy(data, pb_data->data().data(), to_copy); diff --git a/rpcs3/Emu/NP/np_requests_gui.cpp b/rpcs3/Emu/NP/np_requests_gui.cpp index be6026afa6..5e38a57e77 100644 --- a/rpcs3/Emu/NP/np_requests_gui.cpp +++ b/rpcs3/Emu/NP/np_requests_gui.cpp @@ -303,7 +303,11 @@ namespace np gui_cache.del_room(room_status->id); - gui_notifications.list.emplace(std::make_pair(gui_notifications.current_gui_ctx_id, req_id), gui_notification{.event = SCE_NP_MATCHING_EVENT_LEAVE_ROOM_DONE, .edata = std::move(edata)}); + { + std::lock_guard lock(gui_notifications.mutex); + gui_notifications.list.emplace(std::make_pair(gui_notifications.current_gui_ctx_id, req_id), gui_notification{.event = SCE_NP_MATCHING_EVENT_LEAVE_ROOM_DONE, .edata = std::move(edata)}); + } + ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_LEAVE_ROOM_DONE, 0); } @@ -453,7 +457,11 @@ namespace np extra_nps::print_SceNpMatchingRoom(room_info); - gui_notifications.list.emplace(std::make_pair(gui_notifications.current_gui_ctx_id, req_id), gui_notification{.event = SCE_NP_MATCHING_EVENT_GET_ROOM_SEARCH_FLAG_DONE, .edata = std::move(edata)}); + { + std::lock_guard lock(gui_notifications.mutex); + gui_notifications.list.emplace(std::make_pair(gui_notifications.current_gui_ctx_id, req_id), gui_notification{.event = SCE_NP_MATCHING_EVENT_GET_ROOM_SEARCH_FLAG_DONE, .edata = std::move(edata)}); + } + ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_SEARCH_FLAG_DONE, 0); } @@ -548,7 +556,11 @@ namespace np extra_nps::print_SceNpMatchingRoom(room_info); - gui_notifications.list.emplace(std::make_pair(gui_notifications.current_gui_ctx_id, req_id), gui_notification{.event = SCE_NP_MATCHING_EVENT_GET_ROOM_INFO_DONE, .edata = std::move(edata)}); + { + std::lock_guard lock(gui_notifications.mutex); + gui_notifications.list.emplace(std::make_pair(gui_notifications.current_gui_ctx_id, req_id), gui_notification{.event = SCE_NP_MATCHING_EVENT_GET_ROOM_INFO_DONE, .edata = std::move(edata)}); + } + ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_INFO_DONE, 0); } @@ -581,9 +593,13 @@ namespace np SceNpRoomId room_id{}; ensure(!resp->id().empty() && resp->id().size() == sizeof(SceNpRoomId::opt)); + ctx->wakey = 0; std::memcpy(room_id.opt, resp->id().data(), sizeof(SceNpRoomId::opt)); - const auto [_, inserted] = pending_quickmatching.insert_or_assign(room_id, ctx->ctx_id); - ensure(inserted); + { + std::lock_guard lock(this->mutex_quickmatching); + const auto [_, inserted] = pending_quickmatching.insert_or_assign(room_id, ctx->ctx_id); + ensure(inserted); + } // Now that the reply has been received, we start the wait for the notification ctx->thread = std::make_unique>>("NP GUI Timeout Worker", [ctx, req_id, this](SceNpRoomId room_id) @@ -615,7 +631,6 @@ namespace np } }); - ctx->wakey = 0; auto& thread = *ctx->thread; thread(room_id); } diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp index 86a688705e..56cfabd855 100644 --- a/rpcs3/Emu/NP/rpcn_client.cpp +++ b/rpcs3/Emu/NP/rpcn_client.cpp @@ -1489,7 +1489,7 @@ namespace rpcn if (error == ErrorType::NoError) rpcn_log.success("add_friend(\"%s\") succeeded", friend_username); else - rpcn_log.error("add_friend(\"%s\") failed with error: %s", error); + rpcn_log.error("add_friend(\"%s\") failed with error: %s", friend_username, error); return error; } @@ -3085,7 +3085,7 @@ namespace rpcn } case NotificationType::FriendPresenceChanged: { - const std::string username = vdata.get_string(true); + const std::string username = vdata.get_string(false); SceNpCommunicationId pr_com_id = vdata.get_com_id(); std::string pr_title = fmt::truncate(vdata.get_string(true), SCE_NP_BASIC_PRESENCE_TITLE_SIZE_MAX - 1); std::string pr_status = fmt::truncate(vdata.get_string(true), SCE_NP_BASIC_PRESENCE_EXTENDED_STATUS_SIZE_MAX - 1); diff --git a/rpcs3/Emu/NP/signaling_handler.cpp b/rpcs3/Emu/NP/signaling_handler.cpp index 97a2673e0f..85c9321e28 100644 --- a/rpcs3/Emu/NP/signaling_handler.cpp +++ b/rpcs3/Emu/NP/signaling_handler.cpp @@ -774,6 +774,7 @@ void signaling_handler::send_information_packets(u32 addr, u16 port, const SceNp auto& sent_packet = sig_packet; sent_packet.command = signal_info; + retire_packet(si, signal_info); send_signaling_packet(sent_packet, addr, port); queue_signaling_packet(sent_packet, si, steady_clock::now() + REPEAT_INFO_DELAY); wake_up(); diff --git a/rpcs3/Emu/NP/signaling_handler.h b/rpcs3/Emu/NP/signaling_handler.h index b4ec8229b8..a3eb027fe1 100644 --- a/rpcs3/Emu/NP/signaling_handler.h +++ b/rpcs3/Emu/NP/signaling_handler.h @@ -133,7 +133,7 @@ private: signaling_packet sig_packet{}; - std::map qpackets; // (wakeup time, packet) + std::multimap qpackets; // (wakeup time, packet) u32 cur_conn_id = 1; std::unordered_map npid_to_conn_id; // (npid, conn_id) diff --git a/rpcs3/Emu/NP/upnp_handler.cpp b/rpcs3/Emu/NP/upnp_handler.cpp index dca00de986..c19e30d441 100644 --- a/rpcs3/Emu/NP/upnp_handler.cpp +++ b/rpcs3/Emu/NP/upnp_handler.cpp @@ -89,12 +89,10 @@ void upnp_handler::upnp_enable() if (desc_xml) { - IGDdatas igd_data{}; - UPNPUrls igd_urls{}; - parserootdesc(desc_xml, desc_xml_size, &igd_data); + parserootdesc(desc_xml, desc_xml_size, &m_igd_data); free(desc_xml); desc_xml = nullptr; - GetUPNPUrls(&igd_urls, &igd_data, dev->descURL, 1); + GetUPNPUrls(&m_igd_urls, &m_igd_data, dev->descURL, 1); upnp_log.notice("Found UPnP device type:%s at %s", dev->st, dev->descURL); @@ -116,24 +114,28 @@ void upnp_handler::upnp_enable() freeUPNPDevlist(devlist); } -void upnp_handler::add_port_redir(std::string_view addr, u16 internal_port, std::string_view protocol) +void upnp_handler::add_port_redir(const std::string& addr, u16 internal_port, std::string_view protocol) { if (!m_active) return; std::lock_guard lock(m_mutex); - u16 external_port = internal_port; - std::string internal_port_str = fmt::format("%d", internal_port); + if (m_bindings[std::string(protocol)].contains(internal_port)) + return; + + const std::string internal_port_str = fmt::format("%d", internal_port); + const std::string protocol_str(protocol); + const u32 max_port = std::min(static_cast(internal_port) + 100, 0xFFFFu); int res = 0; - for (u16 external_port = internal_port; external_port < internal_port + 100; external_port++) + for (u32 external_port = internal_port; external_port <= max_port; external_port++) { std::string external_port_str = fmt::format("%d", external_port); - res = UPNP_AddPortMapping(m_igd_urls.controlURL, m_igd_data.first.servicetype, external_port_str.c_str(), internal_port_str.c_str(), addr.data(), "RPCS3", protocol.data(), nullptr, nullptr); + res = UPNP_AddPortMapping(m_igd_urls.controlURL, m_igd_data.first.servicetype, external_port_str.c_str(), internal_port_str.c_str(), addr.c_str(), "RPCS3", protocol_str.c_str(), nullptr, nullptr); if (res == UPNPCOMMAND_SUCCESS) { - m_bindings[std::string(protocol)][internal_port] = external_port; + m_bindings[protocol_str][static_cast(internal_port)] = external_port; upnp_log.notice("Successfully bound %s:%d(%s) to IGD:%d", addr, internal_port, protocol, external_port); return; } @@ -146,7 +148,7 @@ void upnp_handler::add_port_redir(std::string_view addr, u16 internal_port, std: // } } - upnp_log.error("Failed to bind %s:%d(%s) to IGD:(%d=>%d): %d", addr, internal_port, protocol, internal_port, external_port, res); + upnp_log.error("Failed to bind %s:%d(%s) to IGD:(%d=>%d): %d", addr, internal_port, protocol, internal_port, internal_port, res); } void upnp_handler::remove_port_redir(u16 internal_port, std::string_view protocol) @@ -156,27 +158,28 @@ void upnp_handler::remove_port_redir(u16 internal_port, std::string_view protoco std::lock_guard lock(m_mutex); - const std::string str_protocol(protocol); + const std::string protocol_str(protocol); - if (!m_bindings.contains(str_protocol) || !::at32(m_bindings, str_protocol).contains(internal_port)) + if (!m_bindings.contains(protocol_str) || !::at32(m_bindings, protocol_str).contains(internal_port)) { upnp_log.error("tried to unbind port mapping %d to IGD(%s) but it isn't bound", internal_port, protocol); return; } - const u16 external_port = ::at32(::at32(m_bindings, str_protocol), internal_port); + const u16 external_port = ::at32(::at32(m_bindings, protocol_str), internal_port); remove_port_redir_external(external_port, protocol); - ensure(::at32(m_bindings, str_protocol).erase(internal_port)); + ensure(::at32(m_bindings, protocol_str).erase(internal_port)); upnp_log.notice("Successfully deleted port mapping %d to IGD:%d(%s)", internal_port, external_port, protocol); } void upnp_handler::remove_port_redir_external(u16 external_port, std::string_view protocol, bool verbose) { const std::string str_ext_port = fmt::format("%d", external_port); + const std::string protocol_str(protocol); - if (int res = UPNP_DeletePortMapping(m_igd_urls.controlURL, m_igd_data.first.servicetype, str_ext_port.c_str(), protocol.data(), nullptr); res != 0 && verbose) + if (int res = UPNP_DeletePortMapping(m_igd_urls.controlURL, m_igd_data.first.servicetype, str_ext_port.c_str(), protocol_str.c_str(), nullptr); res != 0 && verbose) upnp_log.error("Failed to delete port mapping IGD:%s(%s): %d", str_ext_port, protocol, res); } diff --git a/rpcs3/Emu/NP/upnp_handler.h b/rpcs3/Emu/NP/upnp_handler.h index 40892f7443..1486702cbb 100644 --- a/rpcs3/Emu/NP/upnp_handler.h +++ b/rpcs3/Emu/NP/upnp_handler.h @@ -13,7 +13,7 @@ public: ~upnp_handler(); void upnp_enable(); - void add_port_redir(std::string_view addr, u16 internal_port, std::string_view protocol); + void add_port_redir(const std::string& addr, u16 internal_port, std::string_view protocol); void remove_port_redir(u16 internal_port, std::string_view protocol); bool is_active() const;