Compare commits

...

40 Commits

Author SHA1 Message Date
zeph
c54dcd1565
Merge e6e745fec6 into cf87f24587 2025-12-15 20:12:26 +01:00
Megamouse
cf87f24587 cellGem: improve bayer demosaicing
Some checks are pending
Generate Translation Template / Generate Translation Template (push) Waiting to run
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (/rpcs3/.ci/build-linux-aarch64.sh, gcc, rpcs3/rpcs3-ci-jammy-aarch64:1.7, ubuntu-24.04-arm) (push) Waiting to run
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (/rpcs3/.ci/build-linux.sh, gcc, rpcs3/rpcs3-ci-jammy:1.7, ubuntu-24.04) (push) Waiting to run
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1, rpcs3/rpcs3-binaries-linux-arm64, /rpcs3/.ci/build-linux-aarch64.sh, clang, rpcs3/rpcs3-ci-jammy-aarch64:1.7, ubuntu-24.04-arm) (push) Waiting to run
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (d812f1254a1157c80fd402f94446310560f54e5f, rpcs3/rpcs3-binaries-linux, /rpcs3/.ci/build-linux.sh, clang, rpcs3/rpcs3-ci-jammy:1.7, ubuntu-24.04) (push) Waiting to run
Build RPCS3 / RPCS3 Mac ${{ matrix.name }} (51ae32f468089a8169aaf1567de355ff4a3e0842, rpcs3/rpcs3-binaries-mac, .ci/build-mac.sh, Intel) (push) Waiting to run
Build RPCS3 / RPCS3 Mac ${{ matrix.name }} (8e21bdbc40711a3fccd18fbf17b742348b0f4281, rpcs3/rpcs3-binaries-mac-arm64, .ci/build-mac-arm64.sh, Apple Silicon) (push) Waiting to run
Build RPCS3 / RPCS3 Windows (push) Waiting to run
Build RPCS3 / RPCS3 Windows Clang (win64, clang, clang64) (push) Waiting to run
Build RPCS3 / RPCS3 FreeBSD (push) Waiting to run
2025-12-15 14:20:34 +01:00
Megamouse
12a3818fcf Fix logging of gem configs 2025-12-15 14:20:34 +01:00
kd-11
d9da5f26c0 vk: Ignore memory pressure handling when allocating data heaps into ReBAR memory. 2025-12-15 11:53:47 +00:00
kd-11
1a3e150a62 vk: Extend memory allocation system to explicitly allow requesting no VRAM recovery on allocation fail. 2025-12-15 11:53:47 +00:00
zeph
e6e745fec6
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 <zephyrzefa15@gmail.com>
2025-12-12 01:35:17 +01:00
zeph
19e9dcbbef
Clans: covert constructor
Signed-off-by: zeph <zephyrzefa15@gmail.com>
2025-12-10 21:09:59 +01:00
zeph
69924c43ed
Clans: stray import
Signed-off-by: zeph <zephyrzefa15@gmail.com>
2025-12-10 20:56:38 +01:00
zeph
520d0390e4
Clans: fix empty dialog accidentally spawning
Signed-off-by: zeph <zephyrzefa15@gmail.com>
2025-12-10 20:55:21 +01:00
zeph
6afa8085be
Merge branch 'feat/clans' of github.com:zephyrcodesstuff/rpcs3 into feat/clans
Signed-off-by: zeph <zephyrzefa15@gmail.com>
2025-12-10 20:44:58 +01:00
zeph
c1cc185009
Clans: caught more unnecessary strcpy_trunc
Signed-off-by: zeph <zephyrzefa15@gmail.com>
2025-12-10 20:44:29 +01:00
zeph
e9d59ccfde
Merge branch 'RPCS3:master' into feat/clans 2025-12-10 20:42:16 +01:00
zeph
08091994d9
Clans: safety fixes
- Replaced `sscanf` with `np::string_to_npid`
- Reinstated log messages on level `warning` for implemented functions

Signed-off-by: zeph <zephyrzefa15@gmail.com>
2025-12-10 20:42:01 +01:00
zeph
cf373334a4
Clans: clean-up
- Switched from `vm::ptr<handle>` to `handle` downcasted to a u32 for immutability
- Using a single `atomic_t<u32>` instead of a mutex combination for ticket requesting

Signed-off-by: zeph <zephyrzefa15@gmail.com>
2025-12-10 18:36:24 +01:00
zeph
259d6c6376
Merge branch 'RPCS3:master' into feat/clans 2025-12-09 18:29:20 +01:00
zeph
b2195bd6ff
Clans: clang compiler fix-ups
Signed-off-by: zeph <zephyrzefa15@gmail.com>
2025-12-09 19:28:38 +01:00
zeph
fee3dff037 Clans(CI): fix missing Qt moc 2025-12-09 12:13:47 +01:00
zeph
68e5dec1e8 Clans(CI): fix curl dependency for emucore (MSVC) 2025-12-09 11:47:04 +01:00
zeph
b5796f5880 Clans(CI): fix imports 2025-12-09 11:14:15 +01:00
zeph
1c1935229c
Clang: fix temporary variable usage
Signed-off-by: zeph <zephyrzefa15@gmail.com>
2025-12-08 23:35:04 +01:00
zeph
6ecc5e6900
Clans: switch fmt::split to fmt::split_sv
Signed-off-by: zeph <zephyrzefa15@gmail.com>
2025-12-08 22:40:26 +01:00
zeph
d0b2136579
Clans: switch fmt::split to fmt::split_sv
Signed-off-by: zeph <zephyrzefa15@gmail.com>
2025-12-08 22:36:16 +01:00
zeph
185c4da934
Merge branch 'RPCS3:master' into feat/clans 2025-12-08 22:34:38 +01:00
zeph
198c965f8b
Clans: improved ticket requesting and waiting logic
Using proper atomic synchronisation primitives, instead of a busy loop

