From e47f224bc21cddcc64a702ef9afbdfc2a94c4de4 Mon Sep 17 00:00:00 2001 From: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com> Date: Sun, 7 Dec 2025 17:52:22 +0100 Subject: [PATCH 01/30] Clans: initial implementation - Needs GUI implementation to pick the server host - Needs code to select the server host from config Signed-off-by: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com> --- rpcs3/Emu/CMakeLists.txt | 1 + rpcs3/Emu/Cell/Modules/sceNp.h | 1 + rpcs3/Emu/Cell/Modules/sceNpClans.cpp | 449 +++++++++-- rpcs3/Emu/Cell/Modules/sceNpClans.h | 32 +- rpcs3/Emu/NP/clan_client.cpp | 1035 +++++++++++++++++++++++++ rpcs3/Emu/NP/clan_client.h | 120 +++ 6 files changed, 1546 insertions(+), 92 deletions(-) create mode 100644 rpcs3/Emu/NP/clan_client.cpp create mode 100644 rpcs3/Emu/NP/clan_client.h diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 39e145900a..44210909d8 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -441,6 +441,7 @@ target_sources(rpcs3_emu PRIVATE NP/np_requests.cpp NP/signaling_handler.cpp NP/np_structs_extra.cpp + NP/clan_client.cpp NP/rpcn_client.cpp NP/rpcn_config.cpp NP/rpcn_countries.cpp diff --git a/rpcs3/Emu/Cell/Modules/sceNp.h b/rpcs3/Emu/Cell/Modules/sceNp.h index 12ca388ba2..6f29a2f8a9 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.h +++ b/rpcs3/Emu/Cell/Modules/sceNp.h @@ -32,6 +32,7 @@ using SceNpBasicMessageRecvAction = u32; using SceNpClanId = u32; using SceNpClansMessageId = u32; +using SceNpClansMemberRole = u32; using SceNpClansMemberStatus = s32; using SceNpCustomMenuIndexMask = u32; diff --git a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp index 91d22079fe..29f26d9e2b 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp @@ -1,10 +1,13 @@ #include "stdafx.h" #include "Emu/Cell/PPUModule.h" #include "Emu/IdManager.h" +#include "Emu/NP/np_handler.h" +#include "Emu/NP/clan_client.h" #include "sceNp.h" #include "sceNpClans.h" + LOG_CHANNEL(sceNpClans); template<> @@ -14,6 +17,7 @@ void fmt_class_string::format(std::string& out, u64 arg) { switch (error) { + STR_CASE(SCE_NP_CLANS_SUCCESS); STR_CASE(SCE_NP_CLANS_ERROR_ALREADY_INITIALIZED); STR_CASE(SCE_NP_CLANS_ERROR_NOT_INITIALIZED); STR_CASE(SCE_NP_CLANS_ERROR_NOT_SUPPORTED); @@ -75,8 +79,6 @@ void fmt_class_string::format(std::string& out, u64 arg) error_code sceNpClansInit(vm::cptr commId, vm::cptr passphrase, vm::ptr pool, vm::ptr poolSize, u32 flags) { - sceNpClans.warning("sceNpClansInit(commId=*0x%x, passphrase=*0x%x, pool=*0x%x, poolSize=*0x%x, flags=0x%x)", commId, passphrase, pool, poolSize, flags); - auto& clans_manager = g_fxo->get(); if (clans_manager.is_initialized) @@ -94,6 +96,9 @@ error_code sceNpClansInit(vm::cptr commId, vm::cptr commId, vm::cptrget(); if (!clans_manager.is_initialized) @@ -110,6 +113,7 @@ error_code sceNpClansTerm() return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } + delete clans_manager.client; clans_manager.is_initialized = false; return CELL_OK; @@ -117,8 +121,6 @@ error_code sceNpClansTerm() error_code sceNpClansCreateRequest(vm::ptr handle, u64 flags) { - sceNpClans.todo("sceNpClansCreateRequest(handle=*0x%x, flags=0x%llx)", handle, flags); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -134,33 +136,50 @@ error_code sceNpClansCreateRequest(vm::ptr handle, u64 return SCE_NP_CLANS_ERROR_NOT_SUPPORTED; } + vm::var req; + vm::write32(handle.addr(), req.addr()); + + auto& clans_manager = g_fxo->get(); + SceNpClansError res = clans_manager.client->createRequest(); + if (res != SCE_NP_CLANS_SUCCESS) + { + return res; + } + return CELL_OK; } error_code sceNpClansDestroyRequest(vm::ptr handle) { - sceNpClans.todo("sceNpClansDestroyRequest(handle=*0x%x)", handle); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } + auto& clans_manager = g_fxo->get(); + SceNpClansError res = clans_manager.client->destroyRequest(); + if (res != SCE_NP_CLANS_SUCCESS) + { + return res; + } + return CELL_OK; } error_code sceNpClansAbortRequest(vm::ptr handle) { - sceNpClans.todo("sceNpClansAbortRequest(handle=*0x%x)", handle); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } + auto& clans_manager = g_fxo->get(); + clans_manager.client->destroyRequest(); + return CELL_OK; } +// TODO: requires NpCommerce2 error_code sceNpClansCreateClan(vm::ptr handle, vm::cptr name, vm::cptr tag, vm::ptr clanId) { sceNpClans.todo("sceNpClansCreateClan(handle=*0x%x, name=%s, tag=%s, clanId=*0x%x)", handle, name, tag, clanId); @@ -183,6 +202,8 @@ error_code sceNpClansCreateClan(vm::ptr handle, vm::cpt return CELL_OK; } +// TODO: should probably not be implemented on RPCS3 until `CreateClan` is, +// to not let people disband a clan by accident error_code sceNpClansDisbandClan(vm::ptr handle, SceNpClanId clanId) { sceNpClans.todo("sceNpClansDisbandClan(handle=*0x%x, clanId=*0x%x)", handle, clanId); @@ -192,19 +213,20 @@ error_code sceNpClansDisbandClan(vm::ptr handle, SceNpC return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } - return CELL_OK; + // TEMP: don't let people disband + return SCE_NP_CLANS_SERVER_ERROR_PERMISSION_DENIED; + + // return CELL_OK; } error_code sceNpClansGetClanList(vm::ptr handle, vm::cptr paging, vm::ptr clanList, vm::ptr pageResult) { - sceNpClans.todo("sceNpClansGetClanList(handle=*0x%x, paging=*0x%x, clanList=*0x%x, pageResult=*0x%x)", handle, paging, clanList, pageResult); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } - if (!pageResult || (paging && !clanList)) // TODO: confirm + if (!pageResult || (paging && !clanList)) { return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT; } @@ -217,9 +239,28 @@ error_code sceNpClansGetClanList(vm::ptr handle, vm::cp } } + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpClansPagingRequest host_paging = {}; + std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); + + SceNpClansEntry host_clanList[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; + SceNpClansPagingResult host_pageResult = {}; + + SceNpClansError ret = clans_manager.client->getClanList(nph, &host_paging, host_clanList, &host_pageResult); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + + std::memcpy(clanList.get_ptr(), host_clanList, sizeof(SceNpClansEntry) * host_pageResult.count); + std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult)); + return CELL_OK; } +// TODO: seems to not be needed, even by the PS3..? error_code sceNpClansGetClanListByNpId(vm::ptr handle, vm::cptr paging, vm::cptr npid, vm::ptr clanList, vm::ptr pageResult) { sceNpClans.todo("sceNpClansGetClanListByNpId(handle=*0x%x, paging=*0x%x, npid=*0x%x, clanList=*0x%x, pageResult=*0x%x)", handle, paging, npid, clanList, pageResult); @@ -245,6 +286,7 @@ error_code sceNpClansGetClanListByNpId(vm::ptr handle, return CELL_OK; } +// TODO: seems to not be needed, even by the PS3..? error_code sceNpClansSearchByProfile(vm::ptr handle, vm::cptr paging, vm::cptr search, vm::ptr results, vm::ptr pageResult) { sceNpClans.todo("sceNpClansSearchByProfile(handle=*0x%x, paging=*0x%x, search=*0x%x, results=*0x%x, pageResult=*0x%x)", handle, paging, search, results, pageResult); @@ -272,8 +314,6 @@ error_code sceNpClansSearchByProfile(vm::ptr handle, vm error_code sceNpClansSearchByName(vm::ptr handle, vm::cptr paging, vm::cptr search, vm::ptr results, vm::ptr pageResult) { - sceNpClans.todo("sceNpClansSearchByName(handle=*0x%x, paging=*0x%x, search=*0x%x, results=*0x%x, pageResult=*0x%x)", handle, paging, search, results, pageResult); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -292,13 +332,31 @@ error_code sceNpClansSearchByName(vm::ptr handle, vm::c } } + auto& clans_manager = g_fxo->get(); + + SceNpClansPagingRequest host_paging = {}; + std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); + + SceNpClansSearchableName host_search = {}; + std::memcpy(&host_search, search.get_ptr(), sizeof(SceNpClansSearchableName)); + + SceNpClansClanBasicInfo host_results[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; + SceNpClansPagingResult host_pageResult = {}; + + SceNpClansError ret = clans_manager.client->clanSearch(&host_paging, &host_search, host_results, &host_pageResult); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + + std::memcpy(results.get_ptr(), host_results, sizeof(SceNpClansClanBasicInfo) * host_pageResult.count); + std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult)); + return CELL_OK; } error_code sceNpClansGetClanInfo(vm::ptr handle, SceNpClanId clanId, vm::ptr info) { - sceNpClans.todo("sceNpClansGetClanInfo(handle=*0x%x, clanId=%d, info=*0x%x)", handle, clanId, info); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -310,13 +368,23 @@ error_code sceNpClansGetClanInfo(vm::ptr handle, SceNpC return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT; } + auto& clans_manager = g_fxo->get(); + + SceNpClansClanInfo host_info = {}; + + SceNpClansError ret = clans_manager.client->getClanInfo(clanId, &host_info); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + + std::memcpy(info.get_ptr(), &host_info, sizeof(SceNpClansClanInfo)); + return CELL_OK; } error_code sceNpClansUpdateClanInfo(vm::ptr handle, SceNpClanId clanId, vm::cptr info) { - sceNpClans.todo("sceNpClansUpdateClanInfo(handle=*0x%x, clanId=%d, info=*0x%x)", handle, clanId, info); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -328,18 +396,23 @@ error_code sceNpClansUpdateClanInfo(vm::ptr handle, Sce return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT; } - //if (info->something > X) - //{ - // return SCE_NP_CLANS_ERROR_EXCEEDS_MAX; - //} + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpClansUpdatableClanInfo host_info = {}; + std::memcpy(&host_info, info.get_ptr(), sizeof(SceNpClansUpdatableClanInfo)); + + SceNpClansError ret = clans_manager.client->updateClanInfo(nph, clanId, &host_info); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } return CELL_OK; } error_code sceNpClansGetMemberList(vm::ptr handle, SceNpClanId clanId, vm::cptr paging, SceNpClansMemberStatus status, vm::ptr memList, vm::ptr pageResult) { - sceNpClans.todo("sceNpClansGetMemberList(handle=*0x%x, clanId=%d, paging=*0x%x, status=%d, memList=*0x%x, pageResult=*0x%x)", handle, clanId, paging, status, memList, pageResult); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -358,13 +431,29 @@ error_code sceNpClansGetMemberList(vm::ptr handle, SceN } } + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpClansPagingRequest host_paging = {}; + std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); + + SceNpClansMemberEntry* host_memList_addr = new SceNpClansMemberEntry[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX]; + SceNpClansPagingResult host_pageResult = {}; + + SceNpClansError ret = clans_manager.client->getMemberList(nph, clanId, &host_paging, status, host_memList_addr, &host_pageResult); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + + std::memcpy(memList.get_ptr(), host_memList_addr, sizeof(SceNpClansMemberEntry) * host_pageResult.count); + std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult)); + return CELL_OK; } error_code sceNpClansGetMemberInfo(vm::ptr handle, SceNpClanId clanId, vm::cptr npid, vm::ptr memInfo) { - sceNpClans.todo("sceNpClansGetMemberInfo(handle=*0x%x, clanId=%d, npid=*0x%x, memInfo=*0x%x)", handle, clanId, npid, memInfo); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -375,13 +464,27 @@ error_code sceNpClansGetMemberInfo(vm::ptr handle, SceN return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT; } + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpId host_npid = {}; + std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId)); + + SceNpClansMemberEntry host_memInfo = {}; + + SceNpClansError ret = clans_manager.client->getMemberInfo(nph, clanId, host_npid, &host_memInfo); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + + std::memcpy(memInfo.get_ptr(), &host_memInfo, sizeof(SceNpClansMemberEntry)); + return CELL_OK; } error_code sceNpClansUpdateMemberInfo(vm::ptr handle, SceNpClanId clanId, vm::cptr info) { - sceNpClans.todo("sceNpClansUpdateMemberInfo(handle=*0x%x, clanId=%d, memInfo=*0x%x)", handle, clanId, info); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -389,22 +492,26 @@ error_code sceNpClansUpdateMemberInfo(vm::ptr handle, S if (!info) { - // TODO: add more checks for info return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT; } - //if (info->something > X) - //{ - // return SCE_NP_CLANS_ERROR_EXCEEDS_MAX; - //} + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpClansUpdatableMemberInfo host_info = {}; + std::memcpy(&host_info, info.get_ptr(), sizeof(SceNpClansUpdatableMemberInfo)); + + SceNpClansError ret = clans_manager.client->updateMemberInfo(nph, clanId, &host_info); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } return CELL_OK; } error_code sceNpClansChangeMemberRole(vm::ptr handle, SceNpClanId clanId, vm::cptr npid, u32 role) { - sceNpClans.todo("sceNpClansChangeMemberRole(handle=*0x%x, clanId=%d, npid=*0x%x, role=%d)", handle, clanId, npid, role); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -415,9 +522,22 @@ error_code sceNpClansChangeMemberRole(vm::ptr handle, S return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT; } + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpId host_npid = {}; + std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId)); + + SceNpClansError ret = clans_manager.client->changeMemberRole(nph, clanId, host_npid, role); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + return CELL_OK; } +// TODO: no struct currently implements `autoAccept` as a field error_code sceNpClansGetAutoAcceptStatus(vm::ptr handle, SceNpClanId clanId, vm::ptr enable) { sceNpClans.todo("sceNpClansGetAutoAcceptStatus(handle=*0x%x, clanId=%d, enable=*0x%x)", handle, clanId, enable); @@ -435,6 +555,7 @@ error_code sceNpClansGetAutoAcceptStatus(vm::ptr handle return CELL_OK; } +// TODO: no struct currently implements `autoAccept` as a field error_code sceNpClansUpdateAutoAcceptStatus(vm::ptr handle, SceNpClanId clanId, b8 enable) { sceNpClans.todo("sceNpClansUpdateAutoAcceptStatus(handle=*0x%x, clanId=%d, enable=%d)", handle, clanId, enable); @@ -449,32 +570,44 @@ error_code sceNpClansUpdateAutoAcceptStatus(vm::ptr han error_code sceNpClansJoinClan(vm::ptr handle, SceNpClanId clanId) { - sceNpClans.todo("sceNpClansJoinClan(handle=*0x%x, clanId=%d)", handle, clanId); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpClansError ret = clans_manager.client->joinClan(nph, clanId); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + return CELL_OK; } error_code sceNpClansLeaveClan(vm::ptr handle, SceNpClanId clanId) { - sceNpClans.todo("sceNpClansLeaveClan(handle=*0x%x, clanId=%d)", handle, clanId); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpClansError ret = clans_manager.client->leaveClan(nph, clanId); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + return CELL_OK; } error_code sceNpClansKickMember(vm::ptr handle, SceNpClanId clanId, vm::cptr npid, vm::cptr message) { - sceNpClans.todo("sceNpClansKickMember(handle=*0x%x, clanId=%d, npid=*0x%x, message=*0x%x)", handle, clanId, npid, message); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -493,13 +626,29 @@ error_code sceNpClansKickMember(vm::ptr handle, SceNpCl } } + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpId host_npid = {}; + std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId)); + + SceNpClansMessage host_message = {}; + if (message) + { + std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); + } + + SceNpClansError ret = clans_manager.client->kickMember(nph, clanId, host_npid, &host_message); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + return CELL_OK; } error_code sceNpClansSendInvitation(vm::ptr handle, SceNpClanId clanId, vm::cptr npid, vm::cptr message) { - sceNpClans.todo("sceNpClansSendInvitation(handle=*0x%x, clanId=%d, npid=*0x%x, message=*0x%x)", handle, clanId, npid, message); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -518,13 +667,29 @@ error_code sceNpClansSendInvitation(vm::ptr handle, Sce } } + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpId host_npid = {}; + std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId)); + + SceNpClansMessage host_message = {}; + if (message) + { + std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); + } + + SceNpClansError ret = clans_manager.client->sendInvitation(nph, clanId, host_npid, &host_message); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + return CELL_OK; } error_code sceNpClansCancelInvitation(vm::ptr handle, SceNpClanId clanId, vm::cptr npid) { - sceNpClans.todo("sceNpClansCancelInvitation(handle=*0x%x, clanId=%d, npid=*0x%x)", handle, clanId, npid); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -535,13 +700,23 @@ error_code sceNpClansCancelInvitation(vm::ptr handle, S return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT; } + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpId host_npid = {}; + std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId)); + + SceNpClansError ret = clans_manager.client->cancelInvitation(nph, clanId, host_npid); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + return CELL_OK; } error_code sceNpClansSendInvitationResponse(vm::ptr handle, SceNpClanId clanId, vm::cptr message, b8 accept) { - sceNpClans.todo("sceNpClansSendInvitationResponse(handle=*0x%x, clanId=%d, message=*0x%x, accept=%d)", handle, clanId, message, accept); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -555,13 +730,26 @@ error_code sceNpClansSendInvitationResponse(vm::ptr han } } + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpClansMessage host_message = {}; + if (message) + { + std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); + } + + SceNpClansError ret = clans_manager.client->sendInvitationResponse(nph, clanId, &host_message, accept); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + return CELL_OK; } error_code sceNpClansSendMembershipRequest(vm::ptr handle, u32 clanId, vm::cptr message) { - sceNpClans.todo("sceNpClansSendMembershipRequest(handle=*0x%x, clanId=%d, message=*0x%x)", handle, clanId, message); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -575,25 +763,46 @@ error_code sceNpClansSendMembershipRequest(vm::ptr hand } } + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpClansMessage host_message = {}; + if (message) + { + std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); + } + + SceNpClansError ret = clans_manager.client->requestMembership(nph, clanId, &host_message); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + return CELL_OK; } error_code sceNpClansCancelMembershipRequest(vm::ptr handle, SceNpClanId clanId) { - sceNpClans.todo("sceNpClansCancelMembershipRequest(handle=*0x%x, clanId=%d)", handle, clanId); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpClansError ret = clans_manager.client->cancelRequestMembership(nph, clanId); + + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + return CELL_OK; } error_code sceNpClansSendMembershipResponse(vm::ptr handle, SceNpClanId clanId, vm::cptr npid, vm::cptr message, b8 allow) { - sceNpClans.todo("sceNpClansSendMembershipResponse(handle=*0x%x, clanId=%d, npid=*0x%x, message=*0x%x, allow=%d)", handle, clanId, npid, message, allow); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -612,13 +821,29 @@ error_code sceNpClansSendMembershipResponse(vm::ptr han } } + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpId host_npid = {}; + std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId)); + + SceNpClansMessage host_message = {}; + if (message) + { + std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); + } + + SceNpClansError ret = clans_manager.client->sendMembershipResponse(nph, clanId, host_npid, &host_message, allow); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + return CELL_OK; } error_code sceNpClansGetBlacklist(vm::ptr handle, SceNpClanId clanId, vm::cptr paging, vm::ptr bl, vm::ptr pageResult) { - sceNpClans.todo("sceNpClansGetBlacklist(handle=*0x%x, clanId=%d, paging=*0x%x, bl=*0x%x, pageResult=*0x%x)", handle, clanId, paging, bl, pageResult); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -637,13 +862,29 @@ error_code sceNpClansGetBlacklist(vm::ptr handle, SceNp } } + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpClansPagingRequest host_paging = {}; + std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); + + SceNpClansBlacklistEntry host_bl[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; + SceNpClansPagingResult host_pageResult = {}; + + SceNpClansError ret = clans_manager.client->getBlacklist(nph, clanId, &host_paging, host_bl, &host_pageResult); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + + std::memcpy(bl.get_ptr(), host_bl, sizeof(SceNpClansBlacklistEntry) * host_pageResult.count); + std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult)); + return CELL_OK; } error_code sceNpClansAddBlacklistEntry(vm::ptr handle, SceNpClanId clanId, vm::cptr npid) { - sceNpClans.todo("sceNpClansAddBlacklistEntry(handle=*0x%x, clanId=%d, npid=*0x%x)", handle, clanId, npid); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -654,13 +895,23 @@ error_code sceNpClansAddBlacklistEntry(vm::ptr handle, return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT; } + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpId host_npid = {}; + std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId)); + + SceNpClansError ret = clans_manager.client->addBlacklistEntry(nph, clanId, host_npid); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + return CELL_OK; } error_code sceNpClansRemoveBlacklistEntry(vm::ptr handle, SceNpClanId clanId, vm::cptr npid) { - sceNpClans.todo("sceNpClansRemoveBlacklistEntry(handle=*0x%x, clanId=%d, npid=*0x%x)", handle, clanId, npid); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -671,19 +922,29 @@ error_code sceNpClansRemoveBlacklistEntry(vm::ptr handl return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT; } + auto& nph = g_fxo->get>(); + auto& clans_manager = g_fxo->get(); + + SceNpId host_npid = {}; + std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId)); + + SceNpClansError ret = clans_manager.client->removeBlacklistEntry(nph, clanId, host_npid); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + return CELL_OK; } error_code sceNpClansRetrieveAnnouncements(vm::ptr handle, SceNpClanId clanId, vm::cptr paging, vm::ptr mlist, vm::ptr pageResult) { - sceNpClans.todo("sceNpClansRetrieveAnnouncements(handle=*0x%x, clanId=%d, paging=*0x%x, mlist=*0x%x, pageResult=*0x%x)", handle, clanId, paging, mlist, pageResult); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } - if (!pageResult || (paging && !mlist)) // TODO: confirm + if (!pageResult || (paging && !mlist) || clanId == UINT32_MAX) // TODO: confirm { return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT; } @@ -696,13 +957,29 @@ error_code sceNpClansRetrieveAnnouncements(vm::ptr hand } } + auto& clans_manager = g_fxo->get(); + auto& nph = g_fxo->get>(); + + SceNpClansPagingRequest host_paging = {}; + std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); + + SceNpClansMessageEntry host_mlist[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; + SceNpClansPagingResult host_pageResult = {}; + + SceNpClansError ret = clans_manager.client->retrieveAnnouncements(nph, clanId, &host_paging, host_mlist, &host_pageResult); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + + std::memcpy(mlist.get_ptr(), host_mlist, sizeof(SceNpClansMessageEntry) * host_pageResult.count); + std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult)); + return CELL_OK; } error_code sceNpClansPostAnnouncement(vm::ptr handle, SceNpClanId clanId, vm::cptr message, vm::cptr data, u32 duration, vm::ptr mId) { - sceNpClans.todo("sceNpClansPostAnnouncement(handle=*0x%x, clanId=%d, message=*0x%x, data=*0x%x, duration=%d, mId=*0x%x)", handle, clanId, message, data, duration, mId); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -713,28 +990,52 @@ error_code sceNpClansPostAnnouncement(vm::ptr handle, S return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT; } - if (!data) // TODO verify - { - return SCE_NP_CLANS_ERROR_NOT_SUPPORTED; - } - if (strlen(message->body) > SCE_NP_CLANS_ANNOUNCEMENT_MESSAGE_BODY_MAX_LENGTH || strlen(message->subject) > SCE_NP_CLANS_MESSAGE_SUBJECT_MAX_LENGTH) // TODO: correct max? { return SCE_NP_CLANS_ERROR_EXCEEDS_MAX; } + auto& clans_manager = g_fxo->get(); + auto& nph = g_fxo->get>(); + + SceNpClansMessage host_message = {}; + std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); + + SceNpClansMessageData host_data = {}; + if (data) + { + std::memcpy(&host_data, data.get_ptr(), sizeof(SceNpClansMessageData)); + } + + SceNpClansMessageId host_mId = 0; + + SceNpClansError ret = clans_manager.client->postAnnouncement(nph, clanId, &host_message, &host_data, duration, &host_mId); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + + *mId = host_mId; + return CELL_OK; } error_code sceNpClansRemoveAnnouncement(vm::ptr handle, SceNpClanId clanId, SceNpClansMessageId mId) { - sceNpClans.todo("sceNpClansPostAnnouncement(handle=*0x%x, clanId=%d, mId=%d)", handle, clanId, mId); - if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } + auto& clans_manager = g_fxo->get(); + auto& nph = g_fxo->get>(); + + SceNpClansError ret = clans_manager.client->deleteAnnouncement(nph, clanId, mId); + if (ret != SCE_NP_CLANS_SUCCESS) + { + return ret; + } + return CELL_OK; } @@ -880,4 +1181,4 @@ DECLARE(ppu_module_manager::sceNpClans)("sceNpClans", []() REG_FUNC(sceNpClans, sceNpClansRemovePostedChallenge); REG_FUNC(sceNpClans, sceNpClansRetrieveChallenges); REG_FUNC(sceNpClans, sceNpClansRemoveChallenge); -}); +}); \ No newline at end of file diff --git a/rpcs3/Emu/Cell/Modules/sceNpClans.h b/rpcs3/Emu/Cell/Modules/sceNpClans.h index f0d1d54630..8e686bbeab 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpClans.h +++ b/rpcs3/Emu/Cell/Modules/sceNpClans.h @@ -5,6 +5,8 @@ // Return codes enum SceNpClansError : u32 { + SCE_NP_CLANS_SUCCESS = CELL_OK, + SCE_NP_CLANS_ERROR_ALREADY_INITIALIZED = 0x80022701, SCE_NP_CLANS_ERROR_NOT_INITIALIZED = 0x80022702, SCE_NP_CLANS_ERROR_NOT_SUPPORTED = 0x80022703, @@ -97,7 +99,7 @@ enum SCE_NP_CLANS_ANNOUNCEMENT_MESSAGE_BODY_MAX_LENGTH = 1536, SCE_NP_CLANS_CLAN_BINARY_ATTRIBUTE1_MAX_SIZE = 190, SCE_NP_CLANS_CLAN_BINARY_DATA_MAX_SIZE = 10240, - SCE_NP_CLANS_MEMBER_BINARY_ATTRIBUTE1_MAX_SIZE = 16, + SCE_NP_CLANS_MEMBER_BINARY_ATTRIBUTE1_MAX_SIZE = 15, SCE_NP_CLANS_MEMBER_DESCRIPTION_MAX_LENGTH = 255, SCE_NP_CLANS_MEMBER_BINARY_DATA_MAX_SIZE = 1024, SCE_NP_CLANS_MESSAGE_BODY_MAX_LENGTH = 1536, @@ -138,7 +140,8 @@ enum }; // Request handle for clan API -using SceNpClansRequestHandle = vm::ptr; +struct SceNpClansRequest {}; +using SceNpClansRequestHandle = vm::ptr; // Paging request structure struct SceNpClansPagingRequest @@ -159,8 +162,8 @@ struct SceNpClansClanBasicInfo { be_t clanId; be_t numMembers; - s8 name[SCE_NP_CLANS_CLAN_NAME_MAX_LENGTH + 1]; - s8 tag[SCE_NP_CLANS_CLAN_TAG_MAX_LENGTH + 1]; + char name[SCE_NP_CLANS_CLAN_NAME_MAX_LENGTH + 1]; + char tag[SCE_NP_CLANS_CLAN_TAG_MAX_LENGTH + 1]; u8 reserved[2]; }; @@ -197,7 +200,7 @@ struct SceNpClansSearchableProfile be_t intAttr2SearchOp; be_t intAttr3SearchOp; be_t binAttr1SearchOp; - s8 tag[SCE_NP_CLANS_CLAN_TAG_MAX_LENGTH + 1]; + char tag[SCE_NP_CLANS_CLAN_TAG_MAX_LENGTH + 1]; u8 reserved[3]; }; @@ -205,7 +208,7 @@ struct SceNpClansSearchableProfile struct SceNpClansSearchableName { be_t nameSearchOp; - s8 name[SCE_NP_CLANS_CLAN_NAME_MAX_LENGTH + 1]; + char name[SCE_NP_CLANS_CLAN_NAME_MAX_LENGTH + 1]; u8 reserved[3]; }; @@ -213,7 +216,7 @@ struct SceNpClansSearchableName struct SceNpClansUpdatableClanInfo { be_t fields; - s8 description[SCE_NP_CLANS_CLAN_DESCRIPTION_MAX_LENGTH + 1]; + char description[SCE_NP_CLANS_CLAN_DESCRIPTION_MAX_LENGTH + 1]; SceNpClansSearchableAttr attr; u8 binData1; be_t binData1Size; @@ -233,8 +236,8 @@ struct SceNpClansUpdatableMemberInfo be_t fields; u8 binData1; be_t binData1Size; - u8 binAttr1[SCE_NP_CLANS_CLAN_BINARY_ATTRIBUTE1_MAX_SIZE + 1]; - s8 description[SCE_NP_CLANS_MEMBER_DESCRIPTION_MAX_LENGTH + 1]; + u8 binAttr1[SCE_NP_CLANS_MEMBER_BINARY_ATTRIBUTE1_MAX_SIZE + 1]; + char description[SCE_NP_CLANS_MEMBER_DESCRIPTION_MAX_LENGTH + 1]; b8 allowMsg; u8 reserved[3]; }; @@ -271,7 +274,7 @@ struct SceNpClansMessageEntry SceNpClansMessage message; SceNpClansMessageData data; SceNpId npid; - u8 reserved[4]; + SceNpClanId postedBy; }; // Blacklist entry structure @@ -279,11 +282,4 @@ struct SceNpClansBlacklistEntry { SceNpId entry; SceNpId registeredBy; -}; - -// fxm objects - -struct sce_np_clans_manager -{ - atomic_t is_initialized = false; -}; +}; \ No newline at end of file diff --git a/rpcs3/Emu/NP/clan_client.cpp b/rpcs3/Emu/NP/clan_client.cpp new file mode 100644 index 0000000000..283b524132 --- /dev/null +++ b/rpcs3/Emu/NP/clan_client.cpp @@ -0,0 +1,1035 @@ +#include "stdafx.h" +#include "util/types.hpp" + +#include +#include +#include +#include + +LOG_CHANNEL(clan_log, "clans"); + +const char* HOST_VIEW = "clans-view01.ww.np.community.playstation.net"; +const char* HOST_UPDATE = "clans-rec01.ww.np.community.playstation.net"; + +const char* REQ_TYPE_FUNC = "func"; +const char* REQ_TYPE_SEC = "sec"; + +constexpr const char* JID_FORMAT = "%s@un.br.np.playstation.net"; + +const char* CLANS_SERVICE_ID = "IV0001-NPXS01001_00"; +const char* CLANS_ENTITLEMENT_ID = "NPWR00432_00"; + +namespace std +{ + template <> + struct formatter : formatter + { + auto format(clan::ClanRequestType value, format_context& ctx) const + { + string_view name = "unknown"; + switch (value) + { + case clan::ClanRequestType::FUNC: name = "func"; break; + case clan::ClanRequestType::SEC: name = "sec"; break; + } + return formatter::format(string(name), ctx); + } + }; + + template <> + struct formatter : formatter + { + auto format(clan::ClanManagerOperationType value, format_context& ctx) const + { + string_view name = "unknown"; + switch (value) + { + case clan::ClanManagerOperationType::VIEW: name = "view"; break; + case clan::ClanManagerOperationType::UPDATE: name = "update"; break; + } + return formatter::format(string(name), ctx); + } + }; + + template <> + struct formatter : formatter + { + auto format(clan::ClanSearchFilterOperator value, format_context& ctx) const + { + string_view name = "unknown"; + switch (value) + { + case clan::ClanSearchFilterOperator::Equal: name = "eq"; break; + case clan::ClanSearchFilterOperator::NotEqual: name = "ne"; break; + case clan::ClanSearchFilterOperator::GreaterThan: name = "gt"; break; + case clan::ClanSearchFilterOperator::GreaterThanOrEqual: name = "ge"; break; + case clan::ClanSearchFilterOperator::LessThan: name = "lt"; break; + case clan::ClanSearchFilterOperator::LessThanOrEqual: name = "le"; break; + case clan::ClanSearchFilterOperator::Like: name = "lk"; break; + } + return formatter::format(string(name), ctx); + } + }; + + template <> + struct formatter : formatter + { + auto format(clan::ClanRequestAction value, format_context& ctx) const + { + string_view name = "unknown"; + switch (value) + { + case clan::ClanRequestAction::GetClanList: name = "get_clan_list"; break; + case clan::ClanRequestAction::GetClanInfo: name = "get_clan_info"; break; + case clan::ClanRequestAction::GetMemberInfo: name = "get_member_info"; break; + case clan::ClanRequestAction::GetMemberList: name = "get_member_list"; break; + case clan::ClanRequestAction::GetBlacklist: name = "get_blacklist"; break; + case clan::ClanRequestAction::RecordBlacklistEntry: name = "record_blacklist_entry"; break; + case clan::ClanRequestAction::DeleteBlacklistEntry: name = "delete_blacklist_entry"; break; + case clan::ClanRequestAction::ClanSearch: name = "clan_search"; break; + case clan::ClanRequestAction::RequestMembership: name = "request_membership"; break; + case clan::ClanRequestAction::CancelRequestMembership: name = "cancel_request_membership"; break; + case clan::ClanRequestAction::AcceptMembershipRequest: name = "accept_membership_request"; break; + case clan::ClanRequestAction::DeclineMembershipRequest: name = "decline_membership_request"; break; + case clan::ClanRequestAction::SendInvitation: name = "send_invitation"; break; + case clan::ClanRequestAction::CancelInvitation: name = "cancel_invitation"; break; + case clan::ClanRequestAction::AcceptInvitation: name = "accept_invitation"; break; + case clan::ClanRequestAction::DeclineInvitation: name = "decline_invitation"; break; + case clan::ClanRequestAction::UpdateMemberInfo: name = "update_member_info"; break; + case clan::ClanRequestAction::UpdateClanInfo: name = "update_clan_info"; break; + case clan::ClanRequestAction::JoinClan: name = "join_clan"; break; + case clan::ClanRequestAction::LeaveClan: name = "leave_clan"; break; + case clan::ClanRequestAction::KickMember: name = "kick_member"; break; + case clan::ClanRequestAction::ChangeMemberRole: name = "change_member_role"; break; + case clan::ClanRequestAction::RetrieveAnnouncements: name = "retrieve_announcements"; break; + case clan::ClanRequestAction::PostAnnouncement: name = "post_announcement"; break; + case clan::ClanRequestAction::DeleteAnnouncement: name = "delete_announcement"; break; + } + return formatter::format(string(name), ctx); + } + }; +} + +namespace clan +{ + struct curl_memory + { + char* response; + size_t size; + }; + + size_t clan_client::curlWriteCallback(void* data, size_t size, size_t nmemb, void* clientp) + { + size_t realsize = size * nmemb; + std::vector* mem = static_cast*>(clientp); + + size_t old_size = mem->size(); + mem->resize(old_size + realsize); + memcpy(mem->data() + old_size, data, realsize); + + return realsize; + } + + clan_client::clan_client() + { + createRequest(); + } + + clan_client::~clan_client() + { + destroyRequest(); + } + + SceNpClansError clan_client::createRequest() + { + if (curl) + return SceNpClansError::SCE_NP_CLANS_ERROR_ALREADY_INITIALIZED; + + curl = curl_easy_init(); + if (!curl) + { + return SceNpClansError::SCE_NP_CLANS_ERROR_NOT_INITIALIZED; + } + + curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); + + return SceNpClansError::SCE_NP_CLANS_SUCCESS; + } + + SceNpClansError clan_client::destroyRequest() + { + if (curl) + { + curl_easy_cleanup(curl); + curl = nullptr; + } + + return SceNpClansError::SCE_NP_CLANS_SUCCESS; + } + + SceNpClansError clan_client::sendRequest(ClanRequestAction action, ClanManagerOperationType opType, pugi::xml_document* xmlBody, pugi::xml_document* outResponse) + { + if (!curl) + return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; + + ClanRequestType reqType = ClanRequestType::FUNC; + pugi::xml_node clan = xmlBody->child("clan"); + if (clan && clan.child("ticket")) + { + reqType = ClanRequestType::SEC; + } + + std::string host = opType == ClanManagerOperationType::VIEW ? HOST_VIEW : HOST_UPDATE; + std::string url = std::format("https://{}/clan_manager_{}/{}/{}", host, opType, reqType, action); + + std::ostringstream oss; + xmlBody->save(oss, "\t", 8U); + + std::string xml = oss.str(); + + char err_buf[CURL_ERROR_SIZE]; + err_buf[0] = '\0'; + + std::vector response_buffer; + + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_buffer); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, err_buf); + + curl_easy_setopt(curl, CURLOPT_POST, 1); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, xml.c_str()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, xml.size()); + + res = curl_easy_perform(curl); + + if (res != CURLE_OK) + { + outResponse = nullptr; + clan_log.error("curl_easy_perform() failed: %s", curl_easy_strerror(res)); + clan_log.error("Error buffer: %s", err_buf); + return SCE_NP_CLANS_ERROR_BAD_REQUEST; + } + + response_buffer.push_back('\0'); + + pugi::xml_parse_result res = outResponse->load_string(response_buffer.data()); + if (!res) + { + clan_log.error("XML parsing failed: %s", res.description()); + return SCE_NP_CLANS_ERROR_BAD_RESPONSE; + } + + pugi::xml_node clanResult = outResponse->child("clan"); + if (!clanResult) + return SCE_NP_CLANS_ERROR_BAD_RESPONSE; + + pugi::xml_attribute result = clanResult.attribute("result"); + if (!result) + return SCE_NP_CLANS_ERROR_BAD_RESPONSE; + + std::string result_str = result.as_string(); + if (result_str != "00") + return static_cast(0x80022800 | std::stoul(result_str, nullptr, 16)); + + return SCE_NP_CLANS_SUCCESS; + } + + std::string clan_client::getClanTicket(np::np_handler& nph) + { + if (nph.get_ticket().size() > 0) { + std::vector ticket_bytes(1024); + uint32_t ticket_size = UINT32_MAX; + + Base64_Encode_NoNl(nph.get_ticket().data(), nph.get_ticket().size(), ticket_bytes.data(), &ticket_size); + return std::string(reinterpret_cast(ticket_bytes.data()), ticket_size); + } + + const auto& npid = nph.get_npid(); + + const char* service_id = CLANS_SERVICE_ID; + const unsigned char* cookie = nullptr; + const u32 cookie_size = 0; + const char* entitlement_id = CLANS_ENTITLEMENT_ID; + const u32 consumed_count = 0; + + nph.req_ticket(0x00020001, &npid, service_id, cookie, cookie_size, entitlement_id, consumed_count); + + np::ticket ticket; + + // TODO: convert this to use events? + int retries = 0; + while (ticket.empty() && retries < 100) + { + ticket = nph.get_ticket(); + if (ticket.empty()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + retries++; + } + } + + if (ticket.empty()) + { + clan_log.error("Failed to get clan ticket"); + return ""; + } + + std::vector ticket_bytes(1024); + uint32_t ticket_size = UINT32_MAX; + + Base64_Encode_NoNl(ticket.data(), ticket.size(), ticket_bytes.data(), &ticket_size); + std::string ticket_str = std::string(reinterpret_cast(ticket_bytes.data()), ticket_size); + + return ticket_str; + } + +#pragma region Outgoing API Requests + SceNpClansError clan_client::getClanList(np::np_handler& nph, SceNpClansPagingRequest* paging, SceNpClansEntry* clanList, SceNpClansPagingResult* pageResult) + { + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + + std::string ticket = getClanTicket(nph); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("start").text().set(paging->startPos); + clan.append_child("max").text().set(paging->max); + + pugi::xml_document response = pugi::xml_document(); + SceNpClansError clanRes = sendRequest(ClanRequestAction::GetClanList, ClanManagerOperationType::VIEW, &doc, &response); + + if (clanRes != SCE_NP_CLANS_SUCCESS) + return clanRes; + + pugi::xml_node clanResult = response.child("clan"); + pugi::xml_node list = clanResult.child("list"); + + pugi::xml_attribute results = list.attribute("results"); + uint32_t results_count = results.as_uint(); + + pugi::xml_attribute total = list.attribute("total"); + uint32_t total_count = total.as_uint(); + + int i = 0; + for (pugi::xml_node info = list.child("info"); info; info = info.next_sibling("info"), i++) + { + pugi::xml_attribute id = info.attribute("id"); + uint32_t clanId = id.as_uint(); + + pugi::xml_node name = info.child("name"); + std::string name_str = name.text().as_string(); + + pugi::xml_node tag = info.child("tag"); + std::string tag_str = tag.text().as_string(); + + pugi::xml_node role = info.child("role"); + int32_t role_int = role.text().as_uint(); + + pugi::xml_node status = info.child("status"); + uint32_t status_int = status.text().as_uint(); + + pugi::xml_node onlinename = info.child("onlinename"); + std::string onlinename_str = onlinename.text().as_string(); + + pugi::xml_node members = info.child("members"); + uint32_t members_int = members.text().as_uint(); + + SceNpClansEntry entry = SceNpClansEntry{ + .info = SceNpClansClanBasicInfo{ + .clanId = clanId, + .numMembers = members_int, + .name = "", + .tag = "", + .reserved = {0, 0}, + }, + .role = static_cast(role_int), + .status = static_cast(status_int)}; + + snprintf(entry.info.name, sizeof(entry.info.name), "%s", name_str.c_str()); + snprintf(entry.info.tag, sizeof(entry.info.tag), "%s", tag_str.c_str()); + + clanList[i] = entry; + i++; + } + + *pageResult = SceNpClansPagingResult{ + .count = results_count, + .total = total_count}; + + return SCE_NP_CLANS_SUCCESS; + } + + SceNpClansError clan_client::getClanInfo(SceNpClanId clanId, SceNpClansClanInfo* clanInfo) + { + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("id").text().set(clanId); + + pugi::xml_document response = pugi::xml_document(); + SceNpClansError clanRes = sendRequest(ClanRequestAction::GetClanInfo, ClanManagerOperationType::VIEW, &doc, &response); + + if (clanRes != SCE_NP_CLANS_SUCCESS) + return clanRes; + + pugi::xml_node clanResult = response.child("clan"); + pugi::xml_node info = clanResult.child("info"); + + std::string name_str = info.child("name").text().as_string(); + std::string tag_str = info.child("tag").text().as_string(); + uint32_t members_int = info.child("members").text().as_uint(); + std::string date_created_str = info.child("date-created").text().as_string(); + std::string description_str = info.child("description").text().as_string(); + + *clanInfo = SceNpClansClanInfo{ + .info = SceNpClansClanBasicInfo{ + .clanId = clanId, + .numMembers = members_int, + .name = "", + .tag = "", + }, + .updatable = SceNpClansUpdatableClanInfo{ + .description = "", + }}; + + snprintf(clanInfo->info.name, sizeof(clanInfo->info.name), "%s", name_str.c_str()); + snprintf(clanInfo->info.tag, sizeof(clanInfo->info.tag), "%s", tag_str.c_str()); + snprintf(clanInfo->updatable.description, sizeof(clanInfo->updatable.description), "%s", description_str.c_str()); + + return SCE_NP_CLANS_SUCCESS; + } + + SceNpClansError clan_client::getMemberInfo(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMemberEntry* memInfo) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + + std::string jid_str = std::format(JID_FORMAT, npId.handle.data); + clan.append_child("jid").text().set(jid_str.c_str()); + + pugi::xml_document response = pugi::xml_document(); + SceNpClansError clanRes = sendRequest(ClanRequestAction::GetMemberInfo, ClanManagerOperationType::VIEW, &doc, &response); + + if (clanRes != SCE_NP_CLANS_SUCCESS) + return clanRes; + + pugi::xml_node clanResult = response.child("clan"); + pugi::xml_node info = clanResult.child("info"); + + pugi::xml_attribute jid = info.attribute("jid"); + std::string npid_str = jid.as_string(); + + char username[SCE_NET_NP_ONLINEID_MAX_LENGTH + 1] = {0}; + + sscanf(npid_str.c_str(), "%16[^@]", username); + + SceNpId npid; + + if (!strcmp(username, nph.get_npid().handle.data)) + { + npid = nph.get_npid(); + } + else + { + npid = SceNpId {}; + std::strncpy(npid.handle.data, username, SCE_NET_NP_ONLINEID_MAX_LENGTH + 1); + } + + pugi::xml_node role = info.child("role"); + uint32_t role_int = role.text().as_uint(); + + pugi::xml_node status = info.child("status"); + uint32_t status_int = status.text().as_uint(); + + pugi::xml_node description = info.child("description"); + std::string description_str = description.text().as_string(); + + char description_char[256] = {0}; + snprintf(description_char, sizeof(description_char), "%s", description_str.c_str()); + + *memInfo = SceNpClansMemberEntry + { + .npid = npid, + .role = static_cast(role_int), + .status = static_cast(status_int), + .updatable = SceNpClansUpdatableMemberInfo{ + .description = "", + } + }; + + snprintf(memInfo->npid.handle.data, sizeof(memInfo->npid.handle.data), "%s", username); + snprintf(memInfo->updatable.description, sizeof(memInfo->updatable.description), "%s", description_char); + + return SCE_NP_CLANS_SUCCESS; + } + + SceNpClansError clan_client::getMemberList(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMemberStatus /*status*/, SceNpClansMemberEntry* memList, SceNpClansPagingResult* pageResult) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + clan.append_child("start").text().set(paging->startPos); + clan.append_child("max").text().set(paging->max); + + pugi::xml_document response = pugi::xml_document(); + SceNpClansError clanRes = sendRequest(ClanRequestAction::GetMemberList, ClanManagerOperationType::VIEW, &doc, &response); + + if (clanRes != SCE_NP_CLANS_SUCCESS) + return clanRes; + + pugi::xml_node clanResult = response.child("clan"); + pugi::xml_node list = clanResult.child("list"); + + pugi::xml_attribute results = list.attribute("results"); + uint32_t results_count = results.as_uint(); + + pugi::xml_attribute total = list.attribute("total"); + uint32_t total_count = total.as_uint(); + + int i = 0; + for (pugi::xml_node info = list.child("info"); info; info = info.next_sibling("info")) + { + std::string npid_str = info.attribute("jid").as_string(); + + char username[SCE_NET_NP_ONLINEID_MAX_LENGTH + 1] = {0}; + + sscanf(npid_str.c_str(), "%16[^@]", username); + + SceNpId npid; + + if (!strcmp(username, nph.get_npid().handle.data)) + { + npid = nph.get_npid(); + } + else + { + npid = SceNpId {}; + std::strncpy(npid.handle.data, username, SCE_NET_NP_ONLINEID_MAX_LENGTH + 1); + } + + uint32_t role_int = info.child("role").text().as_uint(); + uint32_t status_int = info.child("status").text().as_uint(); + std::string description_str = info.child("description").text().as_string(); + + char description_char[256] = {0}; + snprintf(description_char, sizeof(description_char), "%s", description_str.c_str()); + + SceNpClansMemberEntry entry = SceNpClansMemberEntry + { + .npid = npid, + .role = static_cast(role_int), + .status = static_cast(status_int), + }; + + snprintf(entry.updatable.description, sizeof(entry.updatable.description), "%s", description_char); + + memList[i] = entry; + i++; + } + + *pageResult = SceNpClansPagingResult + { + .count = results_count, + .total = total_count + }; + + return SCE_NP_CLANS_SUCCESS; + } + + SceNpClansError clan_client::getBlacklist(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansBlacklistEntry* bl, SceNpClansPagingResult* pageResult) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + clan.append_child("start").text().set(paging->startPos); + clan.append_child("max").text().set(paging->max); + + pugi::xml_document response = pugi::xml_document(); + SceNpClansError clanRes = sendRequest(ClanRequestAction::GetBlacklist, ClanManagerOperationType::VIEW, &doc, &response); + + if (clanRes != SCE_NP_CLANS_SUCCESS) + return clanRes; + + pugi::xml_node clanResult = response.child("clan"); + pugi::xml_node list = clanResult.child("list"); + + pugi::xml_attribute results = list.attribute("results"); + uint32_t results_count = results.as_uint(); + + pugi::xml_attribute total = list.attribute("total"); + uint32_t total_count = total.as_uint(); + + int i = 0; + for (pugi::xml_node node = list.child("entry"); node; node = node.next_sibling("entry")) + { + pugi::xml_node id = node.child("jid"); + std::string npid_str = id.text().as_string(); + + char username[SCE_NET_NP_ONLINEID_MAX_LENGTH + 1] = {0}; + + sscanf(npid_str.c_str(), "%16[^@]", username); + + SceNpId npid = SceNpId + { + .handle = SceNpOnlineId + { + .data = "" + } + }; + + snprintf(npid.handle.data, sizeof(npid.handle.data), "%s", username); + + SceNpClansBlacklistEntry entry = SceNpClansBlacklistEntry + { + .entry = npid, + }; + + bl[i] = entry; + i++; + } + + *pageResult = SceNpClansPagingResult + { + .count = results_count, + .total = total_count + }; + + return SCE_NP_CLANS_SUCCESS; + } + + SceNpClansError clan_client::addBlacklistEntry(np::np_handler& nph, SceNpClanId clanId, SceNpId npId) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + + std::string jid_str = std::format(JID_FORMAT, npId.handle.data); + clan.append_child("jid").text().set(jid_str.c_str()); + + pugi::xml_document response = pugi::xml_document(); + return sendRequest(ClanRequestAction::RecordBlacklistEntry, ClanManagerOperationType::UPDATE, &doc, &response); + } + + SceNpClansError clan_client::removeBlacklistEntry(np::np_handler& nph, SceNpClanId clanId, SceNpId npId) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + + std::string jid_str = std::format(JID_FORMAT, npId.handle.data); + clan.append_child("jid").text().set(jid_str.c_str()); + + pugi::xml_document response = pugi::xml_document(); + return sendRequest(ClanRequestAction::DeleteBlacklistEntry, ClanManagerOperationType::UPDATE, &doc, &response); + } + + SceNpClansError clan_client::clanSearch(SceNpClansPagingRequest* paging, SceNpClansSearchableName* search, SceNpClansClanBasicInfo* clanList, SceNpClansPagingResult* pageResult) + { + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("start").text().set(paging->startPos); + clan.append_child("max").text().set(paging->max); + + pugi::xml_node filter = clan.append_child("filter"); + pugi::xml_node name = filter.append_child("name"); + + std::string op_name = std::format("{}", static_cast(static_cast(search->nameSearchOp))); + name.append_attribute("op").set_value(op_name.c_str()); + name.append_attribute("value").set_value(search->name); + + pugi::xml_document response = pugi::xml_document(); + SceNpClansError clanRes = sendRequest(ClanRequestAction::ClanSearch, ClanManagerOperationType::VIEW, &doc, &response); + + if (clanRes != SCE_NP_CLANS_SUCCESS) + return clanRes; + + pugi::xml_node clanResult = response.child("clan"); + pugi::xml_node list = clanResult.child("list"); + + pugi::xml_attribute results = list.attribute("results"); + uint32_t results_count = results.as_uint(); + + pugi::xml_attribute total = list.attribute("total"); + uint32_t total_count = total.as_uint(); + + int i = 0; + for (pugi::xml_node node = list.child("info"); node; node = node.next_sibling("info")) + { + uint32_t clanId = node.attribute("id").as_uint(); + std::string name_str = node.child("name").text().as_string(); + std::string tag_str = node.child("tag").text().as_string(); + uint32_t members_int = node.child("members").text().as_uint(); + + SceNpClansClanBasicInfo entry = SceNpClansClanBasicInfo + { + .clanId = clanId, + .numMembers = members_int, + .name = "", + .tag = "", + .reserved = {0, 0}, + }; + + snprintf(entry.name, sizeof(entry.name), "%s", name_str.c_str()); + snprintf(entry.tag, sizeof(entry.tag), "%s", tag_str.c_str()); + + clanList[i] = entry; + i++; + } + + *pageResult = SceNpClansPagingResult + { + .count = results_count, + .total = total_count + }; + + return SCE_NP_CLANS_SUCCESS; + } + + SceNpClansError clan_client::requestMembership(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* /*message*/) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + + pugi::xml_document response = pugi::xml_document(); + return sendRequest(ClanRequestAction::RequestMembership, ClanManagerOperationType::UPDATE, &doc, &response); + } + + SceNpClansError clan_client::cancelRequestMembership(np::np_handler& nph, SceNpClanId clanId) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + + pugi::xml_document response = pugi::xml_document(); + return sendRequest(ClanRequestAction::CancelRequestMembership, ClanManagerOperationType::UPDATE, &doc, &response); + } + + SceNpClansError clan_client::sendMembershipResponse(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/, b8 allow) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + + std::string jid_str = std::format(JID_FORMAT, npId.handle.data); + clan.append_child("jid").text().set(jid_str.c_str()); + + pugi::xml_document response = pugi::xml_document(); + return sendRequest(allow ? ClanRequestAction::AcceptMembershipRequest : ClanRequestAction::DeclineMembershipRequest, ClanManagerOperationType::UPDATE, &doc, &response); + } + + SceNpClansError clan_client::sendInvitation(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + + std::string jid_str = std::format(JID_FORMAT, npId.handle.data); + clan.append_child("jid").text().set(jid_str.c_str()); + + pugi::xml_document response = pugi::xml_document(); + return sendRequest(ClanRequestAction::SendInvitation, ClanManagerOperationType::UPDATE, &doc, &response); + } + + SceNpClansError clan_client::cancelInvitation(np::np_handler& nph, SceNpClanId clanId, SceNpId npId) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + + std::string jid_str = std::format(JID_FORMAT, npId.handle.data); + clan.append_child("jid").text().set(jid_str.c_str()); + + pugi::xml_document response = pugi::xml_document(); + return sendRequest(ClanRequestAction::CancelInvitation, ClanManagerOperationType::UPDATE, &doc, &response); + } + + SceNpClansError clan_client::sendInvitationResponse(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* /*message*/, b8 accept) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + + pugi::xml_document response = pugi::xml_document(); + return sendRequest(accept ? ClanRequestAction::AcceptInvitation : ClanRequestAction::DeclineInvitation, ClanManagerOperationType::UPDATE, &doc, &response); + } + + SceNpClansError clan_client::updateMemberInfo(np::np_handler& nph, SceNpClanId clanId, SceNpClansUpdatableMemberInfo* info) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + + pugi::xml_node role = clan.append_child("onlinename"); + role.text().set(nph.get_npid().handle.data); + + pugi::xml_node description = clan.append_child("description"); + description.text().set(info->description); + + pugi::xml_node status = clan.append_child("bin-attr1"); + + byte binAttr1[SCE_NP_CLANS_MEMBER_BINARY_ATTRIBUTE1_MAX_SIZE * 2 + 1] = {0}; + uint32_t binAttr1Size = UINT32_MAX; + Base64_Encode_NoNl(info->binAttr1, info->binData1Size, binAttr1, &binAttr1Size); + + if (binAttr1Size == UINT32_MAX) + return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT; + + // `reinterpret_cast` used to let the compiler select the correct overload of `set` + status.text().set(reinterpret_cast(binAttr1)); + + pugi::xml_node allowMsg = clan.append_child("allow-msg"); + allowMsg.text().set(static_cast(info->allowMsg)); + + pugi::xml_node size = clan.append_child("size"); + size.text().set(info->binData1Size); + + pugi::xml_document response = pugi::xml_document(); + return sendRequest(ClanRequestAction::UpdateMemberInfo, ClanManagerOperationType::UPDATE, &doc, &response); + } + + SceNpClansError clan_client::updateClanInfo(np::np_handler& nph, SceNpClanId clanId, SceNpClansUpdatableClanInfo* info) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + + // TODO: implement binary and integer attributes (not implemented in server yet) + + pugi::xml_node description = clan.append_child("description"); + description.text().set(info->description); + + pugi::xml_document response = pugi::xml_document(); + return sendRequest(ClanRequestAction::UpdateClanInfo, ClanManagerOperationType::UPDATE, &doc, &response); + } + + SceNpClansError clan_client::joinClan(np::np_handler& nph, SceNpClanId clanId) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + + pugi::xml_document response = pugi::xml_document(); + return sendRequest(ClanRequestAction::JoinClan, ClanManagerOperationType::UPDATE, &doc, &response); + } + + SceNpClansError clan_client::leaveClan(np::np_handler& nph, SceNpClanId clanId) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + + pugi::xml_document response = pugi::xml_document(); + return sendRequest(ClanRequestAction::LeaveClan, ClanManagerOperationType::UPDATE, &doc, &response); + } + + SceNpClansError clan_client::kickMember(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + + std::string jid_str = std::format(JID_FORMAT, npId.handle.data); + clan.append_child("jid").text().set(jid_str.c_str()); + + pugi::xml_document response = pugi::xml_document(); + return sendRequest(ClanRequestAction::KickMember, ClanManagerOperationType::UPDATE, &doc, &response); + } + + SceNpClansError clan_client::changeMemberRole(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMemberRole role) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + + std::string jid_str = std::format(JID_FORMAT, npId.handle.data); + clan.append_child("jid").text().set(jid_str.c_str()); + + pugi::xml_node roleNode = clan.append_child("role"); + roleNode.text().set(static_cast(role)); + + pugi::xml_document response = pugi::xml_document(); + return sendRequest(ClanRequestAction::ChangeMemberRole, ClanManagerOperationType::UPDATE, &doc, &response); + } + + SceNpClansError clan_client::retrieveAnnouncements(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMessageEntry* announcements, SceNpClansPagingResult* pageResult) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + clan.append_child("start").text().set(paging->startPos); + clan.append_child("max").text().set(paging->max); + + pugi::xml_document response = pugi::xml_document(); + SceNpClansError clanRes = sendRequest(ClanRequestAction::RetrieveAnnouncements, ClanManagerOperationType::VIEW, &doc, &response); + + if (clanRes != SCE_NP_CLANS_SUCCESS) + return clanRes; + + pugi::xml_node clanResult = response.child("clan"); + pugi::xml_node list = clanResult.child("list"); + + pugi::xml_attribute results = list.attribute("results"); + uint32_t results_count = results.as_uint(); + + pugi::xml_attribute total = list.attribute("total"); + uint32_t total_count = total.as_uint(); + + int i = 0; + for (pugi::xml_node node = list.child("msg-info"); node; node = node.next_sibling("msg-info")) + { + pugi::xml_attribute id = node.attribute("id"); + uint32_t msgId = id.as_uint(); + + std::string subject_str = node.child("subject").text().as_string(); + std::string msg_str = node.child("msg").text().as_string(); + std::string npid_str = node.child("jid").text().as_string(); + std::string msg_date = node.child("msg-date").text().as_string(); + + char username[SCE_NET_NP_ONLINEID_MAX_LENGTH + 1] = {0}; + sscanf(npid_str.c_str(), "%16[^@]", username); + + SceNpId npid; + + if (!strcmp(username, nph.get_npid().handle.data)) + { + npid = nph.get_npid(); + } + else + { + npid = SceNpId {}; + std::strncpy(npid.handle.data, username, SCE_NET_NP_ONLINEID_MAX_LENGTH + 1); + } + + // TODO: implement `binData` and `fromId` + + SceNpClansMessageEntry entry = SceNpClansMessageEntry + { + .mId = msgId, + .message = SceNpClansMessage { + .subject = "", + .body = "", + }, + .npid = npid, + .postedBy = clanId, + }; + + + strncpy(entry.message.subject, subject_str.c_str(), subject_str.size()); + strncpy(entry.message.body, msg_str.c_str(), msg_str.size()); + + announcements[i] = entry; + i++; + } + + *pageResult = SceNpClansPagingResult + { + .count = results_count, + .total = total_count + }; + + return SCE_NP_CLANS_SUCCESS; + } + + SceNpClansError clan_client::postAnnouncement(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* announcement, SceNpClansMessageData* /*data*/, u32 duration, SceNpClansMessageId* msgId) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + + pugi::xml_node subject = clan.append_child("subject"); + subject.text().set(announcement->subject); + + pugi::xml_node msg = clan.append_child("msg"); + msg.text().set(announcement->body); + + pugi::xml_node expireDate = clan.append_child("expire-date"); + expireDate.text().set(duration); + + pugi::xml_document response = pugi::xml_document(); + SceNpClansError clanRes = sendRequest(ClanRequestAction::PostAnnouncement, ClanManagerOperationType::UPDATE, &doc, &response); + + if (clanRes != SCE_NP_CLANS_SUCCESS) + return clanRes; + + pugi::xml_node clanResult = response.child("clan"); + pugi::xml_node msgIdNode = clanResult.child("id"); + *msgId = msgIdNode.text().as_uint(); + + return SCE_NP_CLANS_SUCCESS; + } + + SceNpClansError clan_client::deleteAnnouncement(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessageId announcementId) + { + std::string ticket = getClanTicket(nph); + + pugi::xml_document doc = pugi::xml_document(); + pugi::xml_node clan = doc.append_child("clan"); + clan.append_child("ticket").text().set(ticket.c_str()); + clan.append_child("id").text().set(clanId); + clan.append_child("msg-id").text().set(announcementId); + + pugi::xml_document response = pugi::xml_document(); + return sendRequest(ClanRequestAction::DeleteAnnouncement, ClanManagerOperationType::UPDATE, &doc, &response); + } +} +#pragma endregion \ No newline at end of file diff --git a/rpcs3/Emu/NP/clan_client.h b/rpcs3/Emu/NP/clan_client.h new file mode 100644 index 0000000000..1703c59c78 --- /dev/null +++ b/rpcs3/Emu/NP/clan_client.h @@ -0,0 +1,120 @@ +#pragma once + +#include <3rdparty/curl/curl/include/curl/curl.h> +#include +#include +#include + +namespace clan +{ + enum class ClanManagerOperationType + { + VIEW, + UPDATE + }; + + enum class ClanRequestType + { + FUNC, + SEC + }; + + enum class ClanSearchFilterOperator : u8 + { + Equal, + NotEqual, + GreaterThan, + GreaterThanOrEqual, + LessThan, + LessThanOrEqual, + Like, + }; + + enum class ClanRequestAction + { + GetClanList, + GetClanInfo, + GetMemberInfo, + GetMemberList, + GetBlacklist, + RecordBlacklistEntry, + DeleteBlacklistEntry, + ClanSearch, + RequestMembership, + CancelRequestMembership, + AcceptMembershipRequest, + DeclineMembershipRequest, + SendInvitation, + CancelInvitation, + AcceptInvitation, + DeclineInvitation, + UpdateMemberInfo, + UpdateClanInfo, + JoinClan, + LeaveClan, + KickMember, + ChangeMemberRole, + RetrieveAnnouncements, + PostAnnouncement, + DeleteAnnouncement + }; + + class clan_client + { + private: + CURL* curl = nullptr; + CURLcode res = CURLE_OK; + + static size_t curlWriteCallback(void* data, size_t size, size_t nmemb, void* clientp); + SceNpClansError sendRequest(ClanRequestAction action, ClanManagerOperationType type, pugi::xml_document* xmlBody, pugi::xml_document* outResponse); + + /// @brief Forge and get a V2.1 Ticket for clan operations + std::string getClanTicket(np::np_handler& nph); + + public: + clan_client(); + ~clan_client(); + + SceNpClansError createRequest(); + SceNpClansError destroyRequest(); + + SceNpClansError clanSearch(SceNpClansPagingRequest* paging, SceNpClansSearchableName* search, SceNpClansClanBasicInfo* clanList, SceNpClansPagingResult* pageResult); + + SceNpClansError getClanList(np::np_handler& nph, SceNpClansPagingRequest* paging, SceNpClansEntry* clanList, SceNpClansPagingResult* pageResult); + SceNpClansError getClanInfo(SceNpClanId clanId, SceNpClansClanInfo* clanInfo); + + SceNpClansError getMemberInfo(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMemberEntry* memInfo); + SceNpClansError getMemberList(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMemberStatus status, SceNpClansMemberEntry* memList, SceNpClansPagingResult* pageResult); + + SceNpClansError getBlacklist(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansBlacklistEntry* bl, SceNpClansPagingResult* pageResult); + SceNpClansError addBlacklistEntry(np::np_handler& nph, SceNpClanId clanId, SceNpId npId); + SceNpClansError removeBlacklistEntry(np::np_handler& nph, SceNpClanId clanId, SceNpId npId); + + SceNpClansError requestMembership(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* message); + SceNpClansError cancelRequestMembership(np::np_handler& nph, SceNpClanId clanId); + SceNpClansError sendMembershipResponse(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* message, b8 allow); + + SceNpClansError sendInvitation(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* message); + SceNpClansError cancelInvitation(np::np_handler& nph, SceNpClanId clanId, SceNpId npId); + SceNpClansError sendInvitationResponse(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* message, b8 accept); + + SceNpClansError joinClan(np::np_handler& nph, SceNpClanId clanId); + SceNpClansError leaveClan(np::np_handler& nph, SceNpClanId clanId); + + SceNpClansError updateMemberInfo(np::np_handler& nph, SceNpClanId clanId, SceNpClansUpdatableMemberInfo* info); + SceNpClansError updateClanInfo(np::np_handler& nph, SceNpClanId clanId, SceNpClansUpdatableClanInfo* info); + + SceNpClansError kickMember(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* message); + SceNpClansError changeMemberRole(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMemberRole role); + + SceNpClansError retrieveAnnouncements(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMessageEntry* announcements, SceNpClansPagingResult* pageResult); + SceNpClansError postAnnouncement(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* announcement, SceNpClansMessageData* data, u32 duration, SceNpClansMessageId* announcementId); + SceNpClansError deleteAnnouncement(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessageId announcementId); + }; +} // namespace clan + +struct sce_np_clans_manager +{ + atomic_t is_initialized = false; + clan::clan_client* client; +}; \ No newline at end of file From d618ec9135cf4be7b5b19b7f60743e4fc5bee6f2 Mon Sep 17 00:00:00 2001 From: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com> Date: Sun, 7 Dec 2025 18:11:22 +0100 Subject: [PATCH 02/30] Clans: Qt GUI configuration impl Signed-off-by: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com> --- rpcs3/Emu/CMakeLists.txt | 3 +- rpcs3/Emu/Cell/Modules/sceNpClans.cpp | 4 +- .../NP/{clan_client.cpp => clans_client.cpp} | 62 +++---- .../Emu/NP/{clan_client.h => clans_client.h} | 8 +- rpcs3/Emu/NP/clans_config.cpp | 140 ++++++++++++++ rpcs3/Emu/NP/clans_config.h | 26 +++ rpcs3/rpcs3qt/CMakeLists.txt | 1 + rpcs3/rpcs3qt/clans_settings_dialog.cpp | 172 ++++++++++++++++++ rpcs3/rpcs3qt/clans_settings_dialog.h | 29 +++ rpcs3/rpcs3qt/main_window.cpp | 7 + rpcs3/rpcs3qt/main_window.ui | 9 + 11 files changed, 423 insertions(+), 38 deletions(-) rename rpcs3/Emu/NP/{clan_client.cpp => clans_client.cpp} (90%) rename rpcs3/Emu/NP/{clan_client.h => clans_client.h} (98%) create mode 100644 rpcs3/Emu/NP/clans_config.cpp create mode 100644 rpcs3/Emu/NP/clans_config.h create mode 100644 rpcs3/rpcs3qt/clans_settings_dialog.cpp create mode 100644 rpcs3/rpcs3qt/clans_settings_dialog.h diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 44210909d8..5feef8f8ca 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -441,7 +441,8 @@ target_sources(rpcs3_emu PRIVATE NP/np_requests.cpp NP/signaling_handler.cpp NP/np_structs_extra.cpp - NP/clan_client.cpp + NP/clans_client.cpp + NP/clans_config.cpp NP/rpcn_client.cpp NP/rpcn_config.cpp NP/rpcn_countries.cpp diff --git a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp index 29f26d9e2b..51960f58bd 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp @@ -2,7 +2,7 @@ #include "Emu/Cell/PPUModule.h" #include "Emu/IdManager.h" #include "Emu/NP/np_handler.h" -#include "Emu/NP/clan_client.h" +#include "Emu/NP/clans_client.h" #include "sceNp.h" #include "sceNpClans.h" @@ -97,7 +97,7 @@ error_code sceNpClansInit(vm::cptr commId, vm::cptr -#include +#include #include #include @@ -118,7 +118,7 @@ namespace clan size_t size; }; - size_t clan_client::curlWriteCallback(void* data, size_t size, size_t nmemb, void* clientp) + size_t clans_client::curlWriteCallback(void* data, size_t size, size_t nmemb, void* clientp) { size_t realsize = size * nmemb; std::vector* mem = static_cast*>(clientp); @@ -130,17 +130,17 @@ namespace clan return realsize; } - clan_client::clan_client() + clans_client::clans_client() { createRequest(); } - clan_client::~clan_client() + clans_client::~clans_client() { destroyRequest(); } - SceNpClansError clan_client::createRequest() + SceNpClansError clans_client::createRequest() { if (curl) return SceNpClansError::SCE_NP_CLANS_ERROR_ALREADY_INITIALIZED; @@ -156,7 +156,7 @@ namespace clan return SceNpClansError::SCE_NP_CLANS_SUCCESS; } - SceNpClansError clan_client::destroyRequest() + SceNpClansError clans_client::destroyRequest() { if (curl) { @@ -167,7 +167,7 @@ namespace clan return SceNpClansError::SCE_NP_CLANS_SUCCESS; } - SceNpClansError clan_client::sendRequest(ClanRequestAction action, ClanManagerOperationType opType, pugi::xml_document* xmlBody, pugi::xml_document* outResponse) + SceNpClansError clans_client::sendRequest(ClanRequestAction action, ClanManagerOperationType opType, pugi::xml_document* xmlBody, pugi::xml_document* outResponse) { if (!curl) return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -237,7 +237,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - std::string clan_client::getClanTicket(np::np_handler& nph) + std::string clans_client::getClanTicket(np::np_handler& nph) { if (nph.get_ticket().size() > 0) { std::vector ticket_bytes(1024); @@ -287,7 +287,7 @@ namespace clan } #pragma region Outgoing API Requests - SceNpClansError clan_client::getClanList(np::np_handler& nph, SceNpClansPagingRequest* paging, SceNpClansEntry* clanList, SceNpClansPagingResult* pageResult) + SceNpClansError clans_client::getClanList(np::np_handler& nph, SceNpClansPagingRequest* paging, SceNpClansEntry* clanList, SceNpClansPagingResult* pageResult) { pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -361,7 +361,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clan_client::getClanInfo(SceNpClanId clanId, SceNpClansClanInfo* clanInfo) + SceNpClansError clans_client::getClanInfo(SceNpClanId clanId, SceNpClansClanInfo* clanInfo) { pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -400,7 +400,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clan_client::getMemberInfo(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMemberEntry* memInfo) + SceNpClansError clans_client::getMemberInfo(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMemberEntry* memInfo) { std::string ticket = getClanTicket(nph); @@ -468,7 +468,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clan_client::getMemberList(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMemberStatus /*status*/, SceNpClansMemberEntry* memList, SceNpClansPagingResult* pageResult) + SceNpClansError clans_client::getMemberList(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMemberStatus /*status*/, SceNpClansMemberEntry* memList, SceNpClansPagingResult* pageResult) { std::string ticket = getClanTicket(nph); @@ -544,7 +544,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clan_client::getBlacklist(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansBlacklistEntry* bl, SceNpClansPagingResult* pageResult) + SceNpClansError clans_client::getBlacklist(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansBlacklistEntry* bl, SceNpClansPagingResult* pageResult) { std::string ticket = getClanTicket(nph); @@ -608,7 +608,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clan_client::addBlacklistEntry(np::np_handler& nph, SceNpClanId clanId, SceNpId npId) + SceNpClansError clans_client::addBlacklistEntry(np::np_handler& nph, SceNpClanId clanId, SceNpId npId) { std::string ticket = getClanTicket(nph); @@ -624,7 +624,7 @@ namespace clan return sendRequest(ClanRequestAction::RecordBlacklistEntry, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clan_client::removeBlacklistEntry(np::np_handler& nph, SceNpClanId clanId, SceNpId npId) + SceNpClansError clans_client::removeBlacklistEntry(np::np_handler& nph, SceNpClanId clanId, SceNpId npId) { std::string ticket = getClanTicket(nph); @@ -640,7 +640,7 @@ namespace clan return sendRequest(ClanRequestAction::DeleteBlacklistEntry, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clan_client::clanSearch(SceNpClansPagingRequest* paging, SceNpClansSearchableName* search, SceNpClansClanBasicInfo* clanList, SceNpClansPagingResult* pageResult) + SceNpClansError clans_client::clanSearch(SceNpClansPagingRequest* paging, SceNpClansSearchableName* search, SceNpClansClanBasicInfo* clanList, SceNpClansPagingResult* pageResult) { pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -702,7 +702,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clan_client::requestMembership(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* /*message*/) + SceNpClansError clans_client::requestMembership(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* /*message*/) { std::string ticket = getClanTicket(nph); @@ -715,7 +715,7 @@ namespace clan return sendRequest(ClanRequestAction::RequestMembership, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clan_client::cancelRequestMembership(np::np_handler& nph, SceNpClanId clanId) + SceNpClansError clans_client::cancelRequestMembership(np::np_handler& nph, SceNpClanId clanId) { std::string ticket = getClanTicket(nph); @@ -728,7 +728,7 @@ namespace clan return sendRequest(ClanRequestAction::CancelRequestMembership, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clan_client::sendMembershipResponse(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/, b8 allow) + SceNpClansError clans_client::sendMembershipResponse(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/, b8 allow) { std::string ticket = getClanTicket(nph); @@ -744,7 +744,7 @@ namespace clan return sendRequest(allow ? ClanRequestAction::AcceptMembershipRequest : ClanRequestAction::DeclineMembershipRequest, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clan_client::sendInvitation(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/) + SceNpClansError clans_client::sendInvitation(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/) { std::string ticket = getClanTicket(nph); @@ -760,7 +760,7 @@ namespace clan return sendRequest(ClanRequestAction::SendInvitation, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clan_client::cancelInvitation(np::np_handler& nph, SceNpClanId clanId, SceNpId npId) + SceNpClansError clans_client::cancelInvitation(np::np_handler& nph, SceNpClanId clanId, SceNpId npId) { std::string ticket = getClanTicket(nph); @@ -776,7 +776,7 @@ namespace clan return sendRequest(ClanRequestAction::CancelInvitation, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clan_client::sendInvitationResponse(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* /*message*/, b8 accept) + SceNpClansError clans_client::sendInvitationResponse(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* /*message*/, b8 accept) { std::string ticket = getClanTicket(nph); @@ -789,7 +789,7 @@ namespace clan return sendRequest(accept ? ClanRequestAction::AcceptInvitation : ClanRequestAction::DeclineInvitation, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clan_client::updateMemberInfo(np::np_handler& nph, SceNpClanId clanId, SceNpClansUpdatableMemberInfo* info) + SceNpClansError clans_client::updateMemberInfo(np::np_handler& nph, SceNpClanId clanId, SceNpClansUpdatableMemberInfo* info) { std::string ticket = getClanTicket(nph); @@ -826,7 +826,7 @@ namespace clan return sendRequest(ClanRequestAction::UpdateMemberInfo, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clan_client::updateClanInfo(np::np_handler& nph, SceNpClanId clanId, SceNpClansUpdatableClanInfo* info) + SceNpClansError clans_client::updateClanInfo(np::np_handler& nph, SceNpClanId clanId, SceNpClansUpdatableClanInfo* info) { std::string ticket = getClanTicket(nph); @@ -844,7 +844,7 @@ namespace clan return sendRequest(ClanRequestAction::UpdateClanInfo, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clan_client::joinClan(np::np_handler& nph, SceNpClanId clanId) + SceNpClansError clans_client::joinClan(np::np_handler& nph, SceNpClanId clanId) { std::string ticket = getClanTicket(nph); @@ -857,7 +857,7 @@ namespace clan return sendRequest(ClanRequestAction::JoinClan, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clan_client::leaveClan(np::np_handler& nph, SceNpClanId clanId) + SceNpClansError clans_client::leaveClan(np::np_handler& nph, SceNpClanId clanId) { std::string ticket = getClanTicket(nph); @@ -870,7 +870,7 @@ namespace clan return sendRequest(ClanRequestAction::LeaveClan, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clan_client::kickMember(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/) + SceNpClansError clans_client::kickMember(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/) { std::string ticket = getClanTicket(nph); @@ -886,7 +886,7 @@ namespace clan return sendRequest(ClanRequestAction::KickMember, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clan_client::changeMemberRole(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMemberRole role) + SceNpClansError clans_client::changeMemberRole(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMemberRole role) { std::string ticket = getClanTicket(nph); @@ -905,7 +905,7 @@ namespace clan return sendRequest(ClanRequestAction::ChangeMemberRole, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clan_client::retrieveAnnouncements(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMessageEntry* announcements, SceNpClansPagingResult* pageResult) + SceNpClansError clans_client::retrieveAnnouncements(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMessageEntry* announcements, SceNpClansPagingResult* pageResult) { std::string ticket = getClanTicket(nph); @@ -987,7 +987,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clan_client::postAnnouncement(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* announcement, SceNpClansMessageData* /*data*/, u32 duration, SceNpClansMessageId* msgId) + SceNpClansError clans_client::postAnnouncement(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* announcement, SceNpClansMessageData* /*data*/, u32 duration, SceNpClansMessageId* msgId) { std::string ticket = getClanTicket(nph); @@ -1018,7 +1018,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clan_client::deleteAnnouncement(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessageId announcementId) + SceNpClansError clans_client::deleteAnnouncement(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessageId announcementId) { std::string ticket = getClanTicket(nph); diff --git a/rpcs3/Emu/NP/clan_client.h b/rpcs3/Emu/NP/clans_client.h similarity index 98% rename from rpcs3/Emu/NP/clan_client.h rename to rpcs3/Emu/NP/clans_client.h index 1703c59c78..c488f6be8a 100644 --- a/rpcs3/Emu/NP/clan_client.h +++ b/rpcs3/Emu/NP/clans_client.h @@ -59,7 +59,7 @@ namespace clan DeleteAnnouncement }; - class clan_client + class clans_client { private: CURL* curl = nullptr; @@ -72,8 +72,8 @@ namespace clan std::string getClanTicket(np::np_handler& nph); public: - clan_client(); - ~clan_client(); + clans_client(); + ~clans_client(); SceNpClansError createRequest(); SceNpClansError destroyRequest(); @@ -116,5 +116,5 @@ namespace clan struct sce_np_clans_manager { atomic_t is_initialized = false; - clan::clan_client* client; + clan::clans_client* client; }; \ No newline at end of file diff --git a/rpcs3/Emu/NP/clans_config.cpp b/rpcs3/Emu/NP/clans_config.cpp new file mode 100644 index 0000000000..86b062fa5f --- /dev/null +++ b/rpcs3/Emu/NP/clans_config.cpp @@ -0,0 +1,140 @@ +#include "stdafx.h" +#include "clans_config.h" +#include "Utilities/File.h" + +cfg_clans g_cfg_clans; + +LOG_CHANNEL(clans_config_log, "clans_config"); + +void cfg_clans::load() +{ + const std::string path = cfg_clans::get_path(); + + fs::file cfg_file(path, fs::read); + if (cfg_file) + { + clans_config_log.notice("Loading Clans config. Path: %s", path); + from_string(cfg_file.to_string()); + } + else + { + clans_config_log.notice("Clans config missing. Using default settings. Path: %s", path); + from_default(); + } +} + +void cfg_clans::save() const +{ +#ifdef _WIN32 + const std::string path_to_cfg = fs::get_config_dir(true); + if (!fs::create_path(path_to_cfg)) + { + clans_config_log.error("Could not create path: %s", path_to_cfg); + } +#endif + + const std::string path = cfg_clans::get_path(); + + if (!cfg::node::save(path)) + { + clans_config_log.error("Could not save config: %s (error=%s)", path, fs::g_tls_error); + } +} + +std::string cfg_clans::get_path() +{ + return fs::get_config_dir(true) + "clans.yml"; +} + +std::string cfg_clans::get_host() const +{ + return host.to_string(); +} + +std::vector> cfg_clans::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) + { + clans_config_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; +} + +void cfg_clans::set_host(std::string_view host) +{ + this->host.from_string(host); +} + +void cfg_clans::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_clans::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_clans::del_host(std::string_view del_description, std::string_view del_host) +{ + // Do not delete default servers + if (del_description == "Official Clans Server" && del_host == "clans.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/clans_config.h b/rpcs3/Emu/NP/clans_config.h new file mode 100644 index 0000000000..4a9f09d06b --- /dev/null +++ b/rpcs3/Emu/NP/clans_config.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Utilities/Config.h" + +struct cfg_clans : cfg::node +{ + cfg::uint32 version{this, "Version", 1}; + cfg::string host{this, "Host", "clans.rpcs3.net"}; + cfg::string hosts{this, "Hosts", "Official Clans Server|clans.rpcs3.net"}; + + void load(); + void save() const; + + std::string get_host() const; + std::vector> get_hosts(); + + void set_host(std::string_view host); + bool add_host(std::string_view description, std::string_view host); + bool del_host(std::string_view description, std::string_view host); + +private: + static std::string get_path(); + void set_hosts(const std::vector>& vec_hosts); +}; + +extern cfg_clans g_cfg_clans; diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt index 43c0024905..98be856a25 100644 --- a/rpcs3/rpcs3qt/CMakeLists.txt +++ b/rpcs3/rpcs3qt/CMakeLists.txt @@ -8,6 +8,7 @@ add_library(rpcs3_ui STATIC camera_settings_dialog.cpp cg_disasm_window.cpp cheat_manager.cpp + clans_settings_dialog.cpp config_adapter.cpp config_checker.cpp curl_handle.cpp diff --git a/rpcs3/rpcs3qt/clans_settings_dialog.cpp b/rpcs3/rpcs3qt/clans_settings_dialog.cpp new file mode 100644 index 0000000000..ff2113297f --- /dev/null +++ b/rpcs3/rpcs3qt/clans_settings_dialog.cpp @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "clans_settings_dialog.h" +#include "Emu/NP/clans_config.h" + +clans_settings_dialog::clans_settings_dialog(QWidget* parent) + : QDialog(parent) +{ + g_cfg_clans.load(); + + setWindowTitle(tr("Clans Configuration")); + setObjectName("clans_settings_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); + + setLayout(vbox_global); + + connect(cbx_servers, &QComboBox::currentIndexChanged, this, [this](int index) + { + if (index < 0) + return; + + QVariant host = cbx_servers->itemData(index); + + if (!host.isValid() || !host.canConvert()) + return; + + g_cfg_clans.set_host(host.toString().toStdString()); + g_cfg_clans.save(); + }); + + connect(btn_add_server, &QAbstractButton::clicked, this, [this]() + { + clans_add_server_dialog dlg(this); + dlg.exec(); + const auto& new_server = dlg.get_new_server(); + if (new_server) + { + if (!g_cfg_clans.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_clans.save(); + refresh_combobox(); + } + }); + + connect(btn_del_server, &QAbstractButton::clicked, this, [this]() + { + const int index = cbx_servers->currentIndex(); + + if (index < 0) + return; + + const std::string desc = cbx_servers->itemText(index).toStdString(); + const std::string host = cbx_servers->itemData(index).toString().toStdString(); + + if (g_cfg_clans.del_host(desc, host)) + { + g_cfg_clans.save(); + refresh_combobox(); + } + else + { + QMessageBox::warning(this, tr("Cannot Delete"), tr("This server cannot be deleted."), QMessageBox::Ok); + } + }); +} + +void clans_settings_dialog::refresh_combobox() +{ + g_cfg_clans.load(); + const auto vec_hosts = g_cfg_clans.get_hosts(); + const auto cur_host = g_cfg_clans.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); +} + +clans_add_server_dialog::clans_add_server_dialog(QWidget* parent) + : QDialog(parent) +{ + setWindowTitle(tr("Clans: Add Server")); + setObjectName("clans_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]() + { + const QString description = edt_description->text(); + const QString 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>& clans_add_server_dialog::get_new_server() const +{ + return m_new_server; +} diff --git a/rpcs3/rpcs3qt/clans_settings_dialog.h b/rpcs3/rpcs3qt/clans_settings_dialog.h new file mode 100644 index 0000000000..7672dfaa14 --- /dev/null +++ b/rpcs3/rpcs3qt/clans_settings_dialog.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +class clans_settings_dialog : public QDialog +{ + Q_OBJECT +public: + clans_settings_dialog(QWidget* parent = nullptr); + +private: + void refresh_combobox(); + +private: + QComboBox* cbx_servers = nullptr; +}; + +class clans_add_server_dialog : public QDialog +{ + Q_OBJECT +public: + clans_add_server_dialog(QWidget* parent = nullptr); + const std::optional>& get_new_server() const; + +private: + std::optional> m_new_server; +}; diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 80d71a44c2..aee3ce5ca2 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -12,6 +12,7 @@ #include "log_frame.h" #include "settings_dialog.h" #include "rpcn_settings_dialog.h" +#include "clans_settings_dialog.h" #include "auto_pause_settings_dialog.h" #include "cg_disasm_window.h" #include "log_viewer.h" @@ -2991,6 +2992,12 @@ void main_window::CreateConnects() dlg.exec(); }); + connect(ui->confClansAct, &QAction::triggered, this, [this]() + { + clans_settings_dialog dlg(this); + dlg.exec(); + }); + connect(ui->confIPCAct, &QAction::triggered, this, [this]() { ipc_settings_dialog dlg(this); diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index 72861a5d72..c9c8c0645d 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -282,6 +282,7 @@ + @@ -1252,6 +1253,14 @@ Configure RPCN + + + Clans + + + Configure Clans + + IPC From f467a15e833b5f655d146068b90e2d8c39a45505 Mon Sep 17 00:00:00 2001 From: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com> Date: Sun, 7 Dec 2025 18:15:14 +0100 Subject: [PATCH 03/30] Clans: using config for host - Enabled SSL host verification Signed-off-by: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com> --- rpcs3/Emu/NP/clans_client.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index 7ec8177346..8291c23c77 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -3,13 +3,13 @@ #include #include +#include #include #include LOG_CHANNEL(clan_log, "clans"); -const char* HOST_VIEW = "clans-view01.ww.np.community.playstation.net"; -const char* HOST_UPDATE = "clans-rec01.ww.np.community.playstation.net"; + const char* REQ_TYPE_FUNC = "func"; const char* REQ_TYPE_SEC = "sec"; @@ -179,7 +179,7 @@ namespace clan reqType = ClanRequestType::SEC; } - std::string host = opType == ClanManagerOperationType::VIEW ? HOST_VIEW : HOST_UPDATE; + std::string host = g_cfg_clans.get_host(); std::string url = std::format("https://{}/clan_manager_{}/{}/{}", host, opType, reqType, action); std::ostringstream oss; @@ -193,8 +193,6 @@ namespace clan std::vector response_buffer; curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_buffer); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, err_buf); From 53cd84300d1f4ff2e82516bed5b6bf25c260cabf Mon Sep 17 00:00:00 2001 From: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com> Date: Sun, 7 Dec 2025 18:43:42 +0100 Subject: [PATCH 04/30] Clans: HTTP/S support Signed-off-by: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com> --- rpcs3/Emu/NP/clans_client.cpp | 7 ++++--- rpcs3/Emu/NP/clans_config.cpp | 10 ++++++++++ rpcs3/Emu/NP/clans_config.h | 3 +++ rpcs3/rpcs3qt/clans_settings_dialog.cpp | 15 +++++++++++++++ rpcs3/rpcs3qt/clans_settings_dialog.h | 1 + 5 files changed, 33 insertions(+), 3 deletions(-) diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index 8291c23c77..11ecf382ce 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -180,7 +180,8 @@ namespace clan } std::string host = g_cfg_clans.get_host(); - std::string url = std::format("https://{}/clan_manager_{}/{}/{}", host, opType, reqType, action); + std::string protocol = g_cfg_clans.get_use_https() ? "https" : "http"; + std::string url = std::format("{}://{}/clan_manager_{}/{}/{}", protocol, host, opType, reqType, action); std::ostringstream oss; xmlBody->save(oss, "\t", 8U); @@ -969,8 +970,8 @@ namespace clan }; - strncpy(entry.message.subject, subject_str.c_str(), subject_str.size()); - strncpy(entry.message.body, msg_str.c_str(), msg_str.size()); + snprintf(entry.message.subject, sizeof(entry.message.subject), "%s", subject_str.c_str()); + snprintf(entry.message.body, sizeof(entry.message.body), "%s", msg_str.c_str()); announcements[i] = entry; i++; diff --git a/rpcs3/Emu/NP/clans_config.cpp b/rpcs3/Emu/NP/clans_config.cpp index 86b062fa5f..c13d225d82 100644 --- a/rpcs3/Emu/NP/clans_config.cpp +++ b/rpcs3/Emu/NP/clans_config.cpp @@ -51,6 +51,11 @@ std::string cfg_clans::get_host() const return host.to_string(); } +bool cfg_clans::get_use_https() const +{ + return use_https.get(); +} + std::vector> cfg_clans::get_hosts() { std::vector> vec_hosts; @@ -82,6 +87,11 @@ void cfg_clans::set_host(std::string_view host) this->host.from_string(host); } +void cfg_clans::set_use_https(bool use_https) +{ + this->use_https.set(use_https); +} + void cfg_clans::set_hosts(const std::vector>& vec_hosts) { std::string final_string; diff --git a/rpcs3/Emu/NP/clans_config.h b/rpcs3/Emu/NP/clans_config.h index 4a9f09d06b..96dcde497d 100644 --- a/rpcs3/Emu/NP/clans_config.h +++ b/rpcs3/Emu/NP/clans_config.h @@ -7,14 +7,17 @@ struct cfg_clans : cfg::node cfg::uint32 version{this, "Version", 1}; cfg::string host{this, "Host", "clans.rpcs3.net"}; cfg::string hosts{this, "Hosts", "Official Clans Server|clans.rpcs3.net"}; + cfg::_bool use_https{this, "Use HTTPS", true}; void load(); void save() const; std::string get_host() const; + bool get_use_https() const; std::vector> get_hosts(); void set_host(std::string_view host); + void set_use_https(bool use_https); bool add_host(std::string_view description, std::string_view host); bool del_host(std::string_view description, std::string_view host); diff --git a/rpcs3/rpcs3qt/clans_settings_dialog.cpp b/rpcs3/rpcs3qt/clans_settings_dialog.cpp index ff2113297f..891ede4ded 100644 --- a/rpcs3/rpcs3qt/clans_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/clans_settings_dialog.cpp @@ -25,11 +25,17 @@ clans_settings_dialog::clans_settings_dialog(QWidget* parent) QHBoxLayout* hbox_lbl_combo = new QHBoxLayout(); QLabel* lbl_server = new QLabel(tr("Server:")); cbx_servers = new QComboBox(); + cbx_protocol = new QComboBox(); + + cbx_protocol->addItem("HTTPS"); + cbx_protocol->addItem("HTTP"); + cbx_protocol->setCurrentIndex(g_cfg_clans.get_use_https() ? 0 : 1); refresh_combobox(); hbox_lbl_combo->addWidget(lbl_server); hbox_lbl_combo->addWidget(cbx_servers); + hbox_lbl_combo->addWidget(cbx_protocol); QHBoxLayout* hbox_buttons = new QHBoxLayout(); QPushButton* btn_add_server = new QPushButton(tr("Add")); @@ -60,6 +66,15 @@ clans_settings_dialog::clans_settings_dialog(QWidget* parent) g_cfg_clans.save(); }); + connect(cbx_protocol, &QComboBox::currentIndexChanged, this, [this](int index) + { + if (index < 0) + return; + + g_cfg_clans.set_use_https(index == 0); + g_cfg_clans.save(); + }); + connect(btn_add_server, &QAbstractButton::clicked, this, [this]() { clans_add_server_dialog dlg(this); diff --git a/rpcs3/rpcs3qt/clans_settings_dialog.h b/rpcs3/rpcs3qt/clans_settings_dialog.h index 7672dfaa14..59ee0185be 100644 --- a/rpcs3/rpcs3qt/clans_settings_dialog.h +++ b/rpcs3/rpcs3qt/clans_settings_dialog.h @@ -15,6 +15,7 @@ private: private: QComboBox* cbx_servers = nullptr; + QComboBox* cbx_protocol = nullptr; }; class clans_add_server_dialog : public QDialog From 8125d7f21a502935e34c5faa847f5eeae7870618 Mon Sep 17 00:00:00 2001 From: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com> Date: Sun, 7 Dec 2025 19:03:45 +0100 Subject: [PATCH 05/30] Clans: fix requests being unavailable Signed-off-by: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com> --- rpcs3/Emu/Cell/Modules/sceNpClans.cpp | 15 +++++++++++++++ rpcs3/Emu/NP/clans_client.cpp | 2 +- rpcs3/rpcs3qt/clans_settings_dialog.cpp | 7 +++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp index 51960f58bd..737e481745 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp @@ -1063,6 +1063,9 @@ error_code sceNpClansPostChallenge(vm::ptr handle, SceN return SCE_NP_CLANS_ERROR_EXCEEDS_MAX; } + vm::var req; + vm::write32(handle.addr(), req.addr()); + return CELL_OK; } @@ -1088,6 +1091,9 @@ error_code sceNpClansRetrievePostedChallenges(vm::ptr h } } + vm::var req; + vm::write32(handle.addr(), req.addr()); + return CELL_OK; } @@ -1100,6 +1106,9 @@ error_code sceNpClansRemovePostedChallenge(vm::ptr hand return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } + vm::var req; + vm::write32(handle.addr(), req.addr()); + return CELL_OK; } @@ -1125,6 +1134,9 @@ error_code sceNpClansRetrieveChallenges(vm::ptr handle, } } + vm::var req; + vm::write32(handle.addr(), req.addr()); + return CELL_OK; } @@ -1137,6 +1149,9 @@ error_code sceNpClansRemoveChallenge(SceNpClansRequestHandle handle, SceNpClanId return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } + vm::var req; + vm::write32(handle.addr(), req.addr()); + return CELL_OK; } diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index 11ecf382ce..ebf0dfe97c 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -143,7 +143,7 @@ namespace clan SceNpClansError clans_client::createRequest() { if (curl) - return SceNpClansError::SCE_NP_CLANS_ERROR_ALREADY_INITIALIZED; + return SceNpClansError::SCE_NP_CLANS_SUCCESS; curl = curl_easy_init(); if (!curl) diff --git a/rpcs3/rpcs3qt/clans_settings_dialog.cpp b/rpcs3/rpcs3qt/clans_settings_dialog.cpp index 891ede4ded..0e0a17240f 100644 --- a/rpcs3/rpcs3qt/clans_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/clans_settings_dialog.cpp @@ -8,10 +8,17 @@ #include "clans_settings_dialog.h" #include "Emu/NP/clans_config.h" +#include "Emu/System.h" clans_settings_dialog::clans_settings_dialog(QWidget* parent) : QDialog(parent) { + if (!Emu.IsStopped()) + { + QMessageBox::critical(this, tr("Error: Emulation Running"), tr("You need to stop the emulator before editing Clans connection information!"), QMessageBox::Ok); + return; + } + g_cfg_clans.load(); setWindowTitle(tr("Clans Configuration")); From ac036a05800904c1f3ab6ebbdef0976dea6e2e73 Mon Sep 17 00:00:00 2001 From: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com> Date: Sun, 7 Dec 2025 19:14:22 +0100 Subject: [PATCH 06/30] Clans: fix clans config not being auto-loaded Signed-off-by: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com> --- rpcs3/Emu/NP/clans_client.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index ebf0dfe97c..a557f0804c 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -132,6 +132,8 @@ namespace clan clans_client::clans_client() { + g_cfg_clans.load(); + createRequest(); } From 17eb5a5150cc4a71a4f29756dfe397e93f727ece Mon Sep 17 00:00:00 2001 From: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com> Date: Sun, 7 Dec 2025 19:43:29 +0100 Subject: [PATCH 07/30] Clans: fixed system constant back to correct value Signed-off-by: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com> --- rpcs3/Emu/Cell/Modules/sceNpClans.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/Modules/sceNpClans.h b/rpcs3/Emu/Cell/Modules/sceNpClans.h index 8e686bbeab..dfdeff2929 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpClans.h +++ b/rpcs3/Emu/Cell/Modules/sceNpClans.h @@ -99,7 +99,7 @@ enum SCE_NP_CLANS_ANNOUNCEMENT_MESSAGE_BODY_MAX_LENGTH = 1536, SCE_NP_CLANS_CLAN_BINARY_ATTRIBUTE1_MAX_SIZE = 190, SCE_NP_CLANS_CLAN_BINARY_DATA_MAX_SIZE = 10240, - SCE_NP_CLANS_MEMBER_BINARY_ATTRIBUTE1_MAX_SIZE = 15, + SCE_NP_CLANS_MEMBER_BINARY_ATTRIBUTE1_MAX_SIZE = 16, SCE_NP_CLANS_MEMBER_DESCRIPTION_MAX_LENGTH = 255, SCE_NP_CLANS_MEMBER_BINARY_DATA_MAX_SIZE = 1024, SCE_NP_CLANS_MESSAGE_BODY_MAX_LENGTH = 1536, From dccaf789a19e2e837aa1b01433616d1028ea46f2 Mon Sep 17 00:00:00 2001 From: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com> Date: Sun, 7 Dec 2025 19:49:01 +0100 Subject: [PATCH 08/30] Clans: fixed struct size Signed-off-by: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com> --- rpcs3/Emu/Cell/Modules/sceNpClans.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/Modules/sceNpClans.h b/rpcs3/Emu/Cell/Modules/sceNpClans.h index dfdeff2929..d5b6df415a 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpClans.h +++ b/rpcs3/Emu/Cell/Modules/sceNpClans.h @@ -236,7 +236,7 @@ struct SceNpClansUpdatableMemberInfo be_t fields; u8 binData1; be_t binData1Size; - u8 binAttr1[SCE_NP_CLANS_MEMBER_BINARY_ATTRIBUTE1_MAX_SIZE + 1]; + u8 binAttr1[SCE_NP_CLANS_MEMBER_BINARY_ATTRIBUTE1_MAX_SIZE]; char description[SCE_NP_CLANS_MEMBER_DESCRIPTION_MAX_LENGTH + 1]; b8 allowMsg; u8 reserved[3]; From 966fa1cab3b2b39b5cd239ee07c7512dd04fa05c Mon Sep 17 00:00:00 2001 From: zeph Date: Mon, 8 Dec 2025 02:59:05 +0100 Subject: [PATCH 09/30] Clans: code styling & safety fix-ups --- rpcs3/Emu/Cell/Modules/sceNpClans.cpp | 3 --- rpcs3/Emu/Cell/Modules/sceNpClans.h | 2 +- rpcs3/Emu/NP/clans_client.cpp | 34 +++++++++++++-------------- rpcs3/Emu/NP/clans_client.h | 2 +- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp index 737e481745..45e499a6a9 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp @@ -1063,9 +1063,6 @@ error_code sceNpClansPostChallenge(vm::ptr handle, SceN return SCE_NP_CLANS_ERROR_EXCEEDS_MAX; } - vm::var req; - vm::write32(handle.addr(), req.addr()); - return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/sceNpClans.h b/rpcs3/Emu/Cell/Modules/sceNpClans.h index d5b6df415a..6e4585ed43 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpClans.h +++ b/rpcs3/Emu/Cell/Modules/sceNpClans.h @@ -282,4 +282,4 @@ struct SceNpClansBlacklistEntry { SceNpId entry; SceNpId registeredBy; -}; \ No newline at end of file +}; diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index a557f0804c..50e57daddc 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -1,3 +1,4 @@ +#include "Utilities/StrUtil.h" #include "stdafx.h" #include "util/types.hpp" @@ -348,8 +349,8 @@ namespace clan .role = static_cast(role_int), .status = static_cast(status_int)}; - snprintf(entry.info.name, sizeof(entry.info.name), "%s", name_str.c_str()); - snprintf(entry.info.tag, sizeof(entry.info.tag), "%s", tag_str.c_str()); + strcpy_trunc(entry.info.name, name_str.c_str()); + strcpy_trunc(entry.info.tag, tag_str.c_str()); clanList[i] = entry; i++; @@ -394,9 +395,9 @@ namespace clan .description = "", }}; - snprintf(clanInfo->info.name, sizeof(clanInfo->info.name), "%s", name_str.c_str()); - snprintf(clanInfo->info.tag, sizeof(clanInfo->info.tag), "%s", tag_str.c_str()); - snprintf(clanInfo->updatable.description, sizeof(clanInfo->updatable.description), "%s", description_str.c_str()); + strcpy_trunc(clanInfo->info.name, name_str.c_str()); + strcpy_trunc(clanInfo->info.tag, tag_str.c_str()); + strcpy_trunc(clanInfo->updatable.description, description_str.c_str()); return SCE_NP_CLANS_SUCCESS; } @@ -451,7 +452,7 @@ namespace clan std::string description_str = description.text().as_string(); char description_char[256] = {0}; - snprintf(description_char, sizeof(description_char), "%s", description_str.c_str()); + strcpy_trunc(description_char, description_str.c_str()); *memInfo = SceNpClansMemberEntry { @@ -463,8 +464,8 @@ namespace clan } }; - snprintf(memInfo->npid.handle.data, sizeof(memInfo->npid.handle.data), "%s", username); - snprintf(memInfo->updatable.description, sizeof(memInfo->updatable.description), "%s", description_char); + strcpy_trunc(memInfo->npid.handle.data, username); + strcpy_trunc(memInfo->updatable.description, description_char); return SCE_NP_CLANS_SUCCESS; } @@ -521,7 +522,7 @@ namespace clan std::string description_str = info.child("description").text().as_string(); char description_char[256] = {0}; - snprintf(description_char, sizeof(description_char), "%s", description_str.c_str()); + strcpy_trunc(description_char, description_str.c_str()); SceNpClansMemberEntry entry = SceNpClansMemberEntry { @@ -530,7 +531,7 @@ namespace clan .status = static_cast(status_int), }; - snprintf(entry.updatable.description, sizeof(entry.updatable.description), "%s", description_char); + strcpy_trunc(entry.updatable.description, description_char); memList[i] = entry; i++; @@ -589,7 +590,7 @@ namespace clan } }; - snprintf(npid.handle.data, sizeof(npid.handle.data), "%s", username); + strcpy_trunc(npid.handle.data, username); SceNpClansBlacklistEntry entry = SceNpClansBlacklistEntry { @@ -687,8 +688,8 @@ namespace clan .reserved = {0, 0}, }; - snprintf(entry.name, sizeof(entry.name), "%s", name_str.c_str()); - snprintf(entry.tag, sizeof(entry.tag), "%s", tag_str.c_str()); + strcpy_trunc(entry.name, name_str.c_str()); + strcpy_trunc(entry.tag, tag_str.c_str()); clanList[i] = entry; i++; @@ -971,9 +972,8 @@ namespace clan .postedBy = clanId, }; - - snprintf(entry.message.subject, sizeof(entry.message.subject), "%s", subject_str.c_str()); - snprintf(entry.message.body, sizeof(entry.message.body), "%s", msg_str.c_str()); + strcpy_trunc(entry.message.subject, subject_str.c_str()); + strcpy_trunc(entry.message.body, msg_str.c_str()); announcements[i] = entry; i++; @@ -1033,4 +1033,4 @@ namespace clan return sendRequest(ClanRequestAction::DeleteAnnouncement, ClanManagerOperationType::UPDATE, &doc, &response); } } -#pragma endregion \ No newline at end of file +#pragma endregion diff --git a/rpcs3/Emu/NP/clans_client.h b/rpcs3/Emu/NP/clans_client.h index c488f6be8a..57aadad3b4 100644 --- a/rpcs3/Emu/NP/clans_client.h +++ b/rpcs3/Emu/NP/clans_client.h @@ -117,4 +117,4 @@ struct sce_np_clans_manager { atomic_t is_initialized = false; clan::clans_client* client; -}; \ No newline at end of file +}; From 04da8011452ac7f7ad00181e5e675ee25143291b Mon Sep 17 00:00:00 2001 From: zeph Date: Mon, 8 Dec 2025 03:34:12 +0100 Subject: [PATCH 10/30] Clans: added compile paths to MSVC targets --- rpcs3/Emu/Cell/Modules/sceNpClans.cpp | 17 +----- rpcs3/Emu/NP/clans_client.cpp | 85 ++++++++++++++------------- rpcs3/emucore.vcxproj | 2 + rpcs3/rpcs3.vcxproj | 7 +++ rpcs3/rpcs3.vcxproj.filters | 9 +++ 5 files changed, 62 insertions(+), 58 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp index 45e499a6a9..f8bcd44373 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp @@ -136,9 +136,6 @@ error_code sceNpClansCreateRequest(vm::ptr handle, u64 return SCE_NP_CLANS_ERROR_NOT_SUPPORTED; } - vm::var req; - vm::write32(handle.addr(), req.addr()); - auto& clans_manager = g_fxo->get(); SceNpClansError res = clans_manager.client->createRequest(); if (res != SCE_NP_CLANS_SUCCESS) @@ -1088,9 +1085,6 @@ error_code sceNpClansRetrievePostedChallenges(vm::ptr h } } - vm::var req; - vm::write32(handle.addr(), req.addr()); - return CELL_OK; } @@ -1103,9 +1097,6 @@ error_code sceNpClansRemovePostedChallenge(vm::ptr hand return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } - vm::var req; - vm::write32(handle.addr(), req.addr()); - return CELL_OK; } @@ -1131,9 +1122,6 @@ error_code sceNpClansRetrieveChallenges(vm::ptr handle, } } - vm::var req; - vm::write32(handle.addr(), req.addr()); - return CELL_OK; } @@ -1146,9 +1134,6 @@ error_code sceNpClansRemoveChallenge(SceNpClansRequestHandle handle, SceNpClanId return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } - vm::var req; - vm::write32(handle.addr(), req.addr()); - return CELL_OK; } @@ -1193,4 +1178,4 @@ DECLARE(ppu_module_manager::sceNpClans)("sceNpClans", []() REG_FUNC(sceNpClans, sceNpClansRemovePostedChallenge); REG_FUNC(sceNpClans, sceNpClansRetrieveChallenges); REG_FUNC(sceNpClans, sceNpClansRemoveChallenge); -}); \ No newline at end of file +}); diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index 50e57daddc..96591eb006 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -30,8 +30,8 @@ namespace std string_view name = "unknown"; switch (value) { - case clan::ClanRequestType::FUNC: name = "func"; break; - case clan::ClanRequestType::SEC: name = "sec"; break; + case clan::ClanRequestType::FUNC: name = "func"; break; + case clan::ClanRequestType::SEC: name = "sec"; break; } return formatter::format(string(name), ctx); } @@ -45,8 +45,8 @@ namespace std string_view name = "unknown"; switch (value) { - case clan::ClanManagerOperationType::VIEW: name = "view"; break; - case clan::ClanManagerOperationType::UPDATE: name = "update"; break; + case clan::ClanManagerOperationType::VIEW: name = "view"; break; + case clan::ClanManagerOperationType::UPDATE: name = "update"; break; } return formatter::format(string(name), ctx); } @@ -60,13 +60,13 @@ namespace std string_view name = "unknown"; switch (value) { - case clan::ClanSearchFilterOperator::Equal: name = "eq"; break; - case clan::ClanSearchFilterOperator::NotEqual: name = "ne"; break; - case clan::ClanSearchFilterOperator::GreaterThan: name = "gt"; break; - case clan::ClanSearchFilterOperator::GreaterThanOrEqual: name = "ge"; break; - case clan::ClanSearchFilterOperator::LessThan: name = "lt"; break; - case clan::ClanSearchFilterOperator::LessThanOrEqual: name = "le"; break; - case clan::ClanSearchFilterOperator::Like: name = "lk"; break; + case clan::ClanSearchFilterOperator::Equal: name = "eq"; break; + case clan::ClanSearchFilterOperator::NotEqual: name = "ne"; break; + case clan::ClanSearchFilterOperator::GreaterThan: name = "gt"; break; + case clan::ClanSearchFilterOperator::GreaterThanOrEqual: name = "ge"; break; + case clan::ClanSearchFilterOperator::LessThan: name = "lt"; break; + case clan::ClanSearchFilterOperator::LessThanOrEqual: name = "le"; break; + case clan::ClanSearchFilterOperator::Like: name = "lk"; break; } return formatter::format(string(name), ctx); } @@ -80,31 +80,31 @@ namespace std string_view name = "unknown"; switch (value) { - case clan::ClanRequestAction::GetClanList: name = "get_clan_list"; break; - case clan::ClanRequestAction::GetClanInfo: name = "get_clan_info"; break; - case clan::ClanRequestAction::GetMemberInfo: name = "get_member_info"; break; - case clan::ClanRequestAction::GetMemberList: name = "get_member_list"; break; - case clan::ClanRequestAction::GetBlacklist: name = "get_blacklist"; break; - case clan::ClanRequestAction::RecordBlacklistEntry: name = "record_blacklist_entry"; break; - case clan::ClanRequestAction::DeleteBlacklistEntry: name = "delete_blacklist_entry"; break; - case clan::ClanRequestAction::ClanSearch: name = "clan_search"; break; - case clan::ClanRequestAction::RequestMembership: name = "request_membership"; break; - case clan::ClanRequestAction::CancelRequestMembership: name = "cancel_request_membership"; break; - case clan::ClanRequestAction::AcceptMembershipRequest: name = "accept_membership_request"; break; - case clan::ClanRequestAction::DeclineMembershipRequest: name = "decline_membership_request"; break; - case clan::ClanRequestAction::SendInvitation: name = "send_invitation"; break; - case clan::ClanRequestAction::CancelInvitation: name = "cancel_invitation"; break; - case clan::ClanRequestAction::AcceptInvitation: name = "accept_invitation"; break; - case clan::ClanRequestAction::DeclineInvitation: name = "decline_invitation"; break; - case clan::ClanRequestAction::UpdateMemberInfo: name = "update_member_info"; break; - case clan::ClanRequestAction::UpdateClanInfo: name = "update_clan_info"; break; - case clan::ClanRequestAction::JoinClan: name = "join_clan"; break; - case clan::ClanRequestAction::LeaveClan: name = "leave_clan"; break; - case clan::ClanRequestAction::KickMember: name = "kick_member"; break; - case clan::ClanRequestAction::ChangeMemberRole: name = "change_member_role"; break; - case clan::ClanRequestAction::RetrieveAnnouncements: name = "retrieve_announcements"; break; - case clan::ClanRequestAction::PostAnnouncement: name = "post_announcement"; break; - case clan::ClanRequestAction::DeleteAnnouncement: name = "delete_announcement"; break; + case clan::ClanRequestAction::GetClanList: name = "get_clan_list"; break; + case clan::ClanRequestAction::GetClanInfo: name = "get_clan_info"; break; + case clan::ClanRequestAction::GetMemberInfo: name = "get_member_info"; break; + case clan::ClanRequestAction::GetMemberList: name = "get_member_list"; break; + case clan::ClanRequestAction::GetBlacklist: name = "get_blacklist"; break; + case clan::ClanRequestAction::RecordBlacklistEntry: name = "record_blacklist_entry"; break; + case clan::ClanRequestAction::DeleteBlacklistEntry: name = "delete_blacklist_entry"; break; + case clan::ClanRequestAction::ClanSearch: name = "clan_search"; break; + case clan::ClanRequestAction::RequestMembership: name = "request_membership"; break; + case clan::ClanRequestAction::CancelRequestMembership: name = "cancel_request_membership"; break; + case clan::ClanRequestAction::AcceptMembershipRequest: name = "accept_membership_request"; break; + case clan::ClanRequestAction::DeclineMembershipRequest: name = "decline_membership_request"; break; + case clan::ClanRequestAction::SendInvitation: name = "send_invitation"; break; + case clan::ClanRequestAction::CancelInvitation: name = "cancel_invitation"; break; + case clan::ClanRequestAction::AcceptInvitation: name = "accept_invitation"; break; + case clan::ClanRequestAction::DeclineInvitation: name = "decline_invitation"; break; + case clan::ClanRequestAction::UpdateMemberInfo: name = "update_member_info"; break; + case clan::ClanRequestAction::UpdateClanInfo: name = "update_clan_info"; break; + case clan::ClanRequestAction::JoinClan: name = "join_clan"; break; + case clan::ClanRequestAction::LeaveClan: name = "leave_clan"; break; + case clan::ClanRequestAction::KickMember: name = "kick_member"; break; + case clan::ClanRequestAction::ChangeMemberRole: name = "change_member_role"; break; + case clan::ClanRequestAction::RetrieveAnnouncements: name = "retrieve_announcements"; break; + case clan::ClanRequestAction::PostAnnouncement: name = "post_announcement"; break; + case clan::ClanRequestAction::DeleteAnnouncement: name = "delete_announcement"; break; } return formatter::format(string(name), ctx); } @@ -115,8 +115,8 @@ namespace clan { struct curl_memory { - char* response; - size_t size; + char* response; + size_t size; }; size_t clans_client::curlWriteCallback(void* data, size_t size, size_t nmemb, void* clientp) @@ -241,7 +241,8 @@ namespace clan std::string clans_client::getClanTicket(np::np_handler& nph) { - if (nph.get_ticket().size() > 0) { + if (nph.get_ticket().size() > 0) + { std::vector ticket_bytes(1024); uint32_t ticket_size = UINT32_MAX; @@ -249,7 +250,7 @@ namespace clan return std::string(reinterpret_cast(ticket_bytes.data()), ticket_size); } - const auto& npid = nph.get_npid(); + const auto& npid = nph.get_npid(); const char* service_id = CLANS_SERVICE_ID; const unsigned char* cookie = nullptr; @@ -345,11 +346,11 @@ namespace clan .name = "", .tag = "", .reserved = {0, 0}, - }, + }, .role = static_cast(role_int), .status = static_cast(status_int)}; - strcpy_trunc(entry.info.name, name_str.c_str()); + strcpy_trunc(entry.info.name, name_str.c_str()); strcpy_trunc(entry.info.tag, tag_str.c_str()); clanList[i] = entry; diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 1b5716f01b..4203d30b5f 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -111,6 +111,7 @@ + @@ -184,6 +185,7 @@ + diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index b5ffb8ebd8..eaecb47cb3 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -406,6 +406,9 @@ true + + true + true @@ -697,6 +700,9 @@ true + + true + true @@ -807,6 +813,7 @@ + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index aa90e50cd0..2440ee1523 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -864,6 +864,12 @@ Generated Files\Release + + Generated Files\Debug + + + Generated Files\Release + Generated Files\Debug @@ -909,6 +915,9 @@ Gui\rpcn + + Gui\clans + Gui\message dialog From a9b124f9c6db132dbee10079a22409630034869c Mon Sep 17 00:00:00 2001 From: zeph Date: Mon, 8 Dec 2025 03:49:56 +0100 Subject: [PATCH 11/30] Clans: make instance a shared_ptr --- rpcs3/Emu/Cell/Modules/sceNpClans.cpp | 5 +++-- rpcs3/Emu/NP/clans_client.cpp | 22 +++++++++++----------- rpcs3/Emu/NP/clans_client.h | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp index f8bcd44373..3e6ad94663 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp @@ -5,6 +5,7 @@ #include "Emu/NP/clans_client.h" #include "sceNp.h" +#include #include "sceNpClans.h" @@ -97,7 +98,7 @@ error_code sceNpClansInit(vm::cptr commId, vm::cptr client = std::make_shared(); clans_manager.client = client; clans_manager.is_initialized = true; @@ -113,7 +114,7 @@ error_code sceNpClansTerm() return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } - delete clans_manager.client; + clans_manager.client.reset(); clans_manager.is_initialized = false; return CELL_OK; diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index 96591eb006..cb78c4aa46 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -350,8 +350,8 @@ namespace clan .role = static_cast(role_int), .status = static_cast(status_int)}; - strcpy_trunc(entry.info.name, name_str.c_str()); - strcpy_trunc(entry.info.tag, tag_str.c_str()); + strcpy_trunc(entry.info.name, name_str); + strcpy_trunc(entry.info.tag, tag_str); clanList[i] = entry; i++; @@ -396,9 +396,9 @@ namespace clan .description = "", }}; - strcpy_trunc(clanInfo->info.name, name_str.c_str()); - strcpy_trunc(clanInfo->info.tag, tag_str.c_str()); - strcpy_trunc(clanInfo->updatable.description, description_str.c_str()); + strcpy_trunc(clanInfo->info.name, name_str); + strcpy_trunc(clanInfo->info.tag, tag_str); + strcpy_trunc(clanInfo->updatable.description, description_str); return SCE_NP_CLANS_SUCCESS; } @@ -453,7 +453,7 @@ namespace clan std::string description_str = description.text().as_string(); char description_char[256] = {0}; - strcpy_trunc(description_char, description_str.c_str()); + strcpy_trunc(description_char, description_str); *memInfo = SceNpClansMemberEntry { @@ -523,7 +523,7 @@ namespace clan std::string description_str = info.child("description").text().as_string(); char description_char[256] = {0}; - strcpy_trunc(description_char, description_str.c_str()); + strcpy_trunc(description_char, description_str); SceNpClansMemberEntry entry = SceNpClansMemberEntry { @@ -689,8 +689,8 @@ namespace clan .reserved = {0, 0}, }; - strcpy_trunc(entry.name, name_str.c_str()); - strcpy_trunc(entry.tag, tag_str.c_str()); + strcpy_trunc(entry.name, name_str); + strcpy_trunc(entry.tag, tag_str); clanList[i] = entry; i++; @@ -973,8 +973,8 @@ namespace clan .postedBy = clanId, }; - strcpy_trunc(entry.message.subject, subject_str.c_str()); - strcpy_trunc(entry.message.body, msg_str.c_str()); + strcpy_trunc(entry.message.subject, subject_str); + strcpy_trunc(entry.message.body, msg_str); announcements[i] = entry; i++; diff --git a/rpcs3/Emu/NP/clans_client.h b/rpcs3/Emu/NP/clans_client.h index 57aadad3b4..1d4b8b06c0 100644 --- a/rpcs3/Emu/NP/clans_client.h +++ b/rpcs3/Emu/NP/clans_client.h @@ -116,5 +116,5 @@ namespace clan struct sce_np_clans_manager { atomic_t is_initialized = false; - clan::clans_client* client; + std::shared_ptr client; }; From ae34ee9c71bc5395ab6d1ffa277dfc63fc6763c7 Mon Sep 17 00:00:00 2001 From: zeph Date: Mon, 8 Dec 2025 04:29:07 +0100 Subject: [PATCH 12/30] Clans: handle cases when paging is null Apparently, when paging is null we're supposed to return the total count from the server. The game will only access `pageResult->total`. --- rpcs3/Emu/Cell/Modules/sceNpClans.cpp | 82 +++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 10 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp index 3e6ad94663..cd42fddecd 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp @@ -144,6 +144,11 @@ error_code sceNpClansCreateRequest(vm::ptr handle, u64 return res; } + // TODO: replace this mock request with an actual + // generator that yields an actual request. + vm::var req; + vm::write32(handle.addr(), req.addr()); + return CELL_OK; } @@ -241,7 +246,15 @@ error_code sceNpClansGetClanList(vm::ptr handle, vm::cp auto& clans_manager = g_fxo->get(); SceNpClansPagingRequest host_paging = {}; - std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); + if (paging) + { + std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); + } + else + { + host_paging.startPos = 0; + host_paging.max = 0; + } SceNpClansEntry host_clanList[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; SceNpClansPagingResult host_pageResult = {}; @@ -252,7 +265,10 @@ error_code sceNpClansGetClanList(vm::ptr handle, vm::cp return ret; } - std::memcpy(clanList.get_ptr(), host_clanList, sizeof(SceNpClansEntry) * host_pageResult.count); + if (clanList && host_pageResult.count > 0) + { + std::memcpy(clanList.get_ptr(), host_clanList, sizeof(SceNpClansEntry) * host_pageResult.count); + } std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult)); return CELL_OK; @@ -333,7 +349,15 @@ error_code sceNpClansSearchByName(vm::ptr handle, vm::c auto& clans_manager = g_fxo->get(); SceNpClansPagingRequest host_paging = {}; - std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); + if (paging) + { + std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); + } + else + { + host_paging.startPos = 0; + host_paging.max = 0; + } SceNpClansSearchableName host_search = {}; std::memcpy(&host_search, search.get_ptr(), sizeof(SceNpClansSearchableName)); @@ -347,7 +371,10 @@ error_code sceNpClansSearchByName(vm::ptr handle, vm::c return ret; } - std::memcpy(results.get_ptr(), host_results, sizeof(SceNpClansClanBasicInfo) * host_pageResult.count); + if (results && host_pageResult.count > 0) + { + std::memcpy(results.get_ptr(), host_results, sizeof(SceNpClansClanBasicInfo) * host_pageResult.count); + } std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult)); return CELL_OK; @@ -433,7 +460,15 @@ error_code sceNpClansGetMemberList(vm::ptr handle, SceN auto& clans_manager = g_fxo->get(); SceNpClansPagingRequest host_paging = {}; - std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); + if (paging) + { + std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); + } + else + { + host_paging.startPos = 0; + host_paging.max = 0; + } SceNpClansMemberEntry* host_memList_addr = new SceNpClansMemberEntry[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX]; SceNpClansPagingResult host_pageResult = {}; @@ -441,12 +476,17 @@ error_code sceNpClansGetMemberList(vm::ptr handle, SceN SceNpClansError ret = clans_manager.client->getMemberList(nph, clanId, &host_paging, status, host_memList_addr, &host_pageResult); if (ret != SCE_NP_CLANS_SUCCESS) { + delete[] host_memList_addr; return ret; } - std::memcpy(memList.get_ptr(), host_memList_addr, sizeof(SceNpClansMemberEntry) * host_pageResult.count); + if (memList && host_pageResult.count > 0) + { + std::memcpy(memList.get_ptr(), host_memList_addr, sizeof(SceNpClansMemberEntry) * host_pageResult.count); + } std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult)); + delete[] host_memList_addr; return CELL_OK; } @@ -864,7 +904,15 @@ error_code sceNpClansGetBlacklist(vm::ptr handle, SceNp auto& clans_manager = g_fxo->get(); SceNpClansPagingRequest host_paging = {}; - std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); + if (paging) + { + std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); + } + else + { + host_paging.startPos = 0; + host_paging.max = 0; + } SceNpClansBlacklistEntry host_bl[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; SceNpClansPagingResult host_pageResult = {}; @@ -875,7 +923,10 @@ error_code sceNpClansGetBlacklist(vm::ptr handle, SceNp return ret; } - std::memcpy(bl.get_ptr(), host_bl, sizeof(SceNpClansBlacklistEntry) * host_pageResult.count); + if (bl && host_pageResult.count > 0) + { + std::memcpy(bl.get_ptr(), host_bl, sizeof(SceNpClansBlacklistEntry) * host_pageResult.count); + } std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult)); return CELL_OK; @@ -959,7 +1010,15 @@ error_code sceNpClansRetrieveAnnouncements(vm::ptr hand auto& nph = g_fxo->get>(); SceNpClansPagingRequest host_paging = {}; - std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); + if (paging) + { + std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); + } + else + { + host_paging.startPos = 0; + host_paging.max = 0; + } SceNpClansMessageEntry host_mlist[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; SceNpClansPagingResult host_pageResult = {}; @@ -970,7 +1029,10 @@ error_code sceNpClansRetrieveAnnouncements(vm::ptr hand return ret; } - std::memcpy(mlist.get_ptr(), host_mlist, sizeof(SceNpClansMessageEntry) * host_pageResult.count); + if (mlist && host_pageResult.count > 0) + { + std::memcpy(mlist.get_ptr(), host_mlist, sizeof(SceNpClansMessageEntry) * host_pageResult.count); + } std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult)); return CELL_OK; From 7378657c4b9a50a99ca643484712b73f353d069f Mon Sep 17 00:00:00 2001 From: zeph Date: Mon, 8 Dec 2025 13:10:14 +0100 Subject: [PATCH 13/30] Clans: fmt::format instead of std::format - Renamed some fields - Implemented fmt::format for consistency - Made some constant fields `const` --- rpcs3/Emu/NP/clans_client.cpp | 180 ++++++++++++------------ rpcs3/Emu/NP/clans_config.cpp | 8 +- rpcs3/rpcs3qt/clans_settings_dialog.cpp | 32 ++--- rpcs3/rpcs3qt/clans_settings_dialog.h | 4 +- 4 files changed, 112 insertions(+), 112 deletions(-) diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index cb78c4aa46..c4a3099964 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -1,114 +1,112 @@ -#include "Utilities/StrUtil.h" #include "stdafx.h" -#include "util/types.hpp" -#include -#include -#include -#include +#include #include -LOG_CHANNEL(clan_log, "clans"); +#include +#include +#include +#include +#include + +LOG_CHANNEL(clan_log, "clans"); const char* REQ_TYPE_FUNC = "func"; const char* REQ_TYPE_SEC = "sec"; -constexpr const char* JID_FORMAT = "%s@un.br.np.playstation.net"; +constexpr const char JID_FORMAT[] = "%s@un.br.np.playstation.net"; const char* CLANS_SERVICE_ID = "IV0001-NPXS01001_00"; const char* CLANS_ENTITLEMENT_ID = "NPWR00432_00"; -namespace std +template <> +void fmt_class_string::format(std::string& out, u64 arg) { - template <> - struct formatter : formatter - { - auto format(clan::ClanRequestType value, format_context& ctx) const + format_enum(out, arg, [](auto value) { - string_view name = "unknown"; switch (value) { - case clan::ClanRequestType::FUNC: name = "func"; break; - case clan::ClanRequestType::SEC: name = "sec"; break; + case clan::ClanRequestType::FUNC: return "func"; + case clan::ClanRequestType::SEC: return "sec"; } - return formatter::format(string(name), ctx); - } - }; - template <> - struct formatter : formatter - { - auto format(clan::ClanManagerOperationType value, format_context& ctx) const - { - string_view name = "unknown"; - switch (value) - { - case clan::ClanManagerOperationType::VIEW: name = "view"; break; - case clan::ClanManagerOperationType::UPDATE: name = "update"; break; - } - return formatter::format(string(name), ctx); - } - }; + return unknown; + }); +} - template <> - struct formatter : formatter - { - auto format(clan::ClanSearchFilterOperator value, format_context& ctx) const +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](auto value) { - string_view name = "unknown"; switch (value) { - case clan::ClanSearchFilterOperator::Equal: name = "eq"; break; - case clan::ClanSearchFilterOperator::NotEqual: name = "ne"; break; - case clan::ClanSearchFilterOperator::GreaterThan: name = "gt"; break; - case clan::ClanSearchFilterOperator::GreaterThanOrEqual: name = "ge"; break; - case clan::ClanSearchFilterOperator::LessThan: name = "lt"; break; - case clan::ClanSearchFilterOperator::LessThanOrEqual: name = "le"; break; - case clan::ClanSearchFilterOperator::Like: name = "lk"; break; + case clan::ClanManagerOperationType::VIEW: return "view"; + case clan::ClanManagerOperationType::UPDATE: return "update"; } - return formatter::format(string(name), ctx); - } - }; - template <> - struct formatter : formatter - { - auto format(clan::ClanRequestAction value, format_context& ctx) const + return unknown; + }); +} + +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](auto value) { - string_view name = "unknown"; switch (value) { - case clan::ClanRequestAction::GetClanList: name = "get_clan_list"; break; - case clan::ClanRequestAction::GetClanInfo: name = "get_clan_info"; break; - case clan::ClanRequestAction::GetMemberInfo: name = "get_member_info"; break; - case clan::ClanRequestAction::GetMemberList: name = "get_member_list"; break; - case clan::ClanRequestAction::GetBlacklist: name = "get_blacklist"; break; - case clan::ClanRequestAction::RecordBlacklistEntry: name = "record_blacklist_entry"; break; - case clan::ClanRequestAction::DeleteBlacklistEntry: name = "delete_blacklist_entry"; break; - case clan::ClanRequestAction::ClanSearch: name = "clan_search"; break; - case clan::ClanRequestAction::RequestMembership: name = "request_membership"; break; - case clan::ClanRequestAction::CancelRequestMembership: name = "cancel_request_membership"; break; - case clan::ClanRequestAction::AcceptMembershipRequest: name = "accept_membership_request"; break; - case clan::ClanRequestAction::DeclineMembershipRequest: name = "decline_membership_request"; break; - case clan::ClanRequestAction::SendInvitation: name = "send_invitation"; break; - case clan::ClanRequestAction::CancelInvitation: name = "cancel_invitation"; break; - case clan::ClanRequestAction::AcceptInvitation: name = "accept_invitation"; break; - case clan::ClanRequestAction::DeclineInvitation: name = "decline_invitation"; break; - case clan::ClanRequestAction::UpdateMemberInfo: name = "update_member_info"; break; - case clan::ClanRequestAction::UpdateClanInfo: name = "update_clan_info"; break; - case clan::ClanRequestAction::JoinClan: name = "join_clan"; break; - case clan::ClanRequestAction::LeaveClan: name = "leave_clan"; break; - case clan::ClanRequestAction::KickMember: name = "kick_member"; break; - case clan::ClanRequestAction::ChangeMemberRole: name = "change_member_role"; break; - case clan::ClanRequestAction::RetrieveAnnouncements: name = "retrieve_announcements"; break; - case clan::ClanRequestAction::PostAnnouncement: name = "post_announcement"; break; - case clan::ClanRequestAction::DeleteAnnouncement: name = "delete_announcement"; break; + case clan::ClanSearchFilterOperator::Equal: return "eq"; + case clan::ClanSearchFilterOperator::NotEqual: return "ne"; + case clan::ClanSearchFilterOperator::GreaterThan: return "gt"; + case clan::ClanSearchFilterOperator::GreaterThanOrEqual: return "ge"; + case clan::ClanSearchFilterOperator::LessThan: return "lt"; + case clan::ClanSearchFilterOperator::LessThanOrEqual: return "le"; + case clan::ClanSearchFilterOperator::Like: return "lk"; } - return formatter::format(string(name), ctx); - } - }; + + return unknown; + }); +} + +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](auto value) + { + switch (value) + { + case clan::ClanRequestAction::GetClanList: return "get_clan_list"; + case clan::ClanRequestAction::GetClanInfo: return "get_clan_info"; + case clan::ClanRequestAction::GetMemberInfo: return "get_member_info"; + case clan::ClanRequestAction::GetMemberList: return "get_member_list"; + case clan::ClanRequestAction::GetBlacklist: return "get_blacklist"; + case clan::ClanRequestAction::RecordBlacklistEntry: return "record_blacklist_entry"; + case clan::ClanRequestAction::DeleteBlacklistEntry: return "delete_blacklist_entry"; + case clan::ClanRequestAction::ClanSearch: return "clan_search"; + case clan::ClanRequestAction::RequestMembership: return "request_membership"; + case clan::ClanRequestAction::CancelRequestMembership: return "cancel_request_membership"; + case clan::ClanRequestAction::AcceptMembershipRequest: return "accept_membership_request"; + case clan::ClanRequestAction::DeclineMembershipRequest: return "decline_membership_request"; + case clan::ClanRequestAction::SendInvitation: return "send_invitation"; + case clan::ClanRequestAction::CancelInvitation: return "cancel_invitation"; + case clan::ClanRequestAction::AcceptInvitation: return "accept_invitation"; + case clan::ClanRequestAction::DeclineInvitation: return "decline_invitation"; + case clan::ClanRequestAction::UpdateMemberInfo: return "update_member_info"; + case clan::ClanRequestAction::UpdateClanInfo: return "update_clan_info"; + case clan::ClanRequestAction::JoinClan: return "join_clan"; + case clan::ClanRequestAction::LeaveClan: return "leave_clan"; + case clan::ClanRequestAction::KickMember: return "kick_member"; + case clan::ClanRequestAction::ChangeMemberRole: return "change_member_role"; + case clan::ClanRequestAction::RetrieveAnnouncements: return "retrieve_announcements"; + case clan::ClanRequestAction::PostAnnouncement: return "post_announcement"; + case clan::ClanRequestAction::DeleteAnnouncement: return "delete_announcement"; + } + + return unknown; + }); } namespace clan @@ -184,7 +182,7 @@ namespace clan std::string host = g_cfg_clans.get_host(); std::string protocol = g_cfg_clans.get_use_https() ? "https" : "http"; - std::string url = std::format("{}://{}/clan_manager_{}/{}/{}", protocol, host, opType, reqType, action); + std::string url = fmt::format("%s://%s/clan_manager_%s/%s/%s", protocol, host, opType, reqType, action); std::ostringstream oss; xmlBody->save(oss, "\t", 8U); @@ -412,7 +410,7 @@ namespace clan clan.append_child("ticket").text().set(ticket.c_str()); clan.append_child("id").text().set(clanId); - std::string jid_str = std::format(JID_FORMAT, npId.handle.data); + std::string jid_str = fmt::format(JID_FORMAT, npId.handle.data); clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); @@ -620,7 +618,7 @@ namespace clan clan.append_child("ticket").text().set(ticket.c_str()); clan.append_child("id").text().set(clanId); - std::string jid_str = std::format(JID_FORMAT, npId.handle.data); + std::string jid_str = fmt::format(JID_FORMAT, npId.handle.data); clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); @@ -636,7 +634,7 @@ namespace clan clan.append_child("ticket").text().set(ticket.c_str()); clan.append_child("id").text().set(clanId); - std::string jid_str = std::format(JID_FORMAT, npId.handle.data); + std::string jid_str = fmt::format(JID_FORMAT, npId.handle.data); clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); @@ -653,7 +651,7 @@ namespace clan pugi::xml_node filter = clan.append_child("filter"); pugi::xml_node name = filter.append_child("name"); - std::string op_name = std::format("{}", static_cast(static_cast(search->nameSearchOp))); + std::string op_name = fmt::format("%s", static_cast(static_cast(search->nameSearchOp))); name.append_attribute("op").set_value(op_name.c_str()); name.append_attribute("value").set_value(search->name); @@ -740,7 +738,7 @@ namespace clan clan.append_child("ticket").text().set(ticket.c_str()); clan.append_child("id").text().set(clanId); - std::string jid_str = std::format(JID_FORMAT, npId.handle.data); + std::string jid_str = fmt::format(JID_FORMAT, npId.handle.data); clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); @@ -756,7 +754,7 @@ namespace clan clan.append_child("ticket").text().set(ticket.c_str()); clan.append_child("id").text().set(clanId); - std::string jid_str = std::format(JID_FORMAT, npId.handle.data); + std::string jid_str = fmt::format(JID_FORMAT, npId.handle.data); clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); @@ -772,7 +770,7 @@ namespace clan clan.append_child("ticket").text().set(ticket.c_str()); clan.append_child("id").text().set(clanId); - std::string jid_str = std::format(JID_FORMAT, npId.handle.data); + std::string jid_str = fmt::format(JID_FORMAT, npId.handle.data); clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); @@ -882,7 +880,7 @@ namespace clan clan.append_child("ticket").text().set(ticket.c_str()); clan.append_child("id").text().set(clanId); - std::string jid_str = std::format(JID_FORMAT, npId.handle.data); + std::string jid_str = fmt::format(JID_FORMAT, npId.handle.data); clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); @@ -898,7 +896,7 @@ namespace clan clan.append_child("ticket").text().set(ticket.c_str()); clan.append_child("id").text().set(clanId); - std::string jid_str = std::format(JID_FORMAT, npId.handle.data); + std::string jid_str = fmt::format(JID_FORMAT, npId.handle.data); clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_node roleNode = clan.append_child("role"); diff --git a/rpcs3/Emu/NP/clans_config.cpp b/rpcs3/Emu/NP/clans_config.cpp index c13d225d82..3eec6364fa 100644 --- a/rpcs3/Emu/NP/clans_config.cpp +++ b/rpcs3/Emu/NP/clans_config.cpp @@ -59,11 +59,11 @@ bool cfg_clans::get_use_https() const std::vector> cfg_clans::get_hosts() { std::vector> vec_hosts; - auto hosts_list = fmt::split(hosts.to_string(), {"|||"}); + const auto hosts_list = fmt::split(hosts.to_string(), {"|||"}); for (const auto& cur_host : hosts_list) { - auto desc_and_host = fmt::split(cur_host, {"|"}); + const auto desc_and_host = fmt::split(cur_host, {"|"}); if (desc_and_host.size() != 2) { clans_config_log.error("Invalid host in the list of hosts: %s", cur_host); @@ -129,7 +129,9 @@ bool cfg_clans::add_host(std::string_view new_description, std::string_view new_ bool cfg_clans::del_host(std::string_view del_description, std::string_view del_host) { // Do not delete default servers - if (del_description == "Official Clans Server" && del_host == "clans.rpcs3.net") + const auto def_desc_and_host = fmt::split(hosts.def, {"|"}); + ensure(def_desc_and_host.size() == 2); + if (del_description == def_desc_and_host[0] && del_host == def_desc_and_host[1]) { return true; } diff --git a/rpcs3/rpcs3qt/clans_settings_dialog.cpp b/rpcs3/rpcs3qt/clans_settings_dialog.cpp index 0e0a17240f..4a9ab3b2a0 100644 --- a/rpcs3/rpcs3qt/clans_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/clans_settings_dialog.cpp @@ -31,18 +31,18 @@ clans_settings_dialog::clans_settings_dialog(QWidget* parent) QHBoxLayout* hbox_lbl_combo = new QHBoxLayout(); QLabel* lbl_server = new QLabel(tr("Server:")); - cbx_servers = new QComboBox(); - cbx_protocol = new QComboBox(); + m_cbx_servers = new QComboBox(); + m_cbx_protocol = new QComboBox(); - cbx_protocol->addItem("HTTPS"); - cbx_protocol->addItem("HTTP"); - cbx_protocol->setCurrentIndex(g_cfg_clans.get_use_https() ? 0 : 1); + m_cbx_protocol->addItem("HTTPS"); + m_cbx_protocol->addItem("HTTP"); + m_cbx_protocol->setCurrentIndex(g_cfg_clans.get_use_https() ? 0 : 1); refresh_combobox(); hbox_lbl_combo->addWidget(lbl_server); - hbox_lbl_combo->addWidget(cbx_servers); - hbox_lbl_combo->addWidget(cbx_protocol); + hbox_lbl_combo->addWidget(m_cbx_servers); + hbox_lbl_combo->addWidget(m_cbx_protocol); QHBoxLayout* hbox_buttons = new QHBoxLayout(); QPushButton* btn_add_server = new QPushButton(tr("Add")); @@ -59,12 +59,12 @@ clans_settings_dialog::clans_settings_dialog(QWidget* parent) setLayout(vbox_global); - connect(cbx_servers, &QComboBox::currentIndexChanged, this, [this](int index) + connect(m_cbx_servers, &QComboBox::currentIndexChanged, this, [this](int index) { if (index < 0) return; - QVariant host = cbx_servers->itemData(index); + QVariant host = m_cbx_servers->itemData(index); if (!host.isValid() || !host.canConvert()) return; @@ -73,7 +73,7 @@ clans_settings_dialog::clans_settings_dialog(QWidget* parent) g_cfg_clans.save(); }); - connect(cbx_protocol, &QComboBox::currentIndexChanged, this, [this](int index) + connect(m_cbx_protocol, &QComboBox::currentIndexChanged, this, [this](int index) { if (index < 0) return; @@ -102,13 +102,13 @@ clans_settings_dialog::clans_settings_dialog(QWidget* parent) connect(btn_del_server, &QAbstractButton::clicked, this, [this]() { - const int index = cbx_servers->currentIndex(); + const int index = m_cbx_servers->currentIndex(); if (index < 0) return; - const std::string desc = cbx_servers->itemText(index).toStdString(); - const std::string host = cbx_servers->itemData(index).toString().toStdString(); + const std::string desc = m_cbx_servers->itemText(index).toStdString(); + const std::string host = m_cbx_servers->itemData(index).toString().toStdString(); if (g_cfg_clans.del_host(desc, host)) { @@ -129,18 +129,18 @@ void clans_settings_dialog::refresh_combobox() const auto cur_host = g_cfg_clans.get_host(); int i = 0, index = 0; - cbx_servers->clear(); + m_cbx_servers->clear(); for (const auto& [desc, host] : vec_hosts) { - cbx_servers->addItem(QString::fromStdString(desc), QString::fromStdString(host)); + m_cbx_servers->addItem(QString::fromStdString(desc), QString::fromStdString(host)); if (cur_host == host) index = i; i++; } - cbx_servers->setCurrentIndex(index); + m_cbx_servers->setCurrentIndex(index); } clans_add_server_dialog::clans_add_server_dialog(QWidget* parent) diff --git a/rpcs3/rpcs3qt/clans_settings_dialog.h b/rpcs3/rpcs3qt/clans_settings_dialog.h index 59ee0185be..7de6105382 100644 --- a/rpcs3/rpcs3qt/clans_settings_dialog.h +++ b/rpcs3/rpcs3qt/clans_settings_dialog.h @@ -14,8 +14,8 @@ private: void refresh_combobox(); private: - QComboBox* cbx_servers = nullptr; - QComboBox* cbx_protocol = nullptr; + QComboBox* m_cbx_servers = nullptr; + QComboBox* m_cbx_protocol = nullptr; }; class clans_add_server_dialog : public QDialog From fed72dcf907801b4efe885a1bfb9c8cb33534399 Mon Sep 17 00:00:00 2001 From: zeph Date: Mon, 8 Dec 2025 13:57:07 +0100 Subject: [PATCH 14/30] Clans: implemented IDM & ctx for requests Signed-off-by: zeph --- rpcs3/Emu/Cell/Modules/sceNpClans.cpp | 147 +++++++++++++++------- rpcs3/Emu/NP/clans_client.cpp | 172 ++++++++++++++++---------- rpcs3/Emu/NP/clans_client.h | 56 ++++----- 3 files changed, 233 insertions(+), 142 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp index cd42fddecd..1d0d6e84a4 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp @@ -138,16 +138,15 @@ error_code sceNpClansCreateRequest(vm::ptr handle, u64 } auto& clans_manager = g_fxo->get(); - SceNpClansError res = clans_manager.client->createRequest(); + + s32 reqId = 0; + SceNpClansError res = clans_manager.client->createRequest(&reqId); if (res != SCE_NP_CLANS_SUCCESS) { return res; } - // TODO: replace this mock request with an actual - // generator that yields an actual request. - vm::var req; - vm::write32(handle.addr(), req.addr()); + vm::write32(handle.addr(), static_cast(reqId)); return CELL_OK; } @@ -160,7 +159,10 @@ error_code sceNpClansDestroyRequest(vm::ptr handle) } auto& clans_manager = g_fxo->get(); - SceNpClansError res = clans_manager.client->destroyRequest(); + + s32 reqId = static_cast(handle.addr()); + + SceNpClansError res = clans_manager.client->destroyRequest(reqId); if (res != SCE_NP_CLANS_SUCCESS) { return res; @@ -177,7 +179,10 @@ error_code sceNpClansAbortRequest(vm::ptr handle) } auto& clans_manager = g_fxo->get(); - clans_manager.client->destroyRequest(); + + s32 reqId = static_cast(handle.addr()); + + clans_manager.client->destroyRequest(reqId); return CELL_OK; } @@ -259,7 +264,9 @@ error_code sceNpClansGetClanList(vm::ptr handle, vm::cp SceNpClansEntry host_clanList[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; SceNpClansPagingResult host_pageResult = {}; - SceNpClansError ret = clans_manager.client->getClanList(nph, &host_paging, host_clanList, &host_pageResult); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->getClanList(nph, reqId, &host_paging, host_clanList, &host_pageResult); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -365,7 +372,9 @@ error_code sceNpClansSearchByName(vm::ptr handle, vm::c SceNpClansClanBasicInfo host_results[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; SceNpClansPagingResult host_pageResult = {}; - SceNpClansError ret = clans_manager.client->clanSearch(&host_paging, &host_search, host_results, &host_pageResult); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->clanSearch(reqId, &host_paging, &host_search, host_results, &host_pageResult); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -397,7 +406,9 @@ error_code sceNpClansGetClanInfo(vm::ptr handle, SceNpC SceNpClansClanInfo host_info = {}; - SceNpClansError ret = clans_manager.client->getClanInfo(clanId, &host_info); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->getClanInfo(reqId, clanId, &host_info); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -427,7 +438,9 @@ error_code sceNpClansUpdateClanInfo(vm::ptr handle, Sce SceNpClansUpdatableClanInfo host_info = {}; std::memcpy(&host_info, info.get_ptr(), sizeof(SceNpClansUpdatableClanInfo)); - SceNpClansError ret = clans_manager.client->updateClanInfo(nph, clanId, &host_info); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->updateClanInfo(nph, reqId, clanId, &host_info); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -473,7 +486,9 @@ error_code sceNpClansGetMemberList(vm::ptr handle, SceN SceNpClansMemberEntry* host_memList_addr = new SceNpClansMemberEntry[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX]; SceNpClansPagingResult host_pageResult = {}; - SceNpClansError ret = clans_manager.client->getMemberList(nph, clanId, &host_paging, status, host_memList_addr, &host_pageResult); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->getMemberList(nph, reqId, clanId, &host_paging, status, host_memList_addr, &host_pageResult); if (ret != SCE_NP_CLANS_SUCCESS) { delete[] host_memList_addr; @@ -510,7 +525,9 @@ error_code sceNpClansGetMemberInfo(vm::ptr handle, SceN SceNpClansMemberEntry host_memInfo = {}; - SceNpClansError ret = clans_manager.client->getMemberInfo(nph, clanId, host_npid, &host_memInfo); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->getMemberInfo(nph, reqId, clanId, host_npid, &host_memInfo); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -539,7 +556,9 @@ error_code sceNpClansUpdateMemberInfo(vm::ptr handle, S SceNpClansUpdatableMemberInfo host_info = {}; std::memcpy(&host_info, info.get_ptr(), sizeof(SceNpClansUpdatableMemberInfo)); - SceNpClansError ret = clans_manager.client->updateMemberInfo(nph, clanId, &host_info); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->updateMemberInfo(nph, reqId, clanId, &host_info); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -566,7 +585,9 @@ error_code sceNpClansChangeMemberRole(vm::ptr handle, S SceNpId host_npid = {}; std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId)); - SceNpClansError ret = clans_manager.client->changeMemberRole(nph, clanId, host_npid, role); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->changeMemberRole(nph, reqId, clanId, host_npid, static_cast(role)); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -616,7 +637,9 @@ error_code sceNpClansJoinClan(vm::ptr handle, SceNpClan auto& nph = g_fxo->get>(); auto& clans_manager = g_fxo->get(); - SceNpClansError ret = clans_manager.client->joinClan(nph, clanId); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->joinClan(nph, reqId, clanId); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -635,7 +658,9 @@ error_code sceNpClansLeaveClan(vm::ptr handle, SceNpCla auto& nph = g_fxo->get>(); auto& clans_manager = g_fxo->get(); - SceNpClansError ret = clans_manager.client->leaveClan(nph, clanId); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->leaveClan(nph, reqId, clanId); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -676,7 +701,9 @@ error_code sceNpClansKickMember(vm::ptr handle, SceNpCl std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); } - SceNpClansError ret = clans_manager.client->kickMember(nph, clanId, host_npid, &host_message); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->kickMember(nph, reqId, clanId, host_npid, &host_message); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -717,7 +744,9 @@ error_code sceNpClansSendInvitation(vm::ptr handle, Sce std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); } - SceNpClansError ret = clans_manager.client->sendInvitation(nph, clanId, host_npid, &host_message); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->sendInvitation(nph, reqId, clanId, host_npid, &host_message); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -744,7 +773,9 @@ error_code sceNpClansCancelInvitation(vm::ptr handle, S SceNpId host_npid = {}; std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId)); - SceNpClansError ret = clans_manager.client->cancelInvitation(nph, clanId, host_npid); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->cancelInvitation(nph, reqId, clanId, host_npid); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -777,7 +808,14 @@ error_code sceNpClansSendInvitationResponse(vm::ptr han std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); } - SceNpClansError ret = clans_manager.client->sendInvitationResponse(nph, clanId, &host_message, accept); + if (message) + { + std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); + } + + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->sendInvitationResponse(nph, reqId, clanId, &host_message, accept); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -810,7 +848,9 @@ error_code sceNpClansSendMembershipRequest(vm::ptr hand std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); } - SceNpClansError ret = clans_manager.client->requestMembership(nph, clanId, &host_message); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->requestMembership(nph, reqId, clanId, &host_message); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -829,7 +869,9 @@ error_code sceNpClansCancelMembershipRequest(vm::ptr ha auto& nph = g_fxo->get>(); auto& clans_manager = g_fxo->get(); - SceNpClansError ret = clans_manager.client->cancelRequestMembership(nph, clanId); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->cancelRequestMembership(nph, reqId, clanId); if (ret != SCE_NP_CLANS_SUCCESS) { @@ -871,7 +913,9 @@ error_code sceNpClansSendMembershipResponse(vm::ptr han std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); } - SceNpClansError ret = clans_manager.client->sendMembershipResponse(nph, clanId, host_npid, &host_message, allow); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->sendMembershipResponse(nph, reqId, clanId, host_npid, &host_message, allow); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -914,10 +958,12 @@ error_code sceNpClansGetBlacklist(vm::ptr handle, SceNp host_paging.max = 0; } - SceNpClansBlacklistEntry host_bl[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; + SceNpClansBlacklistEntry host_blacklist[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; SceNpClansPagingResult host_pageResult = {}; - SceNpClansError ret = clans_manager.client->getBlacklist(nph, clanId, &host_paging, host_bl, &host_pageResult); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->getBlacklist(nph, reqId, clanId, &host_paging, host_blacklist, &host_pageResult); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -925,21 +971,21 @@ error_code sceNpClansGetBlacklist(vm::ptr handle, SceNp if (bl && host_pageResult.count > 0) { - std::memcpy(bl.get_ptr(), host_bl, sizeof(SceNpClansBlacklistEntry) * host_pageResult.count); + std::memcpy(bl.get_ptr(), host_blacklist, sizeof(SceNpClansBlacklistEntry) * host_pageResult.count); } std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult)); return CELL_OK; } -error_code sceNpClansAddBlacklistEntry(vm::ptr handle, SceNpClanId clanId, vm::cptr npid) +error_code sceNpClansAddBlacklistEntry(vm::ptr handle, SceNpClanId clanId, vm::cptr member) { if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } - if (!npid) + if (!member) { return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT; } @@ -947,10 +993,12 @@ error_code sceNpClansAddBlacklistEntry(vm::ptr handle, auto& nph = g_fxo->get>(); auto& clans_manager = g_fxo->get(); - SceNpId host_npid = {}; - std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId)); + SceNpId host_member = {}; + std::memcpy(&host_member, member.get_ptr(), sizeof(SceNpId)); - SceNpClansError ret = clans_manager.client->addBlacklistEntry(nph, clanId, host_npid); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->addBlacklistEntry(nph, reqId, clanId, host_member); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -959,14 +1007,14 @@ error_code sceNpClansAddBlacklistEntry(vm::ptr handle, return CELL_OK; } -error_code sceNpClansRemoveBlacklistEntry(vm::ptr handle, SceNpClanId clanId, vm::cptr npid) +error_code sceNpClansRemoveBlacklistEntry(vm::ptr handle, SceNpClanId clanId, vm::cptr member) { if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } - if (!npid) + if (!member) { return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT; } @@ -974,10 +1022,12 @@ error_code sceNpClansRemoveBlacklistEntry(vm::ptr handl auto& nph = g_fxo->get>(); auto& clans_manager = g_fxo->get(); - SceNpId host_npid = {}; - std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId)); + SceNpId host_member = {}; + std::memcpy(&host_member, member.get_ptr(), sizeof(SceNpId)); - SceNpClansError ret = clans_manager.client->removeBlacklistEntry(nph, clanId, host_npid); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->removeBlacklistEntry(nph, reqId, clanId, host_member); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -1020,10 +1070,12 @@ error_code sceNpClansRetrieveAnnouncements(vm::ptr hand host_paging.max = 0; } - SceNpClansMessageEntry host_mlist[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; + SceNpClansMessageEntry host_announcements[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; SceNpClansPagingResult host_pageResult = {}; - SceNpClansError ret = clans_manager.client->retrieveAnnouncements(nph, clanId, &host_paging, host_mlist, &host_pageResult); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->retrieveAnnouncements(nph, reqId, clanId, &host_paging, host_announcements, &host_pageResult); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -1031,7 +1083,7 @@ error_code sceNpClansRetrieveAnnouncements(vm::ptr hand if (mlist && host_pageResult.count > 0) { - std::memcpy(mlist.get_ptr(), host_mlist, sizeof(SceNpClansMessageEntry) * host_pageResult.count); + std::memcpy(mlist.get_ptr(), host_announcements, sizeof(SceNpClansMessageEntry) * host_pageResult.count); } std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult)); @@ -1058,8 +1110,8 @@ error_code sceNpClansPostAnnouncement(vm::ptr handle, S auto& clans_manager = g_fxo->get(); auto& nph = g_fxo->get>(); - SceNpClansMessage host_message = {}; - std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); + SceNpClansMessage host_announcement = {}; + std::memcpy(&host_announcement, message.get_ptr(), sizeof(SceNpClansMessage)); SceNpClansMessageData host_data = {}; if (data) @@ -1067,15 +1119,16 @@ error_code sceNpClansPostAnnouncement(vm::ptr handle, S std::memcpy(&host_data, data.get_ptr(), sizeof(SceNpClansMessageData)); } - SceNpClansMessageId host_mId = 0; + s32 reqId = static_cast(handle.addr()); - SceNpClansError ret = clans_manager.client->postAnnouncement(nph, clanId, &host_message, &host_data, duration, &host_mId); + SceNpClansMessageId host_announcementId = 0; + SceNpClansError ret = clans_manager.client->postAnnouncement(nph, reqId, clanId, &host_announcement, &host_data, duration, &host_announcementId); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; } - *mId = host_mId; + *mId = host_announcementId; return CELL_OK; } @@ -1090,7 +1143,9 @@ error_code sceNpClansRemoveAnnouncement(vm::ptr handle, auto& clans_manager = g_fxo->get(); auto& nph = g_fxo->get>(); - SceNpClansError ret = clans_manager.client->deleteAnnouncement(nph, clanId, mId); + s32 reqId = static_cast(handle.addr()); + + SceNpClansError ret = clans_manager.client->deleteAnnouncement(nph, reqId, clanId, mId); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index c4a3099964..c308483ac5 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -1,3 +1,4 @@ +#include "Emu/Cell/Modules/sceNpClans.h" #include "stdafx.h" #include @@ -129,50 +130,85 @@ namespace clan return realsize; } + struct clan_request_ctx + { + clan_request_ctx() + { + curl = curl_easy_init(); + if (curl) + { + curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); + } + } + + ~clan_request_ctx() + { + if (curl) + { + curl_easy_cleanup(curl); + curl = nullptr; + } + } + + CURL* curl = nullptr; + + // TODO: this was arbitrarily chosen -- see if there's a real amount + static const u32 SCE_NP_CLANS_MAX_CTX_NUM = 16; + + static const u32 id_base = 0xA001; + static const u32 id_step = 1; + static const u32 id_count = SCE_NP_CLANS_MAX_CTX_NUM; + SAVESTATE_INIT_POS(55); + }; + clans_client::clans_client() { g_cfg_clans.load(); - - createRequest(); } clans_client::~clans_client() { - destroyRequest(); + idm::clear(); } - SceNpClansError clans_client::createRequest() + SceNpClansError clans_client::createRequest(s32* reqId) { - if (curl) - return SceNpClansError::SCE_NP_CLANS_SUCCESS; + const s32 id = idm::make(); - curl = curl_easy_init(); - if (!curl) + if (id == id_manager::id_traits::invalid) { + return SceNpClansError::SCE_NP_CLANS_ERROR_EXCEEDS_MAX; + } + + auto ctx = idm::get_unlocked(id); + if (!ctx || !ctx->curl) + { + idm::remove(id); return SceNpClansError::SCE_NP_CLANS_ERROR_NOT_INITIALIZED; } - curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); + *reqId = id; return SceNpClansError::SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::destroyRequest() + SceNpClansError clans_client::destroyRequest(s32 reqId) { - if (curl) - { - curl_easy_cleanup(curl); - curl = nullptr; - } + if (idm::remove(reqId)) + return SceNpClansError::SCE_NP_CLANS_SUCCESS; - return SceNpClansError::SCE_NP_CLANS_SUCCESS; + return SceNpClansError::SCE_NP_CLANS_ERROR_INVALID_ARGUMENT; } - SceNpClansError clans_client::sendRequest(ClanRequestAction action, ClanManagerOperationType opType, pugi::xml_document* xmlBody, pugi::xml_document* outResponse) + SceNpClansError clans_client::sendRequest(s32 reqId, ClanRequestAction action, ClanManagerOperationType opType, pugi::xml_document* xmlBody, pugi::xml_document* outResponse) { - if (!curl) + auto ctx = idm::get_unlocked(reqId); + + if (!ctx || !ctx->curl) return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; + CURL* curl = ctx->curl; + ClanRequestType reqType = ClanRequestType::FUNC; pugi::xml_node clan = xmlBody->child("clan"); if (clan && clan.child("ticket")) @@ -203,7 +239,7 @@ namespace clan curl_easy_setopt(curl, CURLOPT_POSTFIELDS, xml.c_str()); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, xml.size()); - res = curl_easy_perform(curl); + CURLcode res = curl_easy_perform(curl); if (res != CURLE_OK) { @@ -215,10 +251,10 @@ namespace clan response_buffer.push_back('\0'); - pugi::xml_parse_result res = outResponse->load_string(response_buffer.data()); - if (!res) + pugi::xml_parse_result xml_res = outResponse->load_string(response_buffer.data()); + if (!xml_res) { - clan_log.error("XML parsing failed: %s", res.description()); + clan_log.error("XML parsing failed: %s", xml_res.description()); return SCE_NP_CLANS_ERROR_BAD_RESPONSE; } @@ -288,7 +324,7 @@ namespace clan } #pragma region Outgoing API Requests - SceNpClansError clans_client::getClanList(np::np_handler& nph, SceNpClansPagingRequest* paging, SceNpClansEntry* clanList, SceNpClansPagingResult* pageResult) + SceNpClansError clans_client::getClanList(np::np_handler& nph, s32 reqId, SceNpClansPagingRequest* paging, SceNpClansEntry* clanList, SceNpClansPagingResult* pageResult) { pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -299,7 +335,7 @@ namespace clan clan.append_child("max").text().set(paging->max); pugi::xml_document response = pugi::xml_document(); - SceNpClansError clanRes = sendRequest(ClanRequestAction::GetClanList, ClanManagerOperationType::VIEW, &doc, &response); + SceNpClansError clanRes = sendRequest(reqId, ClanRequestAction::GetClanList, ClanManagerOperationType::VIEW, &doc, &response); if (clanRes != SCE_NP_CLANS_SUCCESS) return clanRes; @@ -362,14 +398,14 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::getClanInfo(SceNpClanId clanId, SceNpClansClanInfo* clanInfo) + SceNpClansError clans_client::getClanInfo(s32 reqId, SceNpClanId clanId, SceNpClansClanInfo* clanInfo) { pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); clan.append_child("id").text().set(clanId); pugi::xml_document response = pugi::xml_document(); - SceNpClansError clanRes = sendRequest(ClanRequestAction::GetClanInfo, ClanManagerOperationType::VIEW, &doc, &response); + SceNpClansError clanRes = sendRequest(reqId, ClanRequestAction::GetClanInfo, ClanManagerOperationType::VIEW, &doc, &response); if (clanRes != SCE_NP_CLANS_SUCCESS) return clanRes; @@ -401,7 +437,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::getMemberInfo(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMemberEntry* memInfo) + SceNpClansError clans_client::getMemberInfo(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMemberEntry* memInfo) { std::string ticket = getClanTicket(nph); @@ -414,7 +450,7 @@ namespace clan clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); - SceNpClansError clanRes = sendRequest(ClanRequestAction::GetMemberInfo, ClanManagerOperationType::VIEW, &doc, &response); + SceNpClansError clanRes = sendRequest(reqId, ClanRequestAction::GetMemberInfo, ClanManagerOperationType::VIEW, &doc, &response); if (clanRes != SCE_NP_CLANS_SUCCESS) return clanRes; @@ -469,7 +505,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::getMemberList(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMemberStatus /*status*/, SceNpClansMemberEntry* memList, SceNpClansPagingResult* pageResult) + SceNpClansError clans_client::getMemberList(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMemberStatus /*status*/, SceNpClansMemberEntry* memList, SceNpClansPagingResult* pageResult) { std::string ticket = getClanTicket(nph); @@ -481,7 +517,7 @@ namespace clan clan.append_child("max").text().set(paging->max); pugi::xml_document response = pugi::xml_document(); - SceNpClansError clanRes = sendRequest(ClanRequestAction::GetMemberList, ClanManagerOperationType::VIEW, &doc, &response); + SceNpClansError clanRes = sendRequest(reqId, ClanRequestAction::GetMemberList, ClanManagerOperationType::VIEW, &doc, &response); if (clanRes != SCE_NP_CLANS_SUCCESS) return clanRes; @@ -545,7 +581,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::getBlacklist(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansBlacklistEntry* bl, SceNpClansPagingResult* pageResult) + SceNpClansError clans_client::getBlacklist(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansBlacklistEntry* bl, SceNpClansPagingResult* pageResult) { std::string ticket = getClanTicket(nph); @@ -557,7 +593,7 @@ namespace clan clan.append_child("max").text().set(paging->max); pugi::xml_document response = pugi::xml_document(); - SceNpClansError clanRes = sendRequest(ClanRequestAction::GetBlacklist, ClanManagerOperationType::VIEW, &doc, &response); + SceNpClansError clanRes = sendRequest(reqId, ClanRequestAction::GetBlacklist, ClanManagerOperationType::VIEW, &doc, &response); if (clanRes != SCE_NP_CLANS_SUCCESS) return clanRes; @@ -609,7 +645,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::addBlacklistEntry(np::np_handler& nph, SceNpClanId clanId, SceNpId npId) + SceNpClansError clans_client::addBlacklistEntry(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId) { std::string ticket = getClanTicket(nph); @@ -622,10 +658,10 @@ namespace clan clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); - return sendRequest(ClanRequestAction::RecordBlacklistEntry, ClanManagerOperationType::UPDATE, &doc, &response); + return sendRequest(reqId, ClanRequestAction::RecordBlacklistEntry, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::removeBlacklistEntry(np::np_handler& nph, SceNpClanId clanId, SceNpId npId) + SceNpClansError clans_client::removeBlacklistEntry(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId) { std::string ticket = getClanTicket(nph); @@ -638,10 +674,10 @@ namespace clan clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); - return sendRequest(ClanRequestAction::DeleteBlacklistEntry, ClanManagerOperationType::UPDATE, &doc, &response); + return sendRequest(reqId, ClanRequestAction::DeleteBlacklistEntry, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::clanSearch(SceNpClansPagingRequest* paging, SceNpClansSearchableName* search, SceNpClansClanBasicInfo* clanList, SceNpClansPagingResult* pageResult) + SceNpClansError clans_client::clanSearch(s32 reqId, SceNpClansPagingRequest* paging, SceNpClansSearchableName* search, SceNpClansClanBasicInfo* clanList, SceNpClansPagingResult* pageResult) { pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -656,7 +692,7 @@ namespace clan name.append_attribute("value").set_value(search->name); pugi::xml_document response = pugi::xml_document(); - SceNpClansError clanRes = sendRequest(ClanRequestAction::ClanSearch, ClanManagerOperationType::VIEW, &doc, &response); + SceNpClansError clanRes = sendRequest(reqId, ClanRequestAction::ClanSearch, ClanManagerOperationType::VIEW, &doc, &response); if (clanRes != SCE_NP_CLANS_SUCCESS) return clanRes; @@ -703,7 +739,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::requestMembership(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* /*message*/) + SceNpClansError clans_client::requestMembership(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessage* /*message*/) { std::string ticket = getClanTicket(nph); @@ -713,10 +749,10 @@ namespace clan clan.append_child("id").text().set(clanId); pugi::xml_document response = pugi::xml_document(); - return sendRequest(ClanRequestAction::RequestMembership, ClanManagerOperationType::UPDATE, &doc, &response); + return sendRequest(reqId, ClanRequestAction::RequestMembership, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::cancelRequestMembership(np::np_handler& nph, SceNpClanId clanId) + SceNpClansError clans_client::cancelRequestMembership(np::np_handler& nph, s32 reqId, SceNpClanId clanId) { std::string ticket = getClanTicket(nph); @@ -726,10 +762,10 @@ namespace clan clan.append_child("id").text().set(clanId); pugi::xml_document response = pugi::xml_document(); - return sendRequest(ClanRequestAction::CancelRequestMembership, ClanManagerOperationType::UPDATE, &doc, &response); + return sendRequest(reqId, ClanRequestAction::CancelRequestMembership, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::sendMembershipResponse(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/, b8 allow) + SceNpClansError clans_client::sendMembershipResponse(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/, b8 allow) { std::string ticket = getClanTicket(nph); @@ -742,10 +778,10 @@ namespace clan clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); - return sendRequest(allow ? ClanRequestAction::AcceptMembershipRequest : ClanRequestAction::DeclineMembershipRequest, ClanManagerOperationType::UPDATE, &doc, &response); + return sendRequest(reqId, allow ? ClanRequestAction::AcceptMembershipRequest : ClanRequestAction::DeclineMembershipRequest, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::sendInvitation(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/) + SceNpClansError clans_client::sendInvitation(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/) { std::string ticket = getClanTicket(nph); @@ -758,10 +794,10 @@ namespace clan clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); - return sendRequest(ClanRequestAction::SendInvitation, ClanManagerOperationType::UPDATE, &doc, &response); + return sendRequest(reqId, ClanRequestAction::SendInvitation, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::cancelInvitation(np::np_handler& nph, SceNpClanId clanId, SceNpId npId) + SceNpClansError clans_client::cancelInvitation(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId) { std::string ticket = getClanTicket(nph); @@ -774,10 +810,10 @@ namespace clan clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); - return sendRequest(ClanRequestAction::CancelInvitation, ClanManagerOperationType::UPDATE, &doc, &response); + return sendRequest(reqId, ClanRequestAction::CancelInvitation, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::sendInvitationResponse(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* /*message*/, b8 accept) + SceNpClansError clans_client::sendInvitationResponse(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessage* /*message*/, b8 accept) { std::string ticket = getClanTicket(nph); @@ -787,10 +823,10 @@ namespace clan clan.append_child("id").text().set(clanId); pugi::xml_document response = pugi::xml_document(); - return sendRequest(accept ? ClanRequestAction::AcceptInvitation : ClanRequestAction::DeclineInvitation, ClanManagerOperationType::UPDATE, &doc, &response); + return sendRequest(reqId, accept ? ClanRequestAction::AcceptInvitation : ClanRequestAction::DeclineInvitation, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::updateMemberInfo(np::np_handler& nph, SceNpClanId clanId, SceNpClansUpdatableMemberInfo* info) + SceNpClansError clans_client::updateMemberInfo(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansUpdatableMemberInfo* info) { std::string ticket = getClanTicket(nph); @@ -824,10 +860,10 @@ namespace clan size.text().set(info->binData1Size); pugi::xml_document response = pugi::xml_document(); - return sendRequest(ClanRequestAction::UpdateMemberInfo, ClanManagerOperationType::UPDATE, &doc, &response); + return sendRequest(reqId, ClanRequestAction::UpdateMemberInfo, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::updateClanInfo(np::np_handler& nph, SceNpClanId clanId, SceNpClansUpdatableClanInfo* info) + SceNpClansError clans_client::updateClanInfo(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansUpdatableClanInfo* info) { std::string ticket = getClanTicket(nph); @@ -842,10 +878,10 @@ namespace clan description.text().set(info->description); pugi::xml_document response = pugi::xml_document(); - return sendRequest(ClanRequestAction::UpdateClanInfo, ClanManagerOperationType::UPDATE, &doc, &response); + return sendRequest(reqId, ClanRequestAction::UpdateClanInfo, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::joinClan(np::np_handler& nph, SceNpClanId clanId) + SceNpClansError clans_client::joinClan(np::np_handler& nph, s32 reqId, SceNpClanId clanId) { std::string ticket = getClanTicket(nph); @@ -855,10 +891,10 @@ namespace clan clan.append_child("id").text().set(clanId); pugi::xml_document response = pugi::xml_document(); - return sendRequest(ClanRequestAction::JoinClan, ClanManagerOperationType::UPDATE, &doc, &response); + return sendRequest(reqId, ClanRequestAction::JoinClan, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::leaveClan(np::np_handler& nph, SceNpClanId clanId) + SceNpClansError clans_client::leaveClan(np::np_handler& nph, s32 reqId, SceNpClanId clanId) { std::string ticket = getClanTicket(nph); @@ -868,10 +904,10 @@ namespace clan clan.append_child("id").text().set(clanId); pugi::xml_document response = pugi::xml_document(); - return sendRequest(ClanRequestAction::LeaveClan, ClanManagerOperationType::UPDATE, &doc, &response); + return sendRequest(reqId, ClanRequestAction::LeaveClan, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::kickMember(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/) + SceNpClansError clans_client::kickMember(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/) { std::string ticket = getClanTicket(nph); @@ -884,10 +920,10 @@ namespace clan clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); - return sendRequest(ClanRequestAction::KickMember, ClanManagerOperationType::UPDATE, &doc, &response); + return sendRequest(reqId, ClanRequestAction::KickMember, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::changeMemberRole(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMemberRole role) + SceNpClansError clans_client::changeMemberRole(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMemberRole role) { std::string ticket = getClanTicket(nph); @@ -903,10 +939,10 @@ namespace clan roleNode.text().set(static_cast(role)); pugi::xml_document response = pugi::xml_document(); - return sendRequest(ClanRequestAction::ChangeMemberRole, ClanManagerOperationType::UPDATE, &doc, &response); + return sendRequest(reqId, ClanRequestAction::ChangeMemberRole, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::retrieveAnnouncements(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMessageEntry* announcements, SceNpClansPagingResult* pageResult) + SceNpClansError clans_client::retrieveAnnouncements(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMessageEntry* announcements, SceNpClansPagingResult* pageResult) { std::string ticket = getClanTicket(nph); @@ -918,7 +954,7 @@ namespace clan clan.append_child("max").text().set(paging->max); pugi::xml_document response = pugi::xml_document(); - SceNpClansError clanRes = sendRequest(ClanRequestAction::RetrieveAnnouncements, ClanManagerOperationType::VIEW, &doc, &response); + SceNpClansError clanRes = sendRequest(reqId, ClanRequestAction::RetrieveAnnouncements, ClanManagerOperationType::VIEW, &doc, &response); if (clanRes != SCE_NP_CLANS_SUCCESS) return clanRes; @@ -987,7 +1023,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::postAnnouncement(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* announcement, SceNpClansMessageData* /*data*/, u32 duration, SceNpClansMessageId* msgId) + SceNpClansError clans_client::postAnnouncement(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessage* announcement, SceNpClansMessageData* /*data*/, u32 duration, SceNpClansMessageId* msgId) { std::string ticket = getClanTicket(nph); @@ -1006,7 +1042,7 @@ namespace clan expireDate.text().set(duration); pugi::xml_document response = pugi::xml_document(); - SceNpClansError clanRes = sendRequest(ClanRequestAction::PostAnnouncement, ClanManagerOperationType::UPDATE, &doc, &response); + SceNpClansError clanRes = sendRequest(reqId, ClanRequestAction::PostAnnouncement, ClanManagerOperationType::UPDATE, &doc, &response); if (clanRes != SCE_NP_CLANS_SUCCESS) return clanRes; @@ -1018,7 +1054,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::deleteAnnouncement(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessageId announcementId) + SceNpClansError clans_client::deleteAnnouncement(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessageId announcementId) { std::string ticket = getClanTicket(nph); @@ -1029,7 +1065,7 @@ namespace clan clan.append_child("msg-id").text().set(announcementId); pugi::xml_document response = pugi::xml_document(); - return sendRequest(ClanRequestAction::DeleteAnnouncement, ClanManagerOperationType::UPDATE, &doc, &response); + return sendRequest(reqId, ClanRequestAction::DeleteAnnouncement, ClanManagerOperationType::UPDATE, &doc, &response); } } #pragma endregion diff --git a/rpcs3/Emu/NP/clans_client.h b/rpcs3/Emu/NP/clans_client.h index 1d4b8b06c0..b05ab8efae 100644 --- a/rpcs3/Emu/NP/clans_client.h +++ b/rpcs3/Emu/NP/clans_client.h @@ -3,6 +3,7 @@ #include <3rdparty/curl/curl/include/curl/curl.h> #include #include +#include #include namespace clan @@ -62,11 +63,10 @@ namespace clan class clans_client { private: - CURL* curl = nullptr; - CURLcode res = CURLE_OK; + static size_t curlWriteCallback(void* data, size_t size, size_t nmemb, void* clientp); - SceNpClansError sendRequest(ClanRequestAction action, ClanManagerOperationType type, pugi::xml_document* xmlBody, pugi::xml_document* outResponse); + SceNpClansError sendRequest(s32 reqId, ClanRequestAction action, ClanManagerOperationType type, pugi::xml_document* xmlBody, pugi::xml_document* outResponse); /// @brief Forge and get a V2.1 Ticket for clan operations std::string getClanTicket(np::np_handler& nph); @@ -75,41 +75,41 @@ namespace clan clans_client(); ~clans_client(); - SceNpClansError createRequest(); - SceNpClansError destroyRequest(); + SceNpClansError createRequest(s32* reqId); + SceNpClansError destroyRequest(s32 reqId); - SceNpClansError clanSearch(SceNpClansPagingRequest* paging, SceNpClansSearchableName* search, SceNpClansClanBasicInfo* clanList, SceNpClansPagingResult* pageResult); + SceNpClansError clanSearch(s32 reqId, SceNpClansPagingRequest* paging, SceNpClansSearchableName* search, SceNpClansClanBasicInfo* clanList, SceNpClansPagingResult* pageResult); - SceNpClansError getClanList(np::np_handler& nph, SceNpClansPagingRequest* paging, SceNpClansEntry* clanList, SceNpClansPagingResult* pageResult); - SceNpClansError getClanInfo(SceNpClanId clanId, SceNpClansClanInfo* clanInfo); + SceNpClansError getClanList(np::np_handler& nph, s32 reqId, SceNpClansPagingRequest* paging, SceNpClansEntry* clanList, SceNpClansPagingResult* pageResult); + SceNpClansError getClanInfo(s32 reqId, SceNpClanId clanId, SceNpClansClanInfo* clanInfo); - SceNpClansError getMemberInfo(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMemberEntry* memInfo); - SceNpClansError getMemberList(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMemberStatus status, SceNpClansMemberEntry* memList, SceNpClansPagingResult* pageResult); + SceNpClansError getMemberInfo(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMemberEntry* memInfo); + SceNpClansError getMemberList(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMemberStatus status, SceNpClansMemberEntry* memList, SceNpClansPagingResult* pageResult); - SceNpClansError getBlacklist(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansBlacklistEntry* bl, SceNpClansPagingResult* pageResult); - SceNpClansError addBlacklistEntry(np::np_handler& nph, SceNpClanId clanId, SceNpId npId); - SceNpClansError removeBlacklistEntry(np::np_handler& nph, SceNpClanId clanId, SceNpId npId); + SceNpClansError getBlacklist(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansBlacklistEntry* bl, SceNpClansPagingResult* pageResult); + SceNpClansError addBlacklistEntry(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId); + SceNpClansError removeBlacklistEntry(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId); - SceNpClansError requestMembership(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* message); - SceNpClansError cancelRequestMembership(np::np_handler& nph, SceNpClanId clanId); - SceNpClansError sendMembershipResponse(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* message, b8 allow); + SceNpClansError requestMembership(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessage* message); + SceNpClansError cancelRequestMembership(np::np_handler& nph, s32 reqId, SceNpClanId clanId); + SceNpClansError sendMembershipResponse(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* message, b8 allow); - SceNpClansError sendInvitation(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* message); - SceNpClansError cancelInvitation(np::np_handler& nph, SceNpClanId clanId, SceNpId npId); - SceNpClansError sendInvitationResponse(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* message, b8 accept); + SceNpClansError sendInvitation(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* message); + SceNpClansError cancelInvitation(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId); + SceNpClansError sendInvitationResponse(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessage* message, b8 accept); - SceNpClansError joinClan(np::np_handler& nph, SceNpClanId clanId); - SceNpClansError leaveClan(np::np_handler& nph, SceNpClanId clanId); + SceNpClansError joinClan(np::np_handler& nph, s32 reqId, SceNpClanId clanId); + SceNpClansError leaveClan(np::np_handler& nph, s32 reqId, SceNpClanId clanId); - SceNpClansError updateMemberInfo(np::np_handler& nph, SceNpClanId clanId, SceNpClansUpdatableMemberInfo* info); - SceNpClansError updateClanInfo(np::np_handler& nph, SceNpClanId clanId, SceNpClansUpdatableClanInfo* info); + SceNpClansError updateMemberInfo(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansUpdatableMemberInfo* info); + SceNpClansError updateClanInfo(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansUpdatableClanInfo* info); - SceNpClansError kickMember(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* message); - SceNpClansError changeMemberRole(np::np_handler& nph, SceNpClanId clanId, SceNpId npId, SceNpClansMemberRole role); + SceNpClansError kickMember(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* message); + SceNpClansError changeMemberRole(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMemberRole role); - SceNpClansError retrieveAnnouncements(np::np_handler& nph, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMessageEntry* announcements, SceNpClansPagingResult* pageResult); - SceNpClansError postAnnouncement(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessage* announcement, SceNpClansMessageData* data, u32 duration, SceNpClansMessageId* announcementId); - SceNpClansError deleteAnnouncement(np::np_handler& nph, SceNpClanId clanId, SceNpClansMessageId announcementId); + SceNpClansError retrieveAnnouncements(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMessageEntry* announcements, SceNpClansPagingResult* pageResult); + SceNpClansError postAnnouncement(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessage* announcement, SceNpClansMessageData* data, u32 duration, SceNpClansMessageId* announcementId); + SceNpClansError deleteAnnouncement(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessageId announcementId); }; } // namespace clan From 485484d258c53d3220ed2078eeecfc76478527f1 Mon Sep 17 00:00:00 2001 From: zeph Date: Mon, 8 Dec 2025 14:01:19 +0100 Subject: [PATCH 15/30] Clans: handling case when tickets fail to get requested Signed-off-by: zeph --- rpcs3/Emu/NP/clans_client.cpp | 43 ++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index c308483ac5..eb76e71130 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -326,10 +326,13 @@ namespace clan #pragma region Outgoing API Requests SceNpClansError clans_client::getClanList(np::np_handler& nph, s32 reqId, SceNpClansPagingRequest* paging, SceNpClansEntry* clanList, SceNpClansPagingResult* pageResult) { + std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; + pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); - std::string ticket = getClanTicket(nph); clan.append_child("ticket").text().set(ticket.c_str()); clan.append_child("start").text().set(paging->startPos); clan.append_child("max").text().set(paging->max); @@ -440,6 +443,8 @@ namespace clan SceNpClansError clans_client::getMemberInfo(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMemberEntry* memInfo) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -508,6 +513,8 @@ namespace clan SceNpClansError clans_client::getMemberList(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMemberStatus /*status*/, SceNpClansMemberEntry* memList, SceNpClansPagingResult* pageResult) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -648,6 +655,8 @@ namespace clan SceNpClansError clans_client::addBlacklistEntry(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -664,6 +673,8 @@ namespace clan SceNpClansError clans_client::removeBlacklistEntry(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -742,6 +753,8 @@ namespace clan SceNpClansError clans_client::requestMembership(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessage* /*message*/) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -755,6 +768,8 @@ namespace clan SceNpClansError clans_client::cancelRequestMembership(np::np_handler& nph, s32 reqId, SceNpClanId clanId) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -768,6 +783,8 @@ namespace clan SceNpClansError clans_client::sendMembershipResponse(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/, b8 allow) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -784,6 +801,8 @@ namespace clan SceNpClansError clans_client::sendInvitation(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -800,6 +819,8 @@ namespace clan SceNpClansError clans_client::cancelInvitation(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -816,6 +837,8 @@ namespace clan SceNpClansError clans_client::sendInvitationResponse(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessage* /*message*/, b8 accept) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -829,6 +852,8 @@ namespace clan SceNpClansError clans_client::updateMemberInfo(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansUpdatableMemberInfo* info) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -866,6 +891,8 @@ namespace clan SceNpClansError clans_client::updateClanInfo(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansUpdatableClanInfo* info) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -884,6 +911,8 @@ namespace clan SceNpClansError clans_client::joinClan(np::np_handler& nph, s32 reqId, SceNpClanId clanId) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -897,6 +926,8 @@ namespace clan SceNpClansError clans_client::leaveClan(np::np_handler& nph, s32 reqId, SceNpClanId clanId) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -910,6 +941,8 @@ namespace clan SceNpClansError clans_client::kickMember(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -926,6 +959,8 @@ namespace clan SceNpClansError clans_client::changeMemberRole(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMemberRole role) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -945,6 +980,8 @@ namespace clan SceNpClansError clans_client::retrieveAnnouncements(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMessageEntry* announcements, SceNpClansPagingResult* pageResult) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -1026,6 +1063,8 @@ namespace clan SceNpClansError clans_client::postAnnouncement(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessage* announcement, SceNpClansMessageData* /*data*/, u32 duration, SceNpClansMessageId* msgId) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -1057,6 +1096,8 @@ namespace clan SceNpClansError clans_client::deleteAnnouncement(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessageId announcementId) { std::string ticket = getClanTicket(nph); + if (ticket.empty()) + return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); From 198c965f8bdfcfa3cac2f7b93111bf24e533604d Mon Sep 17 00:00:00 2001 From: zeph Date: Mon, 8 Dec 2025 15:15:56 +0100 Subject: [PATCH 16/30] Clans: improved ticket requesting and waiting logic Using proper atomic synchronisation primitives, instead of a busy loop Signed-off-by: zeph --- rpcs3/Emu/NP/clans_client.cpp | 52 ++++++++++------------------------- rpcs3/Emu/NP/clans_client.h | 3 ++ rpcs3/Emu/NP/np_handler.cpp | 26 ++++++++++++++++++ rpcs3/Emu/NP/np_handler.h | 9 ++++++ rpcs3/Emu/NP/np_requests.cpp | 12 ++++++++ 5 files changed, 64 insertions(+), 38 deletions(-) diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index eb76e71130..f198cb4e63 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -19,9 +19,6 @@ const char* REQ_TYPE_SEC = "sec"; constexpr const char JID_FORMAT[] = "%s@un.br.np.playstation.net"; -const char* CLANS_SERVICE_ID = "IV0001-NPXS01001_00"; -const char* CLANS_ENTITLEMENT_ID = "NPWR00432_00"; - template <> void fmt_class_string::format(std::string& out, u64 arg) { @@ -274,39 +271,18 @@ namespace clan } std::string clans_client::getClanTicket(np::np_handler& nph) - { - if (nph.get_ticket().size() > 0) - { - std::vector ticket_bytes(1024); - uint32_t ticket_size = UINT32_MAX; - - Base64_Encode_NoNl(nph.get_ticket().data(), nph.get_ticket().size(), ticket_bytes.data(), &ticket_size); - return std::string(reinterpret_cast(ticket_bytes.data()), ticket_size); - } - + { const auto& npid = nph.get_npid(); - const char* service_id = CLANS_SERVICE_ID; - const unsigned char* cookie = nullptr; - const u32 cookie_size = 0; - const char* entitlement_id = CLANS_ENTITLEMENT_ID; - const u32 consumed_count = 0; + const char* service_id = CLANS_SERVICE_ID; + const unsigned char* cookie = nullptr; + const u32 cookie_size = 0; + const char* entitlement_id = CLANS_ENTITLEMENT_ID; + const u32 consumed_count = 0; - nph.req_ticket(0x00020001, &npid, service_id, cookie, cookie_size, entitlement_id, consumed_count); + nph.req_ticket(0x00020001, &npid, service_id, cookie, cookie_size, entitlement_id, consumed_count); - np::ticket ticket; - - // TODO: convert this to use events? - int retries = 0; - while (ticket.empty() && retries < 100) - { - ticket = nph.get_ticket(); - if (ticket.empty()) - { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - retries++; - } - } + np::ticket ticket = nph.get_clan_ticket(); if (ticket.empty()) { @@ -314,14 +290,14 @@ namespace clan return ""; } - std::vector ticket_bytes(1024); - uint32_t ticket_size = UINT32_MAX; + std::vector ticket_bytes(1024); + uint32_t ticket_size = UINT32_MAX; - Base64_Encode_NoNl(ticket.data(), ticket.size(), ticket_bytes.data(), &ticket_size); - std::string ticket_str = std::string(reinterpret_cast(ticket_bytes.data()), ticket_size); + Base64_Encode_NoNl(ticket.data(), ticket.size(), ticket_bytes.data(), &ticket_size); + std::string ticket_str = std::string(reinterpret_cast(ticket_bytes.data()), ticket_size); - return ticket_str; - } + return ticket_str; + } #pragma region Outgoing API Requests SceNpClansError clans_client::getClanList(np::np_handler& nph, s32 reqId, SceNpClansPagingRequest* paging, SceNpClansEntry* clanList, SceNpClansPagingResult* pageResult) diff --git a/rpcs3/Emu/NP/clans_client.h b/rpcs3/Emu/NP/clans_client.h index b05ab8efae..3db0243895 100644 --- a/rpcs3/Emu/NP/clans_client.h +++ b/rpcs3/Emu/NP/clans_client.h @@ -6,6 +6,9 @@ #include #include +inline const char* CLANS_ENTITLEMENT_ID = "NPWR00432_00"; +inline const char* CLANS_SERVICE_ID = "IV0001-NPXS01001_00"; + namespace clan { enum class ClanManagerOperationType diff --git a/rpcs3/Emu/NP/np_handler.cpp b/rpcs3/Emu/NP/np_handler.cpp index 30eac7f470..099f5c5b93 100644 --- a/rpcs3/Emu/NP/np_handler.cpp +++ b/rpcs3/Emu/NP/np_handler.cpp @@ -239,6 +239,25 @@ namespace np return true; } + std::string ticket::get_service_id() const + { + if (!parse_success) + { + return ""; + } + + const auto& node = nodes[0].data.data_nodes[8]; + if (node.len != SCE_NP_SERVICE_ID_SIZE) + { + return ""; + } + + // Trim null characters + const auto& vec = node.data.data_vec; + auto it = std::find(vec.begin(), vec.end(), 0); + return std::string(vec.begin(), it); + } + std::optional ticket::parse_node(std::size_t index) const { if ((index + MIN_TICKET_DATA_SIZE) > size()) @@ -1345,6 +1364,13 @@ namespace np return history; } + ticket np_handler::get_clan_ticket() + { + std::unique_lock lock(mutex_clan_ticket); + cv_clan_ticket.wait(lock, [this] { return clan_ticket_ready.load(); }); + return clan_ticket; + } + constexpr usz MAX_HISTORY_ENTRIES = 200; void np_handler::add_player_to_history(const SceNpId* npid, const char* description) diff --git a/rpcs3/Emu/NP/np_handler.h b/rpcs3/Emu/NP/np_handler.h index c7a086f224..5b8e841961 100644 --- a/rpcs3/Emu/NP/np_handler.h +++ b/rpcs3/Emu/NP/np_handler.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "Emu/Memory/vm_ptr.h" #include "Emu/Cell/Modules/sceNp.h" @@ -69,6 +70,7 @@ namespace np bool empty() const; bool get_value(s32 param_id, vm::ptr param) const; + std::string get_service_id() const; private: std::optional parse_node(std::size_t index) const; @@ -253,6 +255,7 @@ namespace np // Misc stuff void req_ticket(u32 version, const SceNpId* npid, const char* service_id, const u8* cookie, u32 cookie_size, const char* entitlement_id, u32 consumed_count); const ticket& get_ticket() const; + ticket get_clan_ticket(); void add_player_to_history(const SceNpId* npid, const char* description); u32 add_players_to_history(const SceNpId* npids, const char* description, u32 count); u32 get_players_history_count(u32 options); @@ -415,6 +418,12 @@ namespace np ticket current_ticket; + // Clan ticket + std::mutex mutex_clan_ticket; + std::condition_variable_any cv_clan_ticket; + atomic_t clan_ticket_ready = false; + ticket clan_ticket; + // IP & DNS info std::string hostname = "localhost"; std::array ether_address{}; diff --git a/rpcs3/Emu/NP/np_requests.cpp b/rpcs3/Emu/NP/np_requests.cpp index 23e18f1353..05fae258e3 100644 --- a/rpcs3/Emu/NP/np_requests.cpp +++ b/rpcs3/Emu/NP/np_requests.cpp @@ -1,5 +1,6 @@ #include "Emu/Cell/Modules/sceNp.h" #include "Emu/Cell/Modules/sceNp2.h" +#include "Emu/NP/clans_client.h" #include "Emu/NP/rpcn_types.h" #include "Utilities/StrFmt.h" #include "stdafx.h" @@ -866,6 +867,17 @@ namespace np current_ticket = ticket(std::move(ticket_raw)); auto ticket_size = static_cast(current_ticket.size()); + + // Clans: check if ticket belongs to the clan service. If so, store it. + if (current_ticket.get_service_id() == CLANS_SERVICE_ID) + { + std::lock_guard lock(mutex_clan_ticket); + clan_ticket = current_ticket; + clan_ticket_ready = true; + cv_clan_ticket.notify_all(); + return; + } + if (manager_cb) { sysutil_register_cb([manager_cb = this->manager_cb, ticket_size, manager_cb_arg = this->manager_cb_arg](ppu_thread& cb_ppu) -> s32 From d0b2136579be4930c4f46580e29faf4bb5dbea21 Mon Sep 17 00:00:00 2001 From: zeph Date: Mon, 8 Dec 2025 22:36:16 +0100 Subject: [PATCH 17/30] Clans: switch `fmt::split` to `fmt::split_sv` Signed-off-by: zeph --- rpcs3/Emu/NP/clans_config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Emu/NP/clans_config.cpp b/rpcs3/Emu/NP/clans_config.cpp index 3eec6364fa..c5c3548f84 100644 --- a/rpcs3/Emu/NP/clans_config.cpp +++ b/rpcs3/Emu/NP/clans_config.cpp @@ -129,7 +129,7 @@ bool cfg_clans::add_host(std::string_view new_description, std::string_view new_ bool cfg_clans::del_host(std::string_view del_description, std::string_view del_host) { // Do not delete default servers - const auto def_desc_and_host = fmt::split(hosts.def, {"|"}); + const auto def_desc_and_host = fmt::split_sv(hosts.def, {"|"}); ensure(def_desc_and_host.size() == 2); if (del_description == def_desc_and_host[0] && del_host == def_desc_and_host[1]) { From 6ecc5e6900bc2fd02af89691573b9afed1d282b3 Mon Sep 17 00:00:00 2001 From: zeph Date: Mon, 8 Dec 2025 22:40:26 +0100 Subject: [PATCH 18/30] Clans: switch `fmt::split` to `fmt::split_sv` Signed-off-by: zeph --- rpcs3/Emu/NP/clans_config.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/NP/clans_config.cpp b/rpcs3/Emu/NP/clans_config.cpp index c5c3548f84..15c289cdd1 100644 --- a/rpcs3/Emu/NP/clans_config.cpp +++ b/rpcs3/Emu/NP/clans_config.cpp @@ -1,3 +1,4 @@ +#include "Utilities/StrUtil.h" #include "stdafx.h" #include "clans_config.h" #include "Utilities/File.h" @@ -59,7 +60,7 @@ bool cfg_clans::get_use_https() const std::vector> cfg_clans::get_hosts() { std::vector> vec_hosts; - const auto hosts_list = fmt::split(hosts.to_string(), {"|||"}); + const auto hosts_list = fmt::split_sv(hosts.to_string(), {"|||"}); for (const auto& cur_host : hosts_list) { From 1c1935229c95d1abc408569e95cceee7ad3e6f57 Mon Sep 17 00:00:00 2001 From: zeph Date: Mon, 8 Dec 2025 23:35:04 +0100 Subject: [PATCH 19/30] Clang: fix temporary variable usage Signed-off-by: zeph --- rpcs3/Emu/NP/clans_config.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/NP/clans_config.cpp b/rpcs3/Emu/NP/clans_config.cpp index 15c289cdd1..08fd09c4b2 100644 --- a/rpcs3/Emu/NP/clans_config.cpp +++ b/rpcs3/Emu/NP/clans_config.cpp @@ -60,7 +60,8 @@ bool cfg_clans::get_use_https() const std::vector> cfg_clans::get_hosts() { std::vector> vec_hosts; - const auto hosts_list = fmt::split_sv(hosts.to_string(), {"|||"}); + const std::string host_str = hosts.to_string(); + const auto hosts_list = fmt::split_sv(host_str, {"|||"}); for (const auto& cur_host : hosts_list) { From b5796f5880553ecd56dd9c19e29bd8c2e1780a4e Mon Sep 17 00:00:00 2001 From: zeph Date: Tue, 9 Dec 2025 11:14:15 +0100 Subject: [PATCH 20/30] Clans(CI): fix imports --- rpcs3/Emu/NP/clans_client.cpp | 6 +++--- rpcs3/Emu/NP/clans_client.h | 5 +++-- rpcs3/Emu/NP/np_requests.cpp | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index f198cb4e63..9c75c1f8b4 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -1,4 +1,3 @@ -#include "Emu/Cell/Modules/sceNpClans.h" #include "stdafx.h" #include @@ -8,8 +7,9 @@ #include #include -#include -#include +#include "Emu/Cell/Modules/sceNpClans.h" +#include "Emu/NP/clans_client.h" +#include "Emu/NP/clans_config.h" LOG_CHANNEL(clan_log, "clans"); diff --git a/rpcs3/Emu/NP/clans_client.h b/rpcs3/Emu/NP/clans_client.h index 3db0243895..776ec17365 100644 --- a/rpcs3/Emu/NP/clans_client.h +++ b/rpcs3/Emu/NP/clans_client.h @@ -1,10 +1,11 @@ #pragma once -#include <3rdparty/curl/curl/include/curl/curl.h> +#include +#include + #include #include #include -#include inline const char* CLANS_ENTITLEMENT_ID = "NPWR00432_00"; inline const char* CLANS_SERVICE_ID = "IV0001-NPXS01001_00"; diff --git a/rpcs3/Emu/NP/np_requests.cpp b/rpcs3/Emu/NP/np_requests.cpp index 05fae258e3..a52d807de5 100644 --- a/rpcs3/Emu/NP/np_requests.cpp +++ b/rpcs3/Emu/NP/np_requests.cpp @@ -1,9 +1,9 @@ +#include "stdafx.h" #include "Emu/Cell/Modules/sceNp.h" #include "Emu/Cell/Modules/sceNp2.h" #include "Emu/NP/clans_client.h" #include "Emu/NP/rpcn_types.h" #include "Utilities/StrFmt.h" -#include "stdafx.h" #include "Emu/Cell/PPUCallback.h" #include "Emu/Cell/lv2/sys_sync.h" #include "Emu/system_config.h" From 68e5dec1e8dbc0f893dd050fc7e1aff3cedc32eb Mon Sep 17 00:00:00 2001 From: zeph Date: Tue, 9 Dec 2025 11:47:04 +0100 Subject: [PATCH 21/30] Clans(CI): fix `curl` dependency for `emucore` (MSVC) --- rpcs3/emucore.vcxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 4203d30b5f..db969c4e60 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -40,7 +40,7 @@ Use - ..\3rdparty\miniupnp\miniupnp\miniupnpc\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\flatbuffers\include;..\3rdparty\libusb\libusb\libusb;..\3rdparty\yaml-cpp\yaml-cpp\include;..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\zlib\zlib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm_build\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm_build\include;$(VULKAN_SDK)\Include;..\3rdparty\zstd\zstd\lib;$(SolutionDir)3rdparty\fusion\fusion\Fusion;$(SolutionDir)3rdparty\wolfssl\extra\win32;$(SolutionDir)3rdparty\libsdl-org\SDL\include;$(SolutionDir)3rdparty\glslang\glslang + ..\3rdparty\miniupnp\miniupnp\miniupnpc\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\flatbuffers\include;..\3rdparty\libusb\libusb\libusb;..\3rdparty\yaml-cpp\yaml-cpp\include;..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\zlib\zlib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm_build\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm_build\include;$(VULKAN_SDK)\Include;..\3rdparty\zstd\zstd\lib;$(SolutionDir)3rdparty\fusion\fusion\Fusion;$(SolutionDir)3rdparty\wolfssl\extra\win32;$(SolutionDir)3rdparty\libsdl-org\SDL\include;$(SolutionDir)3rdparty\glslang\glslang;$(SolutionDir)3rdparty\curl\curl\include MaxSpeed AL_LIBTYPE_STATIC;MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL3;ZLIB_CONST;WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions) AL_LIBTYPE_STATIC;MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL3;ZLIB_CONST;WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions) From fee3dff037c22f130d6a223f139625553e503e8c Mon Sep 17 00:00:00 2001 From: zeph Date: Tue, 9 Dec 2025 12:13:47 +0100 Subject: [PATCH 22/30] Clans(CI): fix missing Qt moc --- rpcs3/rpcs3.vcxproj | 10 ++++++++++ rpcs3/rpcs3.vcxproj.filters | 3 +++ 2 files changed, 13 insertions(+) diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index eaecb47cb3..d8d5053179 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -1495,6 +1495,16 @@ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent" + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent" + $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing %(Identity)... diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 2440ee1523..2e9340b01b 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -1708,6 +1708,9 @@ Gui\rpcn + + Gui\rpcn + Gui\message dialog From b2195bd6ff130f24eabaf1f1693f2cc1e47a6578 Mon Sep 17 00:00:00 2001 From: zeph Date: Tue, 9 Dec 2025 19:28:38 +0100 Subject: [PATCH 23/30] Clans: clang compiler fix-ups Signed-off-by: zeph --- rpcs3/Emu/NP/clans_client.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index 9c75c1f8b4..09fc012a04 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -1,8 +1,21 @@ #include "stdafx.h" #include + +// wolfssl uses old-style casts which break clang builds +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" +#pragma clang diagnostic ignored "-Wextern-c-compat" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif + #include +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + #include #include #include From cf373334a43903b54752282a05e01c2809fbb4ca Mon Sep 17 00:00:00 2001 From: zeph Date: Wed, 10 Dec 2025 18:36:24 +0100 Subject: [PATCH 24/30] Clans: clean-up - Switched from `vm::ptr` to `handle` downcasted to a u32 for immutability - Using a single `atomic_t` instead of a mutex combination for ticket requesting Signed-off-by: zeph --- rpcs3/Emu/Cell/Modules/sceNpClans.cpp | 171 +++++++++----------------- rpcs3/Emu/Cell/Modules/sceNpClans.h | 3 +- rpcs3/Emu/NP/clans_client.cpp | 51 ++++---- rpcs3/Emu/NP/clans_client.h | 50 ++++---- rpcs3/Emu/NP/np_handler.cpp | 10 +- rpcs3/Emu/NP/np_handler.h | 4 +- rpcs3/Emu/NP/np_requests.cpp | 6 +- 7 files changed, 123 insertions(+), 172 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp index 1d0d6e84a4..69204af8d6 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp @@ -151,7 +151,7 @@ error_code sceNpClansCreateRequest(vm::ptr handle, u64 return CELL_OK; } -error_code sceNpClansDestroyRequest(vm::ptr handle) +error_code sceNpClansDestroyRequest(SceNpClansRequestHandle handle) { if (!g_fxo->get().is_initialized) { @@ -160,9 +160,7 @@ error_code sceNpClansDestroyRequest(vm::ptr handle) auto& clans_manager = g_fxo->get(); - s32 reqId = static_cast(handle.addr()); - - SceNpClansError res = clans_manager.client->destroyRequest(reqId); + SceNpClansError res = clans_manager.client->destroyRequest(handle); if (res != SCE_NP_CLANS_SUCCESS) { return res; @@ -171,7 +169,7 @@ error_code sceNpClansDestroyRequest(vm::ptr handle) return CELL_OK; } -error_code sceNpClansAbortRequest(vm::ptr handle) +error_code sceNpClansAbortRequest(SceNpClansRequestHandle handle) { if (!g_fxo->get().is_initialized) { @@ -179,16 +177,13 @@ error_code sceNpClansAbortRequest(vm::ptr handle) } auto& clans_manager = g_fxo->get(); - - s32 reqId = static_cast(handle.addr()); - - clans_manager.client->destroyRequest(reqId); + clans_manager.client->destroyRequest(handle); return CELL_OK; } // TODO: requires NpCommerce2 -error_code sceNpClansCreateClan(vm::ptr handle, vm::cptr name, vm::cptr tag, vm::ptr clanId) +error_code sceNpClansCreateClan(SceNpClansRequestHandle handle, vm::cptr name, vm::cptr tag, vm::ptr clanId) { sceNpClans.todo("sceNpClansCreateClan(handle=*0x%x, name=%s, tag=%s, clanId=*0x%x)", handle, name, tag, clanId); @@ -212,7 +207,7 @@ error_code sceNpClansCreateClan(vm::ptr handle, vm::cpt // TODO: should probably not be implemented on RPCS3 until `CreateClan` is, // to not let people disband a clan by accident -error_code sceNpClansDisbandClan(vm::ptr handle, SceNpClanId clanId) +error_code sceNpClansDisbandClan(SceNpClansRequestHandle handle, SceNpClanId clanId) { sceNpClans.todo("sceNpClansDisbandClan(handle=*0x%x, clanId=*0x%x)", handle, clanId); @@ -227,7 +222,7 @@ error_code sceNpClansDisbandClan(vm::ptr handle, SceNpC // return CELL_OK; } -error_code sceNpClansGetClanList(vm::ptr handle, vm::cptr paging, vm::ptr clanList, vm::ptr pageResult) +error_code sceNpClansGetClanList(SceNpClansRequestHandle handle, vm::cptr paging, vm::ptr clanList, vm::ptr pageResult) { if (!g_fxo->get().is_initialized) { @@ -264,9 +259,7 @@ error_code sceNpClansGetClanList(vm::ptr handle, vm::cp SceNpClansEntry host_clanList[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; SceNpClansPagingResult host_pageResult = {}; - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->getClanList(nph, reqId, &host_paging, host_clanList, &host_pageResult); + SceNpClansError ret = clans_manager.client->getClanList(nph, handle, &host_paging, host_clanList, &host_pageResult); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -282,7 +275,7 @@ error_code sceNpClansGetClanList(vm::ptr handle, vm::cp } // TODO: seems to not be needed, even by the PS3..? -error_code sceNpClansGetClanListByNpId(vm::ptr handle, vm::cptr paging, vm::cptr npid, vm::ptr clanList, vm::ptr pageResult) +error_code sceNpClansGetClanListByNpId(SceNpClansRequestHandle handle, vm::cptr paging, vm::cptr npid, vm::ptr clanList, vm::ptr pageResult) { sceNpClans.todo("sceNpClansGetClanListByNpId(handle=*0x%x, paging=*0x%x, npid=*0x%x, clanList=*0x%x, pageResult=*0x%x)", handle, paging, npid, clanList, pageResult); @@ -308,7 +301,7 @@ error_code sceNpClansGetClanListByNpId(vm::ptr handle, } // TODO: seems to not be needed, even by the PS3..? -error_code sceNpClansSearchByProfile(vm::ptr handle, vm::cptr paging, vm::cptr search, vm::ptr results, vm::ptr pageResult) +error_code sceNpClansSearchByProfile(SceNpClansRequestHandle handle, vm::cptr paging, vm::cptr search, vm::ptr results, vm::ptr pageResult) { sceNpClans.todo("sceNpClansSearchByProfile(handle=*0x%x, paging=*0x%x, search=*0x%x, results=*0x%x, pageResult=*0x%x)", handle, paging, search, results, pageResult); @@ -333,7 +326,7 @@ error_code sceNpClansSearchByProfile(vm::ptr handle, vm return CELL_OK; } -error_code sceNpClansSearchByName(vm::ptr handle, vm::cptr paging, vm::cptr search, vm::ptr results, vm::ptr pageResult) +error_code sceNpClansSearchByName(SceNpClansRequestHandle handle, vm::cptr paging, vm::cptr search, vm::ptr results, vm::ptr pageResult) { if (!g_fxo->get().is_initialized) { @@ -372,9 +365,7 @@ error_code sceNpClansSearchByName(vm::ptr handle, vm::c SceNpClansClanBasicInfo host_results[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; SceNpClansPagingResult host_pageResult = {}; - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->clanSearch(reqId, &host_paging, &host_search, host_results, &host_pageResult); + SceNpClansError ret = clans_manager.client->clanSearch(handle, &host_paging, &host_search, host_results, &host_pageResult); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -389,7 +380,7 @@ error_code sceNpClansSearchByName(vm::ptr handle, vm::c return CELL_OK; } -error_code sceNpClansGetClanInfo(vm::ptr handle, SceNpClanId clanId, vm::ptr info) +error_code sceNpClansGetClanInfo(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::ptr info) { if (!g_fxo->get().is_initialized) { @@ -406,9 +397,7 @@ error_code sceNpClansGetClanInfo(vm::ptr handle, SceNpC SceNpClansClanInfo host_info = {}; - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->getClanInfo(reqId, clanId, &host_info); + SceNpClansError ret = clans_manager.client->getClanInfo(handle, clanId, &host_info); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -419,7 +408,7 @@ error_code sceNpClansGetClanInfo(vm::ptr handle, SceNpC return CELL_OK; } -error_code sceNpClansUpdateClanInfo(vm::ptr handle, SceNpClanId clanId, vm::cptr info) +error_code sceNpClansUpdateClanInfo(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr info) { if (!g_fxo->get().is_initialized) { @@ -438,9 +427,7 @@ error_code sceNpClansUpdateClanInfo(vm::ptr handle, Sce SceNpClansUpdatableClanInfo host_info = {}; std::memcpy(&host_info, info.get_ptr(), sizeof(SceNpClansUpdatableClanInfo)); - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->updateClanInfo(nph, reqId, clanId, &host_info); + SceNpClansError ret = clans_manager.client->updateClanInfo(nph, handle, clanId, &host_info); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -449,7 +436,7 @@ error_code sceNpClansUpdateClanInfo(vm::ptr handle, Sce return CELL_OK; } -error_code sceNpClansGetMemberList(vm::ptr handle, SceNpClanId clanId, vm::cptr paging, SceNpClansMemberStatus status, vm::ptr memList, vm::ptr pageResult) +error_code sceNpClansGetMemberList(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr paging, SceNpClansMemberStatus status, vm::ptr memList, vm::ptr pageResult) { if (!g_fxo->get().is_initialized) { @@ -486,9 +473,7 @@ error_code sceNpClansGetMemberList(vm::ptr handle, SceN SceNpClansMemberEntry* host_memList_addr = new SceNpClansMemberEntry[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX]; SceNpClansPagingResult host_pageResult = {}; - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->getMemberList(nph, reqId, clanId, &host_paging, status, host_memList_addr, &host_pageResult); + SceNpClansError ret = clans_manager.client->getMemberList(nph, handle, clanId, &host_paging, status, host_memList_addr, &host_pageResult); if (ret != SCE_NP_CLANS_SUCCESS) { delete[] host_memList_addr; @@ -505,7 +490,7 @@ error_code sceNpClansGetMemberList(vm::ptr handle, SceN return CELL_OK; } -error_code sceNpClansGetMemberInfo(vm::ptr handle, SceNpClanId clanId, vm::cptr npid, vm::ptr memInfo) +error_code sceNpClansGetMemberInfo(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr npid, vm::ptr memInfo) { if (!g_fxo->get().is_initialized) { @@ -525,9 +510,7 @@ error_code sceNpClansGetMemberInfo(vm::ptr handle, SceN SceNpClansMemberEntry host_memInfo = {}; - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->getMemberInfo(nph, reqId, clanId, host_npid, &host_memInfo); + SceNpClansError ret = clans_manager.client->getMemberInfo(nph, handle, clanId, host_npid, &host_memInfo); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -538,7 +521,7 @@ error_code sceNpClansGetMemberInfo(vm::ptr handle, SceN return CELL_OK; } -error_code sceNpClansUpdateMemberInfo(vm::ptr handle, SceNpClanId clanId, vm::cptr info) +error_code sceNpClansUpdateMemberInfo(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr info) { if (!g_fxo->get().is_initialized) { @@ -556,9 +539,7 @@ error_code sceNpClansUpdateMemberInfo(vm::ptr handle, S SceNpClansUpdatableMemberInfo host_info = {}; std::memcpy(&host_info, info.get_ptr(), sizeof(SceNpClansUpdatableMemberInfo)); - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->updateMemberInfo(nph, reqId, clanId, &host_info); + SceNpClansError ret = clans_manager.client->updateMemberInfo(nph, handle, clanId, &host_info); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -567,7 +548,7 @@ error_code sceNpClansUpdateMemberInfo(vm::ptr handle, S return CELL_OK; } -error_code sceNpClansChangeMemberRole(vm::ptr handle, SceNpClanId clanId, vm::cptr npid, u32 role) +error_code sceNpClansChangeMemberRole(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr npid, u32 role) { if (!g_fxo->get().is_initialized) { @@ -585,9 +566,7 @@ error_code sceNpClansChangeMemberRole(vm::ptr handle, S SceNpId host_npid = {}; std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId)); - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->changeMemberRole(nph, reqId, clanId, host_npid, static_cast(role)); + SceNpClansError ret = clans_manager.client->changeMemberRole(nph, handle, clanId, host_npid, static_cast(role)); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -597,7 +576,7 @@ error_code sceNpClansChangeMemberRole(vm::ptr handle, S } // TODO: no struct currently implements `autoAccept` as a field -error_code sceNpClansGetAutoAcceptStatus(vm::ptr handle, SceNpClanId clanId, vm::ptr enable) +error_code sceNpClansGetAutoAcceptStatus(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::ptr enable) { sceNpClans.todo("sceNpClansGetAutoAcceptStatus(handle=*0x%x, clanId=%d, enable=*0x%x)", handle, clanId, enable); @@ -615,7 +594,7 @@ error_code sceNpClansGetAutoAcceptStatus(vm::ptr handle } // TODO: no struct currently implements `autoAccept` as a field -error_code sceNpClansUpdateAutoAcceptStatus(vm::ptr handle, SceNpClanId clanId, b8 enable) +error_code sceNpClansUpdateAutoAcceptStatus(SceNpClansRequestHandle handle, SceNpClanId clanId, b8 enable) { sceNpClans.todo("sceNpClansUpdateAutoAcceptStatus(handle=*0x%x, clanId=%d, enable=%d)", handle, clanId, enable); @@ -627,7 +606,7 @@ error_code sceNpClansUpdateAutoAcceptStatus(vm::ptr han return CELL_OK; } -error_code sceNpClansJoinClan(vm::ptr handle, SceNpClanId clanId) +error_code sceNpClansJoinClan(SceNpClansRequestHandle handle, SceNpClanId clanId) { if (!g_fxo->get().is_initialized) { @@ -637,9 +616,7 @@ error_code sceNpClansJoinClan(vm::ptr handle, SceNpClan auto& nph = g_fxo->get>(); auto& clans_manager = g_fxo->get(); - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->joinClan(nph, reqId, clanId); + SceNpClansError ret = clans_manager.client->joinClan(nph, handle, clanId); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -648,7 +625,7 @@ error_code sceNpClansJoinClan(vm::ptr handle, SceNpClan return CELL_OK; } -error_code sceNpClansLeaveClan(vm::ptr handle, SceNpClanId clanId) +error_code sceNpClansLeaveClan(SceNpClansRequestHandle handle, SceNpClanId clanId) { if (!g_fxo->get().is_initialized) { @@ -658,9 +635,7 @@ error_code sceNpClansLeaveClan(vm::ptr handle, SceNpCla auto& nph = g_fxo->get>(); auto& clans_manager = g_fxo->get(); - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->leaveClan(nph, reqId, clanId); + SceNpClansError ret = clans_manager.client->leaveClan(nph, handle, clanId); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -669,7 +644,7 @@ error_code sceNpClansLeaveClan(vm::ptr handle, SceNpCla return CELL_OK; } -error_code sceNpClansKickMember(vm::ptr handle, SceNpClanId clanId, vm::cptr npid, vm::cptr message) +error_code sceNpClansKickMember(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr npid, vm::cptr message) { if (!g_fxo->get().is_initialized) { @@ -701,9 +676,7 @@ error_code sceNpClansKickMember(vm::ptr handle, SceNpCl std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); } - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->kickMember(nph, reqId, clanId, host_npid, &host_message); + SceNpClansError ret = clans_manager.client->kickMember(nph, handle, clanId, host_npid, &host_message); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -712,7 +685,7 @@ error_code sceNpClansKickMember(vm::ptr handle, SceNpCl return CELL_OK; } -error_code sceNpClansSendInvitation(vm::ptr handle, SceNpClanId clanId, vm::cptr npid, vm::cptr message) +error_code sceNpClansSendInvitation(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr npid, vm::cptr message) { if (!g_fxo->get().is_initialized) { @@ -744,9 +717,7 @@ error_code sceNpClansSendInvitation(vm::ptr handle, Sce std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); } - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->sendInvitation(nph, reqId, clanId, host_npid, &host_message); + SceNpClansError ret = clans_manager.client->sendInvitation(nph, handle, clanId, host_npid, &host_message); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -755,7 +726,7 @@ error_code sceNpClansSendInvitation(vm::ptr handle, Sce return CELL_OK; } -error_code sceNpClansCancelInvitation(vm::ptr handle, SceNpClanId clanId, vm::cptr npid) +error_code sceNpClansCancelInvitation(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr npid) { if (!g_fxo->get().is_initialized) { @@ -773,9 +744,7 @@ error_code sceNpClansCancelInvitation(vm::ptr handle, S SceNpId host_npid = {}; std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId)); - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->cancelInvitation(nph, reqId, clanId, host_npid); + SceNpClansError ret = clans_manager.client->cancelInvitation(nph, handle, clanId, host_npid); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -784,7 +753,7 @@ error_code sceNpClansCancelInvitation(vm::ptr handle, S return CELL_OK; } -error_code sceNpClansSendInvitationResponse(vm::ptr handle, SceNpClanId clanId, vm::cptr message, b8 accept) +error_code sceNpClansSendInvitationResponse(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr message, b8 accept) { if (!g_fxo->get().is_initialized) { @@ -813,9 +782,7 @@ error_code sceNpClansSendInvitationResponse(vm::ptr han std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); } - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->sendInvitationResponse(nph, reqId, clanId, &host_message, accept); + SceNpClansError ret = clans_manager.client->sendInvitationResponse(nph, handle, clanId, &host_message, accept); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -824,7 +791,7 @@ error_code sceNpClansSendInvitationResponse(vm::ptr han return CELL_OK; } -error_code sceNpClansSendMembershipRequest(vm::ptr handle, u32 clanId, vm::cptr message) +error_code sceNpClansSendMembershipRequest(SceNpClansRequestHandle handle, u32 clanId, vm::cptr message) { if (!g_fxo->get().is_initialized) { @@ -848,9 +815,7 @@ error_code sceNpClansSendMembershipRequest(vm::ptr hand std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); } - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->requestMembership(nph, reqId, clanId, &host_message); + SceNpClansError ret = clans_manager.client->requestMembership(nph, handle, clanId, &host_message); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -859,7 +824,7 @@ error_code sceNpClansSendMembershipRequest(vm::ptr hand return CELL_OK; } -error_code sceNpClansCancelMembershipRequest(vm::ptr handle, SceNpClanId clanId) +error_code sceNpClansCancelMembershipRequest(SceNpClansRequestHandle handle, SceNpClanId clanId) { if (!g_fxo->get().is_initialized) { @@ -869,9 +834,7 @@ error_code sceNpClansCancelMembershipRequest(vm::ptr ha auto& nph = g_fxo->get>(); auto& clans_manager = g_fxo->get(); - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->cancelRequestMembership(nph, reqId, clanId); + SceNpClansError ret = clans_manager.client->cancelRequestMembership(nph, handle, clanId); if (ret != SCE_NP_CLANS_SUCCESS) { @@ -881,7 +844,7 @@ error_code sceNpClansCancelMembershipRequest(vm::ptr ha return CELL_OK; } -error_code sceNpClansSendMembershipResponse(vm::ptr handle, SceNpClanId clanId, vm::cptr npid, vm::cptr message, b8 allow) +error_code sceNpClansSendMembershipResponse(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr npid, vm::cptr message, b8 allow) { if (!g_fxo->get().is_initialized) { @@ -913,9 +876,7 @@ error_code sceNpClansSendMembershipResponse(vm::ptr han std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage)); } - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->sendMembershipResponse(nph, reqId, clanId, host_npid, &host_message, allow); + SceNpClansError ret = clans_manager.client->sendMembershipResponse(nph, handle, clanId, host_npid, &host_message, allow); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -924,7 +885,7 @@ error_code sceNpClansSendMembershipResponse(vm::ptr han return CELL_OK; } -error_code sceNpClansGetBlacklist(vm::ptr handle, SceNpClanId clanId, vm::cptr paging, vm::ptr bl, vm::ptr pageResult) +error_code sceNpClansGetBlacklist(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr paging, vm::ptr bl, vm::ptr pageResult) { if (!g_fxo->get().is_initialized) { @@ -961,9 +922,7 @@ error_code sceNpClansGetBlacklist(vm::ptr handle, SceNp SceNpClansBlacklistEntry host_blacklist[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; SceNpClansPagingResult host_pageResult = {}; - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->getBlacklist(nph, reqId, clanId, &host_paging, host_blacklist, &host_pageResult); + SceNpClansError ret = clans_manager.client->getBlacklist(nph, handle, clanId, &host_paging, host_blacklist, &host_pageResult); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -978,7 +937,7 @@ error_code sceNpClansGetBlacklist(vm::ptr handle, SceNp return CELL_OK; } -error_code sceNpClansAddBlacklistEntry(vm::ptr handle, SceNpClanId clanId, vm::cptr member) +error_code sceNpClansAddBlacklistEntry(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr member) { if (!g_fxo->get().is_initialized) { @@ -996,9 +955,7 @@ error_code sceNpClansAddBlacklistEntry(vm::ptr handle, SceNpId host_member = {}; std::memcpy(&host_member, member.get_ptr(), sizeof(SceNpId)); - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->addBlacklistEntry(nph, reqId, clanId, host_member); + SceNpClansError ret = clans_manager.client->addBlacklistEntry(nph, handle, clanId, host_member); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -1007,7 +964,7 @@ error_code sceNpClansAddBlacklistEntry(vm::ptr handle, return CELL_OK; } -error_code sceNpClansRemoveBlacklistEntry(vm::ptr handle, SceNpClanId clanId, vm::cptr member) +error_code sceNpClansRemoveBlacklistEntry(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr member) { if (!g_fxo->get().is_initialized) { @@ -1025,9 +982,7 @@ error_code sceNpClansRemoveBlacklistEntry(vm::ptr handl SceNpId host_member = {}; std::memcpy(&host_member, member.get_ptr(), sizeof(SceNpId)); - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->removeBlacklistEntry(nph, reqId, clanId, host_member); + SceNpClansError ret = clans_manager.client->removeBlacklistEntry(nph, handle, clanId, host_member); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -1036,7 +991,7 @@ error_code sceNpClansRemoveBlacklistEntry(vm::ptr handl return CELL_OK; } -error_code sceNpClansRetrieveAnnouncements(vm::ptr handle, SceNpClanId clanId, vm::cptr paging, vm::ptr mlist, vm::ptr pageResult) +error_code sceNpClansRetrieveAnnouncements(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr paging, vm::ptr mlist, vm::ptr pageResult) { if (!g_fxo->get().is_initialized) { @@ -1073,9 +1028,7 @@ error_code sceNpClansRetrieveAnnouncements(vm::ptr hand SceNpClansMessageEntry host_announcements[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; SceNpClansPagingResult host_pageResult = {}; - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->retrieveAnnouncements(nph, reqId, clanId, &host_paging, host_announcements, &host_pageResult); + SceNpClansError ret = clans_manager.client->retrieveAnnouncements(nph, handle, clanId, &host_paging, host_announcements, &host_pageResult); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -1090,7 +1043,7 @@ error_code sceNpClansRetrieveAnnouncements(vm::ptr hand return CELL_OK; } -error_code sceNpClansPostAnnouncement(vm::ptr handle, SceNpClanId clanId, vm::cptr message, vm::cptr data, u32 duration, vm::ptr mId) +error_code sceNpClansPostAnnouncement(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr message, vm::cptr data, u32 duration, vm::ptr mId) { if (!g_fxo->get().is_initialized) { @@ -1119,10 +1072,8 @@ error_code sceNpClansPostAnnouncement(vm::ptr handle, S std::memcpy(&host_data, data.get_ptr(), sizeof(SceNpClansMessageData)); } - s32 reqId = static_cast(handle.addr()); - SceNpClansMessageId host_announcementId = 0; - SceNpClansError ret = clans_manager.client->postAnnouncement(nph, reqId, clanId, &host_announcement, &host_data, duration, &host_announcementId); + SceNpClansError ret = clans_manager.client->postAnnouncement(nph, handle, clanId, &host_announcement, &host_data, duration, &host_announcementId); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -1133,7 +1084,7 @@ error_code sceNpClansPostAnnouncement(vm::ptr handle, S return CELL_OK; } -error_code sceNpClansRemoveAnnouncement(vm::ptr handle, SceNpClanId clanId, SceNpClansMessageId mId) +error_code sceNpClansRemoveAnnouncement(SceNpClansRequestHandle handle, SceNpClanId clanId, SceNpClansMessageId mId) { if (!g_fxo->get().is_initialized) { @@ -1143,9 +1094,7 @@ error_code sceNpClansRemoveAnnouncement(vm::ptr handle, auto& clans_manager = g_fxo->get(); auto& nph = g_fxo->get>(); - s32 reqId = static_cast(handle.addr()); - - SceNpClansError ret = clans_manager.client->deleteAnnouncement(nph, reqId, clanId, mId); + SceNpClansError ret = clans_manager.client->deleteAnnouncement(nph, handle, clanId, mId); if (ret != SCE_NP_CLANS_SUCCESS) { return ret; @@ -1154,7 +1103,7 @@ error_code sceNpClansRemoveAnnouncement(vm::ptr handle, return CELL_OK; } -error_code sceNpClansPostChallenge(vm::ptr handle, SceNpClanId clanId, SceNpClanId targetClan, vm::cptr message, vm::cptr data, u32 duration, vm::ptr mId) +error_code sceNpClansPostChallenge(SceNpClansRequestHandle handle, SceNpClanId clanId, SceNpClanId targetClan, vm::cptr message, vm::cptr data, u32 duration, vm::ptr mId) { sceNpClans.todo("sceNpClansPostChallenge(handle=*0x%x, clanId=%d, targetClan=%d, message=*0x%x, data=*0x%x, duration=%d, mId=*0x%x)", handle, clanId, targetClan, message, data, duration, mId); @@ -1181,7 +1130,7 @@ error_code sceNpClansPostChallenge(vm::ptr handle, SceN return CELL_OK; } -error_code sceNpClansRetrievePostedChallenges(vm::ptr handle, SceNpClanId clanId, SceNpClanId targetClan, vm::cptr paging, vm::ptr mList, vm::ptr pageResult) +error_code sceNpClansRetrievePostedChallenges(SceNpClansRequestHandle handle, SceNpClanId clanId, SceNpClanId targetClan, vm::cptr paging, vm::ptr mList, vm::ptr pageResult) { sceNpClans.todo("sceNpClansRetrievePostedChallenges(handle=*0x%x, clanId=%d, targetClan=%d, paging=*0x%x, mList=*0x%x, pageResult=*0x%x)", handle, clanId, targetClan, paging, mList, pageResult); @@ -1206,7 +1155,7 @@ error_code sceNpClansRetrievePostedChallenges(vm::ptr h return CELL_OK; } -error_code sceNpClansRemovePostedChallenge(vm::ptr handle, SceNpClanId clanId, SceNpClanId targetClan, SceNpClansMessageId mId) +error_code sceNpClansRemovePostedChallenge(SceNpClansRequestHandle handle, SceNpClanId clanId, SceNpClanId targetClan, SceNpClansMessageId mId) { sceNpClans.todo("sceNpClansRemovePostedChallenge(handle=*0x%x, clanId=%d, targetClan=%d, mId=%d)", handle, clanId, targetClan, mId); @@ -1218,7 +1167,7 @@ error_code sceNpClansRemovePostedChallenge(vm::ptr hand return CELL_OK; } -error_code sceNpClansRetrieveChallenges(vm::ptr handle, SceNpClanId clanId, vm::cptr paging, vm::ptr mList, vm::ptr pageResult) +error_code sceNpClansRetrieveChallenges(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr paging, vm::ptr mList, vm::ptr pageResult) { sceNpClans.todo("sceNpClansRetrieveChallenges(handle=*0x%x, clanId=%d, paging=*0x%x, mList=*0x%x, pageResult=*0x%x)", handle, clanId, paging, mList, pageResult); diff --git a/rpcs3/Emu/Cell/Modules/sceNpClans.h b/rpcs3/Emu/Cell/Modules/sceNpClans.h index 6e4585ed43..9b31c87d25 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpClans.h +++ b/rpcs3/Emu/Cell/Modules/sceNpClans.h @@ -140,8 +140,7 @@ enum }; // Request handle for clan API -struct SceNpClansRequest {}; -using SceNpClansRequestHandle = vm::ptr; +using SceNpClansRequestHandle = u32; // Paging request structure struct SceNpClansPagingRequest diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index 09fc012a04..f416142d22 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -202,7 +202,7 @@ namespace clan return SceNpClansError::SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::destroyRequest(s32 reqId) + SceNpClansError clans_client::destroyRequest(u32 reqId) { if (idm::remove(reqId)) return SceNpClansError::SCE_NP_CLANS_SUCCESS; @@ -210,7 +210,7 @@ namespace clan return SceNpClansError::SCE_NP_CLANS_ERROR_INVALID_ARGUMENT; } - SceNpClansError clans_client::sendRequest(s32 reqId, ClanRequestAction action, ClanManagerOperationType opType, pugi::xml_document* xmlBody, pugi::xml_document* outResponse) + SceNpClansError clans_client::sendRequest(u32 reqId, ClanRequestAction action, ClanManagerOperationType opType, pugi::xml_document* xmlBody, pugi::xml_document* outResponse) { auto ctx = idm::get_unlocked(reqId); @@ -296,7 +296,6 @@ namespace clan nph.req_ticket(0x00020001, &npid, service_id, cookie, cookie_size, entitlement_id, consumed_count); np::ticket ticket = nph.get_clan_ticket(); - if (ticket.empty()) { clan_log.error("Failed to get clan ticket"); @@ -313,7 +312,7 @@ namespace clan } #pragma region Outgoing API Requests - SceNpClansError clans_client::getClanList(np::np_handler& nph, s32 reqId, SceNpClansPagingRequest* paging, SceNpClansEntry* clanList, SceNpClansPagingResult* pageResult) + SceNpClansError clans_client::getClanList(np::np_handler& nph, u32 reqId, SceNpClansPagingRequest* paging, SceNpClansEntry* clanList, SceNpClansPagingResult* pageResult) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -390,7 +389,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::getClanInfo(s32 reqId, SceNpClanId clanId, SceNpClansClanInfo* clanInfo) + SceNpClansError clans_client::getClanInfo(u32 reqId, SceNpClanId clanId, SceNpClansClanInfo* clanInfo) { pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -429,7 +428,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::getMemberInfo(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMemberEntry* memInfo) + SceNpClansError clans_client::getMemberInfo(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMemberEntry* memInfo) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -499,7 +498,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::getMemberList(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMemberStatus /*status*/, SceNpClansMemberEntry* memList, SceNpClansPagingResult* pageResult) + SceNpClansError clans_client::getMemberList(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMemberStatus /*status*/, SceNpClansMemberEntry* memList, SceNpClansPagingResult* pageResult) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -577,7 +576,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::getBlacklist(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansBlacklistEntry* bl, SceNpClansPagingResult* pageResult) + SceNpClansError clans_client::getBlacklist(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansBlacklistEntry* bl, SceNpClansPagingResult* pageResult) { std::string ticket = getClanTicket(nph); @@ -641,7 +640,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::addBlacklistEntry(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId) + SceNpClansError clans_client::addBlacklistEntry(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpId npId) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -659,7 +658,7 @@ namespace clan return sendRequest(reqId, ClanRequestAction::RecordBlacklistEntry, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::removeBlacklistEntry(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId) + SceNpClansError clans_client::removeBlacklistEntry(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpId npId) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -677,7 +676,7 @@ namespace clan return sendRequest(reqId, ClanRequestAction::DeleteBlacklistEntry, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::clanSearch(s32 reqId, SceNpClansPagingRequest* paging, SceNpClansSearchableName* search, SceNpClansClanBasicInfo* clanList, SceNpClansPagingResult* pageResult) + SceNpClansError clans_client::clanSearch(u32 reqId, SceNpClansPagingRequest* paging, SceNpClansSearchableName* search, SceNpClansClanBasicInfo* clanList, SceNpClansPagingResult* pageResult) { pugi::xml_document doc = pugi::xml_document(); pugi::xml_node clan = doc.append_child("clan"); @@ -739,7 +738,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::requestMembership(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessage* /*message*/) + SceNpClansError clans_client::requestMembership(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansMessage* /*message*/) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -754,7 +753,7 @@ namespace clan return sendRequest(reqId, ClanRequestAction::RequestMembership, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::cancelRequestMembership(np::np_handler& nph, s32 reqId, SceNpClanId clanId) + SceNpClansError clans_client::cancelRequestMembership(np::np_handler& nph, u32 reqId, SceNpClanId clanId) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -769,7 +768,7 @@ namespace clan return sendRequest(reqId, ClanRequestAction::CancelRequestMembership, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::sendMembershipResponse(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/, b8 allow) + SceNpClansError clans_client::sendMembershipResponse(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/, b8 allow) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -787,7 +786,7 @@ namespace clan return sendRequest(reqId, allow ? ClanRequestAction::AcceptMembershipRequest : ClanRequestAction::DeclineMembershipRequest, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::sendInvitation(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/) + SceNpClansError clans_client::sendInvitation(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -805,7 +804,7 @@ namespace clan return sendRequest(reqId, ClanRequestAction::SendInvitation, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::cancelInvitation(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId) + SceNpClansError clans_client::cancelInvitation(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpId npId) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -823,7 +822,7 @@ namespace clan return sendRequest(reqId, ClanRequestAction::CancelInvitation, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::sendInvitationResponse(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessage* /*message*/, b8 accept) + SceNpClansError clans_client::sendInvitationResponse(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansMessage* /*message*/, b8 accept) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -838,7 +837,7 @@ namespace clan return sendRequest(reqId, accept ? ClanRequestAction::AcceptInvitation : ClanRequestAction::DeclineInvitation, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::updateMemberInfo(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansUpdatableMemberInfo* info) + SceNpClansError clans_client::updateMemberInfo(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansUpdatableMemberInfo* info) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -877,7 +876,7 @@ namespace clan return sendRequest(reqId, ClanRequestAction::UpdateMemberInfo, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::updateClanInfo(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansUpdatableClanInfo* info) + SceNpClansError clans_client::updateClanInfo(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansUpdatableClanInfo* info) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -897,7 +896,7 @@ namespace clan return sendRequest(reqId, ClanRequestAction::UpdateClanInfo, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::joinClan(np::np_handler& nph, s32 reqId, SceNpClanId clanId) + SceNpClansError clans_client::joinClan(np::np_handler& nph, u32 reqId, SceNpClanId clanId) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -912,7 +911,7 @@ namespace clan return sendRequest(reqId, ClanRequestAction::JoinClan, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::leaveClan(np::np_handler& nph, s32 reqId, SceNpClanId clanId) + SceNpClansError clans_client::leaveClan(np::np_handler& nph, u32 reqId, SceNpClanId clanId) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -927,7 +926,7 @@ namespace clan return sendRequest(reqId, ClanRequestAction::LeaveClan, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::kickMember(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/) + SceNpClansError clans_client::kickMember(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* /*message*/) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -945,7 +944,7 @@ namespace clan return sendRequest(reqId, ClanRequestAction::KickMember, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::changeMemberRole(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMemberRole role) + SceNpClansError clans_client::changeMemberRole(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMemberRole role) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -966,7 +965,7 @@ namespace clan return sendRequest(reqId, ClanRequestAction::ChangeMemberRole, ClanManagerOperationType::UPDATE, &doc, &response); } - SceNpClansError clans_client::retrieveAnnouncements(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMessageEntry* announcements, SceNpClansPagingResult* pageResult) + SceNpClansError clans_client::retrieveAnnouncements(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMessageEntry* announcements, SceNpClansPagingResult* pageResult) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -1049,7 +1048,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::postAnnouncement(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessage* announcement, SceNpClansMessageData* /*data*/, u32 duration, SceNpClansMessageId* msgId) + SceNpClansError clans_client::postAnnouncement(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansMessage* announcement, SceNpClansMessageData* /*data*/, u32 duration, SceNpClansMessageId* msgId) { std::string ticket = getClanTicket(nph); if (ticket.empty()) @@ -1082,7 +1081,7 @@ namespace clan return SCE_NP_CLANS_SUCCESS; } - SceNpClansError clans_client::deleteAnnouncement(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessageId announcementId) + SceNpClansError clans_client::deleteAnnouncement(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansMessageId announcementId) { std::string ticket = getClanTicket(nph); if (ticket.empty()) diff --git a/rpcs3/Emu/NP/clans_client.h b/rpcs3/Emu/NP/clans_client.h index 776ec17365..15238c4caf 100644 --- a/rpcs3/Emu/NP/clans_client.h +++ b/rpcs3/Emu/NP/clans_client.h @@ -70,7 +70,7 @@ namespace clan static size_t curlWriteCallback(void* data, size_t size, size_t nmemb, void* clientp); - SceNpClansError sendRequest(s32 reqId, ClanRequestAction action, ClanManagerOperationType type, pugi::xml_document* xmlBody, pugi::xml_document* outResponse); + SceNpClansError sendRequest(u32 reqId, ClanRequestAction action, ClanManagerOperationType type, pugi::xml_document* xmlBody, pugi::xml_document* outResponse); /// @brief Forge and get a V2.1 Ticket for clan operations std::string getClanTicket(np::np_handler& nph); @@ -80,40 +80,40 @@ namespace clan ~clans_client(); SceNpClansError createRequest(s32* reqId); - SceNpClansError destroyRequest(s32 reqId); + SceNpClansError destroyRequest(u32 reqId); - SceNpClansError clanSearch(s32 reqId, SceNpClansPagingRequest* paging, SceNpClansSearchableName* search, SceNpClansClanBasicInfo* clanList, SceNpClansPagingResult* pageResult); + SceNpClansError clanSearch(u32 reqId, SceNpClansPagingRequest* paging, SceNpClansSearchableName* search, SceNpClansClanBasicInfo* clanList, SceNpClansPagingResult* pageResult); - SceNpClansError getClanList(np::np_handler& nph, s32 reqId, SceNpClansPagingRequest* paging, SceNpClansEntry* clanList, SceNpClansPagingResult* pageResult); - SceNpClansError getClanInfo(s32 reqId, SceNpClanId clanId, SceNpClansClanInfo* clanInfo); + SceNpClansError getClanList(np::np_handler& nph, u32 reqId, SceNpClansPagingRequest* paging, SceNpClansEntry* clanList, SceNpClansPagingResult* pageResult); + SceNpClansError getClanInfo(u32 reqId, SceNpClanId clanId, SceNpClansClanInfo* clanInfo); - SceNpClansError getMemberInfo(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMemberEntry* memInfo); - SceNpClansError getMemberList(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMemberStatus status, SceNpClansMemberEntry* memList, SceNpClansPagingResult* pageResult); + SceNpClansError getMemberInfo(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMemberEntry* memInfo); + SceNpClansError getMemberList(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMemberStatus status, SceNpClansMemberEntry* memList, SceNpClansPagingResult* pageResult); - SceNpClansError getBlacklist(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansBlacklistEntry* bl, SceNpClansPagingResult* pageResult); - SceNpClansError addBlacklistEntry(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId); - SceNpClansError removeBlacklistEntry(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId); + SceNpClansError getBlacklist(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansBlacklistEntry* bl, SceNpClansPagingResult* pageResult); + SceNpClansError addBlacklistEntry(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpId npId); + SceNpClansError removeBlacklistEntry(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpId npId); - SceNpClansError requestMembership(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessage* message); - SceNpClansError cancelRequestMembership(np::np_handler& nph, s32 reqId, SceNpClanId clanId); - SceNpClansError sendMembershipResponse(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* message, b8 allow); + SceNpClansError requestMembership(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansMessage* message); + SceNpClansError cancelRequestMembership(np::np_handler& nph, u32 reqId, SceNpClanId clanId); + SceNpClansError sendMembershipResponse(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* message, b8 allow); - SceNpClansError sendInvitation(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* message); - SceNpClansError cancelInvitation(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId); - SceNpClansError sendInvitationResponse(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessage* message, b8 accept); + SceNpClansError sendInvitation(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* message); + SceNpClansError cancelInvitation(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpId npId); + SceNpClansError sendInvitationResponse(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansMessage* message, b8 accept); - SceNpClansError joinClan(np::np_handler& nph, s32 reqId, SceNpClanId clanId); - SceNpClansError leaveClan(np::np_handler& nph, s32 reqId, SceNpClanId clanId); + SceNpClansError joinClan(np::np_handler& nph, u32 reqId, SceNpClanId clanId); + SceNpClansError leaveClan(np::np_handler& nph, u32 reqId, SceNpClanId clanId); - SceNpClansError updateMemberInfo(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansUpdatableMemberInfo* info); - SceNpClansError updateClanInfo(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansUpdatableClanInfo* info); + SceNpClansError updateMemberInfo(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansUpdatableMemberInfo* info); + SceNpClansError updateClanInfo(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansUpdatableClanInfo* info); - SceNpClansError kickMember(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* message); - SceNpClansError changeMemberRole(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMemberRole role); + SceNpClansError kickMember(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMessage* message); + SceNpClansError changeMemberRole(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpId npId, SceNpClansMemberRole role); - SceNpClansError retrieveAnnouncements(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMessageEntry* announcements, SceNpClansPagingResult* pageResult); - SceNpClansError postAnnouncement(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessage* announcement, SceNpClansMessageData* data, u32 duration, SceNpClansMessageId* announcementId); - SceNpClansError deleteAnnouncement(np::np_handler& nph, s32 reqId, SceNpClanId clanId, SceNpClansMessageId announcementId); + SceNpClansError retrieveAnnouncements(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansPagingRequest* paging, SceNpClansMessageEntry* announcements, SceNpClansPagingResult* pageResult); + SceNpClansError postAnnouncement(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansMessage* announcement, SceNpClansMessageData* data, u32 duration, SceNpClansMessageId* announcementId); + SceNpClansError deleteAnnouncement(np::np_handler& nph, u32 reqId, SceNpClanId clanId, SceNpClansMessageId announcementId); }; } // namespace clan diff --git a/rpcs3/Emu/NP/np_handler.cpp b/rpcs3/Emu/NP/np_handler.cpp index 099f5c5b93..4aa24a954d 100644 --- a/rpcs3/Emu/NP/np_handler.cpp +++ b/rpcs3/Emu/NP/np_handler.cpp @@ -1366,8 +1366,14 @@ namespace np ticket np_handler::get_clan_ticket() { - std::unique_lock lock(mutex_clan_ticket); - cv_clan_ticket.wait(lock, [this] { return clan_ticket_ready.load(); }); + clan_ticket_ready.wait(false, atomic_wait_timeout{60'000'000'000}); // 60 seconds + + if (!clan_ticket_ready.load()) + { + rpcn_log.error("Failed to get clan ticket within timeout."); + return ticket{}; + } + return clan_ticket; } diff --git a/rpcs3/Emu/NP/np_handler.h b/rpcs3/Emu/NP/np_handler.h index 5b8e841961..d2113688ce 100644 --- a/rpcs3/Emu/NP/np_handler.h +++ b/rpcs3/Emu/NP/np_handler.h @@ -419,9 +419,7 @@ namespace np ticket current_ticket; // Clan ticket - std::mutex mutex_clan_ticket; - std::condition_variable_any cv_clan_ticket; - atomic_t clan_ticket_ready = false; + atomic_t clan_ticket_ready = 0; ticket clan_ticket; // IP & DNS info diff --git a/rpcs3/Emu/NP/np_requests.cpp b/rpcs3/Emu/NP/np_requests.cpp index a52d807de5..ce66c23b30 100644 --- a/rpcs3/Emu/NP/np_requests.cpp +++ b/rpcs3/Emu/NP/np_requests.cpp @@ -871,10 +871,10 @@ namespace np // Clans: check if ticket belongs to the clan service. If so, store it. if (current_ticket.get_service_id() == CLANS_SERVICE_ID) { - std::lock_guard lock(mutex_clan_ticket); clan_ticket = current_ticket; - clan_ticket_ready = true; - cv_clan_ticket.notify_all(); + clan_ticket_ready.store(true); + clan_ticket_ready.notify_all(); + return; } From 08091994d9289d257d8917bbfd06c7563b547a1d Mon Sep 17 00:00:00 2001 From: zeph Date: Wed, 10 Dec 2025 20:42:01 +0100 Subject: [PATCH 25/30] Clans: safety fixes - Replaced `sscanf` with `np::string_to_npid` - Reinstated log messages on level `warning` for implemented functions Signed-off-by: zeph --- rpcs3/Emu/Cell/Modules/sceNpClans.cpp | 83 ++++++++++++++++++--------- rpcs3/Emu/NP/clans_client.cpp | 53 +++++------------ 2 files changed, 70 insertions(+), 66 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp index 69204af8d6..03fb1f9b38 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp @@ -80,6 +80,8 @@ void fmt_class_string::format(std::string& out, u64 arg) error_code sceNpClansInit(vm::cptr commId, vm::cptr passphrase, vm::ptr pool, vm::ptr poolSize, u32 flags) { + sceNpClans.warning("sceNpClansInit(commId=*0x%x, passphrase=*0x%x, pool=*0x%x, poolSize=*0x%x, flags=0x%x)", commId, passphrase, pool, poolSize, flags); + auto& clans_manager = g_fxo->get(); if (clans_manager.is_initialized) @@ -107,6 +109,8 @@ error_code sceNpClansInit(vm::cptr commId, vm::cptrget(); if (!clans_manager.is_initialized) @@ -122,6 +126,8 @@ error_code sceNpClansTerm() error_code sceNpClansCreateRequest(vm::ptr handle, u64 flags) { + sceNpClans.warning("sceNpClansCreateRequest(handle=*0x%x, flags=0x%x)", handle, flags); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -146,13 +152,15 @@ error_code sceNpClansCreateRequest(vm::ptr handle, u64 return res; } - vm::write32(handle.addr(), static_cast(reqId)); + *handle = reqId; return CELL_OK; } error_code sceNpClansDestroyRequest(SceNpClansRequestHandle handle) { + sceNpClans.warning("sceNpClansDestroyRequest(handle=*0x%x)", handle); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -171,6 +179,8 @@ error_code sceNpClansDestroyRequest(SceNpClansRequestHandle handle) error_code sceNpClansAbortRequest(SceNpClansRequestHandle handle) { + sceNpClans.warning("sceNpClansAbortRequest(handle=*0x%x)", handle); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -224,6 +234,8 @@ error_code sceNpClansDisbandClan(SceNpClansRequestHandle handle, SceNpClanId cla error_code sceNpClansGetClanList(SceNpClansRequestHandle handle, vm::cptr paging, vm::ptr clanList, vm::ptr pageResult) { + sceNpClans.warning("sceNpClansGetClanList(handle=*0x%x, paging=*0x%x, clanList=*0x%x, pageResult=*0x%x)", handle, paging, clanList, pageResult); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -250,11 +262,6 @@ error_code sceNpClansGetClanList(SceNpClansRequestHandle handle, vm::cptr paging, vm::cptr search, vm::ptr results, vm::ptr pageResult) { + sceNpClans.warning("sceNpClansSearchByName(handle=*0x%x, paging=*0x%x, search=*0x%x, results=*0x%x, pageResult=*0x%x)", handle, paging, search, results, pageResult); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -353,11 +362,6 @@ error_code sceNpClansSearchByName(SceNpClansRequestHandle handle, vm::cptr info) { + sceNpClans.warning("sceNpClansGetClanInfo(handle=*0x%x, clanId=*0x%x, info=*0x%x)", handle, clanId, info); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -410,6 +416,8 @@ error_code sceNpClansGetClanInfo(SceNpClansRequestHandle handle, SceNpClanId cla error_code sceNpClansUpdateClanInfo(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr info) { + sceNpClans.warning("sceNpClansUpdateClanInfo(handle=*0x%x, clanId=*0x%x, info=*0x%x)", handle, clanId, info); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -438,6 +446,8 @@ error_code sceNpClansUpdateClanInfo(SceNpClansRequestHandle handle, SceNpClanId error_code sceNpClansGetMemberList(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr paging, SceNpClansMemberStatus status, vm::ptr memList, vm::ptr pageResult) { + sceNpClans.warning("sceNpClansGetMemberList(handle=*0x%x, clanId=*0x%x, paging=*0x%x, status=0x%x, memList=*0x%x, pageResult=*0x%x)", handle, clanId, paging, status, memList, pageResult); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -464,11 +474,6 @@ error_code sceNpClansGetMemberList(SceNpClansRequestHandle handle, SceNpClanId c { std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); } - else - { - host_paging.startPos = 0; - host_paging.max = 0; - } SceNpClansMemberEntry* host_memList_addr = new SceNpClansMemberEntry[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX]; SceNpClansPagingResult host_pageResult = {}; @@ -492,6 +497,8 @@ error_code sceNpClansGetMemberList(SceNpClansRequestHandle handle, SceNpClanId c error_code sceNpClansGetMemberInfo(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr npid, vm::ptr memInfo) { + sceNpClans.warning("sceNpClansGetMemberInfo(handle=*0x%x, clanId=*0x%x, npid=*0x%x, memInfo=*0x%x)", handle, clanId, npid, memInfo); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -523,6 +530,8 @@ error_code sceNpClansGetMemberInfo(SceNpClansRequestHandle handle, SceNpClanId c error_code sceNpClansUpdateMemberInfo(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr info) { + sceNpClans.warning("sceNpClansUpdateMemberInfo(handle=*0x%x, clanId=*0x%x, info=*0x%x)", handle, clanId, info); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -550,6 +559,8 @@ error_code sceNpClansUpdateMemberInfo(SceNpClansRequestHandle handle, SceNpClanI error_code sceNpClansChangeMemberRole(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr npid, u32 role) { + sceNpClans.warning("sceNpClansChangeMemberRole(handle=*0x%x, clanId=*0x%x, npid=*0x%x, role=0x%x)", handle, clanId, npid, role); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -608,6 +619,8 @@ error_code sceNpClansUpdateAutoAcceptStatus(SceNpClansRequestHandle handle, SceN error_code sceNpClansJoinClan(SceNpClansRequestHandle handle, SceNpClanId clanId) { + sceNpClans.warning("sceNpClansJoinClan(handle=*0x%x, clanId=*0x%x)", handle, clanId); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -627,6 +640,8 @@ error_code sceNpClansJoinClan(SceNpClansRequestHandle handle, SceNpClanId clanId error_code sceNpClansLeaveClan(SceNpClansRequestHandle handle, SceNpClanId clanId) { + sceNpClans.warning("sceNpClansLeaveClan(handle=*0x%x, clanId=*0x%x)", handle, clanId); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -646,6 +661,8 @@ error_code sceNpClansLeaveClan(SceNpClansRequestHandle handle, SceNpClanId clanI error_code sceNpClansKickMember(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr npid, vm::cptr message) { + sceNpClans.warning("sceNpClansKickMember(handle=*0x%x, clanId=*0x%x, npid=*0x%x, message=*0x%x)", handle, clanId, npid, message); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -687,6 +704,8 @@ error_code sceNpClansKickMember(SceNpClansRequestHandle handle, SceNpClanId clan error_code sceNpClansSendInvitation(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr npid, vm::cptr message) { + sceNpClans.warning("sceNpClansSendInvitation(handle=*0x%x, clanId=*0x%x, npid=*0x%x, message=*0x%x)", handle, clanId, npid, message); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -728,6 +747,8 @@ error_code sceNpClansSendInvitation(SceNpClansRequestHandle handle, SceNpClanId error_code sceNpClansCancelInvitation(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr npid) { + sceNpClans.warning("sceNpClansCancelInvitation(handle=*0x%x, clanId=*0x%x, npid=*0x%x)", handle, clanId, npid); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -755,6 +776,8 @@ error_code sceNpClansCancelInvitation(SceNpClansRequestHandle handle, SceNpClanI error_code sceNpClansSendInvitationResponse(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr message, b8 accept) { + sceNpClans.warning("sceNpClansSendInvitationResponse(handle=*0x%x, clanId=*0x%x, message=*0x%x, accept=%d)", handle, clanId, message, accept); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -793,6 +816,8 @@ error_code sceNpClansSendInvitationResponse(SceNpClansRequestHandle handle, SceN error_code sceNpClansSendMembershipRequest(SceNpClansRequestHandle handle, u32 clanId, vm::cptr message) { + sceNpClans.warning("sceNpClansSendMembershipRequest(handle=*0x%x, clanId=*0x%x, message=*0x%x)", handle, clanId, message); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -826,6 +851,8 @@ error_code sceNpClansSendMembershipRequest(SceNpClansRequestHandle handle, u32 c error_code sceNpClansCancelMembershipRequest(SceNpClansRequestHandle handle, SceNpClanId clanId) { + sceNpClans.warning("sceNpClansCancelMembershipRequest(handle=*0x%x, clanId=*0x%x)", handle, clanId); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -846,6 +873,8 @@ error_code sceNpClansCancelMembershipRequest(SceNpClansRequestHandle handle, Sce error_code sceNpClansSendMembershipResponse(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr npid, vm::cptr message, b8 allow) { + sceNpClans.warning("sceNpClansSendMembershipResponse(handle=*0x%x, clanId=*0x%x, npid=*0x%x, message=*0x%x, allow=%d)", handle, clanId, npid, message, allow); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -887,6 +916,8 @@ error_code sceNpClansSendMembershipResponse(SceNpClansRequestHandle handle, SceN error_code sceNpClansGetBlacklist(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr paging, vm::ptr bl, vm::ptr pageResult) { + sceNpClans.warning("sceNpClansGetBlacklist(handle=*0x%x, clanId=*0x%x, paging=*0x%x, bl=*0x%x, pageResult=*0x%x)", handle, clanId, paging, bl, pageResult); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -913,11 +944,6 @@ error_code sceNpClansGetBlacklist(SceNpClansRequestHandle handle, SceNpClanId cl { std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); } - else - { - host_paging.startPos = 0; - host_paging.max = 0; - } SceNpClansBlacklistEntry host_blacklist[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; SceNpClansPagingResult host_pageResult = {}; @@ -939,6 +965,8 @@ error_code sceNpClansGetBlacklist(SceNpClansRequestHandle handle, SceNpClanId cl error_code sceNpClansAddBlacklistEntry(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr member) { + sceNpClans.warning("sceNpClansAddBlacklistEntry(handle=*0x%x, clanId=*0x%x, member=*0x%x)", handle, clanId, member); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -966,6 +994,8 @@ error_code sceNpClansAddBlacklistEntry(SceNpClansRequestHandle handle, SceNpClan error_code sceNpClansRemoveBlacklistEntry(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr member) { + sceNpClans.warning("sceNpClansRemoveBlacklistEntry(handle=*0x%x, clanId=*0x%x, member=*0x%x)", handle, clanId, member); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -993,6 +1023,8 @@ error_code sceNpClansRemoveBlacklistEntry(SceNpClansRequestHandle handle, SceNpC error_code sceNpClansRetrieveAnnouncements(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr paging, vm::ptr mlist, vm::ptr pageResult) { + sceNpClans.warning("sceNpClansRetrieveAnnouncements(handle=*0x%x, clanId=*0x%x, paging=*0x%x, mlist=*0x%x, pageResult=*0x%x)", handle, clanId, paging, mlist, pageResult); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -1019,11 +1051,6 @@ error_code sceNpClansRetrieveAnnouncements(SceNpClansRequestHandle handle, SceNp { std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); } - else - { - host_paging.startPos = 0; - host_paging.max = 0; - } SceNpClansMessageEntry host_announcements[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; SceNpClansPagingResult host_pageResult = {}; @@ -1045,6 +1072,8 @@ error_code sceNpClansRetrieveAnnouncements(SceNpClansRequestHandle handle, SceNp error_code sceNpClansPostAnnouncement(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr message, vm::cptr data, u32 duration, vm::ptr mId) { + sceNpClans.warning("sceNpClansPostAnnouncement(handle=*0x%x, clanId=*0x%x, message=*0x%x, data=*0x%x, duration=*0x%x, mId=*0x%x)", handle, clanId, message, data, duration, mId); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; @@ -1086,6 +1115,8 @@ error_code sceNpClansPostAnnouncement(SceNpClansRequestHandle handle, SceNpClanI error_code sceNpClansRemoveAnnouncement(SceNpClansRequestHandle handle, SceNpClanId clanId, SceNpClansMessageId mId) { + sceNpClans.warning("sceNpClansRemoveAnnouncement(handle=*0x%x, clanId=*0x%x, mId=*0x%x)", handle, clanId, mId); + if (!g_fxo->get().is_initialized) { return SCE_NP_CLANS_ERROR_NOT_INITIALIZED; diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index f416142d22..b491ef770d 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -23,6 +23,7 @@ #include "Emu/Cell/Modules/sceNpClans.h" #include "Emu/NP/clans_client.h" #include "Emu/NP/clans_config.h" +#include "Emu/NP/np_helpers.h" LOG_CHANNEL(clan_log, "clans"); @@ -453,21 +454,16 @@ namespace clan pugi::xml_attribute jid = info.attribute("jid"); std::string npid_str = jid.as_string(); - - char username[SCE_NET_NP_ONLINEID_MAX_LENGTH + 1] = {0}; - - sscanf(npid_str.c_str(), "%16[^@]", username); + std::string username = fmt::split(npid_str, {std::string("@")})[0]; SceNpId npid; - - if (!strcmp(username, nph.get_npid().handle.data)) + if (!strcmp(username.c_str(), nph.get_npid().handle.data)) { npid = nph.get_npid(); } else { - npid = SceNpId {}; - std::strncpy(npid.handle.data, username, SCE_NET_NP_ONLINEID_MAX_LENGTH + 1); + np::string_to_npid(npid_str, npid); } pugi::xml_node role = info.child("role"); @@ -479,9 +475,6 @@ namespace clan pugi::xml_node description = info.child("description"); std::string description_str = description.text().as_string(); - char description_char[256] = {0}; - strcpy_trunc(description_char, description_str); - *memInfo = SceNpClansMemberEntry { .npid = npid, @@ -492,8 +485,7 @@ namespace clan } }; - strcpy_trunc(memInfo->npid.handle.data, username); - strcpy_trunc(memInfo->updatable.description, description_char); + strcpy_trunc(memInfo->updatable.description, description_str.c_str()); return SCE_NP_CLANS_SUCCESS; } @@ -530,21 +522,16 @@ namespace clan for (pugi::xml_node info = list.child("info"); info; info = info.next_sibling("info")) { std::string npid_str = info.attribute("jid").as_string(); - - char username[SCE_NET_NP_ONLINEID_MAX_LENGTH + 1] = {0}; - - sscanf(npid_str.c_str(), "%16[^@]", username); + std::string username = fmt::split(npid_str, {std::string("@")})[0]; SceNpId npid; - - if (!strcmp(username, nph.get_npid().handle.data)) + if (!strcmp(username.c_str(), nph.get_npid().handle.data)) { npid = nph.get_npid(); } else { - npid = SceNpId {}; - std::strncpy(npid.handle.data, username, SCE_NET_NP_ONLINEID_MAX_LENGTH + 1); + np::string_to_npid(npid_str, npid); } uint32_t role_int = info.child("role").text().as_uint(); @@ -608,19 +595,8 @@ namespace clan pugi::xml_node id = node.child("jid"); std::string npid_str = id.text().as_string(); - char username[SCE_NET_NP_ONLINEID_MAX_LENGTH + 1] = {0}; - - sscanf(npid_str.c_str(), "%16[^@]", username); - - SceNpId npid = SceNpId - { - .handle = SceNpOnlineId - { - .data = "" - } - }; - - strcpy_trunc(npid.handle.data, username); + SceNpId npid = {}; + np::string_to_npid(npid_str.c_str(), npid); SceNpClansBlacklistEntry entry = SceNpClansBlacklistEntry { @@ -1004,19 +980,16 @@ namespace clan std::string npid_str = node.child("jid").text().as_string(); std::string msg_date = node.child("msg-date").text().as_string(); - char username[SCE_NET_NP_ONLINEID_MAX_LENGTH + 1] = {0}; - sscanf(npid_str.c_str(), "%16[^@]", username); - SceNpId npid; + std::string username = fmt::split(npid_str, {std::string("@")})[0]; - if (!strcmp(username, nph.get_npid().handle.data)) + if (!strcmp(username.c_str(), nph.get_npid().handle.data)) { npid = nph.get_npid(); } else { - npid = SceNpId {}; - std::strncpy(npid.handle.data, username, SCE_NET_NP_ONLINEID_MAX_LENGTH + 1); + np::string_to_npid(npid_str, npid); } // TODO: implement `binData` and `fromId` From c1cc1850098b2af82a7431bb7aee0d0874dcb848 Mon Sep 17 00:00:00 2001 From: zeph Date: Wed, 10 Dec 2025 20:44:29 +0100 Subject: [PATCH 26/30] Clans: caught more unnecessary `strcpy_trunc` Signed-off-by: zeph --- rpcs3/Emu/NP/clans_client.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index b491ef770d..3d4d162070 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -485,7 +485,7 @@ namespace clan } }; - strcpy_trunc(memInfo->updatable.description, description_str.c_str()); + strcpy_trunc(memInfo->updatable.description, description_str); return SCE_NP_CLANS_SUCCESS; } @@ -538,9 +538,6 @@ namespace clan uint32_t status_int = info.child("status").text().as_uint(); std::string description_str = info.child("description").text().as_string(); - char description_char[256] = {0}; - strcpy_trunc(description_char, description_str); - SceNpClansMemberEntry entry = SceNpClansMemberEntry { .npid = npid, @@ -548,7 +545,7 @@ namespace clan .status = static_cast(status_int), }; - strcpy_trunc(entry.updatable.description, description_char); + strcpy_trunc(entry.updatable.description, description_str); memList[i] = entry; i++; From 520d0390e47a4c1ce7be32153cff6a14537dc0bc Mon Sep 17 00:00:00 2001 From: zeph Date: Wed, 10 Dec 2025 20:55:21 +0100 Subject: [PATCH 27/30] Clans: fix empty dialog accidentally spawning Signed-off-by: zeph --- rpcs3/rpcs3qt/clans_settings_dialog.cpp | 6 ------ rpcs3/rpcs3qt/main_window.cpp | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/rpcs3/rpcs3qt/clans_settings_dialog.cpp b/rpcs3/rpcs3qt/clans_settings_dialog.cpp index 4a9ab3b2a0..4b54515415 100644 --- a/rpcs3/rpcs3qt/clans_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/clans_settings_dialog.cpp @@ -13,12 +13,6 @@ clans_settings_dialog::clans_settings_dialog(QWidget* parent) : QDialog(parent) { - if (!Emu.IsStopped()) - { - QMessageBox::critical(this, tr("Error: Emulation Running"), tr("You need to stop the emulator before editing Clans connection information!"), QMessageBox::Ok); - return; - } - g_cfg_clans.load(); setWindowTitle(tr("Clans Configuration")); diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index aee3ce5ca2..122b8d6ff0 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -2994,6 +2994,12 @@ void main_window::CreateConnects() connect(ui->confClansAct, &QAction::triggered, this, [this]() { + if (!Emu.IsStopped()) + { + QMessageBox::critical(this, tr("Error: Emulation Running"), tr("You need to stop the emulator before editing Clans connection information!"), QMessageBox::Ok); + return; + } + clans_settings_dialog dlg(this); dlg.exec(); }); From 69924c43edd70282dc569f20912eee738bd9fcdc Mon Sep 17 00:00:00 2001 From: zeph Date: Wed, 10 Dec 2025 20:56:38 +0100 Subject: [PATCH 28/30] Clans: stray import Signed-off-by: zeph --- rpcs3/rpcs3qt/clans_settings_dialog.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/rpcs3/rpcs3qt/clans_settings_dialog.cpp b/rpcs3/rpcs3qt/clans_settings_dialog.cpp index 4b54515415..9745d11294 100644 --- a/rpcs3/rpcs3qt/clans_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/clans_settings_dialog.cpp @@ -8,7 +8,6 @@ #include "clans_settings_dialog.h" #include "Emu/NP/clans_config.h" -#include "Emu/System.h" clans_settings_dialog::clans_settings_dialog(QWidget* parent) : QDialog(parent) From 19e9dcbbef917cecbda39ea8a6a887c7ac2f9dee Mon Sep 17 00:00:00 2001 From: zeph Date: Wed, 10 Dec 2025 21:09:59 +0100 Subject: [PATCH 29/30] Clans: covert constructor Signed-off-by: zeph --- rpcs3/Emu/NP/clans_client.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index 3d4d162070..022b9fa3d6 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -454,7 +454,7 @@ namespace clan pugi::xml_attribute jid = info.attribute("jid"); std::string npid_str = jid.as_string(); - std::string username = fmt::split(npid_str, {std::string("@")})[0]; + std::string username = fmt::split(npid_str, {"@"})[0]; SceNpId npid; if (!strcmp(username.c_str(), nph.get_npid().handle.data)) @@ -522,7 +522,7 @@ namespace clan for (pugi::xml_node info = list.child("info"); info; info = info.next_sibling("info")) { std::string npid_str = info.attribute("jid").as_string(); - std::string username = fmt::split(npid_str, {std::string("@")})[0]; + std::string username = fmt::split(npid_str, {"@"})[0]; SceNpId npid; if (!strcmp(username.c_str(), nph.get_npid().handle.data)) @@ -978,7 +978,7 @@ namespace clan std::string msg_date = node.child("msg-date").text().as_string(); SceNpId npid; - std::string username = fmt::split(npid_str, {std::string("@")})[0]; + std::string username = fmt::split(npid_str, {"@"})[0]; if (!strcmp(username.c_str(), nph.get_npid().handle.data)) { From e6e745fec6dbafb2de4f4c3da6850e05830c6893 Mon Sep 17 00:00:00 2001 From: zeph Date: Fri, 12 Dec 2025 01:35:17 +0100 Subject: [PATCH 30/30] Clans: polishing - Removed usage of raw pointers with `new` - `clans_client::getClanTicket` now _actually_ uses the cached ticket if available. - Avoid setting `current_ticket` and instead hijack it if it's the Clans ticket - Refusing to get a ticket if emulator is not connected to RPCN Signed-off-by: zeph --- rpcs3/Emu/Cell/Modules/sceNpClans.cpp | 4 +--- rpcs3/Emu/NP/clans_client.cpp | 21 +++++++++++++++++---- rpcs3/Emu/NP/np_requests.cpp | 16 +++++++++------- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp index 03fb1f9b38..03c6ce4da3 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp @@ -475,13 +475,12 @@ error_code sceNpClansGetMemberList(SceNpClansRequestHandle handle, SceNpClanId c std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest)); } - SceNpClansMemberEntry* host_memList_addr = new SceNpClansMemberEntry[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX]; + SceNpClansMemberEntry host_memList_addr[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {}; SceNpClansPagingResult host_pageResult = {}; SceNpClansError ret = clans_manager.client->getMemberList(nph, handle, clanId, &host_paging, status, host_memList_addr, &host_pageResult); if (ret != SCE_NP_CLANS_SUCCESS) { - delete[] host_memList_addr; return ret; } @@ -491,7 +490,6 @@ error_code sceNpClansGetMemberList(SceNpClansRequestHandle handle, SceNpClanId c } std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult)); - delete[] host_memList_addr; return CELL_OK; } diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index 022b9fa3d6..2efcebbb7e 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -1,3 +1,4 @@ +#include "Emu/Cell/Modules/sceNp.h" #include "stdafx.h" #include @@ -286,6 +287,11 @@ namespace clan std::string clans_client::getClanTicket(np::np_handler& nph) { + // Pretend we failed to get a ticket if the emulator isn't + // connected to RPCN. + if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE) + return ""; + const auto& npid = nph.get_npid(); const char* service_id = CLANS_SERVICE_ID; @@ -294,13 +300,20 @@ namespace clan const char* entitlement_id = CLANS_ENTITLEMENT_ID; const u32 consumed_count = 0; - nph.req_ticket(0x00020001, &npid, service_id, cookie, cookie_size, entitlement_id, consumed_count); - + // Use the cached ticket if available np::ticket ticket = nph.get_clan_ticket(); if (ticket.empty()) { - clan_log.error("Failed to get clan ticket"); - return ""; + // If not cached, request a new ticket + nph.req_ticket(0x00020001, &npid, service_id, cookie, cookie_size, entitlement_id, consumed_count); + ticket = nph.get_clan_ticket(); + + // If still empty, return error + if (ticket.empty()) + { + clan_log.error("Failed to get clan ticket"); + return ""; + } } std::vector ticket_bytes(1024); diff --git a/rpcs3/Emu/NP/np_requests.cpp b/rpcs3/Emu/NP/np_requests.cpp index ce66c23b30..d05baa6754 100644 --- a/rpcs3/Emu/NP/np_requests.cpp +++ b/rpcs3/Emu/NP/np_requests.cpp @@ -864,20 +864,22 @@ namespace np auto ticket_raw = reply.get_rawdata(); ensure(!reply.is_error(), "Malformed reply to RequestTicket command"); - current_ticket = ticket(std::move(ticket_raw)); - auto ticket_size = static_cast(current_ticket.size()); - - - // Clans: check if ticket belongs to the clan service. If so, store it. - if (current_ticket.get_service_id() == CLANS_SERVICE_ID) + auto incoming_ticket = ticket(std::move(ticket_raw)); + + // Clans: check if ticket belongs to the clan service. + // If so, hijack the ticket and cache it for future use. + if (incoming_ticket.get_service_id() == CLANS_SERVICE_ID) { - clan_ticket = current_ticket; + clan_ticket = incoming_ticket; clan_ticket_ready.store(true); clan_ticket_ready.notify_all(); return; } + current_ticket = incoming_ticket; + auto ticket_size = static_cast(current_ticket.size()); + if (manager_cb) { sysutil_register_cb([manager_cb = this->manager_cb, ticket_size, manager_cb_arg = this->manager_cb_arg](ppu_thread& cb_ppu) -> s32