From eea73deab37bcff72b42041d67aefc3df29eaea9 Mon Sep 17 00:00:00 2001 From: RipleyTom Date: Wed, 18 May 2022 17:44:21 +0200 Subject: [PATCH] RPCN v0.6 --- 3rdparty/flatbuffers | 2 +- 3rdparty/wolfssl/CMakeLists.txt | 3 +- rpcs3/Emu/Cell/Modules/cellNetCtl.cpp | 2 +- rpcs3/Emu/Cell/Modules/sceNp.cpp | 744 +++++++++++----- rpcs3/Emu/Cell/Modules/sceNp.h | 4 +- rpcs3/Emu/Cell/Modules/sceNp2.cpp | 37 +- rpcs3/Emu/Cell/lv2/sys_net.cpp | 10 +- rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp | 7 +- .../Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.cpp | 53 +- .../Emu/Cell/lv2/sys_net/network_context.cpp | 4 +- rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp | 62 +- rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h | 10 +- rpcs3/Emu/NP/fb_helpers.cpp | 13 +- rpcs3/Emu/NP/generated/np2_structs.fbs | 66 ++ .../Emu/NP/generated/np2_structs_generated.h | 811 +++++++++++++++++ rpcs3/Emu/NP/np_cache.cpp | 19 +- rpcs3/Emu/NP/np_cache.h | 1 + rpcs3/Emu/NP/np_contexts.cpp | 60 +- rpcs3/Emu/NP/np_contexts.h | 46 +- rpcs3/Emu/NP/np_handler.cpp | 52 +- rpcs3/Emu/NP/np_handler.h | 38 +- rpcs3/Emu/NP/np_helpers.cpp | 8 +- rpcs3/Emu/NP/np_helpers.h | 8 +- rpcs3/Emu/NP/np_notifications.cpp | 2 +- rpcs3/Emu/NP/np_requests.cpp | 359 +++++++- rpcs3/Emu/NP/np_structs_extra.cpp | 94 ++ rpcs3/Emu/NP/np_structs_extra.h | 5 + rpcs3/Emu/NP/rpcn_client.cpp | 391 +++++--- rpcs3/Emu/NP/rpcn_client.h | 33 +- rpcs3/Emu/NP/rpcn_config.cpp | 102 ++- rpcs3/Emu/NP/rpcn_config.h | 16 +- rpcs3/Emu/NP/signaling_handler.cpp | 186 ++-- rpcs3/Emu/NP/signaling_handler.h | 31 +- rpcs3/Emu/NP/vport0.h | 26 + rpcs3/emucore.vcxproj | 1 + rpcs3/emucore.vcxproj.filters | 3 + rpcs3/rpcs3qt/rpcn_settings_dialog.cpp | 834 ++++++++++++++---- rpcs3/rpcs3qt/rpcn_settings_dialog.h | 74 +- 39 files changed, 3461 insertions(+), 758 deletions(-) create mode 100644 rpcs3/Emu/NP/vport0.h diff --git a/3rdparty/flatbuffers b/3rdparty/flatbuffers index 615616cb55..06c5c7ed0b 160000 --- a/3rdparty/flatbuffers +++ b/3rdparty/flatbuffers @@ -1 +1 @@ -Subproject commit 615616cb5549a34bdf288c04bc1b94bd7a65c396 +Subproject commit 06c5c7ed0bd987a918cf88caafb094f22cdd1721 diff --git a/3rdparty/wolfssl/CMakeLists.txt b/3rdparty/wolfssl/CMakeLists.txt index 94c9607042..4b44755c21 100644 --- a/3rdparty/wolfssl/CMakeLists.txt +++ b/3rdparty/wolfssl/CMakeLists.txt @@ -5,9 +5,10 @@ if(USE_SYSTEM_WOLFSSL) target_link_libraries(wolfssl INTERFACE PkgConfig::WolfSSL) else() # TODO(cjj19970505@live.cn) - # OPENSSL_EXTRA, WOLFSSL_DES_ECB and HAVE_SNI are unconfigurable from CMake cache. + # OPENSSL_EXTRA, and HAVE_SNI are unconfigurable from CMake cache. # but they do have it in a TODO list (wolfssl/CMakeList, 1021) add_compile_definitions(OPENSSL_EXTRA WOLFSSL_DES_ECB HAVE_SNI HAVE_WRITE_DUP) + add_compile_definitions(FP_MAX_BITS=8192) set(WOLFSSL_TLS13 "no" CACHE INTERNAL "") set(WOLFSSL_SHA224 "yes" CACHE INTERNAL "") diff --git a/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp b/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp index 6433ef2667..57bf1f17b2 100644 --- a/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp +++ b/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp @@ -237,7 +237,7 @@ struct netstart_hack error_code cellNetCtlNetStartDialogLoadAsync(vm::cptr param) { - cellNetCtl.error("cellNetCtlNetStartDialogLoadAsync(param=*0x%x)", param); + cellNetCtl.warning("cellNetCtlNetStartDialogLoadAsync(param=*0x%x)", param); auto& nph = g_fxo->get>(); diff --git a/rpcs3/Emu/Cell/Modules/sceNp.cpp b/rpcs3/Emu/Cell/Modules/sceNp.cpp index 28ff050ef9..070e97049a 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp.cpp @@ -1272,7 +1272,7 @@ error_code sceNpBasicAddFriend(vm::cptr contact, vm::cptr body, s error_code sceNpBasicGetFriendListEntryCount(vm::ptr count) { - sceNp.todo("sceNpBasicGetFriendListEntryCount(count=*0x%x)", count); + sceNp.warning("sceNpBasicGetFriendListEntryCount(count=*0x%x)", count); auto& nph = g_fxo->get>(); @@ -3075,7 +3075,7 @@ error_code sceNpManagerGetOnlineId(vm::ptr onlineId) error_code sceNpManagerGetNpId(ppu_thread&, vm::ptr npId) { - sceNp.warning("sceNpManagerGetNpId(npId=*0x%x)", npId); + sceNp.trace("sceNpManagerGetNpId(npId=*0x%x)", npId); auto& nph = g_fxo->get>(); @@ -3354,7 +3354,7 @@ error_code sceNpManagerGetPsHandle(vm::ptr onlineId) error_code sceNpManagerRequestTicket(vm::cptr npId, vm::cptr serviceId, vm::cptr cookie, u32 cookieSize, vm::cptr entitlementId, u32 consumedCount) { - sceNp.error("sceNpManagerRequestTicket(npId=*0x%x, serviceId=%s, cookie=*0x%x, cookieSize=%d, entitlementId=%s, consumedCount=%d)", npId, serviceId, cookie, cookieSize, entitlementId, consumedCount); + sceNp.warning("sceNpManagerRequestTicket(npId=*0x%x, serviceId=%s, cookie=*0x%x, cookieSize=%d, entitlementId=%s, consumedCount=%d)", npId, serviceId, cookie, cookieSize, entitlementId, consumedCount); auto& nph = g_fxo->get>(); @@ -3386,7 +3386,7 @@ error_code sceNpManagerRequestTicket(vm::cptr npId, vm::cptr serv error_code sceNpManagerRequestTicket2(vm::cptr npId, vm::cptr version, vm::cptr serviceId, vm::cptr cookie, u32 cookieSize, vm::cptr entitlementId, u32 consumedCount) { - sceNp.todo("sceNpManagerRequestTicket2(npId=*0x%x, version=*0x%x, serviceId=%s, cookie=*0x%x, cookieSize=%d, entitlementId=%s, consumedCount=%d)", npId, version, serviceId, cookie, cookieSize, + sceNp.warning("sceNpManagerRequestTicket2(npId=*0x%x, version=*0x%x, serviceId=%s, cookie=*0x%x, cookieSize=%d, entitlementId=%s, consumedCount=%d)", npId, version, serviceId, cookie, cookieSize, entitlementId, consumedCount); auto& nph = g_fxo->get>(); @@ -3418,7 +3418,7 @@ error_code sceNpManagerRequestTicket2(vm::cptr npId, vm::cptr buffer, vm::ptr bufferSize) { - sceNp.error("sceNpManagerGetTicket(buffer=*0x%x, bufferSize=*0x%x)", buffer, bufferSize); + sceNp.warning("sceNpManagerGetTicket(buffer=*0x%x, bufferSize=*0x%x)", buffer, bufferSize); auto& nph = g_fxo->get>(); @@ -3791,7 +3791,7 @@ error_code sceNpScoreTerm() error_code sceNpScoreCreateTitleCtx(vm::cptr communicationId, vm::cptr passphrase, vm::cptr selfNpId) { - sceNp.todo("sceNpScoreCreateTitleCtx(communicationId=*0x%x, passphrase=*0x%x, selfNpId=*0x%x)", communicationId, passphrase, selfNpId); + sceNp.warning("sceNpScoreCreateTitleCtx(communicationId=*0x%x, passphrase=*0x%x, selfNpId=*0x%x)", communicationId, passphrase, selfNpId); auto& nph = g_fxo->get>(); @@ -3805,12 +3805,19 @@ error_code sceNpScoreCreateTitleCtx(vm::cptr communication return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT; } - return not_an_error(create_score_context(communicationId, passphrase)); + s32 id = create_score_context(communicationId, passphrase); + + if (id > 0) + { + return not_an_error(id); + } + + return id; } error_code sceNpScoreDestroyTitleCtx(s32 titleCtxId) { - sceNp.todo("sceNpScoreDestroyTitleCtx(titleCtxId=%d)", titleCtxId); + sceNp.warning("sceNpScoreDestroyTitleCtx(titleCtxId=%d)", titleCtxId); auto& nph = g_fxo->get>(); @@ -3827,7 +3834,7 @@ error_code sceNpScoreDestroyTitleCtx(s32 titleCtxId) error_code sceNpScoreCreateTransactionCtx(s32 titleCtxId) { - sceNp.todo("sceNpScoreCreateTransactionCtx(titleCtxId=%d)", titleCtxId); + sceNp.warning("sceNpScoreCreateTransactionCtx(titleCtxId=%d)", titleCtxId); auto& nph = g_fxo->get>(); @@ -3841,12 +3848,26 @@ error_code sceNpScoreCreateTransactionCtx(s32 titleCtxId) return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID; } - return not_an_error(create_score_transaction_context(titleCtxId)); + auto score = idm::get(titleCtxId); + + if (!score) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ID; + } + + s32 id = create_score_transaction_context(score); + + if (id > 0) + { + return not_an_error(id); + } + + return id; } error_code sceNpScoreDestroyTransactionCtx(s32 transId) { - sceNp.todo("sceNpScoreDestroyTransactionCtx(transId=%d)", transId); + sceNp.warning("sceNpScoreDestroyTransactionCtx(transId=%d)", transId); auto& nph = g_fxo->get>(); @@ -3855,12 +3876,17 @@ error_code sceNpScoreDestroyTransactionCtx(s32 transId) return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; } + if (!destroy_score_transaction_context(transId)) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ID; + } + return CELL_OK; } error_code sceNpScoreSetTimeout(s32 ctxId, usecond_t timeout) { - sceNp.todo("sceNpScoreSetTimeout(ctxId=%d, timeout=%d)", ctxId, timeout); + sceNp.warning("sceNpScoreSetTimeout(ctxId=%d, timeout=%d)", ctxId, timeout); auto& nph = g_fxo->get>(); @@ -3869,28 +3895,66 @@ error_code sceNpScoreSetTimeout(s32 ctxId, usecond_t timeout) return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; } - if (timeout > 10000000) // 10 seconds + if (timeout < 10'000'000) // 10 seconds { return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; } + if (static_cast(ctxId) >= score_transaction_ctx::id_base) + { + auto trans = idm::get(ctxId); + if (!trans) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ID; + } + trans->timeout = timeout; + } + else + { + auto score = idm::get(ctxId); + if (!ctxId) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ID; + } + score->timeout = timeout; + } + return CELL_OK; } error_code sceNpScoreSetPlayerCharacterId(s32 ctxId, SceNpScorePcId pcId) { - sceNp.todo("sceNpScoreSetPlayerCharacterId(ctxId=%d, pcId=%d)", ctxId, pcId); + sceNp.warning("sceNpScoreSetPlayerCharacterId(ctxId=%d, pcId=%d)", ctxId, pcId); auto& nph = g_fxo->get>(); + if (pcId < 0) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; + } + if (!nph.is_NP_Score_init) { return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; } - if (pcId < 0) + if (static_cast(ctxId) >= score_transaction_ctx::id_base) { - return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; + auto trans = idm::get(ctxId); + if (!trans) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ID; + } + trans->pcId = pcId; + } + else + { + auto score = idm::get(ctxId); + if (!ctxId) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ID; + } + score->pcId = pcId; } return CELL_OK; @@ -3907,17 +3971,20 @@ error_code sceNpScoreWaitAsync(s32 transId, vm::ptr result) return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; } - if (transId <= 0) + auto trans = idm::get(transId); + if (!trans) { return SCE_NP_COMMUNITY_ERROR_INVALID_ID; } + trans->wait_for_completion(); + return CELL_OK; } error_code sceNpScorePollAsync(s32 transId, vm::ptr result) { - sceNp.todo("sceNpScorePollAsync(transId=%d, result=*0x%x)", transId, result); + sceNp.warning("sceNpScorePollAsync(transId=%d, result=*0x%x)", transId, result); auto& nph = g_fxo->get>(); @@ -3926,18 +3993,25 @@ error_code sceNpScorePollAsync(s32 transId, vm::ptr result) return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; } - if (transId <= 0) + auto trans = idm::get(transId); + if (!trans) { return SCE_NP_COMMUNITY_ERROR_INVALID_ID; } + auto res = trans->get_score_transaction_status(); + + if (!res) + { + return not_an_error(1); + } + + *result = *res; return CELL_OK; } -error_code sceNpScoreGetBoardInfo(s32 transId, SceNpScoreBoardId boardId, vm::ptr boardInfo, vm::ptr option) +error_code scenp_score_get_board_info(s32 transId, SceNpScoreBoardId boardId, vm::ptr boardInfo, vm::ptr option, bool async) { - sceNp.todo("sceNpScoreGetBoardInfo(transId=%d, boardId=%d, boardInfo=*0x%x, option=*0x%x)", transId, boardId, boardInfo, option); - auto& nph = g_fxo->get>(); if (!nph.is_NP_Score_init) @@ -3960,34 +4034,40 @@ error_code sceNpScoreGetBoardInfo(s32 transId, SceNpScoreBoardId boardId, vm::pt return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID; } - return CELL_OK; + auto trans_ctx = idm::get(transId); + + if (!trans_ctx) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ID; + } + + nph.get_board_infos(trans_ctx, boardId, boardInfo, async); + + if (async) + { + return CELL_OK; + } + + return *trans_ctx->result; +} + +error_code sceNpScoreGetBoardInfo(s32 transId, SceNpScoreBoardId boardId, vm::ptr boardInfo, vm::ptr option) +{ + sceNp.warning("sceNpScoreGetBoardInfo(transId=%d, boardId=%d, boardInfo=*0x%x, option=*0x%x)", transId, boardId, boardInfo, option); + + return scenp_score_get_board_info(transId, boardId, boardInfo, option, false); } error_code sceNpScoreGetBoardInfoAsync(s32 transId, SceNpScoreBoardId boardId, vm::ptr boardInfo, s32 prio, vm::ptr option) { - sceNp.todo("sceNpScoreGetBoardInfo(transId=%d, boardId=%d, boardInfo=*0x%x, prio=%d, option=*0x%x)", transId, boardId, boardInfo, prio, option); + sceNp.warning("sceNpScoreGetBoardInfo(transId=%d, boardId=%d, boardInfo=*0x%x, prio=%d, option=*0x%x)", transId, boardId, boardInfo, prio, option); - auto& nph = g_fxo->get>(); - - if (!nph.is_NP_Score_init) - { - return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; - } - - if (option) // option check at least until fw 4.71 - { - return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; - } - - return CELL_OK; + return scenp_score_get_board_info(transId, boardId, boardInfo, option, true); } -error_code sceNpScoreRecordScore(s32 transId, SceNpScoreBoardId boardId, SceNpScoreValue score, vm::cptr scoreComment, vm::cptr gameInfo, - vm::ptr tmpRank, vm::ptr option) +error_code scenp_score_record_score(s32 transId, SceNpScoreBoardId boardId, SceNpScoreValue score, vm::cptr scoreComment, vm::cptr gameInfo, + vm::ptr tmpRank, vm::ptr option, bool async) { - sceNp.todo( - "sceNpScoreRecordScore(transId=%d, boardId=%d, score=%d, scoreComment=*0x%x, gameInfo=*0x%x, tmpRank=*0x%x, option=*0x%x)", transId, boardId, score, scoreComment, gameInfo, tmpRank, option); - auto& nph = g_fxo->get>(); if (!nph.is_NP_Score_init) @@ -3995,44 +4075,79 @@ error_code sceNpScoreRecordScore(s32 transId, SceNpScoreBoardId boardId, SceNpSc return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; } - if (option) // option check at least until fw 4.71 - { - return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; - } - if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE) { return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID; } - return CELL_OK; + auto trans_ctx = idm::get(transId); + + if (!trans_ctx) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ID; + } + + const u8* data = nullptr; + u32 data_size = 0; + + if (!gameInfo) + { + if (option && option->vsInfo) + { + if (option->size != 0xCu) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT; + } + + if (option->reserved) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; + } + + data = &option->vsInfo->data[0]; + data_size = option->vsInfo->infoSize; + } + } + else + { + data = &gameInfo->nativeData[0]; + data_size = 64; + } + + if (option) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; + } + + nph.record_score(trans_ctx, boardId, score, scoreComment, data, data_size, tmpRank, async); + + if (async) + { + return CELL_OK; + } + + return *trans_ctx->result; +} + +error_code sceNpScoreRecordScore(s32 transId, SceNpScoreBoardId boardId, SceNpScoreValue score, vm::cptr scoreComment, vm::cptr gameInfo, + vm::ptr tmpRank, vm::ptr option) +{ + sceNp.warning("sceNpScoreRecordScore(transId=%d, boardId=%d, score=%d, scoreComment=*0x%x, gameInfo=*0x%x, tmpRank=*0x%x, option=*0x%x)", transId, boardId, score, scoreComment, gameInfo, tmpRank, option); + + return scenp_score_record_score(transId, boardId, score, scoreComment, gameInfo, tmpRank, option, false); } error_code sceNpScoreRecordScoreAsync(s32 transId, SceNpScoreBoardId boardId, SceNpScoreValue score, vm::cptr scoreComment, vm::cptr gameInfo, vm::ptr tmpRank, s32 prio, vm::ptr option) { - sceNp.todo("sceNpScoreRecordScoreAsync(transId=%d, boardId=%d, score=%d, scoreComment=*0x%x, gameInfo=*0x%x, tmpRank=*0x%x, prio=%d, option=*0x%x)", transId, boardId, score, scoreComment, gameInfo, + sceNp.warning("sceNpScoreRecordScoreAsync(transId=%d, boardId=%d, score=%d, scoreComment=*0x%x, gameInfo=*0x%x, tmpRank=*0x%x, prio=%d, option=*0x%x)", transId, boardId, score, scoreComment, gameInfo, tmpRank, prio, option); - auto& nph = g_fxo->get>(); - - if (!nph.is_NP_Score_init) - { - return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; - } - - if (option) // option check at least until fw 4.71 - { - return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; - } - - return CELL_OK; + return scenp_score_record_score(transId, boardId, score, scoreComment, gameInfo, tmpRank, option, true); } -error_code sceNpScoreRecordGameData(s32 transId, SceNpScoreBoardId boardId, SceNpScoreValue score, u64 totalSize, u64 sendSize, vm::cptr data, vm::ptr option) +error_code scenp_score_record_game_data(s32 transId, SceNpScoreBoardId boardId, SceNpScoreValue score, u64 totalSize, u64 sendSize, vm::cptr data, vm::ptr option, bool async) { - sceNp.todo("sceNpScoreRecordGameData(transId=%d, boardId=%d, score=%d, totalSize=%d, sendSize=%d, data=*0x%x, option=*0x%x)", transId, boardId, score, totalSize, sendSize, data, option); - auto& nph = g_fxo->get>(); if (!nph.is_NP_Score_init) @@ -4040,6 +4155,11 @@ error_code sceNpScoreRecordGameData(s32 transId, SceNpScoreBoardId boardId, SceN return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; } + if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID; + } + if (!data) { return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT; @@ -4050,38 +4170,32 @@ error_code sceNpScoreRecordGameData(s32 transId, SceNpScoreBoardId boardId, SceN return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; } - if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE) + auto trans_ctx = idm::get(transId); + + if (!trans_ctx) { - return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID; + return SCE_NP_COMMUNITY_ERROR_INVALID_ID; } return CELL_OK; } +error_code sceNpScoreRecordGameData(s32 transId, SceNpScoreBoardId boardId, SceNpScoreValue score, u64 totalSize, u64 sendSize, vm::cptr data, vm::ptr option) +{ + sceNp.todo("sceNpScoreRecordGameData(transId=%d, boardId=%d, score=%d, totalSize=%d, sendSize=%d, data=*0x%x, option=*0x%x)", transId, boardId, score, totalSize, sendSize, data, option); + + return scenp_score_record_game_data(transId, boardId, score, totalSize, sendSize, data, option, false); +} + error_code sceNpScoreRecordGameDataAsync(s32 transId, SceNpScoreBoardId boardId, SceNpScoreValue score, u64 totalSize, u64 sendSize, vm::cptr data, s32 prio, vm::ptr option) { - sceNp.todo( - "sceNpScoreRecordGameDataAsync(transId=%d, boardId=%d, score=%d, totalSize=%d, sendSize=%d, data=*0x%x, prio=%d, option=*0x%x)", transId, boardId, score, totalSize, sendSize, data, prio, option); + sceNp.todo("sceNpScoreRecordGameDataAsync(transId=%d, boardId=%d, score=%d, totalSize=%d, sendSize=%d, data=*0x%x, prio=%d, option=*0x%x)", transId, boardId, score, totalSize, sendSize, data, prio, option); - auto& nph = g_fxo->get>(); - - if (!nph.is_NP_Score_init) - { - return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; - } - - if (option) // option check at least until fw 4.71 - { - return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; - } - - return CELL_OK; + return scenp_score_record_game_data(transId, boardId, score, totalSize, sendSize, data, option, true); } -error_code sceNpScoreGetGameData(s32 transId, SceNpScoreBoardId boardId, vm::cptr npId, vm::ptr totalSize, u64 recvSize, vm::ptr data, vm::ptr option) +error_code scenp_score_get_game_data(s32 transId, SceNpScoreBoardId boardId, vm::cptr npId, vm::ptr totalSize, u64 recvSize, vm::ptr data, vm::ptr option, bool async) { - sceNp.todo("sceNpScoreGetGameDataAsync(transId=%d, boardId=%d, npId=*0x%x, totalSize=*0x%x, recvSize=%d, data=*0x%x, option=*0x%x)", transId, boardId, npId, totalSize, recvSize, data, option); - auto& nph = g_fxo->get>(); if (!nph.is_NP_Score_init) @@ -4107,34 +4221,26 @@ error_code sceNpScoreGetGameData(s32 transId, SceNpScoreBoardId boardId, vm::cpt return CELL_OK; } +error_code sceNpScoreGetGameData(s32 transId, SceNpScoreBoardId boardId, vm::cptr npId, vm::ptr totalSize, u64 recvSize, vm::ptr data, vm::ptr option) +{ + sceNp.todo("sceNpScoreGetGameDataAsync(transId=%d, boardId=%d, npId=*0x%x, totalSize=*0x%x, recvSize=%d, data=*0x%x, option=*0x%x)", transId, boardId, npId, totalSize, recvSize, data, option); + + return scenp_score_get_game_data(transId, boardId, npId, totalSize, recvSize, data, option, false); +} + error_code sceNpScoreGetGameDataAsync(s32 transId, SceNpScoreBoardId boardId, vm::cptr npId, vm::ptr totalSize, u64 recvSize, vm::ptr data, s32 prio, vm::ptr option) { sceNp.todo("sceNpScoreGetGameDataAsync(transId=%d, boardId=%d, npId=*0x%x, totalSize=*0x%x, recvSize=%d, data=*0x%x, prio=%d, option=*0x%x)", transId, boardId, npId, totalSize, recvSize, data, prio, option); - auto& nph = g_fxo->get>(); - - if (!nph.is_NP_Score_init) - { - return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; - } - - if (option) // option check at least until fw 4.71 - { - return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; - } - - return CELL_OK; + return scenp_score_get_game_data(transId, boardId, npId, totalSize, recvSize, data, option, true); } -error_code sceNpScoreGetRankingByNpId(s32 transId, SceNpScoreBoardId boardId, vm::cptr npIdArray, u64 npIdArraySize, vm::ptr rankArray, u64 rankArraySize, - vm::ptr commentArray, u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, - vm::ptr totalRecord, vm::ptr option) +template +error_code scenp_score_get_ranking_by_npid(s32 transId, SceNpScoreBoardId boardId, T npIdArray, u64 npIdArraySize, vm::ptr rankArray, u64 rankArraySize, + vm::ptr commentArray, u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, + vm::ptr totalRecord, vm::ptr option, bool async) { - sceNp.todo("sceNpScoreGetRankingByNpId(transId=%d, boardId=%d, npIdArray=*0x%x, npIdArraySize=%d, rankArray=*0x%x, rankArraySize=%d, commentArray=*0x%x, commentArraySize=%d, infoArray=*0x%x, " - "infoArraySize=%d, arrayNum=%d, lastSortDate=*0x%x, totalRecord=*0x%x, option=*0x%x)", - transId, boardId, npIdArray, npIdArraySize, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, arrayNum, lastSortDate, totalRecord, option); - auto& nph = g_fxo->get>(); if (!nph.is_NP_Score_init) @@ -4142,12 +4248,12 @@ error_code sceNpScoreGetRankingByNpId(s32 transId, SceNpScoreBoardId boardId, vm return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; } - if (!npIdArray || !rankArray || !totalRecord || !lastSortDate || !arrayNum) + if (!npIdArray) { return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT; } - if (option) // option check at least until fw 4.71 + if (!arrayNum) { return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; } @@ -4157,50 +4263,101 @@ error_code sceNpScoreGetRankingByNpId(s32 transId, SceNpScoreBoardId boardId, vm return SCE_NP_COMMUNITY_ERROR_TOO_MANY_NPID; } + auto trans_ctx = idm::get(transId); + if (!trans_ctx) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ID; + } + if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE) { return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID; } - return CELL_OK; + if (commentArray && commentArraySize != (arrayNum * sizeof(SceNpScoreComment))) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT; + } + + if (infoArray && infoArraySize != (arrayNum * sizeof(SceNpScoreGameInfo)) && infoArraySize != (arrayNum * sizeof(SceNpScoreVariableSizeGameInfo))) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT; + } + + if (rankArraySize != (arrayNum * sizeof(SceNpScorePlayerRankData))) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT; + } + + std::vector> npid_vec; + + static constexpr bool is_npid = std::is_same>::value; + static constexpr bool is_npidpcid = std::is_same>::value; + static_assert(is_npid || is_npidpcid, "T should be vm::cptr or vm::cptr"); + + if constexpr (is_npid) + { + if (npIdArraySize != (arrayNum * sizeof(SceNpId))) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT; + } + + for (u64 index = 0; index < arrayNum; index++) + { + npid_vec.push_back(std::make_pair(npIdArray[index], 0)); + } + } + else if constexpr (is_npidpcid) + { + if (npIdArraySize != (arrayNum * sizeof(SceNpScoreNpIdPcId))) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT; + } + + for (u64 index = 0; index < arrayNum; index++) + { + npid_vec.push_back(std::make_pair(npIdArray[index].npId, npIdArray[index].pcId)); + } + } + + nph.get_score_npid(trans_ctx, boardId, npid_vec, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, arrayNum, lastSortDate, totalRecord, async); + + if (async) + { + return CELL_OK; + } + + return *trans_ctx->result; +} + +error_code sceNpScoreGetRankingByNpId(s32 transId, SceNpScoreBoardId boardId, vm::cptr npIdArray, u64 npIdArraySize, vm::ptr rankArray, u64 rankArraySize, + vm::ptr commentArray, u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, + vm::ptr totalRecord, vm::ptr option) +{ + sceNp.warning("sceNpScoreGetRankingByNpId(transId=%d, boardId=%d, npIdArray=*0x%x, npIdArraySize=%d, rankArray=*0x%x, rankArraySize=%d, commentArray=*0x%x, commentArraySize=%d, infoArray=*0x%x, " + "infoArraySize=%d, arrayNum=%d, lastSortDate=*0x%x, totalRecord=*0x%x, option=*0x%x)", + transId, boardId, npIdArray, npIdArraySize, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, arrayNum, lastSortDate, totalRecord, option); + + return scenp_score_get_ranking_by_npid(transId, boardId, npIdArray, npIdArraySize, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, + infoArraySize, arrayNum, lastSortDate, totalRecord, option, false); } error_code sceNpScoreGetRankingByNpIdAsync(s32 transId, SceNpScoreBoardId boardId, vm::cptr npIdArray, u64 npIdArraySize, vm::ptr rankArray, u64 rankArraySize, vm::ptr commentArray, u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, s32 prio, vm::ptr option) { - sceNp.todo("sceNpScoreGetRankingByNpIdAsync(transId=%d, boardId=%d, npIdArray=*0x%x, npIdArraySize=%d, rankArray=*0x%x, rankArraySize=%d, commentArray=*0x%x, commentArraySize=%d, infoArray=*0x%x, " + sceNp.warning("sceNpScoreGetRankingByNpIdAsync(transId=%d, boardId=%d, npIdArray=*0x%x, npIdArraySize=%d, rankArray=*0x%x, rankArraySize=%d, commentArray=*0x%x, commentArraySize=%d, infoArray=*0x%x, " "infoArraySize=%d, arrayNum=%d, lastSortDate=*0x%x, totalRecord=*0x%x, prio=%d, option=*0x%x)", transId, boardId, npIdArray, npIdArraySize, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, arrayNum, lastSortDate, totalRecord, prio, option); - auto& nph = g_fxo->get>(); - - if (!nph.is_NP_Score_init) - { - return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; - } - - if (!npIdArray || !arrayNum) - { - return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT; - } - - if (option) // option check at least until fw 4.71 - { - return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; - } - - return CELL_OK; + return scenp_score_get_ranking_by_npid(transId, boardId, npIdArray, npIdArraySize, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, + infoArraySize, arrayNum, lastSortDate, totalRecord, option, true); } -error_code sceNpScoreGetRankingByRange(s32 transId, SceNpScoreBoardId boardId, SceNpScoreRankNumber startSerialRank, vm::ptr rankArray, u64 rankArraySize, +error_code scenp_score_get_ranking_by_range(s32 transId, SceNpScoreBoardId boardId, SceNpScoreRankNumber startSerialRank, vm::ptr rankArray, u64 rankArraySize, vm::ptr commentArray, u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, - vm::ptr totalRecord, vm::ptr option) + vm::ptr totalRecord, vm::ptr option, bool async) { - sceNp.todo("sceNpScoreGetRankingByRange(transId=%d, boardId=%d, startSerialRank=%d, rankArray=*0x%x, rankArraySize=%d, commentArray=*0x%x, commentArraySize=%d, infoArray=*0x%x, infoArraySize=%d, " - "arrayNum=%d, lastSortDate=*0x%x, totalRecord=*0x%x, option=*0x%x)", - transId, boardId, startSerialRank, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, arrayNum, lastSortDate, totalRecord, option); - auto& nph = g_fxo->get>(); if (!nph.is_NP_Score_init) @@ -4208,64 +4365,102 @@ error_code sceNpScoreGetRankingByRange(s32 transId, SceNpScoreBoardId boardId, S return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; } - if (!rankArray || !totalRecord || !lastSortDate || !arrayNum) + auto trans_ctx = idm::get(transId); + if (!trans_ctx) { - return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT; + return SCE_NP_COMMUNITY_ERROR_INVALID_ID; } - if (!startSerialRank || option) // option check at least until fw 4.71 + if (option) + { + vm::ptr opt_ptr = vm::static_ptr_cast(option); + if (opt_ptr[0] != 0xCu) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; + } + + if (opt_ptr[1]) + { + vm::ptr ssr_ptr = vm::cast(opt_ptr[1]); + startSerialRank = *ssr_ptr; + } + + // It also uses opt_ptr[2] for unknown purposes + } + + if (!startSerialRank) { return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; } + if (!rankArray || !totalRecord || !lastSortDate) + { + return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT; + } + if (arrayNum > SCE_NP_SCORE_MAX_RANGE_NUM_PER_TRANS) { return SCE_NP_COMMUNITY_ERROR_TOO_LARGE_RANGE; } + if (commentArray && commentArraySize != (arrayNum * sizeof(SceNpScoreComment))) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT; + } + + if (infoArray && infoArraySize != (arrayNum * sizeof(SceNpScoreGameInfo)) && infoArraySize != (arrayNum * sizeof(SceNpScoreVariableSizeGameInfo))) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT; + } + + if (rankArraySize != (arrayNum * sizeof(SceNpScoreRankData))) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT; + } + if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE) { return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID; } - return CELL_OK; + nph.get_score_range(trans_ctx, boardId, startSerialRank, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, arrayNum, lastSortDate, totalRecord, async); + + if (async) + { + return CELL_OK; + } + + return *trans_ctx->result; +} + +error_code sceNpScoreGetRankingByRange(s32 transId, SceNpScoreBoardId boardId, SceNpScoreRankNumber startSerialRank, vm::ptr rankArray, u64 rankArraySize, + vm::ptr commentArray, u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, + vm::ptr totalRecord, vm::ptr option) +{ + sceNp.warning("sceNpScoreGetRankingByRange(transId=%d, boardId=%d, startSerialRank=%d, rankArray=*0x%x, rankArraySize=%d, commentArray=*0x%x, commentArraySize=%d, infoArray=*0x%x, infoArraySize=%d, " + "arrayNum=%d, lastSortDate=*0x%x, totalRecord=*0x%x, option=*0x%x)", + transId, boardId, startSerialRank, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, arrayNum, lastSortDate, totalRecord, option); + + return scenp_score_get_ranking_by_range(transId, boardId, startSerialRank, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, + arrayNum, lastSortDate, totalRecord, option, false); } error_code sceNpScoreGetRankingByRangeAsync(s32 transId, SceNpScoreBoardId boardId, SceNpScoreRankNumber startSerialRank, vm::ptr rankArray, u64 rankArraySize, vm::ptr commentArray, u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, s32 prio, vm::ptr option) { - sceNp.todo("sceNpScoreGetRankingByRangeAsync(transId=%d, boardId=%d, startSerialRank=%d, rankArray=*0x%x, rankArraySize=%d, commentArray=*0x%x, commentArraySize=%d, infoArray=*0x%x, " + sceNp.warning("sceNpScoreGetRankingByRangeAsync(transId=%d, boardId=%d, startSerialRank=%d, rankArray=*0x%x, rankArraySize=%d, commentArray=*0x%x, commentArraySize=%d, infoArray=*0x%x, " "infoArraySize=%d, arrayNum=%d, lastSortDate=*0x%x, totalRecord=*0x%x, prio=%d, option=*0x%x)", transId, boardId, startSerialRank, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, arrayNum, lastSortDate, totalRecord, prio, option); - auto& nph = g_fxo->get>(); - - if (!nph.is_NP_Score_init) - { - return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; - } - - if (!arrayNum) - { - return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT; - } - - if (!startSerialRank || option) // option check at least until fw 4.71 - { - return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; - } - - return CELL_OK; + return scenp_score_get_ranking_by_range(transId, boardId, startSerialRank, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, + arrayNum, lastSortDate, totalRecord, option, true); } -error_code sceNpScoreGetFriendsRanking(s32 transId, SceNpScoreBoardId boardId, s32 includeSelf, vm::ptr rankArray, u64 rankArraySize, vm::ptr commentArray, - u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, vm::ptr option) +error_code scenp_score_get_friends_ranking(s32 transId, SceNpScoreBoardId boardId, s32 includeSelf, vm::ptr rankArray, u64 rankArraySize, vm::ptr commentArray, + u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, vm::ptr option, + bool async) { - sceNp.todo("sceNpScoreGetFriendsRanking(transId=%d, boardId=%d, includeSelf=%d, rankArray=*0x%x, rankArraySize=%d, commentArray=*0x%x, commentArraySize=%d, infoArray=*0x%x, infoArraySize=%d, " - "arrayNum=%d, lastSortDate=*0x%x, totalRecord=*0x%x, option=*0x%x)", - transId, boardId, includeSelf, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, arrayNum, lastSortDate, totalRecord, option); - auto& nph = g_fxo->get>(); if (!nph.is_NP_Score_init) @@ -4273,7 +4468,13 @@ error_code sceNpScoreGetFriendsRanking(s32 transId, SceNpScoreBoardId boardId, s return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; } - if (!rankArray || !arrayNum) + auto trans_ctx = idm::get(transId); + if (!trans_ctx) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ID; + } + + if (!rankArray || !totalRecord || !lastSortDate) { return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT; } @@ -4283,35 +4484,57 @@ error_code sceNpScoreGetFriendsRanking(s32 transId, SceNpScoreBoardId boardId, s return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; } - return CELL_OK; + if (commentArray && commentArraySize != (arrayNum * sizeof(SceNpScoreComment))) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT; + } + + if (infoArray && infoArraySize != (arrayNum * sizeof(SceNpScoreGameInfo)) && infoArraySize != (arrayNum * sizeof(SceNpScoreVariableSizeGameInfo))) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT; + } + + if (rankArraySize != (arrayNum * sizeof(SceNpScoreRankData))) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT; + } + + if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID; + } + + nph.get_score_friend(trans_ctx, boardId, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, arrayNum, lastSortDate, totalRecord, async); + + if (async) + { + return CELL_OK; + } + + return *trans_ctx->result; +} + +error_code sceNpScoreGetFriendsRanking(s32 transId, SceNpScoreBoardId boardId, s32 includeSelf, vm::ptr rankArray, u64 rankArraySize, vm::ptr commentArray, + u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, vm::ptr option) +{ + sceNp.warning("sceNpScoreGetFriendsRanking(transId=%d, boardId=%d, includeSelf=%d, rankArray=*0x%x, rankArraySize=%d, commentArray=*0x%x, commentArraySize=%d, infoArray=*0x%x, infoArraySize=%d, " + "arrayNum=%d, lastSortDate=*0x%x, totalRecord=*0x%x, option=*0x%x)", + transId, boardId, includeSelf, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, arrayNum, lastSortDate, totalRecord, option); + + return scenp_score_get_friends_ranking(transId, boardId, includeSelf, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, + arrayNum, lastSortDate, totalRecord, option, false); } error_code sceNpScoreGetFriendsRankingAsync(s32 transId, SceNpScoreBoardId boardId, s32 includeSelf, vm::ptr rankArray, u64 rankArraySize, vm::ptr commentArray, u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, s32 prio, vm::ptr option) { - sceNp.todo("sceNpScoreGetFriendsRankingAsync(transId=%d, boardId=%d, includeSelf=%d, rankArray=*0x%x, rankArraySize=%d, commentArray=*0x%x, commentArraySize=%d, infoArray=*0x%x, infoArraySize=%d, " + sceNp.warning("sceNpScoreGetFriendsRankingAsync(transId=%d, boardId=%d, includeSelf=%d, rankArray=*0x%x, rankArraySize=%d, commentArray=*0x%x, commentArraySize=%d, infoArray=*0x%x, infoArraySize=%d, " "arrayNum=%d, lastSortDate=*0x%x, totalRecord=*0x%x, prio=%d, option=*0x%x)", transId, boardId, includeSelf, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, arrayNum, lastSortDate, totalRecord, prio, option); - auto& nph = g_fxo->get>(); - - if (!nph.is_NP_Score_init) - { - return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; - } - - if (!rankArray || !arrayNum) - { - return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT; - } - - if (arrayNum > SCE_NP_SCORE_MAX_SELECTED_FRIENDS_NUM || option) // option check at least until fw 4.71 - { - return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; - } - - return CELL_OK; + return scenp_score_get_friends_ranking(transId, boardId, includeSelf, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, + arrayNum, lastSortDate, totalRecord, option, true); } error_code sceNpScoreCensorComment(s32 transId, vm::cptr comment, vm::ptr option) @@ -4432,66 +4655,24 @@ error_code sceNpScoreGetRankingByNpIdPcId(s32 transId, SceNpScoreBoardId boardId vm::ptr commentArray, u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, vm::ptr option) { - sceNp.todo("sceNpScoreGetRankingByNpIdPcId(transId=%d, boardId=%d, idArray=*0x%x, idArraySize=%d, rankArray=*0x%x, rankArraySize=%d, commentArray=*0x%x, commentArraySize=%d, infoArray=*0x%x, " + sceNp.warning("sceNpScoreGetRankingByNpIdPcId(transId=%d, boardId=%d, idArray=*0x%x, idArraySize=%d, rankArray=*0x%x, rankArraySize=%d, commentArray=*0x%x, commentArraySize=%d, infoArray=*0x%x, " "infoArraySize=%d, arrayNum=%d, lastSortDate=*0x%x, totalRecord=*0x%x, option=*0x%x)", transId, boardId, idArray, idArraySize, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, arrayNum, lastSortDate, totalRecord, option); - auto& nph = g_fxo->get>(); - - if (!nph.is_NP_Score_init) - { - return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; - } - - if (!idArray || !rankArray || !totalRecord || !lastSortDate || !arrayNum) - { - return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT; - } - - if (option) // option check at least until fw 4.71 - { - return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; - } - - if (arrayNum > SCE_NP_SCORE_MAX_NPID_NUM_PER_TRANS) - { - return SCE_NP_COMMUNITY_ERROR_TOO_MANY_NPID; - } - - if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE) - { - return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID; - } - - return CELL_OK; + return scenp_score_get_ranking_by_npid(transId, boardId, idArray, idArraySize, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, + infoArraySize, arrayNum, lastSortDate, totalRecord, option, false); } error_code sceNpScoreGetRankingByNpIdPcIdAsync(s32 transId, SceNpScoreBoardId boardId, vm::cptr idArray, u64 idArraySize, vm::ptr rankArray, u64 rankArraySize, vm::ptr commentArray, u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, s32 prio, vm::ptr option) { - sceNp.todo("sceNpScoreGetRankingByNpIdPcIdAsync(transId=%d, boardId=%d, idArray=*0x%x, idArraySize=%d, rankArray=*0x%x, rankArraySize=%d, commentArray=*0x%x, commentArraySize=%d, infoArray=*0x%x, " + sceNp.warning("sceNpScoreGetRankingByNpIdPcIdAsync(transId=%d, boardId=%d, idArray=*0x%x, idArraySize=%d, rankArray=*0x%x, rankArraySize=%d, commentArray=*0x%x, commentArraySize=%d, infoArray=*0x%x, " "infoArraySize=%d, arrayNum=%d, lastSortDate=*0x%x, totalRecord=*0x%x, prio=%d, option=*0x%x)", transId, boardId, idArray, idArraySize, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, infoArraySize, arrayNum, lastSortDate, totalRecord, prio, option); - auto& nph = g_fxo->get>(); - - if (!nph.is_NP_Score_init) - { - return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; - } - - if (!idArray || !arrayNum) - { - return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT; - } - - if (option) // option check at least until fw 4.71 - { - return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; - } - - return CELL_OK; + return scenp_score_get_ranking_by_npid(transId, boardId, idArray, idArraySize, rankArray, rankArraySize, commentArray, commentArraySize, infoArray, + infoArraySize, arrayNum, lastSortDate, totalRecord, option, true); } error_code sceNpScoreAbortTransaction(s32 transId) @@ -4505,6 +4686,14 @@ error_code sceNpScoreAbortTransaction(s32 transId) return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; } + auto trans = idm::get(transId); + if (!trans) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ID; + } + + trans->abort_score_transaction(); + return CELL_OK; } @@ -4902,7 +5091,7 @@ error_code sceNpScoreGetClansMembersRankingByRangeAsync(s32 transId, SceNpClanId error_code sceNpSignalingCreateCtx(vm::ptr npId, vm::ptr handler, vm::ptr arg, vm::ptr ctx_id) { - sceNp.todo("sceNpSignalingCreateCtx(npId=*0x%x, handler=*0x%x, arg=*0x%x, ctx_id=*0x%x)", npId, handler, arg, ctx_id); + sceNp.warning("sceNpSignalingCreateCtx(npId=*0x%x, handler=*0x%x, arg=*0x%x, ctx_id=*0x%x)", npId, handler, arg, ctx_id); auto& nph = g_fxo->get>(); @@ -4931,7 +5120,7 @@ error_code sceNpSignalingCreateCtx(vm::ptr npId, vm::ptrget>(); @@ -5044,7 +5233,7 @@ error_code sceNpSignalingDeactivateConnection(u32 ctx_id, u32 conn_id) error_code sceNpSignalingTerminateConnection(u32 ctx_id, u32 conn_id) { - sceNp.todo("sceNpSignalingTerminateConnection(ctx_id=%d, conn_id=%d)", ctx_id, conn_id); + sceNp.warning("sceNpSignalingTerminateConnection(ctx_id=%d, conn_id=%d)", ctx_id, conn_id); auto& nph = g_fxo->get>(); @@ -5053,6 +5242,10 @@ error_code sceNpSignalingTerminateConnection(u32 ctx_id, u32 conn_id) return SCE_NP_SIGNALING_ERROR_NOT_INITIALIZED; } + auto& sigh = g_fxo->get>(); + + sigh.stop_sig(conn_id); + return CELL_OK; } @@ -5076,7 +5269,15 @@ error_code sceNpSignalingGetConnectionStatus(u32 ctx_id, u32 conn_id, vm::ptr info) { - sceNp.todo("sceNpSignalingGetConnectionInfo(ctx_id=%d, conn_id=%d, code=%d, info=*0x%x)", ctx_id, conn_id, code, info); + sceNp.warning("sceNpSignalingGetConnectionInfo(ctx_id=%d, conn_id=%d, code=%d, info=*0x%x)", ctx_id, conn_id, code, info); auto& nph = g_fxo->get>(); @@ -5101,12 +5302,55 @@ error_code sceNpSignalingGetConnectionInfo(u32 ctx_id, u32 conn_id, s32 code, vm return SCE_NP_SIGNALING_ERROR_INVALID_ARGUMENT; } + auto& sigh = g_fxo->get>(); + const auto si = sigh.get_sig_infos(conn_id); + + switch (code) + { + case SCE_NP_SIGNALING_CONN_INFO_RTT: + { + info->rtt = si.rtt; + break; + } + case SCE_NP_SIGNALING_CONN_INFO_BANDWIDTH: + { + info->bandwidth = 10'000'000; // 10 MBPS HACK + break; + } + case SCE_NP_SIGNALING_CONN_INFO_PEER_NPID: + { + info->npId = si.npid; + break; + } + case SCE_NP_SIGNALING_CONN_INFO_PEER_ADDRESS: + { + info->address.port = std::bit_cast>(si.port); + info->address.addr.np_s_addr = si.addr; + break; + } + case SCE_NP_SIGNALING_CONN_INFO_MAPPED_ADDRESS: + { + info->address.port = std::bit_cast>(si.mapped_port); + info->address.addr.np_s_addr = si.mapped_addr; + break; + } + case SCE_NP_SIGNALING_CONN_INFO_PACKET_LOSS: + { + info->packet_loss = 0; // HACK + break; + } + default: + { + return SCE_NP_SIGNALING_ERROR_INVALID_ARGUMENT; + } + } + return CELL_OK; } error_code sceNpSignalingGetConnectionFromNpId(u32 ctx_id, vm::ptr npId, vm::ptr conn_id) { - sceNp.todo("sceNpSignalingGetConnectionFromNpId(ctx_id=%d, npId=*0x%x, conn_id=*0x%x)", ctx_id, npId, conn_id); + sceNp.notice("sceNpSignalingGetConnectionFromNpId(ctx_id=%d, npId=*0x%x, conn_id=*0x%x)", ctx_id, npId, conn_id); auto& nph = g_fxo->get>(); @@ -5120,12 +5364,22 @@ error_code sceNpSignalingGetConnectionFromNpId(u32 ctx_id, vm::ptr npId return SCE_NP_SIGNALING_ERROR_INVALID_ARGUMENT; } + auto& sigh = g_fxo->get>(); + const auto found_conn_id = sigh.get_conn_id_from_npid(npId.get_ptr()); + + if (!found_conn_id) + { + return SCE_NP_SIGNALING_ERROR_CONN_NOT_FOUND; + } + + *conn_id = *found_conn_id; + return CELL_OK; } -error_code sceNpSignalingGetConnectionFromPeerAddress(u32 ctx_id, vm::ptr peer_addr, np_in_port_t peer_port, vm::ptr conn_id) +error_code sceNpSignalingGetConnectionFromPeerAddress(u32 ctx_id, np_in_addr_t peer_addr, np_in_port_t peer_port, vm::ptr conn_id) { - sceNp.todo("sceNpSignalingGetConnectionFromPeerAddress(ctx_id=%d, peer_addr=*0x%x, peer_port=%d, conn_id=*0x%x)", ctx_id, peer_addr, peer_port, conn_id); + sceNp.warning("sceNpSignalingGetConnectionFromPeerAddress(ctx_id=%d, peer_addr=0x%x, peer_port=%d, conn_id=*0x%x)", ctx_id, peer_addr, peer_port, conn_id); auto& nph = g_fxo->get>(); @@ -5139,6 +5393,16 @@ error_code sceNpSignalingGetConnectionFromPeerAddress(u32 ctx_id, vm::ptrget>(); + const auto found_conn_id = sigh.get_conn_id_from_addr(peer_addr, peer_port); + + if (!found_conn_id) + { + return SCE_NP_SIGNALING_ERROR_CONN_NOT_FOUND; + } + + *conn_id = *found_conn_id; + return CELL_OK; } @@ -5252,7 +5516,7 @@ error_code sceNpUtilCmpNpId(vm::ptr id1, vm::ptr id2) if (strncmp(id1->handle.data, id2->handle.data, 16))// || id1->unk1[0] != id2->unk1[0]) { - return SCE_NP_UTIL_ERROR_NOT_MATCH; + return not_an_error(SCE_NP_UTIL_ERROR_NOT_MATCH); } // if (id1->unk1[1] != id2->unk1[1]) diff --git a/rpcs3/Emu/Cell/Modules/sceNp.h b/rpcs3/Emu/Cell/Modules/sceNp.h index 5f6b6226b2..0b29442e77 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.h +++ b/rpcs3/Emu/Cell/Modules/sceNp.h @@ -1393,14 +1393,14 @@ struct SceNpMatchingReqRange struct SceNpScoreVariableSizeGameInfo { - be_t infoSize; + be_t infoSize; u8 data[SCE_NP_SCORE_VARIABLE_SIZE_GAMEINFO_MAXSIZE]; u8 pad[3]; }; struct SceNpScoreRecordOptParam { - be_t size; + be_t size; vm::bptr vsInfo; vm::bptr reserved; }; diff --git a/rpcs3/Emu/Cell/Modules/sceNp2.cpp b/rpcs3/Emu/Cell/Modules/sceNp2.cpp index 09f8db7297..3a33c4b8de 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp2.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp2.cpp @@ -319,7 +319,7 @@ error_code sceNpMatching2Term2() error_code sceNpMatching2DestroyContext(SceNpMatching2ContextId ctxId) { - sceNp2.todo("sceNpMatching2DestroyContext(ctxId=%d)", ctxId); + sceNp2.warning("sceNpMatching2DestroyContext(ctxId=%d)", ctxId); auto& nph = g_fxo->get>(); @@ -611,11 +611,20 @@ error_code sceNpMatching2SignalingGetConnectionInfo( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } + if (!connInfo) + { + return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; + } + + auto& sigh = g_fxo->get>(); + const auto si = sigh.get_sig2_infos(roomId, memberId); + switch (code) { case SCE_NP_SIGNALING_CONN_INFO_RTT: { - connInfo->rtt = 20000; // HACK + connInfo->rtt = si.rtt; + sceNp2.warning("Returning a RTT of %d microseconds", connInfo->rtt); break; } case SCE_NP_SIGNALING_CONN_INFO_BANDWIDTH: @@ -625,22 +634,17 @@ error_code sceNpMatching2SignalingGetConnectionInfo( } case SCE_NP_SIGNALING_CONN_INFO_PEER_NPID: { - // TODO: need an update to whole signaling as matching2 signaling ignores npids atm - sceNp2.fatal("sceNpMatching2SignalingGetConnectionInfo Unimplemented SCE_NP_SIGNALING_CONN_INFO_PEER_NPID"); + connInfo->npId = si.npid; break; } case SCE_NP_SIGNALING_CONN_INFO_PEER_ADDRESS: { - auto& sigh = g_fxo->get>(); - const auto si = sigh.get_sig2_infos(roomId, memberId); connInfo->address.port = std::bit_cast>(si.port); connInfo->address.addr.np_s_addr = si.addr; break; } case SCE_NP_SIGNALING_CONN_INFO_MAPPED_ADDRESS: { - auto& sigh = g_fxo->get>(); - const auto si = sigh.get_sig2_infos(roomId, memberId); connInfo->address.port = std::bit_cast>(si.mapped_port); connInfo->address.addr.np_s_addr = si.mapped_addr; break; @@ -652,8 +656,7 @@ error_code sceNpMatching2SignalingGetConnectionInfo( } default: { - sceNp2.fatal("sceNpMatching2SignalingGetConnectionInfo Unimplemented code: %d", code); - return CELL_OK; + return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } } @@ -715,6 +718,16 @@ error_code sceNpMatching2AbortRequest(SceNpMatching2ContextId ctxId, SceNpMatchi return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } + if (!ctxId) + { + return SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID; + } + + if (!check_match2_context(ctxId)) + { + return SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND; + } + return CELL_OK; } @@ -1349,7 +1362,7 @@ error_code sceNpMatching2SetDefaultRequestOptParam(SceNpMatching2ContextId ctxId error_code sceNpMatching2RegisterRoomEventCallback(SceNpMatching2ContextId ctxId, vm::ptr cbFunc, vm::ptr cbFuncArg) { - sceNp2.todo("sceNpMatching2RegisterRoomEventCallback(ctxId=%d, cbFunc=*0x%x, cbFuncArg=*0x%x)", ctxId, cbFunc, cbFuncArg); + sceNp2.warning("sceNpMatching2RegisterRoomEventCallback(ctxId=%d, cbFunc=*0x%x, cbFuncArg=*0x%x)", ctxId, cbFunc, cbFuncArg); auto& nph = g_fxo->get>(); @@ -1546,7 +1559,7 @@ error_code sceNpMatching2SetLobbyMemberDataInternal( error_code sceNpMatching2RegisterRoomMessageCallback(SceNpMatching2ContextId ctxId, vm::ptr cbFunc, vm::ptr cbFuncArg) { - sceNp2.todo("sceNpMatching2RegisterRoomMessageCallback(ctxId=%d, cbFunc=*0x%x, cbFuncArg=*0x%x)", ctxId, cbFunc, cbFuncArg); + sceNp2.warning("sceNpMatching2RegisterRoomMessageCallback(ctxId=%d, cbFunc=*0x%x, cbFuncArg=*0x%x)", ctxId, cbFunc, cbFuncArg); auto& nph = g_fxo->get>(); diff --git a/rpcs3/Emu/Cell/lv2/sys_net.cpp b/rpcs3/Emu/Cell/lv2/sys_net.cpp index 0156cfc1e2..e1aa90f018 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net.cpp @@ -822,6 +822,7 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr buf, u32 { sn_addr = res_addr; std::memcpy(buf.get_ptr(), vec.data(), res); + sys_net_dump_data("recvfrom", vec.data(), res); } result = res; lv2_obj::awake(&ppu); @@ -998,6 +999,8 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr buf, u32 l return -SYS_NET_EAFNOSUPPORT; } + sys_net_dump_data("sendto", static_cast(buf.get_ptr()), len); + const std::optional sn_addr = addr ? std::optional(*addr) : std::nullopt; const std::vector buf_copy(vm::_ptr(buf.addr()), vm::_ptr(buf.addr()) + len); s32 result{}; @@ -1222,7 +1225,9 @@ error_code sys_net_bnet_close(ppu_thread& ppu, s32 s) } if (sock->get_queue_size()) - sys_net.error("CLOSE"); + { + sock->abort_socket(0); + } sock->close(); @@ -1718,6 +1723,9 @@ error_code lv2_socket::abort_socket(s32 flags) for (auto& [ppu, _] : qcopy) { + if (!ppu) + continue; + sys_net.warning("lv2_socket::abort_socket(): waking up \"%s\": (func: %s, r3=0x%x, r4=0x%x, r5=0x%x, r6=0x%x)", ppu->get_name(), ppu->current_function, ppu->gpr[3], ppu->gpr[4], ppu->gpr[5], ppu->gpr[6]); ppu->gpr[3] = static_cast(-SYS_NET_EINTR); lv2_obj::append(ppu.get()); diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp index b0b34d3fac..f34eed38a6 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp @@ -204,7 +204,7 @@ s32 lv2_socket_p2p::setsockopt(s32 level, s32 optname, const std::vector& op return {}; } -std::optional, sys_net_sockaddr>> lv2_socket_p2p::recvfrom([[maybe_unused]] s32 flags, u32 len, bool is_lock) +std::optional, sys_net_sockaddr>> lv2_socket_p2p::recvfrom(s32 flags, u32 len, bool is_lock) { std::unique_lock lock(mutex, std::defer_lock); @@ -217,7 +217,10 @@ std::optional, sys_net_sockaddr>> lv2_socket_p2p if (data.empty()) { - return {{-SYS_NET_EWOULDBLOCK, {}, {}}}; + if (so_nbio || (flags & SYS_NET_MSG_DONTWAIT)) + return {{-SYS_NET_EWOULDBLOCK, {}, {}}}; + + return std::nullopt; } std::vector res_buf(len); diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp index 2672e64f93..826ff135c0 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp @@ -703,7 +703,7 @@ std::optional, sys_net_sockaddr>> lv2_socket_p2p if (!data_available) { - if (so_nbio) + if (so_nbio || (flags & SYS_NET_MSG_DONTWAIT)) { return {{-SYS_NET_EWOULDBLOCK, {}, {}}}; } diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.cpp index d1d1d7acc8..f91cad281c 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "lv2_socket_raw.h" +#include "Emu/NP/vport0.h" LOG_CHANNEL(sys_net); @@ -20,7 +21,31 @@ void lv2_socket_raw::save(utils::serial& ar) std::tuple, sys_net_sockaddr> lv2_socket_raw::accept([[maybe_unused]] bool is_lock) { - sys_net.todo("lv2_socket_raw::accept"); + sys_net.fatal("[RAW] accept() called on a RAW socket"); + return {}; +} + +std::optional lv2_socket_raw::connect([[maybe_unused]] const sys_net_sockaddr& addr) +{ + sys_net.fatal("[RAW] connect() called on a RAW socket"); + return CELL_OK; +} + +s32 lv2_socket_raw::connect_followup() +{ + sys_net.fatal("[RAW] connect_followup() called on a RAW socket"); + return CELL_OK; +} + +std::pair lv2_socket_raw::getpeername() +{ + sys_net.todo("[RAW] getpeername() called on a RAW socket"); + return {}; +} + +s32 lv2_socket_raw::listen([[maybe_unused]] s32 backlog) +{ + sys_net.todo("[RAW] listen() called on a RAW socket"); return {}; } @@ -30,24 +55,6 @@ s32 lv2_socket_raw::bind([[maybe_unused]] const sys_net_sockaddr& addr) return {}; } -std::optional lv2_socket_raw::connect([[maybe_unused]] const sys_net_sockaddr& addr) -{ - sys_net.todo("lv2_socket_raw::connect"); - return CELL_OK; -} - -s32 lv2_socket_raw::connect_followup() -{ - sys_net.todo("lv2_socket_raw::connect_followup"); - return CELL_OK; -} - -std::pair lv2_socket_raw::getpeername() -{ - sys_net.todo("lv2_socket_raw::getpeername"); - return {}; -} - std::pair lv2_socket_raw::getsockname() { sys_net.todo("lv2_socket_raw::getsockname"); @@ -66,16 +73,10 @@ s32 lv2_socket_raw::setsockopt([[maybe_unused]] s32 level, [[maybe_unused]] s32 return {}; } -s32 lv2_socket_raw::listen([[maybe_unused]] s32 backlog) -{ - sys_net.todo("lv2_socket_raw::listen"); - return {}; -} - std::optional, sys_net_sockaddr>> lv2_socket_raw::recvfrom([[maybe_unused]] s32 flags, [[maybe_unused]] u32 len, [[maybe_unused]] bool is_lock) { sys_net.todo("lv2_socket_raw::recvfrom"); - return {{{}, {}, {}}}; + return {}; } std::optional lv2_socket_raw::sendto([[maybe_unused]] s32 flags, [[maybe_unused]] const std::vector& buf, [[maybe_unused]] std::optional opt_sn_addr, [[maybe_unused]] bool is_lock) diff --git a/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp b/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp index 4c1a540a0e..cf04c3216f 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp @@ -53,9 +53,9 @@ std::vector> get_rpcn_msgs() return msgs; } -std::vector, std::vector>> get_sign_msgs() +std::vector get_sign_msgs() { - std::vector, std::vector>> msgs; + std::vector msgs; auto& nc = g_fxo->get(); { std::lock_guard list_lock(nc.list_p2p_ports_mutex); diff --git a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp index 158375ce45..c2ff4320dc 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp @@ -9,6 +9,7 @@ #include "sys_net_helpers.h" #include "Emu/NP/signaling_handler.h" #include "sys_net_helpers.h" +#include "Emu/NP/vport0.h" LOG_CHANNEL(sys_net); @@ -119,35 +120,48 @@ bool nt_p2p_port::recv_data() u16 dst_vport = reinterpret_cast&>(p2p_recv_data[0]); - if (dst_vport == 0) // Reserved for messages from RPCN server + if (dst_vport == 0) { - std::vector rpcn_msg(recv_res - sizeof(u16)); - memcpy(rpcn_msg.data(), p2p_recv_data.data() + sizeof(u16), recv_res - sizeof(u16)); - - std::lock_guard lock(s_rpcn_mutex); - rpcn_msgs.push_back(std::move(rpcn_msg)); - return true; - } - - if (dst_vport == 65535) // Reserved for signaling - { - std::vector sign_msg(recv_res - sizeof(u16)); - memcpy(sign_msg.data(), p2p_recv_data.data() + sizeof(u16), recv_res - sizeof(u16)); - - std::pair, std::vector> msg; - msg.first.first = reinterpret_cast(&native_addr)->sin_addr.s_addr; - msg.first.second = std::bit_cast>(reinterpret_cast(&native_addr)->sin_port); - msg.second = std::move(sign_msg); - + if (recv_res < VPORT_0_HEADER_SIZE) { - std::lock_guard lock(s_sign_mutex); - sign_msgs.push_back(std::move(msg)); + sys_net.error("Bad vport 0 packet(no subset)!"); + return true; } - auto& sigh = g_fxo->get>(); - sigh.wake_up(); + const u8 subset = p2p_recv_data[2]; + const auto data_size = recv_res - VPORT_0_HEADER_SIZE; + std::vector vport_0_data(p2p_recv_data.data() + VPORT_0_HEADER_SIZE, p2p_recv_data.data() + VPORT_0_HEADER_SIZE + data_size); - return true; + switch (subset) + { + case SUBSET_RPCN: + { + std::lock_guard lock(s_rpcn_mutex); + rpcn_msgs.push_back(std::move(vport_0_data)); + return true; + } + case SUBSET_SIGNALING: + { + signaling_message msg; + msg.src_addr = reinterpret_cast(&native_addr)->sin_addr.s_addr; + msg.src_port = std::bit_cast>(reinterpret_cast(&native_addr)->sin_port); + msg.data = std::move(vport_0_data); + + { + std::lock_guard lock(s_sign_mutex); + sign_msgs.push_back(std::move(msg)); + } + + auto& sigh = g_fxo->get>(); + sigh.wake_up(); + return true; + } + default: + { + sys_net.error("Invalid vport 0 subset!"); + return true; + } + } } { diff --git a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h index 4115a0462e..33a85df473 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h @@ -18,6 +18,14 @@ #endif #endif +struct signaling_message +{ + u32 src_addr = 0; + u16 src_port = 0; + + std::vector data; +}; + struct nt_p2p_port { // Real socket where P2P packets are received/sent @@ -36,7 +44,7 @@ struct nt_p2p_port std::vector> rpcn_msgs{}; // Queued signaling messages shared_mutex s_sign_mutex; - std::vector, std::vector>> sign_msgs{}; + std::vector sign_msgs{}; std::array p2p_recv_data{}; diff --git a/rpcs3/Emu/NP/fb_helpers.cpp b/rpcs3/Emu/NP/fb_helpers.cpp index c6feee14c1..77a771eb7e 100644 --- a/rpcs3/Emu/NP/fb_helpers.cpp +++ b/rpcs3/Emu/NP/fb_helpers.cpp @@ -154,7 +154,7 @@ namespace np SceNpMatching2RoomDataExternal* cur_room; cur_room = (i > 0) ? edata.allocate(sizeof(SceNpMatching2RoomDataExternal), prev_room->next) : - edata.allocate(sizeof(SceNpMatching2RoomDataExternal), search_resp->roomDataExternal); + edata.allocate(sizeof(SceNpMatching2RoomDataExternal), search_resp->roomDataExternal); RoomDataExternal_to_SceNpMatching2RoomDataExternal(edata, fb_room, cur_room); prev_room = cur_room; @@ -173,7 +173,7 @@ namespace np SceNpMatching2RoomDataExternal* cur_room; cur_room = (i > 0) ? edata.allocate(sizeof(SceNpMatching2RoomDataExternal), prev_room->next) : - edata.allocate(sizeof(SceNpMatching2RoomDataExternal), get_resp->roomDataExternal); + edata.allocate(sizeof(SceNpMatching2RoomDataExternal), get_resp->roomDataExternal); RoomDataExternal_to_SceNpMatching2RoomDataExternal(edata, fb_room, cur_room); prev_room = cur_room; @@ -370,10 +370,10 @@ namespace np { rpcn_log.todo("RoomDataInternalUpdateInfo::newRoomGroup"); // TODO - //sce_update_info->newRoomGroupNum = update_info->newRoomGroup()->size(); - //vm::ptr group_info(allocate(sizeof(SceNpMatching2RoomGroup) * sce_update_info->newRoomGroupNum)); - //RoomGroups_to_SceNpMatching2RoomGroup(update_info->newRoomGroup(), group_info); - //sce_update_info->newRoomGroup = group_info; + // sce_update_info->newRoomGroupNum = update_info->newRoomGroup()->size(); + // vm::ptr group_info(allocate(sizeof(SceNpMatching2RoomGroup) * sce_update_info->newRoomGroupNum)); + // RoomGroups_to_SceNpMatching2RoomGroup(update_info->newRoomGroup(), group_info); + // sce_update_info->newRoomGroup = group_info; } if (update_info->newRoomBinAttrInternal() && update_info->newRoomBinAttrInternal()->size() != 0) @@ -497,5 +497,4 @@ namespace np } } } - } // namespace np diff --git a/rpcs3/Emu/NP/generated/np2_structs.fbs b/rpcs3/Emu/NP/generated/np2_structs.fbs index 9a96a23bd9..2fc73926a7 100644 --- a/rpcs3/Emu/NP/generated/np2_structs.fbs +++ b/rpcs3/Emu/NP/generated/np2_structs.fbs @@ -270,3 +270,69 @@ table SendMessageRequest { message:[uint8] (nested_flatbuffer: "MessageDetails"); npids:[string]; } + +table BoardInfo { + rankLimit:uint32; + updateMode:uint32; + sortMode:uint32; + uploadNumLimit:uint32; + uploadSizeLimit:uint32; +} + +table RecordScoreRequest { + boardId:uint32; + pcId:int32; + score:int64; + comment:string; + data:[uint8]; +} + +table GetScoreRangeRequest { + boardId:uint32; + startRank:uint32; + numRanks:uint32; + withComment:bool; + withGameInfo:bool; +} + +table ScoreNpIdPcId { + npid:string; + pcId:int32; +} + +table GetScoreNpIdRequest { + boardId:uint32; + npids:[ScoreNpIdPcId]; + withComment:bool; + withGameInfo:bool; +} + +table GetScoreFriendsRequest { + boardId:uint32; + include_self:bool; + max:uint32; + withComment:bool; + withGameInfo:bool; +} + +table ScoreRankData { + npId:string; + onlineName:string; + pcId:int32; + rank:uint32; + score:int64; + hasGameData:bool; + recordDate:uint64; +} + +table ScoreInfo { + data: [uint8]; +} + +table GetScoreResponse { + rankArray:[ScoreRankData]; + commentArray:[string]; + infoArray:[ScoreInfo]; + lastSortDate:uint64; + totalRecord:uint32; +} diff --git a/rpcs3/Emu/NP/generated/np2_structs_generated.h b/rpcs3/Emu/NP/generated/np2_structs_generated.h index 253d5c7b30..b08e9cb07b 100644 --- a/rpcs3/Emu/NP/generated/np2_structs_generated.h +++ b/rpcs3/Emu/NP/generated/np2_structs_generated.h @@ -6,6 +6,13 @@ #include "flatbuffers/flatbuffers.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 2 && + FLATBUFFERS_VERSION_MINOR == 0 && + FLATBUFFERS_VERSION_REVISION == 8, + "Non-compatible flatbuffers version included"); + struct BinAttr; struct BinAttrBuilder; @@ -111,6 +118,33 @@ struct MessageDetailsBuilder; struct SendMessageRequest; struct SendMessageRequestBuilder; +struct BoardInfo; +struct BoardInfoBuilder; + +struct RecordScoreRequest; +struct RecordScoreRequestBuilder; + +struct GetScoreRangeRequest; +struct GetScoreRangeRequestBuilder; + +struct ScoreNpIdPcId; +struct ScoreNpIdPcIdBuilder; + +struct GetScoreNpIdRequest; +struct GetScoreNpIdRequestBuilder; + +struct GetScoreFriendsRequest; +struct GetScoreFriendsRequestBuilder; + +struct ScoreRankData; +struct ScoreRankDataBuilder; + +struct ScoreInfo; +struct ScoreInfoBuilder; + +struct GetScoreResponse; +struct GetScoreResponseBuilder; + struct BinAttr FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { typedef BinAttrBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -3460,4 +3494,781 @@ inline flatbuffers::Offset CreateSendMessageRequestDirect( npids__); } +struct BoardInfo FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef BoardInfoBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_RANKLIMIT = 4, + VT_UPDATEMODE = 6, + VT_SORTMODE = 8, + VT_UPLOADNUMLIMIT = 10, + VT_UPLOADSIZELIMIT = 12 + }; + uint32_t rankLimit() const { + return GetField(VT_RANKLIMIT, 0); + } + uint32_t updateMode() const { + return GetField(VT_UPDATEMODE, 0); + } + uint32_t sortMode() const { + return GetField(VT_SORTMODE, 0); + } + uint32_t uploadNumLimit() const { + return GetField(VT_UPLOADNUMLIMIT, 0); + } + uint32_t uploadSizeLimit() const { + return GetField(VT_UPLOADSIZELIMIT, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_RANKLIMIT, 4) && + VerifyField(verifier, VT_UPDATEMODE, 4) && + VerifyField(verifier, VT_SORTMODE, 4) && + VerifyField(verifier, VT_UPLOADNUMLIMIT, 4) && + VerifyField(verifier, VT_UPLOADSIZELIMIT, 4) && + verifier.EndTable(); + } +}; + +struct BoardInfoBuilder { + typedef BoardInfo Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_rankLimit(uint32_t rankLimit) { + fbb_.AddElement(BoardInfo::VT_RANKLIMIT, rankLimit, 0); + } + void add_updateMode(uint32_t updateMode) { + fbb_.AddElement(BoardInfo::VT_UPDATEMODE, updateMode, 0); + } + void add_sortMode(uint32_t sortMode) { + fbb_.AddElement(BoardInfo::VT_SORTMODE, sortMode, 0); + } + void add_uploadNumLimit(uint32_t uploadNumLimit) { + fbb_.AddElement(BoardInfo::VT_UPLOADNUMLIMIT, uploadNumLimit, 0); + } + void add_uploadSizeLimit(uint32_t uploadSizeLimit) { + fbb_.AddElement(BoardInfo::VT_UPLOADSIZELIMIT, uploadSizeLimit, 0); + } + explicit BoardInfoBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateBoardInfo( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t rankLimit = 0, + uint32_t updateMode = 0, + uint32_t sortMode = 0, + uint32_t uploadNumLimit = 0, + uint32_t uploadSizeLimit = 0) { + BoardInfoBuilder builder_(_fbb); + builder_.add_uploadSizeLimit(uploadSizeLimit); + builder_.add_uploadNumLimit(uploadNumLimit); + builder_.add_sortMode(sortMode); + builder_.add_updateMode(updateMode); + builder_.add_rankLimit(rankLimit); + return builder_.Finish(); +} + +struct RecordScoreRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef RecordScoreRequestBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_BOARDID = 4, + VT_PCID = 6, + VT_SCORE = 8, + VT_COMMENT = 10, + VT_DATA = 12 + }; + uint32_t boardId() const { + return GetField(VT_BOARDID, 0); + } + int32_t pcId() const { + return GetField(VT_PCID, 0); + } + int64_t score() const { + return GetField(VT_SCORE, 0); + } + const flatbuffers::String *comment() const { + return GetPointer(VT_COMMENT); + } + const flatbuffers::Vector *data() const { + return GetPointer *>(VT_DATA); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_BOARDID, 4) && + VerifyField(verifier, VT_PCID, 4) && + VerifyField(verifier, VT_SCORE, 8) && + VerifyOffset(verifier, VT_COMMENT) && + verifier.VerifyString(comment()) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && + verifier.EndTable(); + } +}; + +struct RecordScoreRequestBuilder { + typedef RecordScoreRequest Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_boardId(uint32_t boardId) { + fbb_.AddElement(RecordScoreRequest::VT_BOARDID, boardId, 0); + } + void add_pcId(int32_t pcId) { + fbb_.AddElement(RecordScoreRequest::VT_PCID, pcId, 0); + } + void add_score(int64_t score) { + fbb_.AddElement(RecordScoreRequest::VT_SCORE, score, 0); + } + void add_comment(flatbuffers::Offset comment) { + fbb_.AddOffset(RecordScoreRequest::VT_COMMENT, comment); + } + void add_data(flatbuffers::Offset> data) { + fbb_.AddOffset(RecordScoreRequest::VT_DATA, data); + } + explicit RecordScoreRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateRecordScoreRequest( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t boardId = 0, + int32_t pcId = 0, + int64_t score = 0, + flatbuffers::Offset comment = 0, + flatbuffers::Offset> data = 0) { + RecordScoreRequestBuilder builder_(_fbb); + builder_.add_score(score); + builder_.add_data(data); + builder_.add_comment(comment); + builder_.add_pcId(pcId); + builder_.add_boardId(boardId); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateRecordScoreRequestDirect( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t boardId = 0, + int32_t pcId = 0, + int64_t score = 0, + const char *comment = nullptr, + const std::vector *data = nullptr) { + auto comment__ = comment ? _fbb.CreateString(comment) : 0; + auto data__ = data ? _fbb.CreateVector(*data) : 0; + return CreateRecordScoreRequest( + _fbb, + boardId, + pcId, + score, + comment__, + data__); +} + +struct GetScoreRangeRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef GetScoreRangeRequestBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_BOARDID = 4, + VT_STARTRANK = 6, + VT_NUMRANKS = 8, + VT_WITHCOMMENT = 10, + VT_WITHGAMEINFO = 12 + }; + uint32_t boardId() const { + return GetField(VT_BOARDID, 0); + } + uint32_t startRank() const { + return GetField(VT_STARTRANK, 0); + } + uint32_t numRanks() const { + return GetField(VT_NUMRANKS, 0); + } + bool withComment() const { + return GetField(VT_WITHCOMMENT, 0) != 0; + } + bool withGameInfo() const { + return GetField(VT_WITHGAMEINFO, 0) != 0; + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_BOARDID, 4) && + VerifyField(verifier, VT_STARTRANK, 4) && + VerifyField(verifier, VT_NUMRANKS, 4) && + VerifyField(verifier, VT_WITHCOMMENT, 1) && + VerifyField(verifier, VT_WITHGAMEINFO, 1) && + verifier.EndTable(); + } +}; + +struct GetScoreRangeRequestBuilder { + typedef GetScoreRangeRequest Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_boardId(uint32_t boardId) { + fbb_.AddElement(GetScoreRangeRequest::VT_BOARDID, boardId, 0); + } + void add_startRank(uint32_t startRank) { + fbb_.AddElement(GetScoreRangeRequest::VT_STARTRANK, startRank, 0); + } + void add_numRanks(uint32_t numRanks) { + fbb_.AddElement(GetScoreRangeRequest::VT_NUMRANKS, numRanks, 0); + } + void add_withComment(bool withComment) { + fbb_.AddElement(GetScoreRangeRequest::VT_WITHCOMMENT, static_cast(withComment), 0); + } + void add_withGameInfo(bool withGameInfo) { + fbb_.AddElement(GetScoreRangeRequest::VT_WITHGAMEINFO, static_cast(withGameInfo), 0); + } + explicit GetScoreRangeRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateGetScoreRangeRequest( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t boardId = 0, + uint32_t startRank = 0, + uint32_t numRanks = 0, + bool withComment = false, + bool withGameInfo = false) { + GetScoreRangeRequestBuilder builder_(_fbb); + builder_.add_numRanks(numRanks); + builder_.add_startRank(startRank); + builder_.add_boardId(boardId); + builder_.add_withGameInfo(withGameInfo); + builder_.add_withComment(withComment); + return builder_.Finish(); +} + +struct ScoreNpIdPcId FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef ScoreNpIdPcIdBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_NPID = 4, + VT_PCID = 6 + }; + const flatbuffers::String *npid() const { + return GetPointer(VT_NPID); + } + int32_t pcId() const { + return GetField(VT_PCID, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_NPID) && + verifier.VerifyString(npid()) && + VerifyField(verifier, VT_PCID, 4) && + verifier.EndTable(); + } +}; + +struct ScoreNpIdPcIdBuilder { + typedef ScoreNpIdPcId Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_npid(flatbuffers::Offset npid) { + fbb_.AddOffset(ScoreNpIdPcId::VT_NPID, npid); + } + void add_pcId(int32_t pcId) { + fbb_.AddElement(ScoreNpIdPcId::VT_PCID, pcId, 0); + } + explicit ScoreNpIdPcIdBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateScoreNpIdPcId( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset npid = 0, + int32_t pcId = 0) { + ScoreNpIdPcIdBuilder builder_(_fbb); + builder_.add_pcId(pcId); + builder_.add_npid(npid); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateScoreNpIdPcIdDirect( + flatbuffers::FlatBufferBuilder &_fbb, + const char *npid = nullptr, + int32_t pcId = 0) { + auto npid__ = npid ? _fbb.CreateString(npid) : 0; + return CreateScoreNpIdPcId( + _fbb, + npid__, + pcId); +} + +struct GetScoreNpIdRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef GetScoreNpIdRequestBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_BOARDID = 4, + VT_NPIDS = 6, + VT_WITHCOMMENT = 8, + VT_WITHGAMEINFO = 10 + }; + uint32_t boardId() const { + return GetField(VT_BOARDID, 0); + } + const flatbuffers::Vector> *npids() const { + return GetPointer> *>(VT_NPIDS); + } + bool withComment() const { + return GetField(VT_WITHCOMMENT, 0) != 0; + } + bool withGameInfo() const { + return GetField(VT_WITHGAMEINFO, 0) != 0; + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_BOARDID, 4) && + VerifyOffset(verifier, VT_NPIDS) && + verifier.VerifyVector(npids()) && + verifier.VerifyVectorOfTables(npids()) && + VerifyField(verifier, VT_WITHCOMMENT, 1) && + VerifyField(verifier, VT_WITHGAMEINFO, 1) && + verifier.EndTable(); + } +}; + +struct GetScoreNpIdRequestBuilder { + typedef GetScoreNpIdRequest Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_boardId(uint32_t boardId) { + fbb_.AddElement(GetScoreNpIdRequest::VT_BOARDID, boardId, 0); + } + void add_npids(flatbuffers::Offset>> npids) { + fbb_.AddOffset(GetScoreNpIdRequest::VT_NPIDS, npids); + } + void add_withComment(bool withComment) { + fbb_.AddElement(GetScoreNpIdRequest::VT_WITHCOMMENT, static_cast(withComment), 0); + } + void add_withGameInfo(bool withGameInfo) { + fbb_.AddElement(GetScoreNpIdRequest::VT_WITHGAMEINFO, static_cast(withGameInfo), 0); + } + explicit GetScoreNpIdRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateGetScoreNpIdRequest( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t boardId = 0, + flatbuffers::Offset>> npids = 0, + bool withComment = false, + bool withGameInfo = false) { + GetScoreNpIdRequestBuilder builder_(_fbb); + builder_.add_npids(npids); + builder_.add_boardId(boardId); + builder_.add_withGameInfo(withGameInfo); + builder_.add_withComment(withComment); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateGetScoreNpIdRequestDirect( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t boardId = 0, + const std::vector> *npids = nullptr, + bool withComment = false, + bool withGameInfo = false) { + auto npids__ = npids ? _fbb.CreateVector>(*npids) : 0; + return CreateGetScoreNpIdRequest( + _fbb, + boardId, + npids__, + withComment, + withGameInfo); +} + +struct GetScoreFriendsRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef GetScoreFriendsRequestBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_BOARDID = 4, + VT_INCLUDE_SELF = 6, + VT_MAX = 8, + VT_WITHCOMMENT = 10, + VT_WITHGAMEINFO = 12 + }; + uint32_t boardId() const { + return GetField(VT_BOARDID, 0); + } + bool include_self() const { + return GetField(VT_INCLUDE_SELF, 0) != 0; + } + uint32_t max() const { + return GetField(VT_MAX, 0); + } + bool withComment() const { + return GetField(VT_WITHCOMMENT, 0) != 0; + } + bool withGameInfo() const { + return GetField(VT_WITHGAMEINFO, 0) != 0; + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_BOARDID, 4) && + VerifyField(verifier, VT_INCLUDE_SELF, 1) && + VerifyField(verifier, VT_MAX, 4) && + VerifyField(verifier, VT_WITHCOMMENT, 1) && + VerifyField(verifier, VT_WITHGAMEINFO, 1) && + verifier.EndTable(); + } +}; + +struct GetScoreFriendsRequestBuilder { + typedef GetScoreFriendsRequest Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_boardId(uint32_t boardId) { + fbb_.AddElement(GetScoreFriendsRequest::VT_BOARDID, boardId, 0); + } + void add_include_self(bool include_self) { + fbb_.AddElement(GetScoreFriendsRequest::VT_INCLUDE_SELF, static_cast(include_self), 0); + } + void add_max(uint32_t max) { + fbb_.AddElement(GetScoreFriendsRequest::VT_MAX, max, 0); + } + void add_withComment(bool withComment) { + fbb_.AddElement(GetScoreFriendsRequest::VT_WITHCOMMENT, static_cast(withComment), 0); + } + void add_withGameInfo(bool withGameInfo) { + fbb_.AddElement(GetScoreFriendsRequest::VT_WITHGAMEINFO, static_cast(withGameInfo), 0); + } + explicit GetScoreFriendsRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateGetScoreFriendsRequest( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t boardId = 0, + bool include_self = false, + uint32_t max = 0, + bool withComment = false, + bool withGameInfo = false) { + GetScoreFriendsRequestBuilder builder_(_fbb); + builder_.add_max(max); + builder_.add_boardId(boardId); + builder_.add_withGameInfo(withGameInfo); + builder_.add_withComment(withComment); + builder_.add_include_self(include_self); + return builder_.Finish(); +} + +struct ScoreRankData FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef ScoreRankDataBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_NPID = 4, + VT_ONLINENAME = 6, + VT_PCID = 8, + VT_RANK = 10, + VT_SCORE = 12, + VT_HASGAMEDATA = 14, + VT_RECORDDATE = 16 + }; + const flatbuffers::String *npId() const { + return GetPointer(VT_NPID); + } + const flatbuffers::String *onlineName() const { + return GetPointer(VT_ONLINENAME); + } + int32_t pcId() const { + return GetField(VT_PCID, 0); + } + uint32_t rank() const { + return GetField(VT_RANK, 0); + } + int64_t score() const { + return GetField(VT_SCORE, 0); + } + bool hasGameData() const { + return GetField(VT_HASGAMEDATA, 0) != 0; + } + uint64_t recordDate() const { + return GetField(VT_RECORDDATE, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_NPID) && + verifier.VerifyString(npId()) && + VerifyOffset(verifier, VT_ONLINENAME) && + verifier.VerifyString(onlineName()) && + VerifyField(verifier, VT_PCID, 4) && + VerifyField(verifier, VT_RANK, 4) && + VerifyField(verifier, VT_SCORE, 8) && + VerifyField(verifier, VT_HASGAMEDATA, 1) && + VerifyField(verifier, VT_RECORDDATE, 8) && + verifier.EndTable(); + } +}; + +struct ScoreRankDataBuilder { + typedef ScoreRankData Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_npId(flatbuffers::Offset npId) { + fbb_.AddOffset(ScoreRankData::VT_NPID, npId); + } + void add_onlineName(flatbuffers::Offset onlineName) { + fbb_.AddOffset(ScoreRankData::VT_ONLINENAME, onlineName); + } + void add_pcId(int32_t pcId) { + fbb_.AddElement(ScoreRankData::VT_PCID, pcId, 0); + } + void add_rank(uint32_t rank) { + fbb_.AddElement(ScoreRankData::VT_RANK, rank, 0); + } + void add_score(int64_t score) { + fbb_.AddElement(ScoreRankData::VT_SCORE, score, 0); + } + void add_hasGameData(bool hasGameData) { + fbb_.AddElement(ScoreRankData::VT_HASGAMEDATA, static_cast(hasGameData), 0); + } + void add_recordDate(uint64_t recordDate) { + fbb_.AddElement(ScoreRankData::VT_RECORDDATE, recordDate, 0); + } + explicit ScoreRankDataBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateScoreRankData( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset npId = 0, + flatbuffers::Offset onlineName = 0, + int32_t pcId = 0, + uint32_t rank = 0, + int64_t score = 0, + bool hasGameData = false, + uint64_t recordDate = 0) { + ScoreRankDataBuilder builder_(_fbb); + builder_.add_recordDate(recordDate); + builder_.add_score(score); + builder_.add_rank(rank); + builder_.add_pcId(pcId); + builder_.add_onlineName(onlineName); + builder_.add_npId(npId); + builder_.add_hasGameData(hasGameData); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateScoreRankDataDirect( + flatbuffers::FlatBufferBuilder &_fbb, + const char *npId = nullptr, + const char *onlineName = nullptr, + int32_t pcId = 0, + uint32_t rank = 0, + int64_t score = 0, + bool hasGameData = false, + uint64_t recordDate = 0) { + auto npId__ = npId ? _fbb.CreateString(npId) : 0; + auto onlineName__ = onlineName ? _fbb.CreateString(onlineName) : 0; + return CreateScoreRankData( + _fbb, + npId__, + onlineName__, + pcId, + rank, + score, + hasGameData, + recordDate); +} + +struct ScoreInfo FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef ScoreInfoBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA = 4 + }; + const flatbuffers::Vector *data() const { + return GetPointer *>(VT_DATA); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && + verifier.EndTable(); + } +}; + +struct ScoreInfoBuilder { + typedef ScoreInfo Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_data(flatbuffers::Offset> data) { + fbb_.AddOffset(ScoreInfo::VT_DATA, data); + } + explicit ScoreInfoBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateScoreInfo( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset> data = 0) { + ScoreInfoBuilder builder_(_fbb); + builder_.add_data(data); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateScoreInfoDirect( + flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *data = nullptr) { + auto data__ = data ? _fbb.CreateVector(*data) : 0; + return CreateScoreInfo( + _fbb, + data__); +} + +struct GetScoreResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef GetScoreResponseBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_RANKARRAY = 4, + VT_COMMENTARRAY = 6, + VT_INFOARRAY = 8, + VT_LASTSORTDATE = 10, + VT_TOTALRECORD = 12 + }; + const flatbuffers::Vector> *rankArray() const { + return GetPointer> *>(VT_RANKARRAY); + } + const flatbuffers::Vector> *commentArray() const { + return GetPointer> *>(VT_COMMENTARRAY); + } + const flatbuffers::Vector> *infoArray() const { + return GetPointer> *>(VT_INFOARRAY); + } + uint64_t lastSortDate() const { + return GetField(VT_LASTSORTDATE, 0); + } + uint32_t totalRecord() const { + return GetField(VT_TOTALRECORD, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_RANKARRAY) && + verifier.VerifyVector(rankArray()) && + verifier.VerifyVectorOfTables(rankArray()) && + VerifyOffset(verifier, VT_COMMENTARRAY) && + verifier.VerifyVector(commentArray()) && + verifier.VerifyVectorOfStrings(commentArray()) && + VerifyOffset(verifier, VT_INFOARRAY) && + verifier.VerifyVector(infoArray()) && + verifier.VerifyVectorOfTables(infoArray()) && + VerifyField(verifier, VT_LASTSORTDATE, 8) && + VerifyField(verifier, VT_TOTALRECORD, 4) && + verifier.EndTable(); + } +}; + +struct GetScoreResponseBuilder { + typedef GetScoreResponse Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_rankArray(flatbuffers::Offset>> rankArray) { + fbb_.AddOffset(GetScoreResponse::VT_RANKARRAY, rankArray); + } + void add_commentArray(flatbuffers::Offset>> commentArray) { + fbb_.AddOffset(GetScoreResponse::VT_COMMENTARRAY, commentArray); + } + void add_infoArray(flatbuffers::Offset>> infoArray) { + fbb_.AddOffset(GetScoreResponse::VT_INFOARRAY, infoArray); + } + void add_lastSortDate(uint64_t lastSortDate) { + fbb_.AddElement(GetScoreResponse::VT_LASTSORTDATE, lastSortDate, 0); + } + void add_totalRecord(uint32_t totalRecord) { + fbb_.AddElement(GetScoreResponse::VT_TOTALRECORD, totalRecord, 0); + } + explicit GetScoreResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateGetScoreResponse( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset>> rankArray = 0, + flatbuffers::Offset>> commentArray = 0, + flatbuffers::Offset>> infoArray = 0, + uint64_t lastSortDate = 0, + uint32_t totalRecord = 0) { + GetScoreResponseBuilder builder_(_fbb); + builder_.add_lastSortDate(lastSortDate); + builder_.add_totalRecord(totalRecord); + builder_.add_infoArray(infoArray); + builder_.add_commentArray(commentArray); + builder_.add_rankArray(rankArray); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateGetScoreResponseDirect( + flatbuffers::FlatBufferBuilder &_fbb, + const std::vector> *rankArray = nullptr, + const std::vector> *commentArray = nullptr, + const std::vector> *infoArray = nullptr, + uint64_t lastSortDate = 0, + uint32_t totalRecord = 0) { + auto rankArray__ = rankArray ? _fbb.CreateVector>(*rankArray) : 0; + auto commentArray__ = commentArray ? _fbb.CreateVector>(*commentArray) : 0; + auto infoArray__ = infoArray ? _fbb.CreateVector>(*infoArray) : 0; + return CreateGetScoreResponse( + _fbb, + rankArray__, + commentArray__, + infoArray__, + lastSortDate, + totalRecord); +} + #endif // FLATBUFFERS_GENERATED_NP2STRUCTS_H_ diff --git a/rpcs3/Emu/NP/np_cache.cpp b/rpcs3/Emu/NP/np_cache.cpp index 18e639165d..15f5a69758 100644 --- a/rpcs3/Emu/NP/np_cache.cpp +++ b/rpcs3/Emu/NP/np_cache.cpp @@ -278,7 +278,7 @@ namespace np if (num_binattrs) { ptr_member->roomMemberBinAttrInternal.set(mem.allocate(sizeof(SceNpMatching2RoomMemberBinAttrInternal) * num_binattrs)); - ptr_member->roomMemberBinAttrInternalNum = num_binattrs; + ptr_member->roomMemberBinAttrInternalNum = num_binattrs; SceNpMatching2RoomMemberBinAttrInternal* bin_ptr = ptr_member->roomMemberBinAttrInternal.get_ptr(); u32 actual_cnt = 0; @@ -288,8 +288,8 @@ namespace np { const auto& bin = ::at32(member.bins, binattrs_list[i]); bin_ptr[actual_cnt].updateDate.tick = bin.updateDate.tick; - bin_ptr[actual_cnt].data.id = bin.id; - bin_ptr[actual_cnt].data.size = bin.data.size(); + bin_ptr[actual_cnt].data.id = bin.id; + bin_ptr[actual_cnt].data.size = bin.data.size(); bin_ptr[actual_cnt].data.ptr.set(mem.allocate(bin.data.size())); memcpy(bin_ptr[actual_cnt].data.ptr.get_ptr(), bin.data.data(), bin.data.size()); actual_cnt++; @@ -299,4 +299,15 @@ namespace np return needed_data_size; } -} // namespace np \ No newline at end of file + + SceNpId cache_manager::get_npid(u64 room_id, u16 member_id) + { + std::lock_guard lock(mutex); + + ensure(rooms.contains(room_id), "cache_manager::get_npid: Room not cached!"); + ensure(::at32(rooms, room_id).members.contains(member_id), "cache_manager::get_npid: Member not cached!"); + + return ::at32(::at32(rooms, room_id).members, member_id).userInfo.npId; + } + +} // namespace np diff --git a/rpcs3/Emu/NP/np_cache.h b/rpcs3/Emu/NP/np_cache.h index 8ac7ef54f6..7d66bf3067 100644 --- a/rpcs3/Emu/NP/np_cache.h +++ b/rpcs3/Emu/NP/np_cache.h @@ -75,6 +75,7 @@ namespace np std::pair> get_memberids(u64 room_id, s32 sort_method); std::pair> get_password(SceNpMatching2RoomId room_id); error_code get_member_and_attrs(SceNpMatching2RoomId room_id, SceNpMatching2RoomMemberId member_id, const std::vector& binattrs_list, SceNpMatching2RoomMemberDataInternal* ptr_member, u32 addr_data, u32 size_data); + SceNpId get_npid(u64 room_id, u16 member_id); private: shared_mutex mutex; diff --git a/rpcs3/Emu/NP/np_contexts.cpp b/rpcs3/Emu/NP/np_contexts.cpp index 37f69bfea0..36dcfe95d6 100644 --- a/rpcs3/Emu/NP/np_contexts.cpp +++ b/rpcs3/Emu/NP/np_contexts.cpp @@ -3,6 +3,8 @@ #include "Emu/IdManager.h" +LOG_CHANNEL(sceNp2); + score_ctx::score_ctx(vm::cptr communicationId, vm::cptr passphrase) { ensure(!communicationId->data[9] && strlen(communicationId->data) == 9); @@ -11,25 +13,72 @@ score_ctx::score_ctx(vm::cptr communicationId, vm::cptr communicationId, vm::cptr passphrase) { - return static_cast(idm::make(communicationId, passphrase)); + s32 score_id = idm::make(communicationId, passphrase); + + if (score_id == id_manager::id_traits::invalid) + { + return SCE_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS; + } + + return static_cast(score_id); } bool destroy_score_context(s32 ctx_id) { return idm::remove(static_cast(ctx_id)); } -score_transaction_ctx::score_transaction_ctx(s32 score_context_id) +score_transaction_ctx::score_transaction_ctx(const std::shared_ptr& score) { - this->score_context_id = score_context_id; + pcId = score->pcId; + communicationId = score->communicationId; + passphrase = score->passphrase; + timeout = score->timeout; } -s32 create_score_transaction_context(s32 score_context_id) +score_transaction_ctx::~score_transaction_ctx() { - return static_cast(idm::make(score_context_id)); + if (thread.joinable()) + thread.join(); +} + +s32 create_score_transaction_context(const std::shared_ptr& score) +{ + s32 trans_id = idm::make(score); + + if (trans_id == id_manager::id_traits::invalid) + { + return SCE_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS; + } + + return static_cast(trans_id); } bool destroy_score_transaction_context(s32 ctx_id) { return idm::remove(static_cast(ctx_id)); } +std::optional score_transaction_ctx::get_score_transaction_status() +{ + std::lock_guard lock(mutex); + + return result; +} +void score_transaction_ctx::abort_score_transaction() +{ + std::lock_guard lock(mutex); + + result = SCE_NP_COMMUNITY_ERROR_ABORTED; + wake_cond.notify_one(); +} +void score_transaction_ctx::wait_for_completion() +{ + std::unique_lock lock(mutex); + + if (result) + { + return; + } + + completion_cond.wait(lock); +} match2_ctx::match2_ctx(vm::cptr communicationId, vm::cptr passphrase) { @@ -39,6 +88,7 @@ match2_ctx::match2_ctx(vm::cptr communicationId, vm::cptr< } u16 create_match2_context(vm::cptr communicationId, vm::cptr passphrase) { + sceNp2.notice("Creating match2 context with communicationId: <%s>", static_cast(communicationId->data)); return static_cast(idm::make(communicationId, passphrase)); } bool destroy_match2_context(u16 ctx_id) diff --git a/rpcs3/Emu/NP/np_contexts.h b/rpcs3/Emu/NP/np_contexts.h index dce086675f..7db54e4548 100644 --- a/rpcs3/Emu/NP/np_contexts.h +++ b/rpcs3/Emu/NP/np_contexts.h @@ -1,5 +1,11 @@ #pragma once +#include +#include +#include + +#include "Utilities/mutex.h" + #include "Emu/Memory/vm_ptr.h" #include "Emu/Cell/Modules/sceNp.h" #include "Emu/Cell/Modules/sceNp2.h" @@ -10,29 +16,51 @@ struct score_ctx { score_ctx(vm::cptr communicationId, vm::cptr passphrase); - static const u32 id_base = 1; + static const u32 id_base = 0x2001; static const u32 id_step = 1; static const u32 id_count = 32; SAVESTATE_INIT_POS(25); + shared_mutex mutex; + + u64 timeout = 60'000'000; // 60 seconds + SceNpCommunicationId communicationId{}; SceNpCommunicationPassphrase passphrase{}; + s32 pcId = 0; }; s32 create_score_context(vm::cptr communicationId, vm::cptr passphrase); bool destroy_score_context(s32 ctx_id); struct score_transaction_ctx { - score_transaction_ctx(s32 score_context_id); + score_transaction_ctx(const std::shared_ptr& score); + ~score_transaction_ctx(); + std::optional get_score_transaction_status(); + void abort_score_transaction(); + void wait_for_completion(); - static const u32 id_base = 1; + static const u32 id_base = 0x1001; static const u32 id_step = 1; static const u32 id_count = 32; SAVESTATE_INIT_POS(26); - s32 score_context_id = 0; + shared_mutex mutex; + std::condition_variable_any wake_cond, completion_cond; + + std::optional result; + std::vector data; + std::vector game_data; + + u64 timeout = 60'000'000; // 60 seconds; + + SceNpCommunicationId communicationId{}; + SceNpCommunicationPassphrase passphrase{}; + s32 pcId = 0; + + std::thread thread; }; -s32 create_score_transaction_context(s32 score_context_id); +s32 create_score_transaction_context(const std::shared_ptr& score); bool destroy_score_transaction_context(s32 ctx_id); // Match2 related @@ -62,7 +90,7 @@ struct lookup_title_ctx { lookup_title_ctx(vm::cptr communicationId); - static const u32 id_base = 1; + static const u32 id_base = 0x3001; static const u32 id_step = 1; static const u32 id_count = 32; SAVESTATE_INIT_POS(28); @@ -77,7 +105,7 @@ struct lookup_transaction_ctx { lookup_transaction_ctx(s32 lt_ctx); - static const u32 id_base = 1; + static const u32 id_base = 0x4001; static const u32 id_step = 1; static const u32 id_count = 32; SAVESTATE_INIT_POS(29); @@ -91,7 +119,7 @@ struct commerce2_ctx { commerce2_ctx(u32 version, vm::cptr npid, vm::ptr handler, vm::ptr arg); - static const u32 id_base = 1; + static const u32 id_base = 0x5001; static const u32 id_step = 1; static const u32 id_count = 32; SAVESTATE_INIT_POS(30); @@ -109,7 +137,7 @@ struct signaling_ctx { signaling_ctx(vm::ptr npid, vm::ptr handler, vm::ptr arg); - static const u32 id_base = 1; + static const u32 id_base = 0x6001; static const u32 id_step = 1; static const u32 id_count = 32; SAVESTATE_INIT_POS(31); diff --git a/rpcs3/Emu/NP/np_handler.cpp b/rpcs3/Emu/NP/np_handler.cpp index 108f71f415..0b64950edc 100644 --- a/rpcs3/Emu/NP/np_handler.cpp +++ b/rpcs3/Emu/NP/np_handler.cpp @@ -384,9 +384,7 @@ namespace np return; } - ar(is_NP_Lookup_init, is_NP_Score_init, is_NP2_init, is_NP2_Match2_init, is_NP_Auth_init - , manager_cb, manager_cb_arg, std::as_bytes(std::span(&basic_handler, 1)), is_connected, is_psn_active - , hostname, ether_address, local_ip_addr, public_ip_addr, dns_ip); + ar(is_NP_Lookup_init, is_NP_Score_init, is_NP2_init, is_NP2_Match2_init, is_NP_Auth_init, manager_cb, manager_cb_arg, std::as_bytes(std::span(&basic_handler, 1)), is_connected, is_psn_active, hostname, ether_address, local_ip_addr, public_ip_addr, dns_ip); // Call init func if needed (np_memory is unaffected when an empty pool is provided) init_NP(0, vm::null); @@ -396,6 +394,27 @@ namespace np // TODO: IDM-tied objects are not yet saved } + np_handler::~np_handler() + { + std::unordered_map> moved_trans; + { + std::lock_guard lock(mutex_score_transactions); + moved_trans = std::move(score_transactions); + score_transactions.clear(); + } + + for (auto& [trans_id, trans] : moved_trans) + { + trans->abort_score_transaction(); + } + + for (auto& [trans_id, trans] : moved_trans) + { + if (trans->thread.joinable()) + trans->thread.join(); + } + } + void np_handler::save(utils::serial& ar) { // TODO: See ctor @@ -408,9 +427,7 @@ namespace np USING_SERIALIZATION_VERSION(sceNp); - ar(is_NP_Lookup_init, is_NP_Score_init, is_NP2_init, is_NP2_Match2_init, is_NP_Auth_init - , manager_cb, manager_cb_arg, std::as_bytes(std::span(&basic_handler, 1)), is_connected, is_psn_active - , hostname, ether_address, local_ip_addr, public_ip_addr, dns_ip); + ar(is_NP_Lookup_init, is_NP_Score_init, is_NP2_init, is_NP2_Match2_init, is_NP_Auth_init, manager_cb, manager_cb_arg, std::as_bytes(std::span(&basic_handler, 1)), is_connected, is_psn_active, hostname, ether_address, local_ip_addr, public_ip_addr, dns_ip); np_memory.save(ar); } @@ -454,10 +471,9 @@ namespace np return; } - // First address is used for now, (TODO combobox with possible local addresses to use?) local_ip_addr = *reinterpret_cast(host->h_addr_list[0]); - // Set public address to local discovered address for now, may be updated later; + // Set public address to local discovered address for now, may be updated later from RPCN socket public_ip_addr = local_ip_addr; nph_log.notice("discover_ip_address: IP was determined to be %s", ip_to_string(local_ip_addr)); @@ -662,6 +678,7 @@ namespace np string_to_online_name(rpcn->get_online_name(), &online_name); string_to_avatar_url(rpcn->get_avatar_url(), &avatar_url); public_ip_addr = rpcn->get_addr_sig(); + local_ip_addr = std::bit_cast>(rpcn->get_addr_local()); break; } @@ -799,6 +816,13 @@ namespace np case rpcn::CommandType::SendRoomMessage: reply_send_room_message(req_id, data); break; case rpcn::CommandType::RequestSignalingInfos: reply_req_sign_infos(req_id, data); break; case rpcn::CommandType::RequestTicket: reply_req_ticket(req_id, data); break; + case rpcn::CommandType::GetBoardInfos: reply_get_board_infos(req_id, data); break; + case rpcn::CommandType::RecordScore: reply_record_score(req_id, data); break; + case rpcn::CommandType::StoreScoreData: reply_store_score_data(req_id, data); break; + case rpcn::CommandType::GetScoreData: reply_get_score_data(req_id, data); break; + case rpcn::CommandType::GetScoreRange: reply_get_score_range(req_id, data); break; + case rpcn::CommandType::GetScoreFriends: reply_get_score_friends(req_id, data); break; + case rpcn::CommandType::GetScoreNpid: reply_get_score_npid(req_id, data); break; default: rpcn_log.error("Unknown reply(%d) received!", command); break; } } @@ -896,16 +920,8 @@ namespace np const u32 req_id = get_req_id(optParam ? optParam->appReqId : ctx->default_match2_optparam.appReqId); ret.ctx_id = ctx_id; - ret.cb_arg = optParam ? optParam->cbFuncArg : ctx->default_match2_optparam.cbFuncArg; - - if (optParam && optParam->cbFunc) - { - ret.cb = optParam->cbFunc; - } - else - { - ret.cb = ctx->default_match2_optparam.cbFunc; - } + ret.cb_arg = (optParam && optParam->cbFuncArg) ? optParam->cbFuncArg : ctx->default_match2_optparam.cbFuncArg; + ret.cb = (optParam && optParam->cbFunc) ? optParam->cbFunc : ctx->default_match2_optparam.cbFunc; nph_log.warning("Callback used is 0x%x", ret.cb); diff --git a/rpcs3/Emu/NP/np_handler.h b/rpcs3/Emu/NP/np_handler.h index 204e2efe19..cdf1bd3d73 100644 --- a/rpcs3/Emu/NP/np_handler.h +++ b/rpcs3/Emu/NP/np_handler.h @@ -14,6 +14,7 @@ #include "Emu/NP/np_allocator.h" #include "Emu/NP/np_cache.h" #include "Emu/NP/np_event_data.h" +#include "Emu/NP/np_contexts.h" namespace np { @@ -69,6 +70,7 @@ namespace np SAVESTATE_INIT_POS(5); np_handler(); + ~np_handler(); np_handler(utils::serial& ar); void save(utils::serial& ar); @@ -92,13 +94,13 @@ namespace np void init_NP(u32 poolsize, vm::ptr poolptr); void terminate_NP(); - bool is_netctl_init = false; - bool is_NP_init = false; - bool is_NP_Lookup_init = false; - bool is_NP_Score_init = false; - bool is_NP2_init = false; - bool is_NP2_Match2_init = false; - bool is_NP_Auth_init = false; + atomic_t is_netctl_init = false; + atomic_t is_NP_init = false; + atomic_t is_NP_Lookup_init = false; + atomic_t is_NP_Score_init = false; + atomic_t is_NP2_init = false; + atomic_t is_NP2_Match2_init = false; + atomic_t is_NP_Auth_init = false; // NP Handlers/Callbacks // Seems to be global @@ -151,6 +153,14 @@ namespace np u32 get_match2_event(SceNpMatching2EventKey event_key, u32 dest_addr, u32 size); + // Score requests + void score_async_handler(std::unique_lock lock, const std::shared_ptr& trans_ctx, u32 req_id, bool async); + void get_board_infos(std::shared_ptr& trans_ctx, SceNpScoreBoardId board_id, vm::ptr board_info, bool async); + void record_score(std::shared_ptr& trans_ctx, SceNpScoreBoardId board_id, SceNpScoreValue score, vm::cptr comment, const u8* data, u32 data_size, vm::ptr tmp_rank, bool async); + void get_score_range(std::shared_ptr& trans_ctx, SceNpScoreBoardId boardId, SceNpScoreRankNumber startSerialRank, vm::ptr rankArray, u64 rankArraySize, vm::ptr commentArray, u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, bool async); + void get_score_npid(std::shared_ptr& trans_ctx, SceNpScoreBoardId boardId, const std::vector>& npid_vec, vm::ptr rankArray, u64 rankArraySize, vm::ptr commentArray, u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, bool async); + void get_score_friend(std::shared_ptr& trans_ctx, SceNpScoreBoardId boardId, vm::ptr rankArray, u64 rankArraySize, vm::ptr commentArray, u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, bool async); + // Local functions std::pair> local_get_room_slots(SceNpMatching2RoomId room_id); std::pair> local_get_room_password(SceNpMatching2RoomId room_id); @@ -173,7 +183,7 @@ namespace np // For custom menu struct custom_menu_action { - s32 id = 0; + s32 id = 0; u32 mask = SCE_NP_CUSTOM_MENU_ACTION_MASK_ME; std::string name; }; @@ -220,6 +230,13 @@ namespace np bool reply_send_room_message(u32 req_id, std::vector& reply_data); bool reply_req_sign_infos(u32 req_id, std::vector& reply_data); bool reply_req_ticket(u32 req_id, std::vector& reply_data); + bool reply_get_board_infos(u32 req_id, std::vector& reply_data); + bool reply_record_score(u32 req_id, std::vector& reply_data); + bool reply_store_score_data(u32 req_id, std::vector& reply_data); + bool reply_get_score_data(u32 req_id, std::vector& reply_data); + bool reply_get_score_range(u32 req_id, std::vector& reply_data); + bool reply_get_score_friends(u32 req_id, std::vector& reply_data); + bool reply_get_score_npid(u32 req_id, std::vector& reply_data); // Helper functions(fb=>np2) void BinAttr_to_SceNpMatching2BinAttr(event_data& edata, const BinAttr* bin_attr, SceNpMatching2BinAttr* binattr_info); @@ -240,6 +257,7 @@ namespace np void RoomMessageInfo_to_SceNpMatching2RoomMessageInfo(event_data& edata, const RoomMessageInfo* mi, SceNpMatching2RoomMessageInfo* sce_mi); void RoomDataInternalUpdateInfo_to_SceNpMatching2RoomDataInternalUpdateInfo(event_data& edata, const RoomDataInternalUpdateInfo* update_info, SceNpMatching2RoomDataInternalUpdateInfo* sce_update_info, const SceNpId& npid); void RoomMemberDataInternalUpdateInfo_to_SceNpMatching2RoomMemberDataInternalUpdateInfo(event_data& edata, const RoomMemberDataInternalUpdateInfo* update_info, SceNpMatching2RoomMemberDataInternalUpdateInfo* sce_update_info); + bool handle_GetScoreResponse(u32 req_id, std::vector& reply_data); struct callback_info { @@ -298,6 +316,10 @@ namespace np } event_data& allocate_req_result(u32 event_key, u32 max_size, u32 initial_size); + // Async score threads + shared_mutex mutex_score_transactions; + std::unordered_map> score_transactions; // (req_id, transaction_ctx) + // RPCN shared_mutex mutex_rpcn; std::shared_ptr rpcn; diff --git a/rpcs3/Emu/NP/np_helpers.cpp b/rpcs3/Emu/NP/np_helpers.cpp index 610fcce5b8..dc06bdb09a 100644 --- a/rpcs3/Emu/NP/np_helpers.cpp +++ b/rpcs3/Emu/NP/np_helpers.cpp @@ -22,24 +22,24 @@ namespace np return fmt::format("%02X:%02X:%02X:%02X:%02X:%02X", ether[0], ether[1], ether[2], ether[3], ether[4], ether[5]); } - void string_to_npid(const std::string& str, SceNpId* npid) + void string_to_npid(std::string_view str, SceNpId* npid) { memset(npid, 0, sizeof(SceNpId)); strcpy_trunc(npid->handle.data, str); // npid->reserved[0] = 1; } - void string_to_online_name(const std::string& str, SceNpOnlineName* online_name) + void string_to_online_name(std::string_view str, SceNpOnlineName* online_name) { strcpy_trunc(online_name->data, str); } - void string_to_avatar_url(const std::string& str, SceNpAvatarUrl* avatar_url) + void string_to_avatar_url(std::string_view str, SceNpAvatarUrl* avatar_url) { strcpy_trunc(avatar_url->data, str); } - void string_to_communication_id(const std::string& str, SceNpCommunicationId* comm_id) + void string_to_communication_id(std::string_view str, SceNpCommunicationId* comm_id) { strcpy_trunc(comm_id->data, str); } diff --git a/rpcs3/Emu/NP/np_helpers.h b/rpcs3/Emu/NP/np_helpers.h index 3da4a835c0..f80f1c9def 100644 --- a/rpcs3/Emu/NP/np_helpers.h +++ b/rpcs3/Emu/NP/np_helpers.h @@ -7,8 +7,8 @@ namespace np std::string ip_to_string(u32 addr); std::string ether_to_string(std::array& ether); - void string_to_npid(const std::string& str, SceNpId* npid); - void string_to_online_name(const std::string& str, SceNpOnlineName* online_name); - void string_to_avatar_url(const std::string& str, SceNpAvatarUrl* avatar_url); - void string_to_communication_id(const std::string& str, SceNpCommunicationId* comm_id); + void string_to_npid(std::string_view str, SceNpId* npid); + void string_to_online_name(std::string_view str, SceNpOnlineName* online_name); + void string_to_avatar_url(std::string_view str, SceNpAvatarUrl* avatar_url); + void string_to_communication_id(std::string_view str, SceNpCommunicationId* comm_id); } diff --git a/rpcs3/Emu/NP/np_notifications.cpp b/rpcs3/Emu/NP/np_notifications.cpp index f96eb4d061..8833781d52 100644 --- a/rpcs3/Emu/NP/np_notifications.cpp +++ b/rpcs3/Emu/NP/np_notifications.cpp @@ -221,7 +221,7 @@ namespace np // Attempt Signaling auto& sigh = g_fxo->get>(); - sigh.set_sig2_infos(room_id, member_id, SCE_NP_SIGNALING_CONN_STATUS_PENDING, addr_p2p, port_p2p); + sigh.set_sig2_infos(room_id, member_id, SCE_NP_SIGNALING_CONN_STATUS_PENDING, addr_p2p, port_p2p, np_cache.get_npid(room_id, member_id)); sigh.start_sig2(room_id, member_id); } } // namespace np diff --git a/rpcs3/Emu/NP/np_requests.cpp b/rpcs3/Emu/NP/np_requests.cpp index 95f2978f71..3cbe7dbc27 100644 --- a/rpcs3/Emu/NP/np_requests.cpp +++ b/rpcs3/Emu/NP/np_requests.cpp @@ -6,6 +6,7 @@ #include "Emu/IdManager.h" #include "np_handler.h" #include "np_contexts.h" +#include "np_helpers.h" #include "np_structs_extra.h" LOG_CHANNEL(rpcn_log, "rpcn"); @@ -163,6 +164,8 @@ namespace np { u32 req_id = generate_callback_info(ctx_id, optParam); + extra_nps::print_createjoinroom(req); + if (!rpcn->createjoin_room(req_id, get_match2_context(ctx_id)->communicationId, req)) { rpcn_log.error("Disconnecting from RPCN!"); @@ -200,7 +203,7 @@ namespace np // Establish Matching2 self signaling info auto& sigh = g_fxo->get>(); sigh.set_self_sig2_info(room_info->roomId, 1); - sigh.set_sig2_infos(room_info->roomId, 1, SCE_NP_SIGNALING_CONN_STATUS_ACTIVE, rpcn->get_addr_sig(), rpcn->get_port_sig(), true); + sigh.set_sig2_infos(room_info->roomId, 1, SCE_NP_SIGNALING_CONN_STATUS_ACTIVE, rpcn->get_addr_sig(), rpcn->get_port_sig(), get_npid(), true); // TODO? Should this send a message to Signaling CB? Is this even necessary? extra_nps::print_create_room_resp(room_resp); @@ -221,6 +224,8 @@ namespace np { u32 req_id = generate_callback_info(ctx_id, optParam); + extra_nps::print_joinroom(req); + if (!rpcn->join_room(req_id, get_match2_context(ctx_id)->communicationId, req)) { rpcn_log.error("Disconnecting from RPCN!"); @@ -257,7 +262,7 @@ namespace np // Establish Matching2 self signaling info auto& sigh = g_fxo->get>(); sigh.set_self_sig2_info(room_info->roomId, member_id); - sigh.set_sig2_infos(room_info->roomId, member_id, SCE_NP_SIGNALING_CONN_STATUS_ACTIVE, rpcn->get_addr_sig(), rpcn->get_port_sig(), true); + sigh.set_sig2_infos(room_info->roomId, member_id, SCE_NP_SIGNALING_CONN_STATUS_ACTIVE, rpcn->get_addr_sig(), rpcn->get_port_sig(), get_npid(), true); // TODO? Should this send a message to Signaling CB? Is this even necessary? if (cb_info.cb) @@ -316,6 +321,8 @@ namespace np { u32 req_id = generate_callback_info(ctx_id, optParam); + extra_nps::print_search_room(req); + if (!rpcn->search_room(req_id, get_match2_context(ctx_id)->communicationId, req)) { rpcn_log.error("Disconnecting from RPCN!"); @@ -342,6 +349,8 @@ namespace np SearchRoomResponse_to_SceNpMatching2SearchRoomResponse(edata, resp, search_resp); np_memory.shrink_allocation(edata.addr(), edata.size()); + extra_nps::print_search_room_resp(search_resp); + if (cb_info.cb) { sysutil_register_cb([=, size = edata.size()](ppu_thread& cb_ppu) -> s32 @@ -358,6 +367,8 @@ namespace np { u32 req_id = generate_callback_info(ctx_id, optParam); + extra_nps::print_get_roomdata_external_list_req(req); + if (!rpcn->get_roomdata_external_list(req_id, get_match2_context(ctx_id)->communicationId, req)) { rpcn_log.error("Disconnecting from RPCN!"); @@ -384,6 +395,8 @@ namespace np GetRoomDataExternalListResponse_to_SceNpMatching2GetRoomDataExternalListResponse(edata, resp, sce_get_room_ext_resp); np_memory.shrink_allocation(edata.addr(), edata.size()); + extra_nps::print_get_roomdata_external_list_resp(sce_get_room_ext_resp); + if (cb_info.cb) { sysutil_register_cb([=, size = edata.size()](ppu_thread& cb_ppu) -> s32 @@ -482,6 +495,8 @@ namespace np { u32 req_id = generate_callback_info(ctx_id, optParam); + // TODO: extra_nps::print_set_roomdata_req(req); + extra_nps::print_set_roomdata_int_req(req); if (!rpcn->set_roomdata_internal(req_id, get_match2_context(ctx_id)->communicationId, req)) @@ -515,6 +530,8 @@ namespace np { u32 req_id = generate_callback_info(ctx_id, optParam); + extra_nps::print_set_roommemberdata_int_req(req); + if (!rpcn->set_roommemberdata_internal(req_id, get_match2_context(ctx_id)->communicationId, req)) { rpcn_log.error("Disconnecting from RPCN!"); @@ -703,4 +720,342 @@ namespace np return true; } + void np_handler::score_async_handler(std::unique_lock lock, const std::shared_ptr& trans_ctx, u32 req_id, bool async) + { + auto worker_function = [trans_ctx = trans_ctx, req_id, this](std::unique_lock lock) + { + auto res = trans_ctx->wake_cond.wait_for(lock, std::chrono::microseconds(trans_ctx->timeout)); + { + std::lock_guard lock_threads(this->mutex_score_transactions); + this->score_transactions.erase(req_id); + } + + if (res == std::cv_status::timeout) + { + trans_ctx->result = SCE_NP_COMMUNITY_ERROR_TIMEOUT; + return; + } + + // Only 2 cases should be timeout or caller setting result + ensure(trans_ctx->result); + + trans_ctx->completion_cond.notify_one(); + }; + + { + std::lock_guard lock_score(mutex_score_transactions); + ensure(score_transactions.insert({req_id, trans_ctx}).second); + } + + if (async) + { + trans_ctx->thread = std::thread(worker_function, std::move(lock)); + return; + } + + return worker_function(std::move(lock)); + } + + void np_handler::get_board_infos(std::shared_ptr& trans_ctx, SceNpScoreBoardId board_id, vm::ptr board_info, bool async) + { + std::unique_lock lock(trans_ctx->mutex); + + u32 req_id = get_req_id(0x3334); + std::string comm_id(static_cast(trans_ctx->communicationId.data)); + + trans_ctx->data.clear(); + trans_ctx->data.push_back(board_info.addr()); + rpcn->get_board_infos(req_id, trans_ctx->communicationId, board_id); + + return score_async_handler(std::move(lock), trans_ctx, req_id, async); + } + + bool np_handler::reply_get_board_infos(u32 req_id, std::vector& reply_data) + { + vec_stream reply(reply_data, 1); + + auto raw_board_info = reply.get_rawdata(); + auto* resp = flatbuffers::GetRoot(raw_board_info.data()); + + SceNpScoreBoardInfo board_info; + + board_info.rankLimit = resp->rankLimit(); + board_info.updateMode = resp->updateMode(); + board_info.sortMode = resp->sortMode(); + board_info.uploadNumLimit = resp->uploadNumLimit(); + board_info.uploadSizeLimit = resp->uploadSizeLimit(); + + std::lock_guard lock_trans(mutex_score_transactions); + if (!score_transactions.count(req_id)) + { + rpcn_log.error("Couldn't find transaction(%d) in trans_id!", req_id); + return false; + } + + auto trans = ::at32(score_transactions, req_id); + std::lock_guard lock(trans->mutex); + ensure(trans->data.size() == 1); + vm::ptr boardinfo_ptr = vm::cast(trans->data[0]); + memcpy(reinterpret_cast(boardinfo_ptr.get_ptr()), &board_info, sizeof(SceNpScoreBoardInfo)); + trans->result = CELL_OK; + trans->wake_cond.notify_one(); + + return true; + } + + void np_handler::record_score(std::shared_ptr& trans_ctx, SceNpScoreBoardId board_id, SceNpScoreValue score, vm::cptr comment, const u8* data, u32 data_size, vm::ptr tmp_rank, bool async) + { + std::unique_lock lock(trans_ctx->mutex); + u32 req_id = get_req_id(0x3334); + std::optional str_comment = comment ? std::optional(std::string(reinterpret_cast(comment->data))) : std::nullopt; + std::optional> vec_data = data ? std::optional(std::vector(data, data + data_size)) : std::nullopt; + trans_ctx->data.clear(); + if (tmp_rank) + { + trans_ctx->data.push_back(tmp_rank.addr()); + } + + rpcn->record_score(req_id, trans_ctx->communicationId, board_id, trans_ctx->pcId, score, str_comment, vec_data); + + return score_async_handler(std::move(lock), trans_ctx, req_id, async); + } + + bool np_handler::reply_record_score(u32 req_id, std::vector& reply_data) + { + std::lock_guard lock_trans(mutex_score_transactions); + if (!score_transactions.count(req_id)) + { + rpcn_log.error("Couldn't find transaction(%d) in trans_id!", req_id); + return false; + } + + auto trans = ::at32(score_transactions, req_id); + std::lock_guard lock(trans->mutex); + + if (rpcn::is_error(static_cast(reply_data[0]))) + { + switch (reply_data[0]) + { + case rpcn::ErrorType::ScoreNotBest: + { + trans->result = SCE_NP_COMMUNITY_SERVER_ERROR_NOT_BEST_SCORE; + trans->wake_cond.notify_one(); + return true; + } + default: return false; + } + } + + vec_stream reply(reply_data, 1); + auto tmp_rank = reply.get(); + + if (reply.is_error()) + { + rpcn_log.error("Error parsing response in reply_record_score"); + return false; + } + + ensure(trans->data.size() == 1 || trans->data.size() == 0); + if (!trans->data.empty()) + { + vm::ptr tmprank_ptr = vm::cast(trans->data[0]); + *tmprank_ptr = tmp_rank; + } + + trans->result = CELL_OK; + trans->wake_cond.notify_one(); + return true; + } + + bool np_handler::reply_store_score_data(u32 req_id, std::vector& reply_data) + { + return true; + } + bool np_handler::reply_get_score_data(u32 req_id, std::vector& reply_data) + { + return true; + } + + void np_handler::get_score_range(std::shared_ptr& trans_ctx, SceNpScoreBoardId boardId, SceNpScoreRankNumber startSerialRank, vm::ptr rankArray, [[maybe_unused]] u64 rankArraySize, vm::ptr commentArray, [[maybe_unused]] u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, bool async) + { + std::unique_lock lock(trans_ctx->mutex); + u32 req_id = get_req_id(0x3334); + trans_ctx->data.clear(); + trans_ctx->data.push_back(static_cast(arrayNum)); + trans_ctx->data.push_back(rankArray.addr()); + trans_ctx->data.push_back(commentArray.addr()); + trans_ctx->data.push_back(infoArray.addr()); + trans_ctx->data.push_back(static_cast((arrayNum * sizeof(SceNpScoreGameInfo)) == infoArraySize)); + trans_ctx->data.push_back(lastSortDate.addr()); + trans_ctx->data.push_back(totalRecord.addr()); + + bool with_comments = !!commentArray; + bool with_gameinfo = !!infoArray; + + rpcn->get_score_range(req_id, trans_ctx->communicationId, boardId, startSerialRank, arrayNum, with_comments, with_gameinfo); + + return score_async_handler(std::move(lock), trans_ctx, req_id, async); + } + + bool np_handler::handle_GetScoreResponse(u32 req_id, std::vector& reply_data) + { + std::lock_guard lock_trans(mutex_score_transactions); + if (!score_transactions.count(req_id)) + { + rpcn_log.error("Couldn't find transaction(%d) in trans_id!", req_id); + return false; + } + + auto trans_ctx = ::at32(score_transactions, req_id); + std::lock_guard lock(trans_ctx->mutex); + + if (rpcn::is_error(static_cast(reply_data[0]))) + { + return false; + } + + vec_stream reply(reply_data, 1); + auto raw_getscore_response = reply.get_rawdata(); + auto* resp = flatbuffers::GetRoot(raw_getscore_response.data()); + flatbuffers::Verifier verifier(raw_getscore_response.data(), raw_getscore_response.size()); + + if (reply.is_error() || !resp->Verify(verifier)) + { + rpcn_log.error("Error parsing response in reply_record_score"); + return false; + } + + ensure(trans_ctx->data.size() == 7); + u32 arrayNum = trans_ctx->data[0]; + vm::ptr rankArray = vm::cast(trans_ctx->data[1]); + vm::ptr commentArray = vm::cast(trans_ctx->data[2]); + vm::ptr infoArray = vm::cast(trans_ctx->data[3]); + bool info_array_is_SceNpScoreGameInfo = trans_ctx->data[4]; + vm::ptr lastSortDate = vm::cast(trans_ctx->data[5]); + vm::ptr totalRecord = vm::cast(trans_ctx->data[6]); + + ensure(resp->rankArray() && resp->rankArray()->size() <= arrayNum); + + memset(rankArray.get_ptr(), 0, sizeof(SceNpScoreRankData) * arrayNum); + auto* fb_rankarray = resp->rankArray(); + for (flatbuffers::uoffset_t i = 0; i < fb_rankarray->size(); i++) + { + const auto* fb_rankdata = fb_rankarray->Get(i); + ensure(fb_rankdata->npId() && fb_rankdata->onlineName()); + string_to_npid(fb_rankdata->npId()->string_view(), &rankArray[i].npId); + string_to_online_name(fb_rankdata->onlineName()->string_view(), &rankArray[i].onlineName); + rankArray[i].pcId = fb_rankdata->pcId(); + rankArray[i].serialRank = fb_rankdata->rank(); + rankArray[i].rank = fb_rankdata->rank(); + rankArray[i].highestRank = fb_rankdata->rank(); + rankArray[i].scoreValue = fb_rankdata->score(); + rankArray[i].hasGameData = fb_rankdata->hasGameData(); + rankArray[i].recordDate.tick = fb_rankdata->recordDate(); + } + + if (commentArray) + { + ensure(resp->commentArray() && resp->commentArray()->size() <= arrayNum); + memset(commentArray.get_ptr(), 0, sizeof(SceNpScoreComment) * arrayNum); + + auto* fb_commentarray = resp->commentArray(); + for (flatbuffers::uoffset_t i = 0; i < fb_commentarray->size(); i++) + { + const auto* fb_comment = fb_commentarray->Get(i); + strcpy_trunc(commentArray[i].data, fb_comment->string_view()); + } + } + + if (infoArray) + { + ensure(resp->infoArray() && resp->infoArray()->size() <= arrayNum); + auto* fb_infoarray = resp->infoArray(); + + if (info_array_is_SceNpScoreGameInfo) + { + vm::ptr ptr_gameinfo = vm::static_ptr_cast(infoArray); + memset(ptr_gameinfo.get_ptr(), 0, sizeof(SceNpScoreGameInfo) * arrayNum); + for (flatbuffers::uoffset_t i = 0; i < fb_infoarray->size(); i++) + { + const auto* fb_info = fb_infoarray->Get(i); + ensure(fb_info->data()->size() <= SCE_NP_SCORE_GAMEINFO_SIZE); + memcpy(ptr_gameinfo[i].nativeData, fb_info->data()->data(), fb_info->data()->size()); + } + } + else + { + vm::ptr ptr_vargameinfo = vm::static_ptr_cast(infoArray); + memset(ptr_vargameinfo.get_ptr(), 0, sizeof(SceNpScoreVariableSizeGameInfo) * arrayNum); + for (flatbuffers::uoffset_t i = 0; i < fb_infoarray->size(); i++) + { + const auto* fb_info = fb_infoarray->Get(i); + ensure(fb_info->data()->size() <= SCE_NP_SCORE_VARIABLE_SIZE_GAMEINFO_MAXSIZE); + ptr_vargameinfo[i].infoSize = fb_info->data()->size(); + memcpy(ptr_vargameinfo[i].data, fb_info->data(), fb_info->data()->size()); + } + } + } + + lastSortDate->tick = resp->lastSortDate(); + *totalRecord = resp->totalRecord(); + + trans_ctx->result = not_an_error(fb_rankarray->size()); + trans_ctx->wake_cond.notify_one(); + return true; + } + bool np_handler::reply_get_score_range(u32 req_id, std::vector& reply_data) + { + return handle_GetScoreResponse(req_id, reply_data); + } + + void np_handler::get_score_friend(std::shared_ptr& trans_ctx, SceNpScoreBoardId boardId, vm::ptr rankArray, [[maybe_unused]] u64 rankArraySize, vm::ptr commentArray, [[maybe_unused]] u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, bool async) + { + std::unique_lock lock(trans_ctx->mutex); + u32 req_id = get_req_id(0x3334); + trans_ctx->data.clear(); + trans_ctx->data.push_back(static_cast(arrayNum)); + trans_ctx->data.push_back(rankArray.addr()); + trans_ctx->data.push_back(commentArray.addr()); + trans_ctx->data.push_back(infoArray.addr()); + trans_ctx->data.push_back(static_cast((arrayNum * sizeof(SceNpScoreGameInfo)) == infoArraySize)); + trans_ctx->data.push_back(lastSortDate.addr()); + trans_ctx->data.push_back(totalRecord.addr()); + + bool with_comments = !!commentArray; + bool with_gameinfo = !!infoArray; + + rpcn->get_score_friend(req_id, trans_ctx->communicationId, boardId, with_comments, with_gameinfo); + + return score_async_handler(std::move(lock), trans_ctx, req_id, async); + } + bool np_handler::reply_get_score_friends(u32 req_id, std::vector& reply_data) + { + return handle_GetScoreResponse(req_id, reply_data); + } + + void np_handler::get_score_npid(std::shared_ptr& trans_ctx, SceNpScoreBoardId boardId, const std::vector>& npid_vec, vm::ptr rankArray, [[maybe_unused]] u64 rankArraySize, vm::ptr commentArray, [[maybe_unused]] u64 commentArraySize, vm::ptr infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, bool async) + { + std::unique_lock lock(trans_ctx->mutex); + u32 req_id = get_req_id(0x3334); + trans_ctx->data.clear(); + trans_ctx->data.push_back(static_cast(arrayNum)); + trans_ctx->data.push_back(rankArray.addr()); + trans_ctx->data.push_back(commentArray.addr()); + trans_ctx->data.push_back(infoArray.addr()); + trans_ctx->data.push_back(static_cast((arrayNum * sizeof(SceNpScoreGameInfo)) == infoArraySize)); + trans_ctx->data.push_back(lastSortDate.addr()); + trans_ctx->data.push_back(totalRecord.addr()); + + bool with_comments = !!commentArray; + bool with_gameinfo = !!infoArray; + + rpcn->get_score_npid(req_id, trans_ctx->communicationId, boardId, npid_vec, with_comments, with_gameinfo); + + return score_async_handler(std::move(lock), trans_ctx, req_id, async); + } + bool np_handler::reply_get_score_npid(u32 req_id, std::vector& reply_data) + { + return handle_GetScoreResponse(req_id, reply_data); + } + } // namespace np diff --git a/rpcs3/Emu/NP/np_structs_extra.cpp b/rpcs3/Emu/NP/np_structs_extra.cpp index 29abc7b59e..aad69fa80a 100644 --- a/rpcs3/Emu/NP/np_structs_extra.cpp +++ b/rpcs3/Emu/NP/np_structs_extra.cpp @@ -8,6 +8,14 @@ LOG_CHANNEL(sceNp2); namespace extra_nps { + void print_userinfo2(const SceNpUserInfo2* user) + { + sceNp2.warning("SceNpUserInfo2:"); + sceNp2.warning("npid: %s", static_cast(user->npId.handle.data)); + sceNp2.warning("onlineName: *0x%x(%s)", user->onlineName, user->onlineName ? static_cast(user->onlineName->data) : ""); + sceNp2.warning("avatarUrl: *0x%x(%s)", user->avatarUrl, user->avatarUrl ? static_cast(user->avatarUrl->data) : ""); + } + void print_sigoptparam(const SceNpMatching2SignalingOptParam* opt) { sceNp2.warning("type: %d", opt->type); @@ -49,6 +57,13 @@ namespace extra_nps sceNp2.warning("Data: %s", dadata); } + void print_range(const SceNpMatching2Range* range) + { + sceNp2.warning("startIndex: %d", range->startIndex); + sceNp2.warning("total: %d", range->total); + sceNp2.warning("size: %d", range->size); + } + void print_range_filter(const SceNpMatching2RangeFilter* filt) { sceNp2.warning("startIndex: %d", filt->startIndex); @@ -133,6 +148,20 @@ namespace extra_nps sceNp2.warning("attrIdNum: %d", req->attrIdNum); } + void print_search_room_resp(const SceNpMatching2SearchRoomResponse* resp) + { + sceNp2.warning("SceNpMatching2SearchRoomResponse:"); + print_range(&resp->range); + + const SceNpMatching2RoomDataExternal *room_ptr = resp->roomDataExternal.get_ptr(); + for (u32 i = 0; i < resp->range.total; i++) + { + sceNp2.warning("SceNpMatching2SearchRoomResponse[%d]:", i); + print_room_data_external(room_ptr); + room_ptr = room_ptr->next.get_ptr(); + } + } + void print_room_member_data_internal(const SceNpMatching2RoomMemberDataInternal* member) { sceNp2.warning("SceNpMatching2RoomMemberDataInternal:"); @@ -182,6 +211,38 @@ namespace extra_nps print_bin_attr_internal(&room->roomBinAttrInternal[i]); } + void print_room_data_external(const SceNpMatching2RoomDataExternal* room) + { + sceNp2.warning("SceNpMatching2RoomDataExternal:"); + sceNp2.warning("next: *0x%x", room->next); + sceNp2.warning("serverId: %d", room->serverId); + sceNp2.warning("worldId: %d", room->worldId); + sceNp2.warning("publicSlotNum: %d", room->publicSlotNum); + sceNp2.warning("privateSlotNum: %d", room->privateSlotNum); + sceNp2.warning("lobbyId: %d", room->lobbyId); + sceNp2.warning("roomId: %d", room->roomId); + sceNp2.warning("openPublicSlotNum: %d", room->openPublicSlotNum); + sceNp2.warning("maxSlot: %d", room->maxSlot); + sceNp2.warning("openPrivateSlotNum: %d", room->openPrivateSlotNum); + sceNp2.warning("curMemberNum: %d", room->curMemberNum); + sceNp2.warning("SceNpMatching2RoomPasswordSlotMask: 0x%x", room->passwordSlotMask); + sceNp2.warning("owner: *0x%x", room->owner); + print_userinfo2(room->owner.get_ptr()); + sceNp2.warning("roomGroup: *0x%x", room->roomGroup); + // TODO: print roomGroup + sceNp2.warning("roomGroupNum: %d", room->roomGroupNum); + sceNp2.warning("flagAttr: 0x%x", room->flagAttr); + sceNp2.warning("roomSearchableIntAttrExternal: *0x%x", room->roomSearchableIntAttrExternal); + sceNp2.warning("roomSearchableIntAttrExternalNum: %d", room->roomSearchableIntAttrExternalNum); + // TODO: print roomSearchableIntAttrExternal + sceNp2.warning("roomSearchableBinAttrExternal: *0x%x", room->roomSearchableBinAttrExternal); + sceNp2.warning("roomSearchableBinAttrExternalNum: %d", room->roomSearchableBinAttrExternalNum); + // TODO: print roomSearchableBinAttrExternal + sceNp2.warning("roomBinAttrExternal: *0x%x", room->roomBinAttrExternal); + sceNp2.warning("roomBinAttrExternalNum: %d", room->roomBinAttrExternalNum); + // TODO: print roomBinAttrExternal + } + void print_create_room_resp(const SceNpMatching2CreateJoinRoomResponse* resp) { sceNp2.warning("SceNpMatching2CreateJoinRoomResponse:"); @@ -231,4 +292,37 @@ namespace extra_nps print_bin_attr(&req->roomMemberBinAttrInternal[i]); } + void print_get_roomdata_external_list_req(const SceNpMatching2GetRoomDataExternalListRequest* req) + { + sceNp2.warning("SceNpMatching2GetRoomDataExternalListRequest:"); + sceNp2.warning("roomId: *0x%x", req->roomId); + sceNp2.warning("roomIdNum: %d", req->roomIdNum); + for (u32 i = 0; i < req->roomIdNum; i++) + { + sceNp2.warning("RoomId[%d] = %d", i, req->roomId[i]); + } + sceNp2.warning("attrId: *0x%x", req->attrId); + sceNp2.warning("attrIdNum: %d", req->attrIdNum); + for (u32 i = 0; i < req->attrIdNum; i++) + { + sceNp2.warning("attrId[%d] = %d", i, req->attrId[i]); + } + } + + void print_get_roomdata_external_list_resp(const SceNpMatching2GetRoomDataExternalListResponse* resp) + { + sceNp2.warning("SceNpMatching2GetRoomDataExternalListResponse:"); + sceNp2.warning("roomDataExternal: *0x%x", resp->roomDataExternal); + sceNp2.warning("roomDataExternalNum: %d", resp->roomDataExternalNum); + + const SceNpMatching2RoomDataExternal* cur_room = resp->roomDataExternal.get_ptr(); + + for (u32 i = 0; i < resp->roomDataExternalNum; i++) + { + sceNp2.warning("SceNpMatching2GetRoomDataExternalListResponse[%d]:", i); + print_room_data_external(cur_room); + cur_room = cur_room->next.get_ptr(); + } + } + } // namespace extra_nps diff --git a/rpcs3/Emu/NP/np_structs_extra.h b/rpcs3/Emu/NP/np_structs_extra.h index 2e6c911d36..9a8af63898 100644 --- a/rpcs3/Emu/NP/np_structs_extra.h +++ b/rpcs3/Emu/NP/np_structs_extra.h @@ -4,18 +4,23 @@ namespace extra_nps { + void print_userinfo2(const SceNpUserInfo2* user); void print_sigoptparam(const SceNpMatching2SignalingOptParam* opt); void print_bin_attr(const SceNpMatching2BinAttr* bin); void print_presence_data(const SceNpMatching2PresenceOptionData* opt); void print_range_filter(const SceNpMatching2RangeFilter* filt); void print_room_data_internal(const SceNpMatching2RoomDataInternal* room); + void print_room_data_external(const SceNpMatching2RoomDataExternal* room); void print_room_member_data_internal(const SceNpMatching2RoomMemberDataInternal* member); void print_createjoinroom(const SceNpMatching2CreateJoinRoomRequest* req); void print_create_room_resp(const SceNpMatching2CreateJoinRoomResponse* resp); void print_joinroom(const SceNpMatching2JoinRoomRequest* req); void print_search_room(const SceNpMatching2SearchRoomRequest* req); + void print_search_room_resp(const SceNpMatching2SearchRoomResponse* resp); void print_set_roomdata_ext_req(const SceNpMatching2SetRoomDataExternalRequest* req); void print_set_roomdata_int_req(const SceNpMatching2SetRoomDataInternalRequest* req); void print_set_roommemberdata_int_req(const SceNpMatching2SetRoomMemberDataInternalRequest* req); + void print_get_roomdata_external_list_req(const SceNpMatching2GetRoomDataExternalListRequest* req); + void print_get_roomdata_external_list_resp(const SceNpMatching2GetRoomDataExternalListResponse* resp); } // namespace extra_nps diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp index dd239f079d..cd4ec4499a 100644 --- a/rpcs3/Emu/NP/rpcn_client.cpp +++ b/rpcs3/Emu/NP/rpcn_client.cpp @@ -12,6 +12,7 @@ #include "Emu/System.h" #include "Emu/NP/rpcn_config.h" #include "Emu/NP/np_helpers.h" +#include "Emu/NP/vport0.h" #include "util/asm.hpp" @@ -43,10 +44,6 @@ LOG_CHANNEL(rpcn_log, "rpcn"); -// Those are defined here to avoid including sys_net.h -s32 send_packet_from_p2p_port(const std::vector& data, const sockaddr_in& addr); -std::vector> get_rpcn_msgs(); - namespace rpcn { localized_string_id rpcn_state_to_localized_string_id(rpcn::rpcn_state state) @@ -74,10 +71,49 @@ namespace rpcn return get_localized_string(rpcn_state_to_localized_string_id(state)); } - constexpr u32 RPCN_PROTOCOL_VERSION = 15; - constexpr usz RPCN_HEADER_SIZE = 13; + constexpr u32 RPCN_PROTOCOL_VERSION = 16; + constexpr usz RPCN_HEADER_SIZE = 15; constexpr usz COMMUNICATION_ID_SIZE = 9; + bool is_error(ErrorType err) + { + if (err >= ErrorType::__error_last) + { + rpcn_log.error("Invalid error returned!"); + return true; + } + + switch (err) + { + case NoError: return false; + case Malformed: rpcn_log.error("Sent packet was malformed!"); break; + case Invalid: rpcn_log.error("Sent command was invalid!"); break; + case InvalidInput: rpcn_log.error("Sent data was invalid!"); break; + case TooSoon: rpcn_log.error("Request happened too soon!"); break; + case LoginError: rpcn_log.error("Unknown login error!"); break; + case LoginAlreadyLoggedIn: rpcn_log.error("User is already logged in!"); break; + case LoginInvalidUsername: rpcn_log.error("Login error: invalid username!"); break; + case LoginInvalidPassword: rpcn_log.error("Login error: invalid password!"); break; + case LoginInvalidToken: rpcn_log.error("Login error: invalid token!"); break; + case CreationError: rpcn_log.error("Error creating an account!"); break; + case CreationExistingUsername: rpcn_log.error("Error creating an account: existing username!"); break; + case CreationBannedEmailProvider: rpcn_log.error("Error creating an account: banned email provider!"); break; + case CreationExistingEmail: rpcn_log.error("Error creating an account: an account with that email already exist!"); break; + case AlreadyJoined: rpcn_log.error("User has already joined!"); break; + case Unauthorized: rpcn_log.error("User attempted an unauthorized operation!"); break; + case DbFail: rpcn_log.error("A db query failed on the server!"); break; + case EmailFail: rpcn_log.error("An email action failed on the server!"); break; + case NotFound: rpcn_log.error("A request replied not found!"); break; + case Blocked: rpcn_log.error("You're blocked!"); break; + case AlreadyFriend: rpcn_log.error("You're already friends!"); break; + case ScoreNotBest: rpcn_log.error("Attempted to register a score that is not better!"); break; + case Unsupported: rpcn_log.error("An unsupported operation was attempted!"); break; + default: rpcn_log.fatal("Unhandled ErrorType reached the switch?"); break; + } + + return true; + } + // Constructor, destructor & singleton manager rpcn_client::rpcn_client() @@ -198,6 +234,7 @@ namespace rpcn { if (want_conn) { + want_conn = false; { std::lock_guard lock(mutex_connected); connect(g_cfg_rpcn.get_host()); @@ -257,9 +294,10 @@ namespace rpcn // Send a packet every 5 seconds and then every 500 ms until reply is received if (now - last_pong_time >= 5s && now - last_ping_time > 500ms) { - std::vector ping(9); + std::vector ping(13); ping[0] = 1; *utils::bless>(&ping[1]) = user_id; + *utils::bless>(&ping[9]) = local_addr_sig; if (send_packet_from_p2p_port(ping, addr_rpcn_udp) == -1) { rpcn_log.error("Failed to send ping to rpcn!"); @@ -301,8 +339,8 @@ namespace rpcn const u8 packet_type = header[0]; const u16 command = *utils::bless>(&header[1]); - const u16 packet_size = *utils::bless>(&header[3]); - const u64 packet_id = *utils::bless>(&header[5]); + const u32 packet_size = *utils::bless>(&header[3]); + const u64 packet_id = *utils::bless>(&header[7]); if (packet_size < RPCN_HEADER_SIZE) return error_and_disconnect("Invalid packet size"); @@ -328,7 +366,8 @@ namespace rpcn if (command == CommandType::Login || command == CommandType::GetServerList || command == CommandType::Create || command == CommandType::AddFriend || command == CommandType::RemoveFriend || command == CommandType::AddBlock || command == CommandType::RemoveBlock || - command == CommandType::SendMessage || command == CommandType::SendToken) + command == CommandType::SendMessage || command == CommandType::SendToken || + command == CommandType::SendResetToken || command == CommandType::ResetPassword) { std::lock_guard lock(mutex_replies_sync); replies_sync.insert(std::make_pair(packet_id, std::make_pair(command, std::move(data)))); @@ -681,6 +720,16 @@ namespace rpcn rpcn_log.notice("connect: Connection successful"); + sockaddr_in client_addr; + socklen_t client_addr_size = sizeof(client_addr); + if (getsockname(sockfd, reinterpret_cast(&client_addr), &client_addr_size) != 0) + { + rpcn_log.error("Failed to get the client address from the socket!"); + } + + update_local_addr(client_addr.sin_addr.s_addr); + rpcn_log.notice("Updated local address to %s", np::ip_to_string(std::bit_cast>(local_addr_sig.load()))); + if (wolfSSL_set_fd(read_wssl, sockfd) != WOLFSSL_SUCCESS) { rpcn_log.error("connect: Failed to associate wolfssl to the socket"); @@ -837,7 +886,7 @@ namespace rpcn return true; } - ErrorType rpcn_client::create_user(const std::string& npid, const std::string& password, const std::string& online_name, const std::string& avatar_url, const std::string& email) + ErrorType rpcn_client::create_user(std::string_view npid, std::string_view password, std::string_view online_name, std::string_view avatar_url, std::string_view email) { std::vector data; std::copy(npid.begin(), npid.end(), std::back_inserter(data)); @@ -908,6 +957,78 @@ namespace rpcn return ErrorType::NoError; } + ErrorType rpcn_client::send_reset_token(std::string_view npid, std::string_view email) + { + if (authentified) + { + // If you're already logged in why do you need a password reset token? + return ErrorType::LoginAlreadyLoggedIn; + } + + std::vector data; + std::copy(npid.begin(), npid.end(), std::back_inserter(data)); + data.push_back(0); + std::copy(email.begin(), email.end(), std::back_inserter(data)); + data.push_back(0); + + u64 req_id = rpcn_request_counter.fetch_add(1); + + std::vector packet_data; + if (!forge_send_reply(CommandType::SendResetToken, req_id, data, packet_data)) + { + return ErrorType::Malformed; + } + + vec_stream reply(packet_data); + auto error = static_cast(reply.get()); + + if (is_error(error)) + { + return error; + } + + rpcn_log.success("Password reset token has successfully been sent!"); + + return ErrorType::NoError; + } + + ErrorType rpcn_client::reset_password(std::string_view npid, std::string_view token, std::string_view password) + { + if (authentified) + { + // If you're already logged in why do you need to reset the password? + return ErrorType::LoginAlreadyLoggedIn; + } + + std::vector data; + std::copy(npid.begin(), npid.end(), std::back_inserter(data)); + data.push_back(0); + std::copy(token.begin(), token.end(), std::back_inserter(data)); + data.push_back(0); + std::copy(password.begin(), password.end(), std::back_inserter(data)); + data.push_back(0); + + u64 req_id = rpcn_request_counter.fetch_add(1); + + std::vector packet_data; + if (!forge_send_reply(CommandType::ResetPassword, req_id, data, packet_data)) + { + return ErrorType::Malformed; + } + + vec_stream reply(packet_data); + auto error = static_cast(reply.get()); + + if (is_error(error)) + { + return error; + } + + rpcn_log.success("Password has successfully been reset!"); + + return ErrorType::NoError; + } + bool rpcn_client::add_friend(const std::string& friend_username) { std::vector data; @@ -1067,18 +1188,13 @@ namespace rpcn memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE); reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = server_id; - if (!forge_send(CommandType::GetWorldList, req_id, data)) - return false; - - return true; + return forge_send(CommandType::GetWorldList, req_id, data); } bool rpcn_client::createjoin_room(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2CreateJoinRoomRequest* req) { std::vector data; - extra_nps::print_createjoinroom(req); - flatbuffers::FlatBufferBuilder builder(1024); flatbuffers::Offset>> final_binattrinternal_vec; @@ -1196,18 +1312,13 @@ namespace rpcn reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = static_cast(bufsize); memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize); - if (!forge_send(CommandType::CreateRoom, req_id, data)) - return false; - - return true; + return forge_send(CommandType::CreateRoom, req_id, data); } bool rpcn_client::join_room(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2JoinRoomRequest* req) { std::vector data; - extra_nps::print_joinroom(req); - flatbuffers::FlatBufferBuilder builder(1024); flatbuffers::Offset> final_roompassword; @@ -1240,10 +1351,7 @@ namespace rpcn reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = static_cast(bufsize); memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize); - if (!forge_send(CommandType::JoinRoom, req_id, data)) - return false; - - return true; + return forge_send(CommandType::JoinRoom, req_id, data); } bool rpcn_client::leave_room(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2LeaveRoomRequest* req) @@ -1262,18 +1370,13 @@ namespace rpcn reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = static_cast(bufsize); memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize); - if (!forge_send(CommandType::LeaveRoom, req_id, data)) - return false; - - return true; + return forge_send(CommandType::LeaveRoom, req_id, data); } bool rpcn_client::search_room(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SearchRoomRequest* req) { std::vector data; - extra_nps::print_search_room(req); - flatbuffers::FlatBufferBuilder builder(1024); flatbuffers::Offset>> final_intfilter_vec; if (req->intFilterNum) @@ -1299,9 +1402,17 @@ namespace rpcn } final_binfilter_vec = builder.CreateVector(davec); } + flatbuffers::Offset> attrid_vec; if (req->attrIdNum) - attrid_vec = builder.CreateVector(utils::bless(req->attrId.get_ptr()), req->attrIdNum); + { + std::vector attr_ids; + for (u32 i = 0; i < req->attrIdNum; i++) + { + attr_ids.push_back(req->attrId[i]); + } + attrid_vec = builder.CreateVector(attr_ids); + } SearchRoomRequestBuilder s_req(builder); s_req.add_option(req->option); @@ -1328,10 +1439,7 @@ namespace rpcn reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = static_cast(bufsize); memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize); - if (!forge_send(CommandType::SearchRoom, req_id, data)) - return false; - - return true; + return forge_send(CommandType::SearchRoom, req_id, data); } bool rpcn_client::get_roomdata_external_list(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2GetRoomDataExternalListRequest* req) @@ -1361,10 +1469,7 @@ namespace rpcn reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = static_cast(bufsize); memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize); - if (!forge_send(CommandType::GetRoomDataExternalList, req_id, data)) - return false; - - return true; + return forge_send(CommandType::GetRoomDataExternalList, req_id, data); } bool rpcn_client::set_roomdata_external(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SetRoomDataExternalRequest* req) @@ -1416,10 +1521,7 @@ namespace rpcn reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = static_cast(bufsize); memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize); - if (!forge_send(CommandType::SetRoomDataExternal, req_id, data)) - return false; - - return true; + return forge_send(CommandType::SetRoomDataExternal, req_id, data); } bool rpcn_client::get_roomdata_internal(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2GetRoomDataInternalRequest* req) @@ -1430,7 +1532,14 @@ namespace rpcn flatbuffers::Offset> final_attr_ids_vec; if (req->attrIdNum) - final_attr_ids_vec = builder.CreateVector(utils::bless(req->attrId.get_ptr()), req->attrIdNum); + { + std::vector attr_ids; + for (u32 i = 0; i < req->attrIdNum; i++) + { + attr_ids.push_back(req->attrId[i]); + } + final_attr_ids_vec = builder.CreateVector(attr_ids); + } auto req_finished = CreateGetRoomDataInternalRequest(builder, req->roomId, final_attr_ids_vec); @@ -1443,18 +1552,13 @@ namespace rpcn reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = static_cast(bufsize); memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize); - if (!forge_send(CommandType::GetRoomDataInternal, req_id, data)) - return false; - - return true; + return forge_send(CommandType::GetRoomDataInternal, req_id, data); } bool rpcn_client::set_roomdata_internal(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SetRoomDataInternalRequest* req) { std::vector data; - // extra_nps::print_set_roomdata_req(req); - flatbuffers::FlatBufferBuilder builder(1024); flatbuffers::Offset>> final_binattrinternal_vec; if (req->roomBinAttrInternalNum) @@ -1484,7 +1588,14 @@ namespace rpcn flatbuffers::Offset> final_ownerprivilege_vec; if (req->ownerPrivilegeRankNum) - final_ownerprivilege_vec = builder.CreateVector(utils::bless(req->ownerPrivilegeRank.get_ptr()), req->ownerPrivilegeRankNum); + { + std::vector priv_ranks; + for (u32 i = 0; i < req->ownerPrivilegeRankNum; i++) + { + priv_ranks.push_back(req->ownerPrivilegeRank[i]); + } + final_ownerprivilege_vec = builder.CreateVector(priv_ranks); + } auto req_finished = CreateSetRoomDataInternalRequest(builder, req->roomId, req->flagFilter, req->flagAttr, final_binattrinternal_vec, final_grouppasswordconfig_vec, final_passwordSlotMask, final_ownerprivilege_vec); @@ -1498,18 +1609,13 @@ namespace rpcn reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = static_cast(bufsize); memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize); - if (!forge_send(CommandType::SetRoomDataInternal, req_id, data)) - return false; - - return true; + return forge_send(CommandType::SetRoomDataInternal, req_id, data); } bool rpcn_client::set_roommemberdata_internal(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SetRoomMemberDataInternalRequest* req) { std::vector data{}; - extra_nps::print_set_roommemberdata_int_req(req); - flatbuffers::FlatBufferBuilder builder(1024); flatbuffers::Offset>> final_binattrinternal_vec; if (req->roomMemberBinAttrInternalNum) @@ -1534,10 +1640,7 @@ namespace rpcn reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = static_cast(bufsize); memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize); - if (!forge_send(CommandType::SetRoomMemberDataInternal, req_id, data)) - return false; - - return true; + return forge_send(CommandType::SetRoomMemberDataInternal, req_id, data); } bool rpcn_client::ping_room_owner(u32 req_id, const SceNpCommunicationId& communication_id, u64 room_id) @@ -1549,10 +1652,7 @@ namespace rpcn memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE); *utils::bless>(&data[COMMUNICATION_ID_SIZE]) = room_id; - if (!forge_send(CommandType::PingRoomOwner, req_id, data)) - return false; - - return true; + return forge_send(CommandType::PingRoomOwner, req_id, data); } bool rpcn_client::send_room_message(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SendRoomMessageRequest* req) @@ -1587,16 +1687,13 @@ namespace rpcn builder.Finish(req_finished); u8* buf = builder.GetBufferPointer(); usz bufsize = builder.GetSize(); - data.resize(COMMUNICATION_ID_SIZE + bufsize + sizeof(u32)); + data.resize(COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize); memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE); reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = static_cast(bufsize); memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize); - if (!forge_send(CommandType::SendRoomMessage, req_id, data)) - return false; - - return true; + return forge_send(CommandType::SendRoomMessage, req_id, data); } bool rpcn_client::req_sign_infos(u32 req_id, const std::string& npid) @@ -1605,10 +1702,7 @@ namespace rpcn std::copy(npid.begin(), npid.end(), std::back_inserter(data)); data.push_back(0); - if (!forge_send(CommandType::RequestSignalingInfos, req_id, data)) - return false; - - return true; + return forge_send(CommandType::RequestSignalingInfos, req_id, data); } bool rpcn_client::req_ticket(u32 req_id, const std::string& service_id, const std::vector& cookie) @@ -1620,10 +1714,7 @@ namespace rpcn std::copy(reinterpret_cast(&size), reinterpret_cast(&size) + sizeof(le_t), std::back_inserter(data)); std::copy(cookie.begin(), cookie.end(), std::back_inserter(data)); - if (!forge_send(CommandType::RequestTicket, req_id, data)) - return false; - - return true; + return forge_send(CommandType::RequestTicket, req_id, data); } bool rpcn_client::sendmessage(const message_data& msg_data, const std::set& npids) @@ -1658,64 +1749,113 @@ namespace rpcn u64 req_id = rpcn_request_counter.fetch_add(1); - if (!forge_send(CommandType::SendMessage, req_id, data)) - return false; + return forge_send(CommandType::SendMessage, req_id, data); + } - return true; + bool rpcn_client::get_board_infos(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id) + { + std::vector data(COMMUNICATION_ID_SIZE + sizeof(u32)); + memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE); + *utils::bless>(&data[COMMUNICATION_ID_SIZE]) = board_id; + + return forge_send(CommandType::GetBoardInfos, req_id, data); + } + + bool rpcn_client::record_score(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id, SceNpScorePcId char_id, SceNpScoreValue score, const std::optional comment, const std::optional> score_data) + { + std::vector data; + flatbuffers::FlatBufferBuilder builder(1024); + + auto req_finished = CreateRecordScoreRequestDirect(builder, board_id, char_id, score, comment ? (*comment).c_str() : nullptr, score_data ? &*score_data : nullptr); + + builder.Finish(req_finished); + u8* buf = builder.GetBufferPointer(); + usz bufsize = builder.GetSize(); + data.resize(COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize); + + memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE); + reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = static_cast(bufsize); + memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize); + + return forge_send(CommandType::RecordScore, req_id, data); + } + + bool rpcn_client::get_score_range(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id, u32 start_rank, u32 num_rank, bool with_comment, bool with_gameinfo) + { + std::vector data; + flatbuffers::FlatBufferBuilder builder(1024); + auto req_finished = CreateGetScoreRangeRequest(builder, board_id, start_rank, num_rank, with_comment, with_gameinfo); + + builder.Finish(req_finished); + u8* buf = builder.GetBufferPointer(); + usz bufsize = builder.GetSize(); + data.resize(COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize); + + memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE); + reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = static_cast(bufsize); + memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize); + + return forge_send(CommandType::GetScoreRange, req_id, data); + } + + bool rpcn_client::get_score_npid(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id, const std::vector>& npids, bool with_comment, bool with_gameinfo) + { + std::vector data; + flatbuffers::FlatBufferBuilder builder(1024); + + std::vector> davec; + for (usz i = 0; i < npids.size(); i++) + { + auto npid = CreateScoreNpIdPcId(builder, builder.CreateString(static_cast(npids[i].first.handle.data)), npids[i].second); + davec.push_back(npid); + } + + auto req_finished = CreateGetScoreNpIdRequest(builder, board_id, builder.CreateVector(davec), with_comment, with_gameinfo); + + builder.Finish(req_finished); + u8* buf = builder.GetBufferPointer(); + usz bufsize = builder.GetSize(); + data.resize(COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize); + + memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE); + reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = static_cast(bufsize); + memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize); + + return forge_send(CommandType::GetScoreNpid, req_id, data); + } + + bool rpcn_client::get_score_friend(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id, bool with_comment, bool with_gameinfo) + { + std::vector data; + flatbuffers::FlatBufferBuilder builder(1024); + auto req_finished = CreateGetScoreFriendsRequest(builder, board_id, with_comment, with_gameinfo); + builder.Finish(req_finished); + + u8* buf = builder.GetBufferPointer(); + usz bufsize = builder.GetSize(); + data.resize(COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize); + + memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE); + reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = static_cast(bufsize); + memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize); + + return forge_send(CommandType::GetScoreFriends, req_id, data); } std::vector rpcn_client::forge_request(u16 command, u64 packet_id, const std::vector& data) const { - u16 packet_size = data.size() + RPCN_HEADER_SIZE; + u32 packet_size = data.size() + RPCN_HEADER_SIZE; std::vector packet(packet_size); packet[0] = PacketType::Request; reinterpret_cast&>(packet[1]) = command; - reinterpret_cast&>(packet[3]) = packet_size; - reinterpret_cast&>(packet[5]) = packet_id; + reinterpret_cast&>(packet[3]) = packet_size; + reinterpret_cast&>(packet[7]) = packet_id; memcpy(packet.data() + RPCN_HEADER_SIZE, data.data(), data.size()); return packet; } - bool rpcn_client::is_error(ErrorType err) const - { - if (err >= ErrorType::__error_last) - { - rpcn_log.error("Invalid error returned!"); - return true; - } - - switch (err) - { - case NoError: return false; - case Malformed: rpcn_log.error("Sent packet was malformed!"); break; - case Invalid: rpcn_log.error("Sent command was invalid!"); break; - case InvalidInput: rpcn_log.error("Sent data was invalid!"); break; - case TooSoon: rpcn_log.error("Request happened too soon!"); break; - case LoginError: rpcn_log.error("Unknown login error!"); break; - case LoginAlreadyLoggedIn: rpcn_log.error("User is already logged in!"); break; - case LoginInvalidUsername: rpcn_log.error("Login error: invalid username!"); break; - case LoginInvalidPassword: rpcn_log.error("Login error: invalid password!"); break; - case LoginInvalidToken: rpcn_log.error("Login error: invalid token!"); break; - case CreationError: rpcn_log.error("Error creating an account!"); break; - case CreationExistingUsername: rpcn_log.error("Error creating an account: existing username!"); break; - case CreationBannedEmailProvider: rpcn_log.error("Error creating an account: banned email provider!"); break; - case CreationExistingEmail: rpcn_log.error("Error creating an account: an account with that email already exist!"); break; - case AlreadyJoined: rpcn_log.error("User has already joined!"); break; - case Unauthorized: rpcn_log.error("User attempted an unauthorized operation!"); break; - case DbFail: rpcn_log.error("A db query failed on the server!"); break; - case EmailFail: rpcn_log.error("An email action failed on the server!"); break; - case NotFound: rpcn_log.error("A request replied not found!"); break; - case Blocked: rpcn_log.error("You're blocked!"); break; - case AlreadyFriend: rpcn_log.error("You're already friends!"); break; - case Unsupported: rpcn_log.error("An unsupported operation was attempted!"); break; - default: rpcn_log.fatal("Unhandled ErrorType reached the switch?"); break; - } - - return true; - } - bool rpcn_client::error_and_disconnect(const std::string& error_msg) { connected = false; @@ -1760,6 +1900,7 @@ namespace rpcn { return state; } + want_auth = true; sem_rpcn.release(); } diff --git a/rpcs3/Emu/NP/rpcn_client.h b/rpcs3/Emu/NP/rpcn_client.h index d1e7e7b11d..94fa06327a 100644 --- a/rpcs3/Emu/NP/rpcn_client.h +++ b/rpcs3/Emu/NP/rpcn_client.h @@ -118,6 +118,8 @@ namespace rpcn Terminate, Create, SendToken, + SendResetToken, + ResetPassword, AddFriend, RemoveFriend, AddBlock, @@ -138,6 +140,13 @@ namespace rpcn RequestSignalingInfos, RequestTicket, SendMessage, + GetBoardInfos, + RecordScore, + StoreScoreData, + GetScoreData, + GetScoreRange, + GetScoreFriends, + GetScoreNpid, }; enum NotificationType : u16 @@ -204,6 +213,7 @@ namespace rpcn NotFound, // Object of the query was not found(room, user, etc) Blocked, // The operation can't complete because you've been blocked AlreadyFriend, // Can't add friend because already friend + ScoreNotBest, // A better score is already registered for that user/character_id Unsupported, __error_last }; @@ -221,6 +231,7 @@ namespace rpcn localized_string_id rpcn_state_to_localized_string_id(rpcn::rpcn_state state); std::string rpcn_state_to_string(rpcn::rpcn_state state); + bool is_error(ErrorType err); class rpcn_client { @@ -290,7 +301,7 @@ namespace rpcn public: ~rpcn_client(); - rpcn_client(rpcn_client& other) = delete; + rpcn_client(rpcn_client& other) = delete; void operator=(const rpcn_client&) = delete; static std::shared_ptr get_instance(); rpcn_state wait_for_connection(); @@ -299,8 +310,10 @@ namespace rpcn void get_friends_and_register_cb(friend_data& friend_infos, friend_cb_func cb_func, void* cb_param); void remove_friend_cb(friend_cb_func, void* cb_param); - ErrorType create_user(const std::string& npid, const std::string& password, const std::string& online_name, const std::string& avatar_url, const std::string& email); + ErrorType create_user(std::string_view npid, std::string_view password, std::string_view online_name, std::string_view avatar_url, std::string_view email); ErrorType resend_token(const std::string& npid, const std::string& password); + ErrorType send_reset_token(std::string_view npid, std::string_view email); + ErrorType reset_password(std::string_view npid, std::string_view token, std::string_view password); bool add_friend(const std::string& friend_username); bool remove_friend(const std::string& friend_username); @@ -350,6 +363,11 @@ namespace rpcn bool req_sign_infos(u32 req_id, const std::string& npid); bool req_ticket(u32 req_id, const std::string& service_id, const std::vector& cookie); bool sendmessage(const message_data& msg_data, const std::set& npids); + bool get_board_infos(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id); + bool record_score(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id, SceNpScorePcId char_id, SceNpScoreValue score, const std::optional comment, const std::optional> score_data); + bool get_score_range(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id, u32 start_rank, u32 num_rank, bool with_comment, bool with_gameinfo); + bool get_score_npid(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id, const std::vector>& npids, bool with_comment, bool with_gameinfo); + bool get_score_friend(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id, bool with_comment, bool with_gameinfo); const std::string& get_online_name() const { @@ -368,6 +386,15 @@ namespace rpcn { return port_sig.load(); } + u32 get_addr_local() const + { + return local_addr_sig.load(); + } + + void update_local_addr(u32 addr) + { + local_addr_sig = std::bit_cast>(addr); + } private: bool get_reply(u64 expected_id, std::vector& data); @@ -376,7 +403,6 @@ namespace rpcn bool forge_send(u16 command, u64 packet_id, const std::vector& data); bool forge_send_reply(u16 command, u64 packet_id, const std::vector& data, std::vector& reply_data); - bool is_error(ErrorType err) const; bool error_and_disconnect(const std::string& error_mgs); std::string get_wolfssl_error(WOLFSSL* wssl, int error) const; @@ -432,6 +458,7 @@ namespace rpcn atomic_t addr_sig{}; atomic_t port_sig{}; + atomic_t local_addr_sig{}; }; } // namespace rpcn diff --git a/rpcs3/Emu/NP/rpcn_config.cpp b/rpcs3/Emu/NP/rpcn_config.cpp index aa9fff8226..3cd068af43 100644 --- a/rpcs3/Emu/NP/rpcn_config.cpp +++ b/rpcs3/Emu/NP/rpcn_config.cpp @@ -21,6 +21,14 @@ void cfg_rpcn::load() rpcn_log.notice("RPCN config missing. Using default settings. Path: %s", path); from_default(); } + + // Update config from old version(s) + if (version == 1) + { + password.from_string(""); + version.set(2); + save(); + } } void cfg_rpcn::save() const @@ -55,7 +63,7 @@ std::string cfg_rpcn::generate_npid() std::string gen_npid = "RPCS3_"; const char list_chars[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; std::srand(time(nullptr)); @@ -72,6 +80,32 @@ std::string cfg_rpcn::get_host() const return host.to_string(); } +std::vector> cfg_rpcn::get_hosts() +{ + std::vector> vec_hosts; + auto hosts_list = fmt::split(hosts.to_string(), {"|||"}); + + for (const auto& cur_host : hosts_list) + { + auto desc_and_host = fmt::split(cur_host, {"|"}); + if (desc_and_host.size() != 2) + { + rpcn_log.error("Invalid host in the list of hosts: %s", cur_host); + continue; + } + vec_hosts.push_back(std::make_pair(std::move(desc_and_host[0]), std::move(desc_and_host[1]))); + } + + if (vec_hosts.empty()) + { + hosts.from_default(); + save(); + return get_hosts(); + } + + return vec_hosts; +} + std::string cfg_rpcn::get_npid() { std::string final_npid = npid.to_string(); @@ -94,22 +128,80 @@ std::string cfg_rpcn::get_token() const return token.to_string(); } -void cfg_rpcn::set_host(const std::string& host) +void cfg_rpcn::set_host(std::string_view host) { this->host.from_string(host); } -void cfg_rpcn::set_npid(const std::string& npid) +void cfg_rpcn::set_npid(std::string_view npid) { this->npid.from_string(npid); } -void cfg_rpcn::set_password(const std::string& password) +void cfg_rpcn::set_password(std::string_view password) { this->password.from_string(password); } -void cfg_rpcn::set_token(const std::string& token) +void cfg_rpcn::set_token(std::string_view token) { this->token.from_string(token); } + +void cfg_rpcn::set_hosts(const std::vector>& vec_hosts) +{ + std::string final_string; + for (const auto& [cur_desc, cur_host] : vec_hosts) + { + fmt::append(final_string, "%s|%s|||", cur_desc, cur_host); + } + + if (final_string.empty()) + { + hosts.from_default(); + return; + } + + final_string.resize(final_string.size() - 3); + hosts.from_string(final_string); +} + +bool cfg_rpcn::add_host(std::string_view new_description, std::string_view new_host) +{ + auto cur_hosts = get_hosts(); + + for (const auto& [cur_desc, cur_host] : cur_hosts) + { + if (cur_desc == new_description && cur_host == new_host) + return false; + } + + cur_hosts.push_back(std::make_pair(std::string(new_description), std::string(new_host))); + set_hosts(cur_hosts); + + return true; +} + +bool cfg_rpcn::del_host(std::string_view del_description, std::string_view del_host) +{ + // Do not delete default servers + if ((del_description == "Official RPCN Server" && del_host == "np.rpcs3.net") || + (del_description == "RPCN Test Server" && del_host == "test-np.rpcs3.net")) + { + return true; + } + + auto cur_hosts = get_hosts(); + + for (auto it = cur_hosts.begin(); it != cur_hosts.end(); it++) + { + if (it->first == del_description && it->second == del_host) + { + cur_hosts.erase(it); + set_hosts(cur_hosts); + return true; + } + } + + return false; +} diff --git a/rpcs3/Emu/NP/rpcn_config.h b/rpcs3/Emu/NP/rpcn_config.h index 9b90bd2ece..b03bdd3acb 100644 --- a/rpcs3/Emu/NP/rpcn_config.h +++ b/rpcs3/Emu/NP/rpcn_config.h @@ -4,10 +4,12 @@ struct cfg_rpcn : cfg::node { + cfg::uint32 version{this, "Version", 1}; cfg::string host{this, "Host", "np.rpcs3.net"}; cfg::string npid{this, "NPID", ""}; cfg::string password{this, "Password", ""}; cfg::string token{this, "Token", ""}; + cfg::string hosts{this, "Hosts", "Official RPCN Server|np.rpcs3.net|||RPCN Test Server|test-np.rpcs3.net"}; void load(); void save() const; @@ -16,15 +18,19 @@ struct cfg_rpcn : cfg::node std::string get_npid(); // not const because it can save if npid is requested and it has never been set std::string get_password() const; std::string get_token() const; + std::vector> get_hosts(); // saves default if no valid server in the list - void set_host(const std::string& host); - void set_npid(const std::string& npid); - void set_password(const std::string& password); - void set_token(const std::string& token); + void set_host(std::string_view host); + void set_npid(std::string_view npid); + void set_password(std::string_view password); + void set_token(std::string_view token); + bool add_host(std::string_view description, std::string_view host); + bool del_host(std::string_view description, std::string_view host); - private: +private: static std::string get_path(); static std::string generate_npid(); + void set_hosts(const std::vector>& vec_hosts); }; extern cfg_rpcn g_cfg_rpcn; diff --git a/rpcs3/Emu/NP/signaling_handler.cpp b/rpcs3/Emu/NP/signaling_handler.cpp index fa37952ea0..ded085e1dd 100644 --- a/rpcs3/Emu/NP/signaling_handler.cpp +++ b/rpcs3/Emu/NP/signaling_handler.cpp @@ -4,6 +4,7 @@ #include "Emu/IdManager.h" #include "Emu/Cell/Modules/cellSysutil.h" #include "np_handler.h" +#include "Emu/NP/vport0.h" #ifdef _WIN32 #include @@ -15,28 +16,26 @@ LOG_CHANNEL(sign_log, "Signaling"); -std::vector, std::vector>> get_sign_msgs(); -s32 send_packet_from_p2p_port(const std::vector& data, const sockaddr_in& addr); void need_network(); template <> void fmt_class_string::format(std::string& out, u64 arg) { format_enum(out, arg, [](auto value) - { - switch (value) { - case signal_ping: return "PING"; - case signal_pong: return "PONG"; - case signal_connect: return "CONNECT"; - case signal_connect_ack: return "CONNECT_ACK"; - case signal_confirm: return "CONFIRM"; - case signal_finished: return "FINISHED"; - case signal_finished_ack: return "FINISHED_ACK"; - } + switch (value) + { + case signal_ping: return "PING"; + case signal_pong: return "PONG"; + case signal_connect: return "CONNECT"; + case signal_connect_ack: return "CONNECT_ACK"; + case signal_confirm: return "CONFIRM"; + case signal_finished: return "FINISHED"; + case signal_finished_ack: return "FINISHED_ACK"; + } - return unknown; - }); + return unknown; + }); } signaling_handler::signaling_handler() @@ -77,10 +76,10 @@ void signaling_handler::signal_sig_callback(u32 conn_id, int event) if (sig_cb) { sysutil_register_cb([sig_cb = this->sig_cb, sig_cb_ctx = this->sig_cb_ctx, conn_id, event, sig_cb_arg = this->sig_cb_arg](ppu_thread& cb_ppu) -> s32 - { - sig_cb(cb_ppu, sig_cb_ctx, conn_id, event, 0, sig_cb_arg); - return 0; - }); + { + sig_cb(cb_ppu, sig_cb_ctx, conn_id, event, 0, sig_cb_arg); + return 0; + }); sign_log.notice("Called sig CB: 0x%x (conn_id: %d)", event, conn_id); } @@ -93,11 +92,11 @@ void signaling_handler::signal_ext_sig_callback(u32 conn_id, int event) const if (sig_ext_cb) { sysutil_register_cb([sig_ext_cb = this->sig_ext_cb, sig_ext_cb_ctx = this->sig_ext_cb_ctx, conn_id, event, sig_ext_cb_arg = this->sig_ext_cb_arg](ppu_thread& cb_ppu) -> s32 - { - sig_ext_cb(cb_ppu, sig_ext_cb_ctx, conn_id, event, 0, sig_ext_cb_arg); - return 0; - }); - sign_log.notice("Called EXT sig CB: 0x%x (conn_id: %d, member_id: %d)", event, conn_id); + { + sig_ext_cb(cb_ppu, sig_ext_cb_ctx, conn_id, event, 0, sig_ext_cb_arg); + return 0; + }); + sign_log.notice("Called EXT sig CB: 0x%x (conn_id: %d)", event, conn_id); } } @@ -107,10 +106,10 @@ void signaling_handler::signal_sig2_callback(u64 room_id, u16 member_id, SceNpMa if (sig2_cb) { sysutil_register_cb([sig2_cb = this->sig2_cb, sig2_cb_ctx = this->sig2_cb_ctx, room_id, member_id, event, sig2_cb_arg = this->sig2_cb_arg](ppu_thread& cb_ppu) -> s32 - { - sig2_cb(cb_ppu, sig2_cb_ctx, room_id, member_id, event, 0, sig2_cb_arg); - return 0; - }); + { + sig2_cb(cb_ppu, sig2_cb_ctx, room_id, member_id, event, 0, sig2_cb_arg); + return 0; + }); } sign_log.notice("Called sig2 CB: 0x%x (room_id: %d, member_id: %d)", event, room_id, member_id); @@ -180,15 +179,15 @@ void signaling_handler::process_incoming_messages() for (const auto& msg : msgs) { - if (msg.second.size() != sizeof(signaling_packet)) + if (msg.data.size() != sizeof(signaling_packet)) { sign_log.error("Received an invalid signaling packet"); continue; } - auto op_addr = msg.first.first; - auto op_port = msg.first.second; - auto* sp = reinterpret_cast(msg.second.data()); + auto op_addr = msg.src_addr; + auto op_port = msg.src_port; + auto* sp = reinterpret_cast(msg.data.data()); if (!validate_signaling_packet(sp)) continue; @@ -231,8 +230,8 @@ void signaling_handler::process_incoming_messages() si = ::at32(sig1_peers, conn_id); // Activate the connection without triggering the main CB si->connStatus = SCE_NP_SIGNALING_CONN_STATUS_ACTIVE; - si->addr = op_addr; - si->port = op_port; + si->addr = op_addr; + si->port = op_port; si->ext_status = ext_sign_peer; // Notify extended callback that peer activated signal_ext_sig_callback(conn_id, SCE_NP_SIGNALING_EVENT_EXT_PEER_ACTIVATED); @@ -259,30 +258,51 @@ void signaling_handler::process_incoming_messages() } } - sent_packet.command = signal_ping; + sent_packet.command = signal_ping; + sent_packet.timestamp = now.time_since_epoch().count(); + send_signaling_packet(sent_packet, si->addr, si->port); queue_signaling_packet(sent_packet, si, now + REPEAT_PING_DELAY); }; + const auto update_rtt = [&]() + { + u32 rtt = now.time_since_epoch().count() - sp->timestamp; + si->last_rtts[(si->rtt_counters % 6)] = rtt; + si->rtt_counters++; + + std::size_t num_rtts = std::min(static_cast(6), si->rtt_counters); + u64 sum = 0; + for (std::size_t index = 0; index < num_rtts; index++) + { + sum += si->last_rtts[index]; + } + + si->rtt = (sum / num_rtts) / 1000; + }; + switch (sp->command) { case signal_ping: - reply = true; - schedule_repeat = false; - sent_packet.command = signal_pong; + reply = true; + schedule_repeat = false; + sent_packet.command = signal_pong; + sent_packet.timestamp = sp->timestamp; break; case signal_pong: + update_rtt(); reply = false; schedule_repeat = false; - reschedule_packet(si, signal_ping, now + 15s); + reschedule_packet(si, signal_ping, now + 10s); break; case signal_connect: - reply = true; - schedule_repeat = true; - sent_packet.command = signal_connect_ack; + reply = true; + schedule_repeat = true; + sent_packet.command = signal_connect_ack; + sent_packet.timestamp = sp->timestamp; // connection is established - // TODO: notify extended callback! break; case signal_connect_ack: + update_rtt(); reply = true; schedule_repeat = false; setup_ping(); @@ -357,6 +377,17 @@ void signaling_handler::operator()() break; // qpackets will be emptied of all packets from this user so we're requeuing } + // Update the timestamp if necessary + switch (it->second.packet.command) + { + case signal_connect: + case signal_ping: + it->second.packet.timestamp = now.time_since_epoch().count(); + break; + default: + break; + } + // Resend the packet send_signaling_packet(it->second.packet, it->second.sig_info->addr, it->second.sig_info->port); @@ -427,7 +458,7 @@ void signaling_handler::update_si_mapped_addr(std::shared_ptr& s if (si->addr != new_addr || si->port != new_port) { in_addr addr_old, addr_new; - addr_old.s_addr = si->addr; + addr_old.s_addr = si->mapped_addr; addr_new.s_addr = new_addr; char ip_str_old[16]; @@ -435,7 +466,7 @@ void signaling_handler::update_si_mapped_addr(std::shared_ptr& s inet_ntop(AF_INET, &addr_old, ip_str_old, sizeof(ip_str_old)); inet_ntop(AF_INET, &addr_new, ip_str_new, sizeof(ip_str_new)); - sign_log.trace("Updated Mapped Address from %s:%d to %s:%d", ip_str_old, si->port, ip_str_new, new_port); + sign_log.trace("Updated Mapped Address from %s:%d to %s:%d", ip_str_old, si->mapped_port, ip_str_new, new_port); si->mapped_addr = new_addr; si->mapped_port = new_port; } @@ -493,9 +524,12 @@ void signaling_handler::set_self_sig2_info(u64 room_id, u16 member_id) void signaling_handler::send_signaling_packet(signaling_packet& sp, u32 addr, u16 port) const { - std::vector packet(sizeof(signaling_packet) + sizeof(u16)); - reinterpret_cast&>(packet[0]) = 65535; - memcpy(packet.data() + sizeof(u16), &sp, sizeof(signaling_packet)); + std::vector packet(sizeof(signaling_packet) + VPORT_0_HEADER_SIZE); + reinterpret_cast&>(packet[0]) = 0; // VPort 0 + packet[2] = SUBSET_SIGNALING; + sp.sent_addr = addr; + sp.sent_port = port; + memcpy(packet.data() + VPORT_0_HEADER_SIZE, &sp, sizeof(signaling_packet)); sockaddr_in dest; memset(&dest, 0, sizeof(sockaddr_in)); @@ -508,9 +542,6 @@ void signaling_handler::send_signaling_packet(signaling_packet& sp, u32 addr, u1 sign_log.trace("Sending %s packet to %s:%d", sp.command, ip_str, port); - sp.sent_addr = addr; - sp.sent_port = port; - if (send_packet_from_p2p_port(packet, dest) == -1) { sign_log.error("Failed to send signaling packet to %s:%d", ip_str, port); @@ -534,7 +565,7 @@ std::shared_ptr signaling_handler::get_signaling_ptr(const signa memcpy(npid_buf, sp->V1.npid.handle.data, 16); std::string npid(npid_buf); - if (!npid_to_conn_id.count(npid)) + if (!npid_to_conn_id.contains(npid)) return nullptr; const u32 conn_id = ::at32(npid_to_conn_id, npid); @@ -566,6 +597,7 @@ void signaling_handler::start_sig_nl(u32 conn_id, u32 addr, u16 port) { auto& sent_packet = sig1_packet; sent_packet.command = signal_connect; + sent_packet.timestamp = steady_clock::now().time_since_epoch().count(); ensure(sig1_peers.contains(conn_id)); std::shared_ptr si = ::at32(sig1_peers, conn_id); @@ -578,12 +610,29 @@ void signaling_handler::start_sig_nl(u32 conn_id, u32 addr, u16 port) wake_up(); } +void signaling_handler::stop_sig(u32 conn_id) +{ + std::lock_guard lock(data_mutex); + + if (!sig1_peers.contains(conn_id)) + return; + + auto& sent_packet = sig1_packet; + sent_packet.command = signal_finished; + + std::shared_ptr si = ::at32(sig1_peers, conn_id); + + send_signaling_packet(sent_packet, si->addr, si->port); + queue_signaling_packet(sent_packet, si, steady_clock::now() + REPEAT_FINISHED_DELAY); +} + void signaling_handler::start_sig2(u64 room_id, u16 member_id) { std::lock_guard lock(data_mutex); auto& sent_packet = sig2_packet; sent_packet.command = signal_connect; + sent_packet.timestamp = steady_clock::now().time_since_epoch().count(); ensure(sig2_peers.contains(room_id)); const auto& sp = ::at32(sig2_peers, room_id); @@ -633,6 +682,7 @@ u32 signaling_handler::create_sig_infos(const SceNpId* npid) sig1_peers.emplace(conn_id, std::make_shared()); ::at32(sig1_peers, conn_id)->version = 1; ::at32(sig1_peers, conn_id)->conn_id = conn_id; + ::at32(sig1_peers, conn_id)->npid = *npid; return conn_id; } @@ -671,10 +721,33 @@ u32 signaling_handler::init_sig_infos(const SceNpId* npid) signaling_info signaling_handler::get_sig_infos(u32 conn_id) { + std::lock_guard lock(data_mutex); return *sig1_peers[conn_id]; } -void signaling_handler::set_sig2_infos(u64 room_id, u16 member_id, s32 status, u32 addr, u16 port, bool self) +std::optional signaling_handler::get_conn_id_from_npid(const SceNpId* npid) +{ + std::lock_guard lock(data_mutex); + // Diff behaviour here depending on SDK version, 420+ always succeeds + return create_sig_infos(npid); +} + +std::optional signaling_handler::get_conn_id_from_addr(u32 addr, u16 port) +{ + std::lock_guard lock(data_mutex); + + for (const auto& [conn_id, conn_info] : sig1_peers) + { + if (conn_info && std::bit_cast>(conn_info->addr) == addr && conn_info->port == port) + { + return conn_id; + } + } + + return std::nullopt; +} + +void signaling_handler::set_sig2_infos(u64 room_id, u16 member_id, s32 status, u32 addr, u16 port, const SceNpId& npid, bool self) { std::lock_guard lock(data_mutex); if (!sig2_peers[room_id][member_id]) @@ -688,6 +761,7 @@ void signaling_handler::set_sig2_infos(u64 room_id, u16 member_id, s32 status, u peer->version = 2; peer->room_id = room_id; peer->member_id = member_id; + peer->npid = npid; } signaling_info signaling_handler::get_sig2_infos(u64 room_id, u16 member_id) @@ -697,10 +771,10 @@ signaling_info signaling_handler::get_sig2_infos(u64 room_id, u16 member_id) if (!sig2_peers[room_id][member_id]) { sig2_peers[room_id][member_id] = std::make_shared(); - auto& peer = sig2_peers[room_id][member_id]; - peer->room_id = room_id; - peer->member_id = member_id; - peer->version = 2; + auto& peer = sig2_peers[room_id][member_id]; + peer->room_id = room_id; + peer->member_id = member_id; + peer->version = 2; } return *sig2_peers[room_id][member_id]; diff --git a/rpcs3/Emu/NP/signaling_handler.h b/rpcs3/Emu/NP/signaling_handler.h index 22e4c9351b..7b5aa04997 100644 --- a/rpcs3/Emu/NP/signaling_handler.h +++ b/rpcs3/Emu/NP/signaling_handler.h @@ -7,11 +7,12 @@ #include #include #include +#include enum ext_signaling_status : u8 { - ext_sign_none = 0, - ext_sign_peer = 1, + ext_sign_none = 0, + ext_sign_peer = 1, ext_sign_mutual = 2, }; @@ -27,15 +28,24 @@ struct signaling_info // For handler steady_clock::time_point time_last_msg_recvd = steady_clock::now(); - bool self = false; u32 version = 0; + SceNpId npid{}; + // Signaling u32 conn_id = 0; ext_signaling_status ext_status = ext_sign_none; + // Matching2 u64 room_id = 0; u16 member_id = 0; + + // Stats + u64 last_rtts[6] = {}; + std::size_t rtt_counters = 0; + u32 rtt = 0; + u32 pings_sent = 1, lost_pings = 0; + u32 packet_loss = 0; }; enum SignalingCommand : u32 @@ -62,8 +72,10 @@ public: u32 init_sig_infos(const SceNpId* npid); signaling_info get_sig_infos(u32 conn_id); + std::optional get_conn_id_from_npid(const SceNpId* npid); + std::optional get_conn_id_from_addr(u32 addr, u16 port); - void set_sig2_infos(u64 room_id, u16 member_id, s32 status, u32 addr, u16 port, bool self = false); + void set_sig2_infos(u64 room_id, u16 member_id, s32 status, u32 addr, u16 port, const SceNpId& npid, bool self = false); signaling_info get_sig2_infos(u64 room_id, u16 member_id); void set_sig_cb(u32 sig_cb_ctx, vm::ptr sig_cb, vm::ptr sig_cb_arg); @@ -71,6 +83,7 @@ public: void set_sig2_cb(u16 sig2_cb_ctx, vm::ptr sig2_cb, vm::ptr sig2_cb_arg); void start_sig(u32 conn_id, u32 addr, u16 port); + void stop_sig(u32 conn_id); void start_sig2(u64 room_id, u16 member_id); void disconnect_sig2_users(u64 room_id); @@ -78,19 +91,21 @@ public: static constexpr auto thread_name = "Signaling Manager Thread"sv; private: - static constexpr auto REPEAT_CONNECT_DELAY = std::chrono::milliseconds(200); - static constexpr auto REPEAT_PING_DELAY = std::chrono::milliseconds(500); - static constexpr auto REPEAT_FINISHED_DELAY = std::chrono::milliseconds(500); + static constexpr auto REPEAT_CONNECT_DELAY = std::chrono::milliseconds(200); + static constexpr auto REPEAT_PING_DELAY = std::chrono::milliseconds(500); + static constexpr auto REPEAT_FINISHED_DELAY = std::chrono::milliseconds(500); static constexpr be_t SIGNALING_SIGNATURE = (static_cast('S') << 24 | static_cast('I') << 16 | static_cast('G') << 8 | static_cast('N')); struct signaling_packet { be_t signature = SIGNALING_SIGNATURE; le_t version; + le_t timestamp; le_t command; le_t sent_addr; le_t sent_port; - union { + union + { struct { SceNpId npid; diff --git a/rpcs3/Emu/NP/vport0.h b/rpcs3/Emu/NP/vport0.h new file mode 100644 index 0000000000..a3ca3bbdf3 --- /dev/null +++ b/rpcs3/Emu/NP/vport0.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "Emu/Cell/lv2/sys_net/nt_p2p_port.h" + +s32 send_packet_from_p2p_port(const std::vector& data, const sockaddr_in& addr); +std::vector get_sign_msgs(); +std::vector> get_rpcn_msgs(); + +constexpr s32 VPORT_0_HEADER_SIZE = sizeof(u16) + sizeof(u8); + +// VPort 0 is invalid for sys_net so we use it for: +// Subset 0: Messages from RPCN server, IP retrieval / UDP hole punching +// Subset 1: Signaling +enum VPORT_0_SUBSET : u8 +{ + SUBSET_RPCN = 0, + SUBSET_SIGNALING = 1, +}; diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index fd7a7f0808..27bc167cd8 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -504,6 +504,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 28bad7f3b6..afecc38ac0 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1923,6 +1923,9 @@ Emu\NP + + Emu\NP + Emu\GPU\RSX\Overlays diff --git a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp index 7b0aaaf560..8d56bcd2cb 100644 --- a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp @@ -6,11 +6,13 @@ #include #include #include +#include #include #include "qt_utils.h" #include "rpcn_settings_dialog.h" +#include "Emu/System.h" #include "Emu/NP/rpcn_config.h" #include @@ -18,13 +20,50 @@ LOG_CHANNEL(rpcn_settings_log, "rpcn settings dlg"); -bool validate_rpcn_username(const std::string& input) +bool validate_rpcn_username(std::string_view username) { - if (input.length() < 3 || input.length() > 16) + if (username.length() < 3 || username.length() > 16) return false; - return std::all_of(input.cbegin(), input.cend(), [](const char c) - { return std::isalnum(c) || c == '-' || c == '_'; }); + return std::all_of(username.cbegin(), username.cend(), [](const char c) + { + return std::isalnum(static_cast(c)) || c == '-' || c == '_'; + }); +} + +bool validate_email(std::string_view email) +{ + const QRegularExpressionValidator simple_email_validator(QRegularExpression("^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$")); + QString qstr_email = QString::fromStdString(std::string(email)); + int pos = 0; + + if (qstr_email.isEmpty() || qstr_email.contains(' ') || qstr_email.contains('\t') || simple_email_validator.validate(qstr_email, pos) != QValidator::Acceptable) + return false; + + return true; +} + +bool validate_token(std::string_view token) +{ + return token.size() == 16 && std::all_of(token.cbegin(), token.cend(), [](const char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z'); }); +} + +std::string derive_password(std::string_view user_password) +{ + std::string_view salt_str = "No matter where you go, everybody's connected."; + + u8 derived_password_digest[SHA3_256_DIGEST_LENGTH]; + ensure(!wc_PBKDF2(derived_password_digest, reinterpret_cast(user_password.data()), user_password.size(), reinterpret_cast(salt_str.data()), salt_str.size(), 200'000, SHA3_256_DIGEST_LENGTH, WC_SHA3_256)); + + std::string derived_password("0000000000000000000000000000000000000000000000000000000000000000"); + for (u32 i = 0; i < SHA3_256_DIGEST_LENGTH; i++) + { + constexpr auto pal = "0123456789ABCDEF"; + derived_password[i * 2] = pal[derived_password_digest[i] >> 4]; + derived_password[(i * 2) + 1] = pal[derived_password_digest[i] & 15]; + } + + return derived_password; } rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent) @@ -51,6 +90,11 @@ rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent) connect(btn_account, &QPushButton::clicked, this, [this]() { + if (!Emu.IsStopped()) + { + QMessageBox::critical(this, tr("Error: Emulation Running"), tr("You need to stop the emulator before editing RPCN account information!"), QMessageBox::Ok); + return; + } rpcn_account_dialog dlg(this); dlg.exec(); }); @@ -66,8 +110,509 @@ rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent) rpcn_account_dialog::rpcn_account_dialog(QWidget* parent) : QDialog(parent) { - setWindowTitle(tr("RPCN: Configuration")); + setWindowTitle(tr("RPCN: Account")); setObjectName("rpcn_account_dialog"); + + QVBoxLayout* vbox_global = new QVBoxLayout(); + + QGroupBox* grp_server = new QGroupBox(tr("Server:")); + QVBoxLayout* vbox_server = new QVBoxLayout(); + + QHBoxLayout* hbox_lbl_combo = new QHBoxLayout(); + QLabel* lbl_server = new QLabel(tr("Server:")); + cbx_servers = new QComboBox(); + + refresh_combobox(); + + hbox_lbl_combo->addWidget(lbl_server); + hbox_lbl_combo->addWidget(cbx_servers); + + QHBoxLayout* hbox_buttons = new QHBoxLayout(); + QPushButton* btn_add_server = new QPushButton(tr("Add")); + QPushButton* btn_del_server = new QPushButton(tr("Del")); + hbox_buttons->addStretch(); + hbox_buttons->addWidget(btn_add_server); + hbox_buttons->addWidget(btn_del_server); + + vbox_server->addLayout(hbox_lbl_combo); + vbox_server->addLayout(hbox_buttons); + + grp_server->setLayout(vbox_server); + vbox_global->addWidget(grp_server); + + QGroupBox* grp_buttons = new QGroupBox(); + QVBoxLayout* vbox_buttons = new QVBoxLayout(); + QPushButton* btn_create = new QPushButton(tr("Create Account")); + QPushButton* btn_edit = new QPushButton(tr("Edit Account")); + QPushButton* btn_test = new QPushButton(tr("Test Account")); + + vbox_buttons->addSpacing(10); + vbox_buttons->addWidget(btn_create); + vbox_buttons->addSpacing(10); + vbox_buttons->addWidget(btn_edit); + vbox_buttons->addSpacing(10); + vbox_buttons->addWidget(btn_test); + vbox_buttons->addSpacing(10); + grp_buttons->setLayout(vbox_buttons); + + vbox_global->addWidget(grp_buttons); + + setLayout(vbox_global); + + connect(cbx_servers, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) + { + if (index < 0) + return; + + QVariant host = cbx_servers->itemData(index); + + if (!host.isValid() || !host.canConvert()) + return; + + g_cfg_rpcn.set_host(host.toString().toStdString()); + g_cfg_rpcn.save(); + }); + + connect(btn_add_server, &QAbstractButton::clicked, this, [this]() + { + rpcn_add_server_dialog dlg(this); + dlg.exec(); + const auto new_server = dlg.get_new_server(); + if (new_server) + { + if (!g_cfg_rpcn.add_host(new_server->first, new_server->second)) + { + QMessageBox::critical(this, tr("Existing Server"), tr("You already have a server with this description & hostname in the list."), QMessageBox::Ok); + return; + } + + g_cfg_rpcn.save(); + refresh_combobox(); + } + }); + + connect(btn_del_server, &QAbstractButton::clicked, this, [this]() + { + const int index = cbx_servers->currentIndex(); + + if (index < 0) + return; + + const auto desc = cbx_servers->itemText(index).toStdString(); + const auto host = cbx_servers->itemData(index).toString().toStdString(); + + ensure(g_cfg_rpcn.del_host(desc, host)); + g_cfg_rpcn.save(); + refresh_combobox(); + }); + + connect(btn_create, &QAbstractButton::clicked, this, [this]() + { + rpcn_ask_username_dialog dlg_username(this, tr("Please enter your username.\n\n" + "Note that these restrictions apply:\n" + "- Username must be between 3 and 16 characters\n" + "- Username can only contain a-z A-Z 0-9 '-' '_'\n" + "- Username is case sensitive\n")); + dlg_username.exec(); + auto username = dlg_username.get_username(); + + if (!username) + return; + + rpcn_ask_password_dialog dlg_password(this, tr("Please choose your password:\n\n")); + dlg_password.exec(); + auto password = dlg_password.get_password(); + + if (!password) + return; + + rpcn_ask_email_dialog dlg_email(this, tr("An email address is required, please note:\n" + "- A valid email is needed to receive the token that validates your account.\n" + "- Your email won't be used for anything beyond sending you this token or the password reset token.\n\n")); + dlg_email.exec(); + auto email = dlg_email.get_email(); + + if (!email) + return; + + if (QMessageBox::question(this, tr("RPCN: Account Creation"), tr("You are about to create an account with:\n-Username:%0\n-Email:%1\n\nIs this correct?").arg(QString::fromStdString(*username)).arg(QString::fromStdString(*email))) != QMessageBox::Yes) + return; + + { + const auto rpcn = rpcn::rpcn_client::get_instance(); + const auto avatar_url = "https://rpcs3.net/cdn/netplay/DefaultAvatar.png"; + + if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure) + { + const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result))); + QMessageBox::critical(this, tr("Error Connecting"), error_message, QMessageBox::Ok); + return; + } + + if (auto error = rpcn->create_user(*username, *password, *username, avatar_url, *email); error != rpcn::ErrorType::NoError) + { + QString error_message; + switch (error) + { + case rpcn::ErrorType::CreationExistingUsername: error_message = tr("An account with that username already exists!"); break; + case rpcn::ErrorType::CreationBannedEmailProvider: error_message = tr("This email provider is banned!"); break; + case rpcn::ErrorType::CreationExistingEmail: error_message = tr("An account with that email already exists!"); break; + case rpcn::ErrorType::CreationError: error_message = tr("Unknown creation error"); break; + default: error_message = tr("Unknown error"); break; + } + QMessageBox::critical(this, tr("Error Creating Account"), tr("Failed to create the account:\n%0").arg(error_message), QMessageBox::Ok); + return; + } + } + + g_cfg_rpcn.set_npid(*username); + g_cfg_rpcn.set_password(*password); + g_cfg_rpcn.save(); + + rpcn_ask_token_dialog token_dlg(this, tr("Your account has been created successfully!\n" + "Your account authentification was saved.\n" + "Now all you need is to enter the token that was sent to your email.\n" + "You can skip this step by leaving it empty and entering it later in the Edit Account section too.\n")); + token_dlg.exec(); + auto token = token_dlg.get_token(); + + if (!token) + return; + + g_cfg_rpcn.set_token(*token); + g_cfg_rpcn.save(); + }); + + connect(btn_edit, &QAbstractButton::clicked, this, [this]() + { + rpcn_account_edit_dialog dlg_edit(this); + dlg_edit.exec(); + }); + + connect(btn_test, &QAbstractButton::clicked, this, [this]() + { + auto rpcn = rpcn::rpcn_client::get_instance(); + + if (auto res = rpcn->wait_for_connection(); res != rpcn::rpcn_state::failure_no_failure) + { + const QString error_msg = tr("Failed to connect to RPCN:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(res))); + QMessageBox::warning(this, tr("Error connecting to RPCN!"), error_msg, QMessageBox::Ok); + return; + } + if (auto res = rpcn->wait_for_authentified(); res != rpcn::rpcn_state::failure_no_failure) + { + const QString error_msg = tr("Failed to authentify to RPCN:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(res))); + QMessageBox::warning(this, tr("Error authentifying to RPCN!"), error_msg, QMessageBox::Ok); + return; + } + + QMessageBox::information(this, tr("RPCN Account Valid!"), tr("Your account is valid!"), QMessageBox::Ok); + }); +} + +void rpcn_account_dialog::refresh_combobox() +{ + g_cfg_rpcn.load(); + const auto vec_hosts = g_cfg_rpcn.get_hosts(); + auto cur_host = g_cfg_rpcn.get_host(); + int i = 0, index = 0; + + cbx_servers->clear(); + + for (const auto& [desc, host] : vec_hosts) + { + cbx_servers->addItem(QString::fromStdString(desc), QString::fromStdString(host)); + if (cur_host == host) + index = i; + + i++; + } + + cbx_servers->setCurrentIndex(index); +} + +rpcn_add_server_dialog::rpcn_add_server_dialog(QWidget* parent) + : QDialog(parent) +{ + setWindowTitle(tr("RPCN: Add Server")); + setObjectName("rpcn_add_server_dialog"); + setMinimumSize(QSize(400, 200)); + + QVBoxLayout* vbox_global = new QVBoxLayout(); + + QLabel* lbl_description = new QLabel(tr("Description:")); + QLineEdit* edt_description = new QLineEdit(); + QLabel* lbl_host = new QLabel(tr("Host:")); + QLineEdit* edt_host = new QLineEdit(); + QDialogButtonBox* btn_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + vbox_global->addWidget(lbl_description); + vbox_global->addWidget(edt_description); + vbox_global->addWidget(lbl_host); + vbox_global->addWidget(edt_host); + vbox_global->addWidget(btn_box); + + setLayout(vbox_global); + + connect(btn_box, &QDialogButtonBox::accepted, this, [this, edt_description, edt_host]() + { + auto description = edt_description->text(); + auto host = edt_host->text(); + + if (description.isEmpty()) + { + QMessageBox::critical(this, tr("Missing Description"), tr("You must enter a description!"), QMessageBox::Ok); + return; + } + if (host.isEmpty()) + { + QMessageBox::critical(this, tr("Missing Hostname"), tr("You must enter a hostname for the server!"), QMessageBox::Ok); + return; + } + + m_new_server = std::make_pair(description.toStdString(), host.toStdString()); + QDialog::accept(); + }); + connect(btn_box, &QDialogButtonBox::rejected, this, &QDialog::reject); +} + +const std::optional>& rpcn_add_server_dialog::get_new_server() const +{ + return m_new_server; +} + +rpcn_ask_username_dialog::rpcn_ask_username_dialog(QWidget* parent, const QString& description) + : QDialog(parent) +{ + setWindowTitle(tr("RPCN: Username")); + setObjectName("rpcn_ask_username_dialog"); + + QVBoxLayout* vbox_global = new QVBoxLayout(); + + QLabel* lbl_username = new QLabel(description); + + QGroupBox* grp_username = new QGroupBox(tr("Username:")); + QHBoxLayout* hbox_grp_username = new QHBoxLayout(); + QLineEdit* edt_username = new QLineEdit(QString::fromStdString(g_cfg_rpcn.get_npid())); + edt_username->setMaxLength(16); + edt_username->setValidator(new QRegularExpressionValidator(QRegularExpression("^[a-zA-Z0-9_\\-]*$"), this)); + hbox_grp_username->addWidget(edt_username); + grp_username->setLayout(hbox_grp_username); + + QDialogButtonBox* btn_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + vbox_global->addWidget(lbl_username); + vbox_global->addWidget(grp_username); + vbox_global->addWidget(btn_box); + + setLayout(vbox_global); + + connect(btn_box, &QDialogButtonBox::accepted, this, [this, edt_username]() + { + const auto username = edt_username->text().toStdString(); + + if (username.empty()) + { + QMessageBox::critical(this, tr("Missing Username"), tr("You must enter a username!"), QMessageBox::Ok); + return; + } + if (!validate_rpcn_username(username)) + { + QMessageBox::critical(this, tr("Invalid Username"), tr("Please enter a valid username!"), QMessageBox::Ok); + } + + m_username = username; + QDialog::accept(); + }); + connect(btn_box, &QDialogButtonBox::rejected, this, &QDialog::reject); +} + +const std::optional& rpcn_ask_username_dialog::get_username() const +{ + return m_username; +} + +rpcn_ask_password_dialog::rpcn_ask_password_dialog(QWidget* parent, const QString& description) + : QDialog(parent) +{ + setWindowTitle(tr("RPCN: Password")); + setObjectName("rpcn_ask_password_dialog"); + + QVBoxLayout* vbox_global = new QVBoxLayout(); + + QLabel* lbl_description = new QLabel(description); + + QGroupBox* gbox_password = new QGroupBox(); + QVBoxLayout* vbox_gbox = new QVBoxLayout(); + + QLabel* lbl_pass1 = new QLabel(tr("Enter your password:")); + QLineEdit* m_edit_pass1 = new QLineEdit(); + m_edit_pass1->setEchoMode(QLineEdit::Password); + QLabel* lbl_pass2 = new QLabel(tr("Enter your password a second time:")); + QLineEdit* m_edit_pass2 = new QLineEdit(); + m_edit_pass2->setEchoMode(QLineEdit::Password); + + vbox_gbox->addWidget(lbl_pass1); + vbox_gbox->addWidget(m_edit_pass1); + vbox_gbox->addWidget(lbl_pass2); + vbox_gbox->addWidget(m_edit_pass2); + gbox_password->setLayout(vbox_gbox); + + QDialogButtonBox* btn_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + vbox_global->addWidget(lbl_description); + vbox_global->addWidget(gbox_password); + vbox_global->addWidget(btn_box); + + setLayout(vbox_global); + + connect(btn_box, &QDialogButtonBox::accepted, this, [this, m_edit_pass1, m_edit_pass2]() + { + if (m_edit_pass1->text() != m_edit_pass2->text()) + { + QMessageBox::critical(this, tr("Wrong Input"), tr("The two passwords you entered don't match!"), QMessageBox::Ok); + return; + } + + if (m_edit_pass1->text().isEmpty()) + { + QMessageBox::critical(this, tr("Missing Password"), tr("You need to enter a password!"), QMessageBox::Ok); + return; + } + + m_password = derive_password(m_edit_pass1->text().toStdString()); + QDialog::accept(); + }); + connect(btn_box, &QDialogButtonBox::rejected, this, &QDialog::reject); +} + +const std::optional& rpcn_ask_password_dialog::get_password() const +{ + return m_password; +} + +rpcn_ask_email_dialog::rpcn_ask_email_dialog(QWidget* parent, const QString& description) + : QDialog(parent) +{ + setWindowTitle(tr("RPCN: Email")); + setObjectName("rpcn_ask_email_dialog"); + + QVBoxLayout* vbox_global = new QVBoxLayout(); + + QLabel* lbl_emailinfo = new QLabel(description); + + QGroupBox* gbox_password = new QGroupBox(); + QVBoxLayout* vbox_gbox = new QVBoxLayout(); + + QLabel* lbl_pass1 = new QLabel(tr("Enter your email:")); + QLineEdit* m_edit_pass1 = new QLineEdit(); + QLabel* lbl_pass2 = new QLabel(tr("Enter your email a second time:")); + QLineEdit* m_edit_pass2 = new QLineEdit(); + + vbox_gbox->addWidget(lbl_pass1); + vbox_gbox->addWidget(m_edit_pass1); + vbox_gbox->addWidget(lbl_pass2); + vbox_gbox->addWidget(m_edit_pass2); + gbox_password->setLayout(vbox_gbox); + + QDialogButtonBox* btn_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + vbox_global->addWidget(lbl_emailinfo); + vbox_global->addWidget(gbox_password); + vbox_global->addWidget(btn_box); + + setLayout(vbox_global); + + connect(btn_box, &QDialogButtonBox::accepted, this, [this, m_edit_pass1, m_edit_pass2]() + { + if (m_edit_pass1->text() != m_edit_pass2->text()) + { + QMessageBox::critical(this, tr("Wrong Input"), tr("The two emails you entered don't match!"), QMessageBox::Ok); + return; + } + + if (m_edit_pass1->text().isEmpty()) + { + QMessageBox::critical(this, tr("Missing Email"), tr("You need to enter an email!"), QMessageBox::Ok); + return; + } + + auto email = m_edit_pass1->text().toStdString(); + if (!validate_email(email)) + { + QMessageBox::critical(this, tr("Invalid Email"), tr("You need to enter a valid email!"), QMessageBox::Ok); + return; + } + + m_email = email; + QDialog::accept(); + }); + connect(btn_box, &QDialogButtonBox::rejected, this, &QDialog::reject); +} + +const std::optional& rpcn_ask_email_dialog::get_email() const +{ + return m_email; +} + +rpcn_ask_token_dialog::rpcn_ask_token_dialog(QWidget* parent, const QString& description) + : QDialog(parent) +{ + setWindowTitle(tr("RPCN: Username")); + setObjectName("rpcn_ask_token_dialog"); + + QVBoxLayout* vbox_global = new QVBoxLayout(); + + QLabel* lbl_token = new QLabel(description); + + QGroupBox* grp_token = new QGroupBox(tr("Token:")); + QHBoxLayout* hbox_grp_token = new QHBoxLayout(); + QLineEdit* edt_token = new QLineEdit(); + edt_token->setMaxLength(16); + hbox_grp_token->addWidget(edt_token); + grp_token->setLayout(hbox_grp_token); + + QDialogButtonBox* btn_box = new QDialogButtonBox(QDialogButtonBox::Ok); + + vbox_global->addWidget(lbl_token); + vbox_global->addWidget(grp_token); + vbox_global->addWidget(btn_box); + + setLayout(vbox_global); + + connect(btn_box, &QDialogButtonBox::accepted, this, [this, edt_token]() + { + const auto token = edt_token->text().toStdString(); + + if (!token.empty()) + { + if (!validate_token(token)) + { + QMessageBox::critical(this, tr("Invalid Token"), tr("The token appears to be invalid:\n" + "-Token should be 16 characters long\n" + "-Token should only contain 0-9 and A-F"), + QMessageBox::Ok); + return; + } + + m_token = token; + } + + QDialog::accept(); + return; + }); +} + +const std::optional& rpcn_ask_token_dialog::get_token() const +{ + return m_token; +} + +rpcn_account_edit_dialog::rpcn_account_edit_dialog(QWidget* parent) + : QDialog(parent) +{ + setWindowTitle(tr("RPCN: Edit Account")); + setObjectName("rpcn_account_edit_dialog"); setMinimumSize(QSize(400, 200)); QVBoxLayout* vbox_global = new QVBoxLayout(); @@ -76,37 +621,30 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent) QVBoxLayout* vbox_edits = new QVBoxLayout(); QHBoxLayout* hbox_buttons = new QHBoxLayout(); - QLabel* label_host = new QLabel(tr("Host:")); - m_edit_host = new QLineEdit(); - QLabel* label_npid = new QLabel(tr("NPID (username):")); - m_edit_npid = new QLineEdit(); - m_edit_npid->setMaxLength(16); - m_edit_npid->setValidator(new QRegularExpressionValidator(QRegularExpression("^[a-zA-Z0-9_\\-]*$"), this)); - QLabel* label_pass = new QLabel(tr("Password:")); + QLabel* lbl_username = new QLabel(tr("Username:")); + m_edit_username = new QLineEdit(); + m_edit_username->setMaxLength(16); + m_edit_username->setValidator(new QRegularExpressionValidator(QRegularExpression("^[a-zA-Z0-9_\\-]*$"), this)); + QLabel* lbl_pass = new QLabel(tr("Password:")); QPushButton* btn_chg_pass = new QPushButton(tr("Set Password")); - QLabel* label_token = new QLabel(tr("Token:")); + QLabel* lbl_token = new QLabel(tr("Token:")); m_edit_token = new QLineEdit(); m_edit_token->setMaxLength(16); - QPushButton* btn_create = new QPushButton(tr("Create Account"), this); - QPushButton* btn_resendtoken = new QPushButton(tr("Resend Token"), this); - QPushButton* btn_changepass = new QPushButton(tr("Change Password"), this); - btn_changepass->setEnabled(false); - QPushButton* btn_save = new QPushButton(tr("Save"), this); + QPushButton* btn_resendtoken = new QPushButton(tr("Resend Token"), this); + QPushButton* btn_change_password = new QPushButton(tr("Change Password"), this); + QPushButton* btn_save = new QPushButton(tr("Save"), this); - vbox_labels->addWidget(label_host); - vbox_labels->addWidget(label_npid); - vbox_labels->addWidget(label_pass); - vbox_labels->addWidget(label_token); + vbox_labels->addWidget(lbl_username); + vbox_labels->addWidget(lbl_pass); + vbox_labels->addWidget(lbl_token); - vbox_edits->addWidget(m_edit_host); - vbox_edits->addWidget(m_edit_npid); + vbox_edits->addWidget(m_edit_username); vbox_edits->addWidget(btn_chg_pass); vbox_edits->addWidget(m_edit_token); - hbox_buttons->addWidget(btn_create); hbox_buttons->addWidget(btn_resendtoken); - hbox_buttons->addWidget(btn_changepass); + hbox_buttons->addWidget(btn_change_password); hbox_buttons->addStretch(); hbox_buttons->addWidget(btn_save); @@ -120,29 +658,14 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent) connect(btn_chg_pass, &QAbstractButton::clicked, this, [this]() { - rpcn_ask_password_dialog ask_pass; + rpcn_ask_password_dialog ask_pass(this, tr("Please enter your password:")); ask_pass.exec(); auto password = ask_pass.get_password(); if (!password) return; - const std::string pass_str = password.value(); - const std::string salt_str = "No matter where you go, everybody's connected."; - - u8 salted_pass[SHA_DIGEST_SIZE]; - - wolfSSL_PKCS5_PBKDF2_HMAC_SHA1(pass_str.c_str(), pass_str.size(), reinterpret_cast(salt_str.c_str()), salt_str.size(), 1000, SHA_DIGEST_SIZE, salted_pass); - - std::string hash("0000000000000000000000000000000000000000"); - for (u32 i = 0; i < 20; i++) - { - constexpr auto pal = "0123456789abcdef"; - hash[i * 2] = pal[salted_pass[i] >> 4]; - hash[1 + i * 2] = pal[salted_pass[i] & 15]; - } - - g_cfg_rpcn.set_password(hash); + g_cfg_rpcn.set_password(*password); g_cfg_rpcn.save(); QMessageBox::information(this, tr("RPCN Password Saved"), tr("Your password was saved successfully!"), QMessageBox::Ok); @@ -153,48 +676,39 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent) if (save_config()) close(); }); - connect(btn_create, &QAbstractButton::clicked, this, &rpcn_account_dialog::create_account); - connect(btn_resendtoken, &QAbstractButton::clicked, this, &rpcn_account_dialog::resend_token); + connect(btn_resendtoken, &QAbstractButton::clicked, this, &rpcn_account_edit_dialog::resend_token); + connect(btn_change_password, &QAbstractButton::clicked, this, &rpcn_account_edit_dialog::change_password); g_cfg_rpcn.load(); - m_edit_host->setText(QString::fromStdString(g_cfg_rpcn.get_host())); - m_edit_npid->setText(QString::fromStdString(g_cfg_rpcn.get_npid())); + m_edit_username->setText(QString::fromStdString(g_cfg_rpcn.get_npid())); m_edit_token->setText(QString::fromStdString(g_cfg_rpcn.get_token())); } -bool rpcn_account_dialog::save_config() +bool rpcn_account_edit_dialog::save_config() { - const auto host = m_edit_host->text().toStdString(); - const auto npid = m_edit_npid->text().toStdString(); - const auto token = m_edit_token->text().toStdString(); + const auto username = m_edit_username->text().toStdString(); + const auto token = m_edit_token->text().toStdString(); - if (host.empty()) + if (username.empty() || g_cfg_rpcn.get_password().empty()) { - QMessageBox::critical(this, tr("Missing host"), tr("You need to enter a host for rpcn!"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Missing Input"), tr("You need to enter a username and a password!"), QMessageBox::Ok); return false; } - if (npid.empty() || g_cfg_rpcn.get_password().empty()) + if (!validate_rpcn_username(username)) { - QMessageBox::critical(this, tr("Wrong input"), tr("You need to enter a username and a password!"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Invalid Username"), tr("Username must be between 3 and 16 characters and can only contain '-', '_' or alphanumeric characters."), QMessageBox::Ok); return false; } - if (!validate_rpcn_username(npid)) + if (!token.empty() && !validate_token(token)) { - QMessageBox::critical(this, tr("Invalid character"), tr("NPID must be between 3 and 16 characters and can only contain '-', '_' or alphanumeric characters."), QMessageBox::Ok); + QMessageBox::critical(this, tr("Invalid Token"), tr("The token you have received should be 16 characters long and contain only 0-9 A-F."), QMessageBox::Ok); return false; } - if (!token.empty() && token.size() != 16) - { - QMessageBox::critical(this, tr("Invalid token"), tr("The token you have received should be 16 characters long."), QMessageBox::Ok); - return false; - } - - g_cfg_rpcn.set_host(host); - g_cfg_rpcn.set_npid(npid); + g_cfg_rpcn.set_npid(username); g_cfg_rpcn.set_token(token); g_cfg_rpcn.save(); @@ -202,66 +716,7 @@ bool rpcn_account_dialog::save_config() return true; } -void rpcn_account_dialog::create_account() -{ - // Validate and save - if (!save_config()) - return; - - QString email; - const QRegularExpressionValidator simple_email_validator(QRegularExpression("^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$")); - - while (true) - { - bool clicked_ok = false; - email = QInputDialog::getText(this, tr("Email address"), tr("An email address is required, please note:\n*A valid email is needed to validate your account.\n*Your email won't be used for anything beyond sending you the token.\n*Upon successful creation a token will be sent to your email which you'll need to login.\n\n"), QLineEdit::Normal, "", &clicked_ok); - if (!clicked_ok) - return; - - int pos = 0; - if (email.isEmpty() || simple_email_validator.validate(email, pos) != QValidator::Acceptable) - { - QMessageBox::critical(this, tr("Wrong input"), tr("You need to enter a valid email!"), QMessageBox::Ok); - } - else - { - break; - } - } - - const auto rpcn = rpcn::rpcn_client::get_instance(); - - const auto npid = g_cfg_rpcn.get_npid(); - const auto online_name = npid; - const auto avatar_url = "https://rpcs3.net/cdn/netplay/DefaultAvatar.png"; - const auto password = g_cfg_rpcn.get_password(); - - if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure) - { - const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result))); - QMessageBox::critical(this, tr("Error Connecting"), error_message, QMessageBox::Ok); - return; - } - - if (auto error = rpcn->create_user(npid, password, online_name, avatar_url, email.toStdString()); error != rpcn::ErrorType::NoError) - { - QString error_message; - switch (error) - { - case rpcn::ErrorType::CreationExistingUsername: error_message = tr("An account with that username already exists!"); break; - case rpcn::ErrorType::CreationBannedEmailProvider: error_message = tr("This email provider is banned!"); break; - case rpcn::ErrorType::CreationExistingEmail: error_message = tr("An account with that email already exists!"); break; - case rpcn::ErrorType::CreationError: error_message = tr("Unknown creation error"); break; - default: error_message = tr("Unknown error"); break; - } - QMessageBox::critical(this, tr("Error Creating Account"), tr("Failed to create the account:\n%0").arg(error_message), QMessageBox::Ok); - return; - } - - QMessageBox::information(this, tr("Account created!"), tr("Your account has been created successfully!\nCheck your email for your token!"), QMessageBox::Ok); -} - -void rpcn_account_dialog::resend_token() +void rpcn_account_edit_dialog::resend_token() { if (!save_config()) return; @@ -284,11 +739,10 @@ void rpcn_account_dialog::resend_token() switch (error) { case rpcn::ErrorType::Invalid: error_message = tr("The server has no email verification and doesn't need a token!"); break; - case rpcn::ErrorType::LoginAlreadyLoggedIn: error_message = tr("You can't ask for your token while authentified!"); break; case rpcn::ErrorType::DbFail: error_message = tr("A database related error happened on the server!"); break; case rpcn::ErrorType::TooSoon: error_message = tr("You can only ask for a token mail once every 24 hours!"); break; case rpcn::ErrorType::EmailFail: error_message = tr("The mail couldn't be sent successfully!"); break; - case rpcn::ErrorType::LoginError: error_message = tr("The login/password pair is invalid!"); break; + case rpcn::ErrorType::LoginError: error_message = tr("The username/password pair is invalid!"); break; default: error_message = tr("Unknown error"); break; } QMessageBox::critical(this, tr("Error Sending Token"), tr("Failed to send the token:\n%0").arg(error_message), QMessageBox::Ok); @@ -298,64 +752,102 @@ void rpcn_account_dialog::resend_token() QMessageBox::information(this, tr("Token Sent!"), tr("Your token was successfully resent to the email associated with your account!"), QMessageBox::Ok); } -rpcn_ask_password_dialog::rpcn_ask_password_dialog(QWidget* parent) - : QDialog(parent) +void rpcn_account_edit_dialog::change_password() { - QVBoxLayout* vbox_global = new QVBoxLayout(); - QHBoxLayout* hbox_buttons = new QHBoxLayout(); + rpcn_ask_username_dialog dlg_username(this, tr("Please confirm your username:")); + dlg_username.exec(); + auto username = dlg_username.get_username(); - QGroupBox* gbox_password = new QGroupBox(); - QVBoxLayout* vbox_gbox = new QVBoxLayout(); + if (!username) + return; - QLabel* label_pass1 = new QLabel(tr("Enter your password:")); - m_edit_pass1 = new QLineEdit(); - m_edit_pass1->setEchoMode(QLineEdit::Password); - QLabel* label_pass2 = new QLabel(tr("Enter your password a second time:")); - m_edit_pass2 = new QLineEdit(); - m_edit_pass2->setEchoMode(QLineEdit::Password); + switch (QMessageBox::question(this, tr("RPCN: Change Password"), tr("Do you already have a reset password token?\n" + "Note that the reset password token is different from the email verification token."))) + { + case QMessageBox::No: + { + rpcn_ask_email_dialog dlg_email(this, tr("Please enter the email you used to create the account:")); + dlg_email.exec(); + const auto email = dlg_email.get_email(); - vbox_gbox->addWidget(label_pass1); - vbox_gbox->addWidget(m_edit_pass1); - vbox_gbox->addWidget(label_pass2); - vbox_gbox->addWidget(m_edit_pass2); - gbox_password->setLayout(vbox_gbox); + if (!email) + return; - QPushButton* btn_ok = new QPushButton(tr("Ok")); - QPushButton* btn_cancel = new QPushButton(tr("Cancel")); - - hbox_buttons->addStretch(); - hbox_buttons->addWidget(btn_ok); - hbox_buttons->addWidget(btn_cancel); - - vbox_global->addWidget(gbox_password); - vbox_global->addLayout(hbox_buttons); - - setLayout(vbox_global); - - connect(btn_ok, &QAbstractButton::clicked, this, [this]() { - if (m_edit_pass1->text() != m_edit_pass2->text()) + const auto rpcn = rpcn::rpcn_client::get_instance(); + if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure) { - QMessageBox::critical(this, tr("Wrong input"), tr("The two passwords you entered don't match!"), QMessageBox::Ok); + const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result))); + QMessageBox::critical(this, tr("Error Connecting"), error_message, QMessageBox::Ok); return; } - if (m_edit_pass1->text().isEmpty()) + if (auto error = rpcn->send_reset_token(*username, *email); error != rpcn::ErrorType::NoError) { - QMessageBox::critical(this, tr("Wrong input"), tr("You need to enter a password!"), QMessageBox::Ok); + QString error_message; + switch (error) + { + case rpcn::ErrorType::Invalid: error_message = tr("The server has no email verification and doesn't support password changes!"); break; + case rpcn::ErrorType::DbFail: error_message = tr("A database related error happened on the server!"); break; + case rpcn::ErrorType::TooSoon: error_message = tr("You can only ask for a reset password token once every 24 hours!"); break; + case rpcn::ErrorType::EmailFail: error_message = tr("The mail couldn't be sent successfully!"); break; + case rpcn::ErrorType::LoginError: error_message = tr("The username/email pair is invalid!"); break; + default: error_message = tr("Unknown error"); break; + } + QMessageBox::critical(this, tr("Error Sending Password Reset Token"), tr("Failed to send the password reset token:\n%0").arg(error_message), QMessageBox::Ok); return; } - m_password = m_edit_pass1->text().toStdString(); - close(); - }); - connect(btn_cancel, &QAbstractButton::clicked, this, [this]() - { this->close(); }); -} + QMessageBox::information(this, tr("Password Reset Token Sent!"), tr("The reset password token has successfully been sent!"), QMessageBox::Ok); + } + } + case QMessageBox::Yes: + { + rpcn_ask_token_dialog dlg_token(this, tr("Please enter the password reset token you received:")); + dlg_token.exec(); + const auto token = dlg_token.get_token(); -std::optional rpcn_ask_password_dialog::get_password() -{ - return m_password; + if (!token) + return; + + rpcn_ask_password_dialog dlg_password(this, tr("Please enter your new password:")); + dlg_password.exec(); + const auto password = dlg_password.get_password(); + + if (!password) + return; + + { + const auto rpcn = rpcn::rpcn_client::get_instance(); + if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure) + { + const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result))); + QMessageBox::critical(this, tr("Error Connecting"), error_message, QMessageBox::Ok); + return; + } + + if (auto error = rpcn->reset_password(*username, *token, *password); error != rpcn::ErrorType::NoError) + { + QString error_message; + switch (error) + { + case rpcn::ErrorType::Invalid: error_message = tr("The server has no email verification and doesn't support password changes!"); break; + case rpcn::ErrorType::DbFail: error_message = tr("A database related error happened on the server!"); break; + case rpcn::ErrorType::TooSoon: error_message = tr("You can only ask for a reset password token once every 24 hours!"); break; + case rpcn::ErrorType::EmailFail: error_message = tr("The mail couldn't be sent successfully!"); break; + case rpcn::ErrorType::LoginError: error_message = tr("The username/token pair is invalid!"); break; + default: error_message = tr("Unknown error"); break; + } + QMessageBox::critical(this, tr("Error Sending Password Reset Token"), tr("Failed to change the password:\n%0").arg(error_message), QMessageBox::Ok); + return; + } + + QMessageBox::information(this, tr("Password Successfully Changed!"), tr("Your password has been successfully changed!"), QMessageBox::Ok); + } + } + default: + return; + } } void friend_callback(void* param, rpcn::NotificationType ntype, const std::string& username, bool status) @@ -636,7 +1128,7 @@ void rpcn_friends_dialog::callback_handler(rpcn::NotificationType ntype, std::st } default: { - rpcn_settings_log.fatal("An unhandled notification type was received by the rpcn friends dialog callback!"); + rpcn_settings_log.fatal("An unhandled notification type was received by the RPCN friends dialog callback!"); break; } } diff --git a/rpcs3/rpcs3qt/rpcn_settings_dialog.h b/rpcs3/rpcs3qt/rpcn_settings_dialog.h index 70ac38834e..831f5eafb9 100644 --- a/rpcs3/rpcs3qt/rpcn_settings_dialog.h +++ b/rpcs3/rpcs3qt/rpcn_settings_dialog.h @@ -22,28 +22,84 @@ public: rpcn_account_dialog(QWidget* parent = nullptr); private: - bool save_config(); + void refresh_combobox(); -private Q_SLOTS: - void create_account(); - void resend_token(); +private: + QComboBox* cbx_servers = nullptr; +}; -protected: - QLineEdit *m_edit_host, *m_edit_npid, *m_edit_token; +class rpcn_add_server_dialog : public QDialog +{ + Q_OBJECT +public: + rpcn_add_server_dialog(QWidget* parent = nullptr); + const std::optional>& get_new_server() const; + +private: + std::optional> m_new_server; +}; + +class rpcn_ask_username_dialog : public QDialog +{ + Q_OBJECT +public: + rpcn_ask_username_dialog(QWidget* parent, const QString& description); + const std::optional& get_username() const; + +private: + std::optional m_username; }; class rpcn_ask_password_dialog : public QDialog { Q_OBJECT public: - rpcn_ask_password_dialog(QWidget* parent = nullptr); - std::optional get_password(); + rpcn_ask_password_dialog(QWidget* parent, const QString& description); + const std::optional& get_password() const; private: - QLineEdit *m_edit_pass1, *m_edit_pass2; std::optional m_password; }; +class rpcn_ask_email_dialog : public QDialog +{ + Q_OBJECT +public: + rpcn_ask_email_dialog(QWidget* parent, const QString& description); + const std::optional& get_email() const; + +private: + std::optional m_email; +}; + +class rpcn_ask_token_dialog : public QDialog +{ + Q_OBJECT +public: + rpcn_ask_token_dialog(QWidget* parent, const QString& description); + const std::optional& get_token() const; + +private: + std::optional m_token; +}; + +class rpcn_account_edit_dialog : public QDialog +{ + Q_OBJECT +public: + rpcn_account_edit_dialog(QWidget* parent = nullptr); + +private: + bool save_config(); + +private Q_SLOTS: + void resend_token(); + void change_password(); + +protected: + QLineEdit *m_edit_username, *m_edit_token; +}; + class rpcn_friends_dialog : public QDialog { Q_OBJECT