Signed-off-by: zeph <zephyrzefa15@gmail.com>
2025-12-08 15:15:56 +01:00
zeph
c358deaf9d
Merge branch 'RPCS3:master' into feat/clans 2025-12-08 14:01:54 +01:00
zeph
485484d258
Clans: handling case when tickets fail to get requested
Signed-off-by: zeph <zephyrzefa15@gmail.com>
2025-12-08 14:01:19 +01:00
zeph
fed72dcf90
Clans: implemented IDM & ctx for requests
Signed-off-by: zeph <zephyrzefa15@gmail.com>
2025-12-08 13:57:07 +01:00
zeph
7378657c4b Clans: fmt::format instead of std::format
- Renamed some fields
- Implemented fmt::format for consistency
- Made some constant fields `const`
2025-12-08 13:10:14 +01:00
zeph
ae34ee9c71 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`.
2025-12-08 04:29:07 +01:00
zeph
a9b124f9c6 Clans: make instance a shared_ptr 2025-12-08 03:49:56 +01:00
zeph
04da801145 Clans: added compile paths to MSVC targets 2025-12-08 03:34:12 +01:00
zeph
966fa1cab3 Clans: code styling & safety fix-ups 2025-12-08 02:59:05 +01:00
zeph
dccaf789a1
Clans: fixed struct size
Signed-off-by: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com>
2025-12-07 19:49:01 +01:00
zeph
17eb5a5150
Clans: fixed system constant back to correct value
Signed-off-by: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com>
2025-12-07 19:43:29 +01:00
zeph
ac036a0580
Clans: fix clans config not being auto-loaded
Signed-off-by: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com>
2025-12-07 19:14:22 +01:00
zeph
8125d7f21a
Clans: fix requests being unavailable
Signed-off-by: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com>
2025-12-07 19:03:45 +01:00
zeph
53cd84300d
Clans: HTTP/S support
Signed-off-by: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com>
2025-12-07 18:43:42 +01:00
zeph
f467a15e83
Clans: using config for host
- Enabled SSL host verification

Signed-off-by: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com>
2025-12-07 18:15:14 +01:00
zeph
d618ec9135
Clans: Qt GUI configuration impl
Signed-off-by: zeph <35661622+ZephyrCodesStuff@users.noreply.github.com>
2025-12-07 18:11:22 +01:00
zeph
e47f224bc2
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>
2025-12-07 17:52:22 +01:00
27 changed files with 2434 additions and 200 deletions

View File

@ -442,6 +442,8 @@ target_sources(rpcs3_emu PRIVATE
NP/np_requests.cpp
NP/signaling_handler.cpp
NP/np_structs_extra.cpp
NP/clans_client.cpp
NP/clans_config.cpp
NP/rpcn_client.cpp
NP/rpcn_config.cpp
NP/rpcn_countries.cpp

View File

@ -626,9 +626,9 @@ public:
cellGem.notice("Could not load mouse gem config. Using defaults.");
}
cellGem.notice("Real gem config=\n", g_cfg_gem_real.to_string());
cellGem.notice("Fake gem config=\n", g_cfg_gem_fake.to_string());
cellGem.notice("Mouse gem config=\n", g_cfg_gem_mouse.to_string());
cellGem.notice("Real gem config=%s", g_cfg_gem_real.to_string());
cellGem.notice("Fake gem config=%s", g_cfg_gem_fake.to_string());
cellGem.notice("Mouse gem config=%s", g_cfg_gem_mouse.to_string());
}
};
@ -719,43 +719,120 @@ namespace gem
constexpr u32 in_pitch = 640;
constexpr u32 out_pitch = 640 * 4;
for (u32 y = 0; y < 480 - 1; y += 2)
// HamiltonAdams demosaicing
for (s32 y = 0; y < 480; y++)
{
const u8* src0 = src + y * in_pitch;
const u8* src1 = src0 + in_pitch;
const bool is_even_y = (y % 2) == 0;
const u8* srcc = src + y * in_pitch;
const u8* srcu = src + std::max(0, y - 1) * in_pitch;
const u8* srcd = src + std::min(480 - 1, y + 1) * in_pitch;
u8* dst0 = dst + y * out_pitch;
u8* dst1 = dst0 + out_pitch;
for (u32 x = 0; x < 640 - 1; x += 2, src0 += 2, src1 += 2, dst0 += 8, dst1 += 8)
// Split loops (roughly twice the performance by removing one condition)
if (is_even_y)
{
u8 b = src0[0];
u8 g0 = src0[1];
u8 g1 = src1[0];
u8 r = src1[1];
for (s32 x = 0; x < 640; x++, dst0 += 4)
{
const bool is_even_x = (x % 2) == 0;
const int xl = std::max(0, x - 1);
const int xr = std::min(640 - 1, x + 1);
u8 r, b, g;
if (is_even_x)
{
// Blue pixel
const u8 up = srcu[x];
const u8 down = srcd[x];
const u8 left = srcc[xl];
const u8 right = srcc[xr];
const int dh = std::abs(int(left) - int(right));
const int dv = std::abs(int(up) - int(down));
r = (srcu[xl] + srcu[xr] + srcd[xl] + srcd[xr]) / 4;
if (dh < dv)
g = (left + right) / 2;
else if (dv < dh)
g = (up + down) / 2;
else
g = (up + down + left + right) / 4;
b = srcc[x];
}
else
{
// Green (on blue row)
r = (srcu[x] + srcd[x]) / 2;
g = srcc[x];
b = (srcc[xl] + srcc[xr]) / 2;
}
if constexpr (use_gain)
{
b = static_cast<u8>(std::clamp(b * gain_b, 0.0f, 255.0f));
g0 = static_cast<u8>(std::clamp(g0 * gain_g, 0.0f, 255.0f));
g1 = static_cast<u8>(std::clamp(g1 * gain_g, 0.0f, 255.0f));
r = static_cast<u8>(std::clamp(r * gain_r, 0.0f, 255.0f));
dst0[0] = static_cast<u8>(std::clamp(r * gain_r, 0.0f, 255.0f));
dst0[1] = static_cast<u8>(std::clamp(b * gain_b, 0.0f, 255.0f));
dst0[2] = static_cast<u8>(std::clamp(g * gain_g, 0.0f, 255.0f));
}
else
{
dst0[0] = r;
dst0[1] = g;
dst0[2] = b;
}
dst0[3] = alpha;
}
}
else
{
for (s32 x = 0; x < 640; x++, dst0 += 4)
{
const bool is_even_x = (x % 2) == 0;
const int xl = std::max(0, x - 1);
const int xr = std::min(640 - 1, x + 1);
u8 r, b, g;
if (is_even_x)
{
// Green (on red row)
r = (srcc[xl] + srcc[xr]) / 2;
g = srcc[x];
b = (srcu[x] + srcd[x]) / 2;
}
else
{
// Red pixel
const u8 up = srcu[x];
const u8 down = srcd[x];
const u8 left = srcc[xl];
const u8 right = srcc[xr];
const int dh = std::abs(int(left) - int(right));
const int dv = std::abs(int(up) - int(down));
r = srcc[x];
if (dh < dv)
g = (left + right) / 2;
else if (dv < dh)
g = (up + down) / 2;
else
g = (up + down + left + right) / 4;
b = (srcu[xl] + srcu[xr] + srcd[xl] + srcd[xr]) / 4;
}
const u8 top[4] = { r, g0, b, alpha };
const u8 bottom[4] = { r, g1, b, alpha };
// Top-Left
std::memcpy(dst0, top, 4);
// Top-Right Pixel
std::memcpy(dst0 + 4, top, 4);
// Bottom-Left Pixel
std::memcpy(dst1, bottom, 4);
// Bottom-Right Pixel
std::memcpy(dst1 + 4, bottom, 4);
if constexpr (use_gain)
{
dst0[0] = static_cast<u8>(std::clamp(r * gain_r, 0.0f, 255.0f));
dst0[1] = static_cast<u8>(std::clamp(b * gain_b, 0.0f, 255.0f));
dst0[2] = static_cast<u8>(std::clamp(g * gain_g, 0.0f, 255.0f));
}
else
{
dst0[0] = r;
dst0[1] = g;
dst0[2] = b;
}
dst0[3] = alpha;
}
}
}
}

View File

@ -32,6 +32,7 @@ using SceNpBasicMessageRecvAction = u32;
using SceNpClanId = u32;
using SceNpClansMessageId = u32;
using SceNpClansMemberRole = u32;
using SceNpClansMemberStatus = s32;
using SceNpCustomMenuIndexMask = u32;

View File

@ -1,10 +1,14 @@
#include "stdafx.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/IdManager.h"
#include "Emu/NP/np_handler.h"
#include "Emu/NP/clans_client.h"
#include "sceNp.h"
#include <memory>
#include "sceNpClans.h"
LOG_CHANNEL(sceNpClans);
template<>
@ -14,6 +18,7 @@ void fmt_class_string<SceNpClansError>::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);
@ -94,6 +99,9 @@ error_code sceNpClansInit(vm::cptr<SceNpCommunicationId> commId, vm::cptr<SceNpC
return SCE_NP_CLANS_ERROR_NOT_SUPPORTED;
}
// Allocate space for a client somewhere
std::shared_ptr<clan::clans_client> client = std::make_shared<clan::clans_client>();
clans_manager.client = client;
clans_manager.is_initialized = true;
return CELL_OK;
@ -110,6 +118,7 @@ error_code sceNpClansTerm()
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
}
clans_manager.client.reset();
clans_manager.is_initialized = false;
return CELL_OK;
@ -117,7 +126,7 @@ error_code sceNpClansTerm()
error_code sceNpClansCreateRequest(vm::ptr<SceNpClansRequestHandle> handle, u64 flags)
{
sceNpClans.todo("sceNpClansCreateRequest(handle=*0x%x, flags=0x%llx)", handle, flags);
sceNpClans.warning("sceNpClansCreateRequest(handle=*0x%x, flags=0x%x)", handle, flags);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
@ -134,34 +143,57 @@ error_code sceNpClansCreateRequest(vm::ptr<SceNpClansRequestHandle> handle, u64
return SCE_NP_CLANS_ERROR_NOT_SUPPORTED;
}
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
s32 reqId = 0;
SceNpClansError res = clans_manager.client->createRequest(&reqId);
if (res != SCE_NP_CLANS_SUCCESS)
{
return res;
}
*handle = reqId;
return CELL_OK;
}
error_code sceNpClansDestroyRequest(vm::ptr<SceNpClansRequestHandle> handle)
error_code sceNpClansDestroyRequest(SceNpClansRequestHandle handle)
{
sceNpClans.todo("sceNpClansDestroyRequest(handle=*0x%x)", handle);
sceNpClans.warning("sceNpClansDestroyRequest(handle=*0x%x)", handle);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
}
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpClansError res = clans_manager.client->destroyRequest(handle);
if (res != SCE_NP_CLANS_SUCCESS)
{
return res;
}
return CELL_OK;
}
error_code sceNpClansAbortRequest(vm::ptr<SceNpClansRequestHandle> handle)
error_code sceNpClansAbortRequest(SceNpClansRequestHandle handle)
{
sceNpClans.todo("sceNpClansAbortRequest(handle=*0x%x)", handle);
sceNpClans.warning("sceNpClansAbortRequest(handle=*0x%x)", handle);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
}
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
clans_manager.client->destroyRequest(handle);
return CELL_OK;
}
error_code sceNpClansCreateClan(vm::ptr<SceNpClansRequestHandle> handle, vm::cptr<char> name, vm::cptr<char> tag, vm::ptr<SceNpClanId> clanId)
// TODO: requires NpCommerce2
error_code sceNpClansCreateClan(SceNpClansRequestHandle handle, vm::cptr<char> name, vm::cptr<char> tag, vm::ptr<SceNpClanId> clanId)
{
sceNpClans.todo("sceNpClansCreateClan(handle=*0x%x, name=%s, tag=%s, clanId=*0x%x)", handle, name, tag, clanId);
@ -183,7 +215,9 @@ error_code sceNpClansCreateClan(vm::ptr<SceNpClansRequestHandle> handle, vm::cpt
return CELL_OK;
}
error_code sceNpClansDisbandClan(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId)
// TODO: should probably not be implemented on RPCS3 until `CreateClan` is,
// to not let people disband a clan by accident
error_code sceNpClansDisbandClan(SceNpClansRequestHandle handle, SceNpClanId clanId)
{
sceNpClans.todo("sceNpClansDisbandClan(handle=*0x%x, clanId=*0x%x)", handle, clanId);
@ -192,19 +226,22 @@ error_code sceNpClansDisbandClan(vm::ptr<SceNpClansRequestHandle> 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<SceNpClansRequestHandle> handle, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansEntry> clanList, vm::ptr<SceNpClansPagingResult> pageResult)
error_code sceNpClansGetClanList(SceNpClansRequestHandle handle, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansEntry> clanList, vm::ptr<SceNpClansPagingResult> pageResult)
{
sceNpClans.todo("sceNpClansGetClanList(handle=*0x%x, paging=*0x%x, clanList=*0x%x, pageResult=*0x%x)", handle, paging, clanList, pageResult);
sceNpClans.warning("sceNpClansGetClanList(handle=*0x%x, paging=*0x%x, clanList=*0x%x, pageResult=*0x%x)", handle, paging, clanList, pageResult);
if (!g_fxo->get<sce_np_clans_manager>().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,10 +254,35 @@ error_code sceNpClansGetClanList(vm::ptr<SceNpClansRequestHandle> handle, vm::cp
}
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpClansPagingRequest host_paging = {};
if (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, handle, &host_paging, host_clanList, &host_pageResult);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
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;
}
error_code sceNpClansGetClanListByNpId(vm::ptr<SceNpClansRequestHandle> handle, vm::cptr<SceNpClansPagingRequest> paging, vm::cptr<SceNpId> npid, vm::ptr<SceNpClansEntry> clanList, vm::ptr<SceNpClansPagingResult> pageResult)
// TODO: seems to not be needed, even by the PS3..?
error_code sceNpClansGetClanListByNpId(SceNpClansRequestHandle handle, vm::cptr<SceNpClansPagingRequest> paging, vm::cptr<SceNpId> npid, vm::ptr<SceNpClansEntry> clanList, vm::ptr<SceNpClansPagingResult> 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,7 +307,8 @@ error_code sceNpClansGetClanListByNpId(vm::ptr<SceNpClansRequestHandle> handle,
return CELL_OK;
}
error_code sceNpClansSearchByProfile(vm::ptr<SceNpClansRequestHandle> handle, vm::cptr<SceNpClansPagingRequest> paging, vm::cptr<SceNpClansSearchableProfile> search, vm::ptr<SceNpClansClanBasicInfo> results, vm::ptr<SceNpClansPagingResult> pageResult)
// TODO: seems to not be needed, even by the PS3..?
error_code sceNpClansSearchByProfile(SceNpClansRequestHandle handle, vm::cptr<SceNpClansPagingRequest> paging, vm::cptr<SceNpClansSearchableProfile> search, vm::ptr<SceNpClansClanBasicInfo> results, vm::ptr<SceNpClansPagingResult> pageResult)
{
sceNpClans.todo("sceNpClansSearchByProfile(handle=*0x%x, paging=*0x%x, search=*0x%x, results=*0x%x, pageResult=*0x%x)", handle, paging, search, results, pageResult);
@ -270,9 +333,9 @@ error_code sceNpClansSearchByProfile(vm::ptr<SceNpClansRequestHandle> handle, vm
return CELL_OK;
}
error_code sceNpClansSearchByName(vm::ptr<SceNpClansRequestHandle> handle, vm::cptr<SceNpClansPagingRequest> paging, vm::cptr<SceNpClansSearchableName> search, vm::ptr<SceNpClansClanBasicInfo> results, vm::ptr<SceNpClansPagingResult> pageResult)
error_code sceNpClansSearchByName(SceNpClansRequestHandle handle, vm::cptr<SceNpClansPagingRequest> paging, vm::cptr<SceNpClansSearchableName> search, vm::ptr<SceNpClansClanBasicInfo> results, vm::ptr<SceNpClansPagingResult> pageResult)
{
sceNpClans.todo("sceNpClansSearchByName(handle=*0x%x, paging=*0x%x, search=*0x%x, results=*0x%x, pageResult=*0x%x)", handle, paging, search, results, 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<sce_np_clans_manager>().is_initialized)
{
@ -292,12 +355,38 @@ error_code sceNpClansSearchByName(vm::ptr<SceNpClansRequestHandle> handle, vm::c
}
}
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpClansPagingRequest host_paging = {};
if (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(handle, &host_paging, &host_search, host_results, &host_pageResult);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
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;
}
error_code sceNpClansGetClanInfo(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::ptr<SceNpClansClanInfo> info)
error_code sceNpClansGetClanInfo(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::ptr<SceNpClansClanInfo> info)
{
sceNpClans.todo("sceNpClansGetClanInfo(handle=*0x%x, clanId=%d, info=*0x%x)", handle, clanId, info);
sceNpClans.warning("sceNpClansGetClanInfo(handle=*0x%x, clanId=*0x%x, info=*0x%x)", handle, clanId, info);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
@ -310,12 +399,24 @@ error_code sceNpClansGetClanInfo(vm::ptr<SceNpClansRequestHandle> handle, SceNpC
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
}
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpClansClanInfo host_info = {};
SceNpClansError ret = clans_manager.client->getClanInfo(handle, 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<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpClansUpdatableClanInfo> info)
error_code sceNpClansUpdateClanInfo(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpClansUpdatableClanInfo> info)
{
sceNpClans.todo("sceNpClansUpdateClanInfo(handle=*0x%x, clanId=%d, info=*0x%x)", handle, clanId, info);
sceNpClans.warning("sceNpClansUpdateClanInfo(handle=*0x%x, clanId=*0x%x, info=*0x%x)", handle, clanId, info);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
@ -328,17 +429,24 @@ error_code sceNpClansUpdateClanInfo(vm::ptr<SceNpClansRequestHandle> 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<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpClansUpdatableClanInfo host_info = {};
std::memcpy(&host_info, info.get_ptr(), sizeof(SceNpClansUpdatableClanInfo));
SceNpClansError ret = clans_manager.client->updateClanInfo(nph, handle, clanId, &host_info);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
return CELL_OK;
}
error_code sceNpClansGetMemberList(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpClansPagingRequest> paging, SceNpClansMemberStatus status, vm::ptr<SceNpClansMemberEntry> memList, vm::ptr<SceNpClansPagingResult> pageResult)
error_code sceNpClansGetMemberList(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpClansPagingRequest> paging, SceNpClansMemberStatus status, vm::ptr<SceNpClansMemberEntry> memList, vm::ptr<SceNpClansPagingResult> 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);
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<sce_np_clans_manager>().is_initialized)
{
@ -358,12 +466,36 @@ error_code sceNpClansGetMemberList(vm::ptr<SceNpClansRequestHandle> handle, SceN
}
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpClansPagingRequest host_paging = {};
if (paging)
{
std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest));
}
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)
{
return ret;
}
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));
return CELL_OK;
}
error_code sceNpClansGetMemberInfo(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, vm::ptr<SceNpClansMemberEntry> memInfo)
error_code sceNpClansGetMemberInfo(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, vm::ptr<SceNpClansMemberEntry> memInfo)
{
sceNpClans.todo("sceNpClansGetMemberInfo(handle=*0x%x, clanId=%d, npid=*0x%x, memInfo=*0x%x)", handle, clanId, npid, memInfo);
sceNpClans.warning("sceNpClansGetMemberInfo(handle=*0x%x, clanId=*0x%x, npid=*0x%x, memInfo=*0x%x)", handle, clanId, npid, memInfo);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
@ -375,12 +507,28 @@ error_code sceNpClansGetMemberInfo(vm::ptr<SceNpClansRequestHandle> handle, SceN
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpId host_npid = {};
std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId));
SceNpClansMemberEntry host_memInfo = {};
SceNpClansError ret = clans_manager.client->getMemberInfo(nph, handle, 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<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpClansUpdatableMemberInfo> info)
error_code sceNpClansUpdateMemberInfo(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpClansUpdatableMemberInfo> info)
{
sceNpClans.todo("sceNpClansUpdateMemberInfo(handle=*0x%x, clanId=%d, memInfo=*0x%x)", handle, clanId, info);
sceNpClans.warning("sceNpClansUpdateMemberInfo(handle=*0x%x, clanId=*0x%x, info=*0x%x)", handle, clanId, info);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
@ -389,21 +537,27 @@ error_code sceNpClansUpdateMemberInfo(vm::ptr<SceNpClansRequestHandle> 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<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpClansUpdatableMemberInfo host_info = {};
std::memcpy(&host_info, info.get_ptr(), sizeof(SceNpClansUpdatableMemberInfo));
SceNpClansError ret = clans_manager.client->updateMemberInfo(nph, handle, clanId, &host_info);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
return CELL_OK;
}
error_code sceNpClansChangeMemberRole(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, u32 role)
error_code sceNpClansChangeMemberRole(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, u32 role)
{
sceNpClans.todo("sceNpClansChangeMemberRole(handle=*0x%x, clanId=%d, npid=*0x%x, role=%d)", handle, clanId, npid, role);
sceNpClans.warning("sceNpClansChangeMemberRole(handle=*0x%x, clanId=*0x%x, npid=*0x%x, role=0x%x)", handle, clanId, npid, role);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
@ -415,10 +569,23 @@ error_code sceNpClansChangeMemberRole(vm::ptr<SceNpClansRequestHandle> handle, S
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpId host_npid = {};
std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId));
SceNpClansError ret = clans_manager.client->changeMemberRole(nph, handle, clanId, host_npid, static_cast<SceNpClansMemberRole>(role));
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
return CELL_OK;
}
error_code sceNpClansGetAutoAcceptStatus(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::ptr<b8> enable)
// TODO: no struct currently implements `autoAccept` as a field
error_code sceNpClansGetAutoAcceptStatus(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::ptr<b8> enable)
{
sceNpClans.todo("sceNpClansGetAutoAcceptStatus(handle=*0x%x, clanId=%d, enable=*0x%x)", handle, clanId, enable);
@ -435,7 +602,8 @@ error_code sceNpClansGetAutoAcceptStatus(vm::ptr<SceNpClansRequestHandle> handle
return CELL_OK;
}
error_code sceNpClansUpdateAutoAcceptStatus(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, b8 enable)
// TODO: no struct currently implements `autoAccept` as a field
error_code sceNpClansUpdateAutoAcceptStatus(SceNpClansRequestHandle handle, SceNpClanId clanId, b8 enable)
{
sceNpClans.todo("sceNpClansUpdateAutoAcceptStatus(handle=*0x%x, clanId=%d, enable=%d)", handle, clanId, enable);
@ -447,33 +615,51 @@ error_code sceNpClansUpdateAutoAcceptStatus(vm::ptr<SceNpClansRequestHandle> han
return CELL_OK;
}
error_code sceNpClansJoinClan(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId)
error_code sceNpClansJoinClan(SceNpClansRequestHandle handle, SceNpClanId clanId)
{
sceNpClans.todo("sceNpClansJoinClan(handle=*0x%x, clanId=%d)", handle, clanId);
sceNpClans.warning("sceNpClansJoinClan(handle=*0x%x, clanId=*0x%x)", handle, clanId);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpClansError ret = clans_manager.client->joinClan(nph, handle, clanId);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
return CELL_OK;
}
error_code sceNpClansLeaveClan(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId)
error_code sceNpClansLeaveClan(SceNpClansRequestHandle handle, SceNpClanId clanId)
{
sceNpClans.todo("sceNpClansLeaveClan(handle=*0x%x, clanId=%d)", handle, clanId);
sceNpClans.warning("sceNpClansLeaveClan(handle=*0x%x, clanId=*0x%x)", handle, clanId);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpClansError ret = clans_manager.client->leaveClan(nph, handle, clanId);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
return CELL_OK;
}
error_code sceNpClansKickMember(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, vm::cptr<SceNpClansMessage> message)
error_code sceNpClansKickMember(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, vm::cptr<SceNpClansMessage> message)
{
sceNpClans.todo("sceNpClansKickMember(handle=*0x%x, clanId=%d, npid=*0x%x, message=*0x%x)", handle, clanId, npid, message);
sceNpClans.warning("sceNpClansKickMember(handle=*0x%x, clanId=*0x%x, npid=*0x%x, message=*0x%x)", handle, clanId, npid, message);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
@ -493,12 +679,30 @@ error_code sceNpClansKickMember(vm::ptr<SceNpClansRequestHandle> handle, SceNpCl
}
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
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, handle, clanId, host_npid, &host_message);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
return CELL_OK;
}
error_code sceNpClansSendInvitation(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, vm::cptr<SceNpClansMessage> message)
error_code sceNpClansSendInvitation(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, vm::cptr<SceNpClansMessage> message)
{
sceNpClans.todo("sceNpClansSendInvitation(handle=*0x%x, clanId=%d, npid=*0x%x, message=*0x%x)", handle, clanId, npid, message);
sceNpClans.warning("sceNpClansSendInvitation(handle=*0x%x, clanId=*0x%x, npid=*0x%x, message=*0x%x)", handle, clanId, npid, message);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
@ -518,12 +722,30 @@ error_code sceNpClansSendInvitation(vm::ptr<SceNpClansRequestHandle> handle, Sce
}
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
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, handle, clanId, host_npid, &host_message);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
return CELL_OK;
}
error_code sceNpClansCancelInvitation(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpId> npid)
error_code sceNpClansCancelInvitation(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpId> npid)
{
sceNpClans.todo("sceNpClansCancelInvitation(handle=*0x%x, clanId=%d, npid=*0x%x)", handle, clanId, npid);
sceNpClans.warning("sceNpClansCancelInvitation(handle=*0x%x, clanId=*0x%x, npid=*0x%x)", handle, clanId, npid);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
@ -535,12 +757,24 @@ error_code sceNpClansCancelInvitation(vm::ptr<SceNpClansRequestHandle> handle, S
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpId host_npid = {};
std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId));
SceNpClansError ret = clans_manager.client->cancelInvitation(nph, handle, clanId, host_npid);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
return CELL_OK;
}
error_code sceNpClansSendInvitationResponse(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpClansMessage> message, b8 accept)
error_code sceNpClansSendInvitationResponse(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpClansMessage> message, b8 accept)
{
sceNpClans.todo("sceNpClansSendInvitationResponse(handle=*0x%x, clanId=%d, message=*0x%x, accept=%d)", handle, clanId, message, accept);
sceNpClans.warning("sceNpClansSendInvitationResponse(handle=*0x%x, clanId=*0x%x, message=*0x%x, accept=%d)", handle, clanId, message, accept);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
@ -555,12 +789,32 @@ error_code sceNpClansSendInvitationResponse(vm::ptr<SceNpClansRequestHandle> han
}
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpClansMessage host_message = {};
if (message)
{
std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage));
}
if (message)
{
std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage));
}
SceNpClansError ret = clans_manager.client->sendInvitationResponse(nph, handle, clanId, &host_message, accept);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
return CELL_OK;
}
error_code sceNpClansSendMembershipRequest(vm::ptr<SceNpClansRequestHandle> handle, u32 clanId, vm::cptr<SceNpClansMessage> message)
error_code sceNpClansSendMembershipRequest(SceNpClansRequestHandle handle, u32 clanId, vm::cptr<SceNpClansMessage> message)
{
sceNpClans.todo("sceNpClansSendMembershipRequest(handle=*0x%x, clanId=%d, message=*0x%x)", handle, clanId, message);
sceNpClans.warning("sceNpClansSendMembershipRequest(handle=*0x%x, clanId=*0x%x, message=*0x%x)", handle, clanId, message);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
@ -575,24 +829,49 @@ error_code sceNpClansSendMembershipRequest(vm::ptr<SceNpClansRequestHandle> hand
}
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpClansMessage host_message = {};
if (message)
{
std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage));
}
SceNpClansError ret = clans_manager.client->requestMembership(nph, handle, clanId, &host_message);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
return CELL_OK;
}
error_code sceNpClansCancelMembershipRequest(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId)
error_code sceNpClansCancelMembershipRequest(SceNpClansRequestHandle handle, SceNpClanId clanId)
{
sceNpClans.todo("sceNpClansCancelMembershipRequest(handle=*0x%x, clanId=%d)", handle, clanId);
sceNpClans.warning("sceNpClansCancelMembershipRequest(handle=*0x%x, clanId=*0x%x)", handle, clanId);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpClansError ret = clans_manager.client->cancelRequestMembership(nph, handle, clanId);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
return CELL_OK;
}
error_code sceNpClansSendMembershipResponse(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, vm::cptr<SceNpClansMessage> message, b8 allow)
error_code sceNpClansSendMembershipResponse(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, vm::cptr<SceNpClansMessage> message, b8 allow)
{
sceNpClans.todo("sceNpClansSendMembershipResponse(handle=*0x%x, clanId=%d, npid=*0x%x, message=*0x%x, allow=%d)", handle, clanId, npid, message, 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<sce_np_clans_manager>().is_initialized)
{
@ -612,12 +891,30 @@ error_code sceNpClansSendMembershipResponse(vm::ptr<SceNpClansRequestHandle> han
}
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
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, handle, clanId, host_npid, &host_message, allow);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
return CELL_OK;
}
error_code sceNpClansGetBlacklist(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansBlacklistEntry> bl, vm::ptr<SceNpClansPagingResult> pageResult)
error_code sceNpClansGetBlacklist(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansBlacklistEntry> bl, vm::ptr<SceNpClansPagingResult> pageResult)
{
sceNpClans.todo("sceNpClansGetBlacklist(handle=*0x%x, clanId=%d, paging=*0x%x, bl=*0x%x, pageResult=*0x%x)", handle, clanId, paging, bl, 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<sce_np_clans_manager>().is_initialized)
{
@ -637,53 +934,101 @@ error_code sceNpClansGetBlacklist(vm::ptr<SceNpClansRequestHandle> handle, SceNp
}
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpClansPagingRequest host_paging = {};
if (paging)
{
std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest));
}
SceNpClansBlacklistEntry host_blacklist[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {};
SceNpClansPagingResult 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;
}
if (bl && host_pageResult.count > 0)
{
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<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpId> npid)
error_code sceNpClansAddBlacklistEntry(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpId> member)
{
sceNpClans.todo("sceNpClansAddBlacklistEntry(handle=*0x%x, clanId=%d, npid=*0x%x)", handle, clanId, npid);
sceNpClans.warning("sceNpClansAddBlacklistEntry(handle=*0x%x, clanId=*0x%x, member=*0x%x)", handle, clanId, member);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
}
if (!npid)
if (!member)
{
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpId host_member = {};
std::memcpy(&host_member, member.get_ptr(), sizeof(SceNpId));
SceNpClansError ret = clans_manager.client->addBlacklistEntry(nph, handle, clanId, host_member);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
return CELL_OK;
}
error_code sceNpClansRemoveBlacklistEntry(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpId> npid)
error_code sceNpClansRemoveBlacklistEntry(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpId> member)
{
sceNpClans.todo("sceNpClansRemoveBlacklistEntry(handle=*0x%x, clanId=%d, npid=*0x%x)", handle, clanId, npid);
sceNpClans.warning("sceNpClansRemoveBlacklistEntry(handle=*0x%x, clanId=*0x%x, member=*0x%x)", handle, clanId, member);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
}
if (!npid)
if (!member)
{
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
SceNpId host_member = {};
std::memcpy(&host_member, member.get_ptr(), sizeof(SceNpId));
SceNpClansError ret = clans_manager.client->removeBlacklistEntry(nph, handle, clanId, host_member);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
return CELL_OK;
}
error_code sceNpClansRetrieveAnnouncements(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansMessageEntry> mlist, vm::ptr<SceNpClansPagingResult> pageResult)
error_code sceNpClansRetrieveAnnouncements(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansMessageEntry> mlist, vm::ptr<SceNpClansPagingResult> pageResult)
{
sceNpClans.todo("sceNpClansRetrieveAnnouncements(handle=*0x%x, clanId=%d, paging=*0x%x, mlist=*0x%x, pageResult=*0x%x)", handle, clanId, paging, mlist, 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<sce_np_clans_manager>().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,12 +1041,36 @@ error_code sceNpClansRetrieveAnnouncements(vm::ptr<SceNpClansRequestHandle> hand
}
}
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
SceNpClansPagingRequest host_paging = {};
if (paging)
{
std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest));
}
SceNpClansMessageEntry host_announcements[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {};
SceNpClansPagingResult 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;
}
if (mlist && host_pageResult.count > 0)
{
std::memcpy(mlist.get_ptr(), host_announcements, sizeof(SceNpClansMessageEntry) * host_pageResult.count);
}
std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult));
return CELL_OK;
}
error_code sceNpClansPostAnnouncement(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpClansMessage> message, vm::cptr<SceNpClansMessageData> data, u32 duration, vm::ptr<SceNpClansMessageId> mId)
error_code sceNpClansPostAnnouncement(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpClansMessage> message, vm::cptr<SceNpClansMessageData> data, u32 duration, vm::ptr<SceNpClansMessageId> 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);
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<sce_np_clans_manager>().is_initialized)
{
@ -713,32 +1082,57 @@ error_code sceNpClansPostAnnouncement(vm::ptr<SceNpClansRequestHandle> 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<sce_np_clans_manager>();
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
SceNpClansMessage host_announcement = {};
std::memcpy(&host_announcement, message.get_ptr(), sizeof(SceNpClansMessage));
SceNpClansMessageData host_data = {};
if (data)
{
std::memcpy(&host_data, data.get_ptr(), sizeof(SceNpClansMessageData));
}
SceNpClansMessageId host_announcementId = 0;
SceNpClansError ret = clans_manager.client->postAnnouncement(nph, handle, clanId, &host_announcement, &host_data, duration, &host_announcementId);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
*mId = host_announcementId;
return CELL_OK;
}
error_code sceNpClansRemoveAnnouncement(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, SceNpClansMessageId mId)
error_code sceNpClansRemoveAnnouncement(SceNpClansRequestHandle handle, SceNpClanId clanId, SceNpClansMessageId mId)
{
sceNpClans.todo("sceNpClansPostAnnouncement(handle=*0x%x, clanId=%d, mId=%d)", handle, clanId, mId);
sceNpClans.warning("sceNpClansRemoveAnnouncement(handle=*0x%x, clanId=*0x%x, mId=*0x%x)", handle, clanId, mId);
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
{
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
}
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
SceNpClansError ret = clans_manager.client->deleteAnnouncement(nph, handle, clanId, mId);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
return CELL_OK;
}
error_code sceNpClansPostChallenge(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, SceNpClanId targetClan, vm::cptr<SceNpClansMessage> message, vm::cptr<SceNpClansMessageData> data, u32 duration, vm::ptr<SceNpClansMessageId> mId)
error_code sceNpClansPostChallenge(SceNpClansRequestHandle handle, SceNpClanId clanId, SceNpClanId targetClan, vm::cptr<SceNpClansMessage> message, vm::cptr<SceNpClansMessageData> data, u32 duration, vm::ptr<SceNpClansMessageId> 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);
@ -765,7 +1159,7 @@ error_code sceNpClansPostChallenge(vm::ptr<SceNpClansRequestHandle> handle, SceN
return CELL_OK;
}
error_code sceNpClansRetrievePostedChallenges(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, SceNpClanId targetClan, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansMessageEntry> mList, vm::ptr<SceNpClansPagingResult> pageResult)
error_code sceNpClansRetrievePostedChallenges(SceNpClansRequestHandle handle, SceNpClanId clanId, SceNpClanId targetClan, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansMessageEntry> mList, vm::ptr<SceNpClansPagingResult> 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);
@ -790,7 +1184,7 @@ error_code sceNpClansRetrievePostedChallenges(vm::ptr<SceNpClansRequestHandle> h
return CELL_OK;
}
error_code sceNpClansRemovePostedChallenge(vm::ptr<SceNpClansRequestHandle> 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);
@ -802,7 +1196,7 @@ error_code sceNpClansRemovePostedChallenge(vm::ptr<SceNpClansRequestHandle> hand
return CELL_OK;
}
error_code sceNpClansRetrieveChallenges(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansMessageEntry> mList, vm::ptr<SceNpClansPagingResult> pageResult)
error_code sceNpClansRetrieveChallenges(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansMessageEntry> mList, vm::ptr<SceNpClansPagingResult> pageResult)
{
sceNpClans.todo("sceNpClansRetrieveChallenges(handle=*0x%x, clanId=%d, paging=*0x%x, mList=*0x%x, pageResult=*0x%x)", handle, clanId, paging, mList, pageResult);

View File

@ -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,
@ -138,7 +140,7 @@ enum
};
// Request handle for clan API
using SceNpClansRequestHandle = vm::ptr<struct SceNpClansRequest>;
using SceNpClansRequestHandle = u32;
// Paging request structure
struct SceNpClansPagingRequest
@ -159,8 +161,8 @@ struct SceNpClansClanBasicInfo
{
be_t<u32> clanId;
be_t<u32> 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 +199,7 @@ struct SceNpClansSearchableProfile
be_t<s32> intAttr2SearchOp;
be_t<s32> intAttr3SearchOp;
be_t<s32> 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 +207,7 @@ struct SceNpClansSearchableProfile
struct SceNpClansSearchableName
{
be_t<s32> 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 +215,7 @@ struct SceNpClansSearchableName
struct SceNpClansUpdatableClanInfo
{
be_t<u32> 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<u32> binData1Size;
@ -233,8 +235,8 @@ struct SceNpClansUpdatableMemberInfo
be_t<u32> fields;
u8 binData1;
be_t<u32> 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];
char description[SCE_NP_CLANS_MEMBER_DESCRIPTION_MAX_LENGTH + 1];
b8 allowMsg;
u8 reserved[3];
};
@ -271,7 +273,7 @@ struct SceNpClansMessageEntry
SceNpClansMessage message;
SceNpClansMessageData data;
SceNpId npid;
u8 reserved[4];
SceNpClanId postedBy;
};
// Blacklist entry structure
@ -280,10 +282,3 @@ struct SceNpClansBlacklistEntry
SceNpId entry;
SceNpId registeredBy;
};
// fxm objects
struct sce_np_clans_manager
{
atomic_t<bool> is_initialized = false;
};

File diff suppressed because it is too large Load Diff

124
rpcs3/Emu/NP/clans_client.h Normal file
View File

@ -0,0 +1,124 @@
#pragma once
#include <curl/curl.h>
#include <pugixml.hpp>
#include <Emu/Cell/Modules/sceNpClans.h>
#include <Emu/NP/np_handler.h>
#include <Emu/IdManager.h>
inline const char* CLANS_ENTITLEMENT_ID = "NPWR00432_00";
inline const char* CLANS_SERVICE_ID = "IV0001-NPXS01001_00";
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 clans_client
{
private:
static size_t curlWriteCallback(void* data, size_t size, size_t nmemb, void* clientp);
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);
public:
clans_client();
~clans_client();
SceNpClansError createRequest(s32* reqId);
SceNpClansError destroyRequest(u32 reqId);
SceNpClansError clanSearch(u32 reqId, SceNpClansPagingRequest* paging, SceNpClansSearchableName* search, SceNpClansClanBasicInfo* clanList, SceNpClansPagingResult* pageResult);
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, 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, 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, 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, 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, u32 reqId, SceNpClanId clanId);
SceNpClansError leaveClan(np::np_handler& nph, u32 reqId, SceNpClanId clanId);
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, 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, 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
struct sce_np_clans_manager
{
atomic_t<bool> is_initialized = false;
std::shared_ptr<clan::clans_client> client;
};

View File

@ -0,0 +1,154 @@
#include "Utilities/StrUtil.h"
#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();
}
bool cfg_clans::get_use_https() const
{
return use_https.get();
}
std::vector<std::pair<std::string, std::string>> cfg_clans::get_hosts()
{
std::vector<std::pair<std::string, std::string>> vec_hosts;
const std::string host_str = hosts.to_string();
const auto hosts_list = fmt::split_sv(host_str, {"|||"});
for (const auto& cur_host : hosts_list)
{
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);
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_use_https(bool use_https)
{
this->use_https.set(use_https);
}
void cfg_clans::set_hosts(const std::vector<std::pair<std::string, std::string>>& 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
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])
{
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;
}

View File

@ -0,0 +1,29 @@
#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"};
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<std::pair<std::string, std::string>> 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);
private:
static std::string get_path();
void set_hosts(const std::vector<std::pair<std::string, std::string>>& vec_hosts);
};
extern cfg_clans g_cfg_clans;

View File

@ -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_data> ticket::parse_node(std::size_t index) const
{
if ((index + MIN_TICKET_DATA_SIZE) > size())
@ -1345,6 +1364,19 @@ namespace np
return history;
}
ticket np_handler::get_clan_ticket()
{
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;
}
constexpr usz MAX_HISTORY_ENTRIES = 200;
void np_handler::add_player_to_history(const SceNpId* npid, const char* description)

View File

@ -3,6 +3,7 @@
#include <queue>
#include <map>
#include <unordered_map>
#include <condition_variable>
#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<SceNpTicketParam> param) const;
std::string get_service_id() const;
private:
std::optional<ticket_data> 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,10 @@ namespace np
ticket current_ticket;
// Clan ticket
atomic_t<u32> clan_ticket_ready = 0;
ticket clan_ticket;
// IP & DNS info
std::string hostname = "localhost";
std::array<u8, 6> ether_address{};

View File

@ -1,8 +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"
@ -863,7 +864,20 @@ 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 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 = incoming_ticket;
clan_ticket_ready.store(true);
clan_ticket_ready.notify_all();
return;
}
current_ticket = incoming_ticket;
auto ticket_size = static_cast<s32>(current_ticket.size());
if (manager_cb)

View File

@ -50,6 +50,7 @@ namespace vk
: m_device(dev)
{
const bool nullable = !!(flags & VK_BUFFER_CREATE_ALLOW_NULL_RPCS3);
const bool no_vmem_recovery = !!(flags & VK_BUFFER_CREATE_IGNORE_VMEM_PRESSURE_RPCS3);
info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
info.flags = flags & ~VK_BUFFER_CREATE_SPECIAL_FLAGS_RPCS3;
@ -69,19 +70,28 @@ namespace vk
fmt::throw_exception("No compatible memory type was found!");
}
memory = std::make_unique<memory_block>(m_device, memory_reqs.size, memory_reqs.alignment, allocation_type_info, allocation_pool, nullable);
memory_allocation_request request
{
.size = memory_reqs.size,
.alignment = memory_reqs.alignment,
.memory_type = &allocation_type_info,
.pool = allocation_pool,
.throw_on_fail = !nullable,
.recover_vmem_on_fail = !no_vmem_recovery
};
memory = std::make_unique<memory_block>(m_device, request);
if (auto device_memory = memory->get_vk_device_memory();
device_memory != VK_NULL_HANDLE)
{
vkBindBufferMemory(dev, value, device_memory, memory->get_vk_device_memory_offset());
return;
}
else
{
ensure(nullable);
vkDestroyBuffer(m_device, value, nullptr);
value = VK_NULL_HANDLE;
}
}
buffer::buffer(const vk::render_device& dev, VkBufferUsageFlags usage, void* host_pointer, u64 size)
: m_device(dev)

View File

@ -9,9 +9,10 @@ namespace vk
{
enum : u32
{
VK_BUFFER_CREATE_ALLOW_NULL_RPCS3 = 0x80000000,
VK_BUFFER_CREATE_ALLOW_NULL_RPCS3 = 0x10000000, // If we cannot allocate memory for the buffer, just return an empty but valid object with a null handle.
VK_BUFFER_CREATE_IGNORE_VMEM_PRESSURE_RPCS3 = 0x20000000, // If we cannot allocate memory for the buffer, do not run recovery routine to recover VRAM. Crash or return empty handle immediately instead.
VK_BUFFER_CREATE_SPECIAL_FLAGS_RPCS3 = (VK_BUFFER_CREATE_ALLOW_NULL_RPCS3)
VK_BUFFER_CREATE_SPECIAL_FLAGS_RPCS3 = (VK_BUFFER_CREATE_ALLOW_NULL_RPCS3 | VK_BUFFER_CREATE_IGNORE_VMEM_PRESSURE_RPCS3)
};
struct buffer_view : public unique_resource

View File

@ -53,7 +53,7 @@ namespace vk
VkFlags create_flags = 0;
if (m_prefer_writethrough)
{
create_flags |= VK_BUFFER_CREATE_ALLOW_NULL_RPCS3;
create_flags |= (VK_BUFFER_CREATE_ALLOW_NULL_RPCS3 | VK_BUFFER_CREATE_IGNORE_VMEM_PRESSURE_RPCS3);
}
heap = std::make_unique<buffer>(*g_render_device, size, memory_index, memory_flags, usage, create_flags, VMM_ALLOCATION_POOL_SYSTEM);
@ -146,7 +146,7 @@ namespace vk
VkFlags create_flags = 0;
if (m_prefer_writethrough)
{
create_flags |= VK_BUFFER_CREATE_ALLOW_NULL_RPCS3;
create_flags |= (VK_BUFFER_CREATE_ALLOW_NULL_RPCS3 | VK_BUFFER_CREATE_IGNORE_VMEM_PRESSURE_RPCS3);
}
heap = std::make_unique<buffer>(*g_render_device, aligned_new_size, memory_index, memory_flags, usage, create_flags, VMM_ALLOCATION_POOL_SYSTEM);

View File

@ -128,7 +128,16 @@ namespace vk
fmt::throw_exception("No compatible memory type was found!");
}
memory = std::make_shared<vk::memory_block>(m_device, memory_req.size, memory_req.alignment, allocation_type_info, allocation_pool, nullable);
memory_allocation_request alloc_request
{
.size = memory_req.size,
.alignment = memory_req.alignment,
.memory_type = &allocation_type_info,
.pool = allocation_pool,
.throw_on_fail = !nullable
};
memory = std::make_shared<vk::memory_block>(m_device, alloc_request);
if (auto device_mem = memory->get_vk_device_memory();
device_mem != VK_NULL_HANDLE) [[likely]]
{

View File

@ -224,7 +224,7 @@ namespace vk
vmaDestroyAllocator(m_allocator);
}
mem_allocator_vk::mem_handle_t mem_allocator_vma::alloc(u64 block_sz, u64 alignment, const memory_type_info& memory_type, vmm_allocation_pool pool, bool throw_on_fail)
mem_allocator_vk::mem_handle_t mem_allocator_vma::alloc(const memory_allocation_request& request)
{
VmaAllocation vma_alloc;
VkMemoryRequirements mem_req = {};
@ -233,11 +233,11 @@ namespace vk
auto do_vma_alloc = [&]() -> std::tuple<VkResult, u32>
{
for (const auto& memory_type_index : memory_type)
for (const auto& memory_type_index : *request.memory_type)
{
mem_req.memoryTypeBits = 1u << memory_type_index;
mem_req.size = ::align2(block_sz, alignment);
mem_req.alignment = alignment;
mem_req.size = ::align2(request.size, request.alignment);
mem_req.alignment = request.alignment;
create_info.memoryTypeBits = 1u << memory_type_index;
create_info.flags = m_allocation_flags;
@ -256,12 +256,14 @@ namespace vk
const auto [status, type] = do_vma_alloc();
if (status == VK_SUCCESS)
{
vmm_notify_memory_allocated(vma_alloc, type, block_sz, pool);
vmm_notify_memory_allocated(vma_alloc, type, request.size, request.pool);
return vma_alloc;
}
}
const auto severity = (throw_on_fail) ? rsx::problem_severity::fatal : rsx::problem_severity::severe;
if (request.recover_vmem_on_fail)
{
const auto severity = (request.throw_on_fail) ? rsx::problem_severity::fatal : rsx::problem_severity::severe;
if (error_code == VK_ERROR_OUT_OF_DEVICE_MEMORY &&
vmm_handle_memory_pressure(severity))
{
@ -270,12 +272,13 @@ namespace vk
if (status == VK_SUCCESS)
{
rsx_log.warning("Renderer ran out of video memory but successfully recovered.");
vmm_notify_memory_allocated(vma_alloc, type, block_sz, pool);
vmm_notify_memory_allocated(vma_alloc, type, request.size, request.pool);
return vma_alloc;
}
}
}
if (!throw_on_fail)
if (!request.throw_on_fail)
{
return VK_NULL_HANDLE;
}
@ -361,18 +364,18 @@ namespace vk
m_allocation_flags = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
}
mem_allocator_vk::mem_handle_t mem_allocator_vk::alloc(u64 block_sz, u64 /*alignment*/, const memory_type_info& memory_type, vmm_allocation_pool pool, bool throw_on_fail)
mem_allocator_vk::mem_handle_t mem_allocator_vk::alloc(const memory_allocation_request& request)
{
VkResult error_code = VK_ERROR_UNKNOWN;
VkDeviceMemory memory;
VkMemoryAllocateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
info.allocationSize = block_sz;
info.allocationSize = request.size;
auto do_vk_alloc = [&]() -> std::tuple<VkResult, u32>
{
for (const auto& memory_type_index : memory_type)
for (const auto& memory_type_index : *request.memory_type)
{
info.memoryTypeIndex = memory_type_index;
error_code = vkAllocateMemory(m_device, &info, nullptr, &memory);
@ -389,12 +392,14 @@ namespace vk
const auto [status, type] = do_vk_alloc();
if (status == VK_SUCCESS)
{
vmm_notify_memory_allocated(memory, type, block_sz, pool);
vmm_notify_memory_allocated(memory, type, request.size, request.pool);
return memory;
}
}
const auto severity = (throw_on_fail) ? rsx::problem_severity::fatal : rsx::problem_severity::severe;
if (request.recover_vmem_on_fail)
{
const auto severity = (request.throw_on_fail) ? rsx::problem_severity::fatal : rsx::problem_severity::severe;
if (error_code == VK_ERROR_OUT_OF_DEVICE_MEMORY &&
vmm_handle_memory_pressure(severity))
{
@ -403,12 +408,13 @@ namespace vk
if (status == VK_SUCCESS)
{
rsx_log.warning("Renderer ran out of video memory but successfully recovered.");
vmm_notify_memory_allocated(memory, type, block_sz, pool);
vmm_notify_memory_allocated(memory, type, request.size, request.pool);
return memory;
}
}
}
if (!throw_on_fail)
if (!request.throw_on_fail)
{
return VK_NULL_HANDLE;
}
@ -455,11 +461,11 @@ namespace vk
return g_render_device->get_allocator();
}
memory_block::memory_block(VkDevice dev, u64 block_sz, u64 alignment, const memory_type_info& memory_type, vmm_allocation_pool pool, bool nullable)
: m_device(dev), m_size(block_sz)
memory_block::memory_block(VkDevice dev, const memory_allocation_request& alloc_request)
: m_device(dev), m_size(alloc_request.size)
{
m_mem_allocator = get_current_mem_allocator();
m_mem_handle = m_mem_allocator->alloc(block_sz, alignment, memory_type, pool, !nullable);
m_mem_handle = m_mem_allocator->alloc(alloc_request);
}
memory_block::~memory_block()

View File

@ -66,6 +66,16 @@ namespace vk
u64 size;
};
struct memory_allocation_request
{
u64 size = 0;
u64 alignment = 1;
const memory_type_info* memory_type = nullptr;
vmm_allocation_pool pool = VMM_ALLOCATION_POOL_UNDEFINED;
bool throw_on_fail = true;
bool recover_vmem_on_fail = true;
};
class mem_allocator_base
{
public:
@ -76,7 +86,7 @@ namespace vk
virtual void destroy() = 0;
virtual mem_handle_t alloc(u64 block_sz, u64 alignment, const memory_type_info& memory_type, vmm_allocation_pool pool, bool throw_on_fail) = 0;
virtual mem_handle_t alloc(const memory_allocation_request& request) = 0;
virtual void free(mem_handle_t mem_handle) = 0;
virtual void* map(mem_handle_t mem_handle, u64 offset, u64 size) = 0;
virtual void unmap(mem_handle_t mem_handle) = 0;
@ -104,7 +114,7 @@ namespace vk
void destroy() override;
mem_handle_t alloc(u64 block_sz, u64 alignment, const memory_type_info& memory_type, vmm_allocation_pool pool, bool throw_on_fail) override;
mem_handle_t alloc(const memory_allocation_request& request) override;
void free(mem_handle_t mem_handle) override;
void* map(mem_handle_t mem_handle, u64 offset, u64 /*size*/) override;
@ -134,7 +144,7 @@ namespace vk
void destroy() override {}
mem_handle_t alloc(u64 block_sz, u64 /*alignment*/, const memory_type_info& memory_type, vmm_allocation_pool pool, bool throw_on_fail) override;
mem_handle_t alloc(const memory_allocation_request& request) override;
void free(mem_handle_t mem_handle) override;
void* map(mem_handle_t mem_handle, u64 offset, u64 size) override;
@ -147,7 +157,7 @@ namespace vk
struct memory_block
{
memory_block(VkDevice dev, u64 block_sz, u64 alignment, const memory_type_info& memory_type, vmm_allocation_pool pool, bool nullable = false);
memory_block(VkDevice dev, const memory_allocation_request& alloc_request);
virtual ~memory_block();
virtual VkDeviceMemory get_vk_device_memory();

View File

@ -40,7 +40,7 @@
<ItemDefinitionGroup>
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<AdditionalIncludeDirectories>..\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</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\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</AdditionalIncludeDirectories>
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MaxSpeed</Optimization>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AL_LIBTYPE_STATIC;MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL3;ZLIB_CONST;WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AL_LIBTYPE_STATIC;MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL3;ZLIB_CONST;WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -112,6 +112,7 @@
<ClCompile Include="Emu\IPC_config.cpp" />
<ClCompile Include="Emu\IPC_socket.cpp" />
<ClCompile Include="Emu\localized_string.cpp" />
<ClCompile Include="Emu\NP\clans_config.cpp" />
<ClCompile Include="Emu\NP\rpcn_config.cpp" />
<ClCompile Include="Emu\NP\rpcn_countries.cpp" />
<ClCompile Include="Emu\NP\upnp_config.cpp" />
@ -188,6 +189,7 @@
<ClCompile Include="Emu\NP\np_requests.cpp" />
<ClCompile Include="Emu\NP\signaling_handler.cpp" />
<ClCompile Include="Emu\NP\np_structs_extra.cpp" />
<ClCompile Include="Emu\NP\clans_client.cpp" />
<ClCompile Include="Emu\NP\rpcn_client.cpp" />
<ClCompile Include="Emu\NP\ip_address.cpp" />
<ClCompile Include="Emu\vfs_config.cpp" />

View File

@ -406,6 +406,9 @@
<ClCompile Include="QTGeneratedFiles\Debug\moc_render_creator.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_clans_settings_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_rpcn_settings_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -697,6 +700,9 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_render_creator.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_clans_settings_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_rpcn_settings_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -807,6 +813,7 @@
<ClCompile Include="rpcs3qt\permissions.cpp" />
<ClCompile Include="rpcs3qt\ps_move_tracker_dialog.cpp" />
<ClCompile Include="rpcs3qt\cheat_manager.cpp" />
<ClCompile Include="rpcs3qt\clans_settings_dialog.cpp" />
<ClCompile Include="rpcs3qt\config_adapter.cpp" />
<ClCompile Include="rpcs3qt\config_checker.cpp" />
<ClCompile Include="rpcs3qt\curl_handle.cpp" />
@ -1488,6 +1495,16 @@
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(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"</Command>
</CustomBuild>
<CustomBuild Include="rpcs3qt\clans_settings_dialog.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(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"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(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"</Command>
</CustomBuild>
<CustomBuild Include="rpcs3qt\localized_emu.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>

View File

@ -864,6 +864,12 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_downloader.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_clans_settings_dialog.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_clans_settings_dialog.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_rpcn_settings_dialog.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
@ -909,6 +915,9 @@
<ClCompile Include="rpcs3qt\rpcn_settings_dialog.cpp">
<Filter>Gui\rpcn</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\clans_settings_dialog.cpp">
<Filter>Gui\clans</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\sendmessage_dialog_frame.cpp">
<Filter>Gui\message dialog</Filter>
</ClCompile>
@ -1699,6 +1708,9 @@
<CustomBuild Include="rpcs3qt\rpcn_settings_dialog.h">
<Filter>Gui\rpcn</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\clans_settings_dialog.h">
<Filter>Gui\rpcn</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\sendmessage_dialog_frame.h">
<Filter>Gui\message dialog</Filter>
</CustomBuild>

View File

@ -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

View File

@ -0,0 +1,187 @@
#include <QMessageBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QGroupBox>
#include <QDialogButtonBox>
#include <QLineEdit>
#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:"));
m_cbx_servers = new QComboBox();
m_cbx_protocol = new QComboBox();
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(m_cbx_servers);
hbox_lbl_combo->addWidget(m_cbx_protocol);
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(m_cbx_servers, &QComboBox::currentIndexChanged, this, [this](int index)
{
if (index < 0)
return;
QVariant host = m_cbx_servers->itemData(index);
if (!host.isValid() || !host.canConvert<QString>())
return;
g_cfg_clans.set_host(host.toString().toStdString());
g_cfg_clans.save();
});
connect(m_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);
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 = m_cbx_servers->currentIndex();
if (index < 0)
return;
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))
{
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;
m_cbx_servers->clear();
for (const auto& [desc, host] : vec_hosts)
{
m_cbx_servers->addItem(QString::fromStdString(desc), QString::fromStdString(host));
if (cur_host == host)
index = i;
i++;
}
m_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<std::pair<std::string, std::string>>& clans_add_server_dialog::get_new_server() const
{
return m_new_server;
}

View File

@ -0,0 +1,30 @@
#pragma once
#include <optional>
#include <QDialog>
#include <QComboBox>
class clans_settings_dialog : public QDialog
{
Q_OBJECT
public:
clans_settings_dialog(QWidget* parent = nullptr);
private:
void refresh_combobox();
private:
QComboBox* m_cbx_servers = nullptr;
QComboBox* m_cbx_protocol = nullptr;
};
class clans_add_server_dialog : public QDialog
{
Q_OBJECT
public:
clans_add_server_dialog(QWidget* parent = nullptr);
const std::optional<std::pair<std::string, std::string>>& get_new_server() const;
private:
std::optional<std::pair<std::string, std::string>> m_new_server;
};

View File

@ -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,18 @@ void main_window::CreateConnects()
dlg.exec();
});
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();
});
connect(ui->confIPCAct, &QAction::triggered, this, [this]()
{
ipc_settings_dialog dlg(this);

View File

@ -282,6 +282,7 @@
<addaction name="confGuiAct"/>
<addaction name="separator"/>
<addaction name="confRPCNAct"/>
<addaction name="confClansAct"/>
<addaction name="confIPCAct"/>
<addaction name="separator"/>
<addaction name="confShortcutsAct"/>
@ -1252,6 +1253,14 @@
<string>Configure RPCN</string>
</property>
</action>
<action name="confClansAct">
<property name="text">
<string>Clans</string>
</property>
<property name="toolTip">
<string>Configure Clans</string>
</property>
</action>
<action name="confIPCAct">
<property name="text">
<string>IPC</string>

View File

@ -114,45 +114,60 @@ bool qt_camera_video_sink::present(const QVideoFrame& frame)
case CELL_CAMERA_RAW8: // The game seems to expect BGGR
{
// Let's use a very simple algorithm to convert the image to raw BGGR
const auto convert_to_bggr = [&image_buffer, &image, width, height](u32 y_begin, u32 y_end)
const auto convert_to_bggr = [this, &image_buffer, &image, width, height](u32 y_begin, u32 y_end)
{
u8* dst = &image_buffer.data[image_buffer.width * y_begin];
for (u32 y = y_begin; y < height && y < y_end; y++)
{
const u8* src = image.constScanLine(y);
const u8* srcu = image.constScanLine(std::max<s32>(0, y - 1));
const u8* srcd = image.constScanLine(std::min(height - 1, y + 1));
const bool is_top_pixel = (y % 2) == 0;
// We apply gaussian blur to get better demosaicing results later when debayering again
const auto blurred = [&](s32 x, s32 c)
{
const s32 i = x * 4 + c;
const s32 il = std::max(0, x - 1) * 4 + c;
const s32 ir = std::min<s32>(width - 1, x + 1) * 4 + c;
const s32 sum =
srcu[i] +
src[il] + 4 * src[i] + src[ir] +
srcd[i];
return static_cast<u8>(std::clamp((sum + 4) / 8, 0, 255));
};
// Split loops (roughly twice the performance by removing one condition)
if (is_top_pixel)
{
for (u32 x = 0; x < width; x++, dst++, src += 4)
for (u32 x = 0; x < width; x++, dst++)
{
const bool is_left_pixel = (x % 2) == 0;
if (is_left_pixel)
{
*dst = src[2]; // Blue
*dst = blurred(x, 2); // Blue
}
else
{
*dst = src[1]; // Green
*dst = blurred(x, 1); // Green
}
}
}
else
{
for (u32 x = 0; x < width; x++, dst++, src += 4)
for (u32 x = 0; x < width; x++, dst++)
{
const bool is_left_pixel = (x % 2) == 0;
if (is_left_pixel)
{
*dst = src[1]; // Green
*dst = blurred(x, 1); // Green
}
else
{
*dst = src[0]; // Red
*dst = blurred(x, 0); // Red
}
}
}
@ -182,13 +197,13 @@ bool qt_camera_video_sink::present(const QVideoFrame& frame)
// Simple RGB to Y0_U_Y1_V conversion from stackoverflow.
const auto convert_to_yuv422 = [&image_buffer, &image, width, height, format = m_format](u32 y_begin, u32 y_end)
{
constexpr int yuv_bytes_per_pixel = 2;
const int yuv_pitch = image_buffer.width * yuv_bytes_per_pixel;
constexpr s32 yuv_bytes_per_pixel = 2;
const s32 yuv_pitch = image_buffer.width * yuv_bytes_per_pixel;
const int y0_offset = (format == CELL_CAMERA_Y0_U_Y1_V) ? 0 : 3;
const int u_offset = (format == CELL_CAMERA_Y0_U_Y1_V) ? 1 : 2;
const int y1_offset = (format == CELL_CAMERA_Y0_U_Y1_V) ? 2 : 1;
const int v_offset = (format == CELL_CAMERA_Y0_U_Y1_V) ? 3 : 0;
const s32 y0_offset = (format == CELL_CAMERA_Y0_U_Y1_V) ? 0 : 3;
const s32 u_offset = (format == CELL_CAMERA_Y0_U_Y1_V) ? 1 : 2;
const s32 y1_offset = (format == CELL_CAMERA_Y0_U_Y1_V) ? 2 : 1;
const s32 v_offset = (format == CELL_CAMERA_Y0_U_Y1_V) ? 3 : 0;
for (u32 y = y_begin; y < height && y < y_end; y++)
{
@ -197,19 +212,19 @@ bool qt_camera_video_sink::present(const QVideoFrame& frame)
for (u32 x = 0; x < width - 1; x += 2, src += 8)
{
const float r1 = src[0];
const float g1 = src[1];
const float b1 = src[2];
const float r2 = src[4];
const float g2 = src[5];
const float b2 = src[6];
const f32 r1 = src[0];
const f32 g1 = src[1];
const f32 b1 = src[2];
const f32 r2 = src[4];
const f32 g2 = src[5];
const f32 b2 = src[6];
const int y0 = (0.257f * r1) + (0.504f * g1) + (0.098f * b1) + 16.0f;
const int u = -(0.148f * r1) - (0.291f * g1) + (0.439f * b1) + 128.0f;
const int v = (0.439f * r1) - (0.368f * g1) - (0.071f * b1) + 128.0f;
const int y1 = (0.257f * r2) + (0.504f * g2) + (0.098f * b2) + 16.0f;
const s32 y0 = (0.257f * r1) + (0.504f * g1) + (0.098f * b1) + 16.0f;
const s32 u = -(0.148f * r1) - (0.291f * g1) + (0.439f * b1) + 128.0f;
const s32 v = (0.439f * r1) - (0.368f * g1) - (0.071f * b1) + 128.0f;
const s32 y1 = (0.257f * r2) + (0.504f * g2) + (0.098f * b2) + 16.0f;
const int yuv_index = x * yuv_bytes_per_pixel;
const s32 yuv_index = x * yuv_bytes_per_pixel;
yuv_row_ptr[yuv_index + y0_offset] = static_cast<u8>(std::clamp(y0, 0, 255));
yuv_row_ptr[yuv_index + u_offset] = static_cast<u8>(std::clamp( u, 0, 255));
yuv_row_ptr[yuv_index + y1_offset] = static_cast<u8>(std::clamp(y1, 0, 255));