diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index 99211616a..0ad23c379 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -1278,6 +1278,18 @@ struct NpTitleId { char data[13]; char pad[3]; }; struct NpTitleSecret { std::uint8_t data[128]; }; struct NpContentRestriction { std::uint8_t opaque[128]; }; struct NpOnlineId { char data[17]; char pad[3]; }; + +struct SharePlayConnectionInfoA { + std::int32_t status; + std::int32_t mode; + NpOnlineId hostOnlineId; + NpOnlineId visitorOnlineId; + std::uint64_t hostAccountId; + std::uint64_t visitorAccountId; + std::int32_t hostUserId; + std::int32_t visitorUserId; +}; +static_assert(sizeof(SharePlayConnectionInfoA) == 72 && offsetof(SharePlayConnectionInfoA, hostAccountId) == 48 && offsetof(SharePlayConnectionInfoA, hostUserId) == 64); struct NpId { NpOnlineId online_id; std::uint8_t opaque[4]; }; struct NpCreateAsyncRequestParameter { std::uint8_t opaque[64]; }; struct NpCheckPremiumParameter { std::uint8_t opaque[64]; }; diff --git a/core/libs/prx/libSceSharePlay/Export.cpp b/core/libs/prx/libSceSharePlay/Export.cpp index c2af9d13c..6b0387b94 100644 --- a/core/libs/prx/libSceSharePlay/Export.cpp +++ b/core/libs/prx/libSceSharePlay/Export.cpp @@ -3,6 +3,9 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +static constexpr int SCE_SHARE_PLAY_ERROR_INVALID_ARGS = static_cast(0x810E0001); +static constexpr std::int32_t SCE_USER_SERVICE_USER_ID_INVALID = -1; + extern "C" { int APS5_VABI sceSharePlayInitialize(void* heap, size_t heapSize) { @@ -15,8 +18,11 @@ int APS5_VABI sceSharePlayTerminate(void) { return 0; } -int APS5_VABI sceSharePlayGetCurrentConnectionInfoA(void* info) { - if (info == nullptr) APS5_INVALID_ARG_EX; +int APS5_VABI sceSharePlayGetCurrentConnectionInfoA(SharePlayConnectionInfoA* info) { + if (info == nullptr) return SCE_SHARE_PLAY_ERROR_INVALID_ARGS; + *info = {}; + info->hostUserId = SCE_USER_SERVICE_USER_ID_INVALID; + info->visitorUserId = SCE_USER_SERVICE_USER_ID_INVALID; return 0; } diff --git a/core/libs/tests/GuestSharePlay.cpp b/core/libs/tests/GuestSharePlay.cpp index cdc064419..ba62ad7da 100644 --- a/core/libs/tests/GuestSharePlay.cpp +++ b/core/libs/tests/GuestSharePlay.cpp @@ -1,12 +1,14 @@ +#include "SceTypes.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include +#include #include extern "C" { int APS5_VABI sceSharePlayInitialize(void*, std::size_t); int APS5_VABI sceSharePlayTerminate(void); -int APS5_VABI sceSharePlayGetCurrentConnectionInfoA(void*); +int APS5_VABI sceSharePlayGetCurrentConnectionInfoA(SharePlayConnectionInfoA*); } static void Require(bool value) { if (!value) std::abort(); } @@ -29,7 +31,11 @@ int main() { Require(sceSharePlayTerminate() == 0); Require(Throws([&] { sceSharePlayInitialize(heap, 0); })); Require(Throws([&] { sceSharePlayInitialize(nullptr, 0); })); - unsigned char info[0x100] = {}; - Require(sceSharePlayGetCurrentConnectionInfoA(info) == 0); - Require(Throws([&] { sceSharePlayGetCurrentConnectionInfoA(nullptr); })); + SharePlayConnectionInfoA info; + std::memset(&info, 0x5a, sizeof(info)); + Require(sceSharePlayGetCurrentConnectionInfoA(&info) == 0); + Require(info.status == 0 && info.mode == 0 && info.hostAccountId == 0 && info.visitorAccountId == 0); + Require(info.hostOnlineId.data[0] == 0 && info.visitorOnlineId.data[0] == 0 && info.hostOnlineId.pad[2] == 0); + Require(info.hostUserId == -1 && info.visitorUserId == -1); + Require(sceSharePlayGetCurrentConnectionInfoA(nullptr) == static_cast(0x810E0001)); } diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index aafe0531c..3dfb8c913 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -552,4 +552,4 @@ Throughout the project, every function at every stage either **does exactly what - [Null vertex buffer V#](../../core/libs/prx/libSceAgcDriver/Graphics/include/VertexInput.hpp) (libSceAgcDriver) - a V# whose four dwords are all zero is bound to a zeroed vertex buffer of the attribute's size with stride 0, so every component of its attribute reads 0. This follows the V# fields in the [AMD RDNA2 ISA](https://gpuopen.com/download/rdna2-shader-instruction-set-architecture.pdf) (`NUM_RECORDS` 0, `DST_SEL` 0 selects 0), not measured on hardware. - [_fstatfs](../../core/libs/prx/libkernel/File/src/Stdio.cpp) (libkernel) fills the FreeBSD 11 `struct statfs` of the PS5 payload SDK with the host volume that holds the file: block size, transfer size, total, free and available blocks, maximum name length and the read-only, `nosuid` and `noexec` flags (on Windows only read-only, and `f_files` and `f_ffree` are 0, as NTFS has no fixed number of files). The filesystem type number and name, mount paths, owner, filesystem id and I/O counters are left 0 or empty, since they would describe a host mount, not a console one. Sockets and pipes return `EINVAL`, as FreeBSD's `getvnode` does; a Windows descriptor that is neither a file nor a pipe throws. Stremio Plus (PPSA74126) reads the free space before a download and only logs the other fields - [_WStoul](../../core/libs/prx/libc/src/Strings.cpp), [_Iswctype](../../core/libs/prx/libc/src/RuneLocale.cpp) and [swscanf_s](../../core/libs/prx/libc/src/ScanningWide.cpp) (libc) - `_WStoul` is `wcstoul` and throws for a base other than 0 or 2 to 36. `_Iswctype` takes the class numbers of Dinkumware's [xwcc.h](https://github.com/superna9999/ti-cgt6x/blob/cgt6x_7_3_4/include/xwcc.h) (1 `alnum` to 11 `xdigit`, 12 `blank`) and throws for any other value, including the FreeBSD masks `wctype` returns; the numbering is not confirmed on the console's `libc.prx`. `swscanf_s` follows C11 7.29.2.2 and K.3.9.1.5, but reads the size argument as `unsigned int` like `sscanf_s` where K.3.9.1.5 specifies `rsize_t`; the console's width is unverified. A runtime-constraint violation (null buffer, format or receiving pointer) only returns `EOF`, and a too small array gets a null first element as in `sscanf_s`. A literal mismatch is a matching failure and a field that is only a prefix, such as `0x` or `1e`, does not convert, as C11 requires; FreeBSD's `vfwscanf` reports an input failure for the first and converts the second. Integers out of range are truncated from `strtoll`/`strtoull`. It throws for `%p`, `%S`, `%C`, unknown length modifiers, scanset ranges, `%n` with suppression or width, and narrow `%s`, `%c` or `%[` input that the C-locale `wcrtomb` cannot convert -- [sceSharePlayGetCurrentConnectionInfoA](../../core/libs/prx/libSceSharePlay/Export.cpp) (libSceSharePlay) - succeeds without writing the connection info, because the library keeps no Share Play session. A null pointer throws; the real error code and the layout of the info are unknown. +- [sceSharePlayGetCurrentConnectionInfoA](../../core/libs/prx/libSceSharePlay/Export.cpp) (libSceSharePlay) - reports no Share Play session, as the library keeps none: status 0 (DORMANT), mode 0, empty online IDs, account IDs 0 and both user IDs -1 (`SCE_USER_SERVICE_USER_ID_INVALID`). A null pointer returns `SCE_SHARE_PLAY_ERROR_INVALID_ARGS` (0x810E0001). The 72-byte `SceSharePlayConnectionInfoA` layout and the error code follow [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/libNet.cpp) and [fpPS4](https://github.com/red-prig/fpPS4/blob/trunk/src/ps4_libsceshareplay.pas), which agree on both; KytyPS5 sets the user IDs to -1 and fpPS4 leaves them 0, and which one the console returns is not verified. KytyPS5 also returns `SCE_SHARE_PLAY_ERROR_NOT_INITIALIZED` before `sceSharePlayInitialize`; this library keeps no initialization state and does not.