From 1ead4a120c07b80e7ca8cfbaa89a53da11c39e54 Mon Sep 17 00:00:00 2001 From: lorkydey <57268822+Lorkydey@users.noreply.github.com> Date: Sun, 11 Oct 2026 15:57:20 +0200 Subject: [PATCH 1/3] feat(libc): implement guest fstatvfs volume statistics --- .../prx/libSceLibcInternal/CMakeLists.txt | 17 +++- .../FilesystemStatistics.cpp | 46 +++++++++ .../libkernel/File/include/KernelStatfs.hpp | 33 +++++++ core/libs/prx/libkernel/File/src/Stdio.cpp | 31 +----- core/libs/tests/GuestFstatvfs.cpp | 97 +++++++++++++++++++ core/libs/tests/GuestFstatvfsConversion.cpp | 78 +++++++++++++++ 6 files changed, 271 insertions(+), 31 deletions(-) create mode 100644 core/libs/prx/libSceLibcInternal/FilesystemStatistics.cpp create mode 100644 core/libs/prx/libkernel/File/include/KernelStatfs.hpp create mode 100644 core/libs/tests/GuestFstatvfs.cpp create mode 100644 core/libs/tests/GuestFstatvfsConversion.cpp diff --git a/core/libs/prx/libSceLibcInternal/CMakeLists.txt b/core/libs/prx/libSceLibcInternal/CMakeLists.txt index 40dbd1e76..f3f1f0cdd 100644 --- a/core/libs/prx/libSceLibcInternal/CMakeLists.txt +++ b/core/libs/prx/libSceLibcInternal/CMakeLists.txt @@ -1,9 +1,24 @@ -add_library(libSceLibcInternal SHARED EXCLUDE_FROM_ALL Export.cpp FileTime.cpp UserContext.cpp) +add_library(libSceLibcInternal SHARED EXCLUDE_FROM_ALL Export.cpp FileTime.cpp UserContext.cpp FilesystemStatistics.cpp) target_include_directories(libSceLibcInternal PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(libSceLibcInternal PRIVATE ${CMAKE_DL_LIBS}) configure_windows_unwind(libSceLibcInternal) if(BUILD_TESTING) + add_test_executable(guest_fstatvfs_conversion_tests ${LIBS_INCLUDE_DIR}/tests/GuestFstatvfsConversion.cpp FilesystemStatistics.cpp) + target_include_directories(guest_fstatvfs_conversion_tests PRIVATE ${LIBS_INCLUDE_DIR}) + configure_windows_unwind(guest_fstatvfs_conversion_tests) + add_test(NAME guest_fstatvfs_conversion COMMAND guest_fstatvfs_conversion_tests) + set_tests_properties(guest_fstatvfs_conversion PROPERTIES TIMEOUT 20) + add_test_executable(guest_fstatvfs_tests ${LIBS_INCLUDE_DIR}/tests/GuestFstatvfs.cpp) + target_include_directories(guest_fstatvfs_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_fstatvfs_tests PRIVATE libSceLibcInternal libkernel libc) + configure_windows_unwind(guest_fstatvfs_tests) + add_test(NAME guest_fstatvfs COMMAND guest_fstatvfs_tests) + set_tests_properties(guest_fstatvfs PROPERTIES TIMEOUT 20) + if(WIN32) + set_tests_properties(guest_fstatvfs PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test_executable(guest_utime_forwarding_tests ${LIBS_INCLUDE_DIR}/tests/GuestUtimeForwarding.cpp FileTime.cpp) target_include_directories(guest_utime_forwarding_tests PRIVATE ${LIBS_INCLUDE_DIR}) configure_windows_unwind(guest_utime_forwarding_tests) diff --git a/core/libs/prx/libSceLibcInternal/FilesystemStatistics.cpp b/core/libs/prx/libSceLibcInternal/FilesystemStatistics.cpp new file mode 100644 index 000000000..083431cef --- /dev/null +++ b/core/libs/prx/libSceLibcInternal/FilesystemStatistics.cpp @@ -0,0 +1,46 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include "prx/libkernel/File/include/KernelStatfs.hpp" +#include +#include +#include + +struct LibcStatvfs { + std::uint64_t f_bavail; + std::uint64_t f_bfree; + std::uint64_t f_blocks; + std::uint64_t f_favail; + std::uint64_t f_ffree; + std::uint64_t f_files; + std::uint64_t f_bsize; + std::uint64_t f_flag; + std::uint64_t f_frsize; + std::uint64_t f_fsid; + std::uint64_t f_namemax; +}; +static_assert(sizeof(LibcStatvfs) == 88); +static_assert(offsetof(LibcStatvfs, f_bsize) == 48); +static_assert(offsetof(LibcStatvfs, f_namemax) == 80); + +extern "C" int APS5_VABI _fstatfs_nid_postfix(int, KernelStatfs*); +extern "C" int* APS5_VABI __error_nid_postfix(); + +extern "C" int APS5_VABI fstatvfs_nid_postfix(int descriptor, LibcStatvfs* output) { + int* const error = __error_nid_postfix(); + const int saved = *error; + KernelStatfs volume{}; + const int status = _fstatfs_nid_postfix(descriptor, &volume); + if (status != 0) return status; + if (output == nullptr) { + *error = 14; + return -1; + } + const LibcStatvfs result{ + static_cast(volume.f_bavail), volume.f_bfree, volume.f_blocks, + static_cast(volume.f_ffree), static_cast(volume.f_ffree), volume.f_files, + volume.f_iosize, (volume.f_flags & 1) | ((volume.f_flags & 8) >> 2), volume.f_bsize, + 0, volume.f_namemax + }; + std::memcpy(output, &result, sizeof(result)); + *error = saved; + return 0; +} diff --git a/core/libs/prx/libkernel/File/include/KernelStatfs.hpp b/core/libs/prx/libkernel/File/include/KernelStatfs.hpp new file mode 100644 index 000000000..7ecfe985f --- /dev/null +++ b/core/libs/prx/libkernel/File/include/KernelStatfs.hpp @@ -0,0 +1,33 @@ +#pragma once +#include +#include + +struct KernelStatfs { + std::uint32_t f_version; + std::uint32_t f_type; + std::uint64_t f_flags; + std::uint64_t f_bsize; + std::uint64_t f_iosize; + std::uint64_t f_blocks; + std::uint64_t f_bfree; + std::int64_t f_bavail; + std::uint64_t f_files; + std::int64_t f_ffree; + std::uint64_t f_syncwrites; + std::uint64_t f_asyncwrites; + std::uint64_t f_syncreads; + std::uint64_t f_asyncreads; + std::uint64_t f_spare[10]; + std::uint32_t f_namemax; + std::uint32_t f_owner; + std::int32_t f_fsid[2]; + char f_charspare[80]; + char f_fstypename[16]; + char f_mntfromname[88]; + char f_mntonname[88]; +}; +static_assert(sizeof(KernelStatfs) == 472); +static_assert(offsetof(KernelStatfs, f_bavail) == 48); +static_assert(offsetof(KernelStatfs, f_namemax) == 184); +static_assert(offsetof(KernelStatfs, f_fstypename) == 280); +static_assert(offsetof(KernelStatfs, f_mntonname) == 384); diff --git a/core/libs/prx/libkernel/File/src/Stdio.cpp b/core/libs/prx/libkernel/File/src/Stdio.cpp index 255a3d9fe..5b728b8da 100644 --- a/core/libs/prx/libkernel/File/src/Stdio.cpp +++ b/core/libs/prx/libkernel/File/src/Stdio.cpp @@ -8,6 +8,7 @@ #include "prx/libkernel/File/include/File.hpp" #include "prx/libkernel/File/include/FileFlags.hpp" #include "prx/libkernel/File/include/NativeStat.hpp" +#include "prx/libkernel/File/include/KernelStatfs.hpp" #include "prx/libkernel/File/include/DirectoryDescriptor.hpp" #include "prx/libkernel/File/include/FileLock.hpp" #include "prx/libkernel/File/include/RandomDevice.hpp" @@ -31,36 +32,6 @@ static_assert(sizeof(KernelIovec) == sizeof(GuestSockets::Iovec)); static constexpr int KERNEL_IOV_MAX = 1024; -struct KernelStatfs { - std::uint32_t f_version; - std::uint32_t f_type; - std::uint64_t f_flags; - std::uint64_t f_bsize; - std::uint64_t f_iosize; - std::uint64_t f_blocks; - std::uint64_t f_bfree; - std::int64_t f_bavail; - std::uint64_t f_files; - std::int64_t f_ffree; - std::uint64_t f_syncwrites; - std::uint64_t f_asyncwrites; - std::uint64_t f_syncreads; - std::uint64_t f_asyncreads; - std::uint64_t f_spare[10]; - std::uint32_t f_namemax; - std::uint32_t f_owner; - std::int32_t f_fsid[2]; - char f_charspare[80]; - char f_fstypename[16]; - char f_mntfromname[88]; - char f_mntonname[88]; -}; -static_assert(sizeof(KernelStatfs) == 472); -static_assert(offsetof(KernelStatfs, f_bavail) == 48); -static_assert(offsetof(KernelStatfs, f_namemax) == 184); -static_assert(offsetof(KernelStatfs, f_fstypename) == 280); -static_assert(offsetof(KernelStatfs, f_mntonname) == 384); - static constexpr std::uint32_t KERNEL_STATFS_VERSION = 0x20030518; static constexpr std::uint64_t KERNEL_MNT_RDONLY = 0x1; static constexpr std::uint64_t KERNEL_MNT_NOEXEC = 0x4; diff --git a/core/libs/tests/GuestFstatvfs.cpp b/core/libs/tests/GuestFstatvfs.cpp new file mode 100644 index 000000000..487e8bacc --- /dev/null +++ b/core/libs/tests/GuestFstatvfs.cpp @@ -0,0 +1,97 @@ +#include "prx/libc/include/General.hpp" +#include "prx/libkernel/File/include/FileFlags.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +int APS5_VABI fstatvfs_nid_postfix(int, void*); +int APS5_VABI sceKernelOpen(const char*, int, std::uint16_t); +std::int64_t APS5_VABI sceKernelLseek(int, std::int64_t, int); +int APS5_VABI close_nid_postfix(int); +int APS5_VABI pipe_nid_postfix(int*); +int APS5_VABI socketpair_nid_postfix(int, int, int, int*); +int* APS5_VABI __error_nid_postfix(); +} + +static void Check(bool value, int line) { + if (!value) { + std::fprintf(stderr, "fstatvfs check failed at line %d\n", line); + std::abort(); + } +} +#define Require(value) Check((value), __LINE__) + +static void RequireFailure(int descriptor, int error) { + std::array output; + output.fill(0x5A5A5A5A5A5A5A5Aull); + const auto saved = output; + *__error_nid_postfix() = 0; + Require(fstatvfs_nid_postfix(descriptor, output.data()) == -1); + Require(*__error_nid_postfix() == error); + Require(output == saved); +} + +static void RequireVolume(int descriptor, const std::filesystem::path& path) { + std::array guarded; + guarded.fill(0xA5A5A5A5A5A5A5A5ull); + *__error_nid_postfix() = 12345; + Require(fstatvfs_nid_postfix(descriptor, guarded.data() + 1) == 0); + Require(*__error_nid_postfix() == 12345); + Require(guarded.front() == 0xA5A5A5A5A5A5A5A5ull && guarded.back() == guarded.front()); + const auto* fields = guarded.data() + 1; + const auto capacity = std::filesystem::space(path).capacity; + Require(fields[6] > 0 && fields[8] > 0); + Require(fields[2] > 0 && fields[2] == capacity / fields[8]); + Require(fields[0] <= fields[1] && fields[1] <= fields[2]); + Require(fields[3] == fields[4]); + Require((fields[7] & ~std::uint64_t{3}) == 0); + Require(fields[9] == 0 && fields[10] >= 255); +} + +int main() { + const auto root = std::filesystem::temp_directory_path() / + ("anyps5-fstatvfs-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count())); + Require(std::filesystem::create_directory(root)); + const auto filePath = root / "file.txt"; + { + std::ofstream file(filePath, std::ios::binary); + file << "fstatvfs"; + Require(static_cast(file)); + } + AddPathAlias_nid_no_patch("/anyps5-fstatvfs-test", root.string().c_str()); + const int file = sceKernelOpen("/anyps5-fstatvfs-test/file.txt", SCE_KERNEL_O_RDONLY, 0); + Require(file >= 0); + Require(sceKernelLseek(file, 3, 0) == 3); + RequireVolume(file, filePath); + Require(sceKernelLseek(file, 0, 1) == 3); + Require(fstatvfs_nid_postfix(file, nullptr) == -1 && *__error_nid_postfix() == 14); + + const int directory = sceKernelOpen("/anyps5-fstatvfs-test", SCE_KERNEL_O_RDONLY | SCE_KERNEL_O_DIRECTORY, 0); + Require(directory >= 0); + RequireVolume(directory, root); + Require(close_nid_postfix(directory) == 0); + + int pipes[2]; + Require(pipe_nid_postfix(pipes) == 0); + RequireFailure(pipes[0], 22); + RequireFailure(pipes[1], 22); + for (int fd : pipes) Require(close_nid_postfix(fd) == 0); + int sockets[2]; + Require(socketpair_nid_postfix(1, 1, 0, sockets) == 0); + RequireFailure(sockets[0], 22); + for (int fd : sockets) Require(close_nid_postfix(fd) == 0); + RequireFailure(sockets[0], 9); + RequireFailure(-1, 9); + Require(close_nid_postfix(file) == 0); + RemovePathAlias_nid_no_patch("/anyps5-fstatvfs-test"); + Require(std::filesystem::remove(filePath)); + Require(std::filesystem::remove(root)); +} diff --git a/core/libs/tests/GuestFstatvfsConversion.cpp b/core/libs/tests/GuestFstatvfsConversion.cpp new file mode 100644 index 000000000..f93e221e6 --- /dev/null +++ b/core/libs/tests/GuestFstatvfsConversion.cpp @@ -0,0 +1,78 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include "prx/libkernel/File/include/KernelStatfs.hpp" +#include +#include +#include +#include +#include +#include + +extern "C" int APS5_VABI fstatvfs_nid_postfix(int, void*); + +namespace { +KernelStatfs backend; +int backendError; +int guestError; +int calls; + +void Check(bool value, int line) { + if (!value) { + std::fprintf(stderr, "fstatvfs conversion check failed at line %d\n", line); + std::abort(); + } +} +} +#define Require(value) Check((value), __LINE__) + +extern "C" int* APS5_VABI __error_nid_postfix() { return &guestError; } + +extern "C" int APS5_VABI _fstatfs_nid_postfix(int descriptor, KernelStatfs* output) { + Require(descriptor == 42); + ++calls; + *output = backend; + guestError = backendError ? backendError : 71; + return backendError ? -1 : 0; +} + +int main() { + backend.f_blocks = 0x100000002ull; + backend.f_bfree = 0x100000001ull; + backend.f_bavail = 0x100000000ll; + backend.f_files = 0x200000002ull; + backend.f_ffree = 0x200000001ll; + backend.f_iosize = 65536; + backend.f_bsize = 4096; + backend.f_namemax = 255; + backend.f_fsid[0] = 123; + for (std::uint64_t flags = 0; flags != 16; ++flags) { + backend.f_flags = flags; + std::array output; + output.fill(0xA5A5A5A5A5A5A5A5ull); + const std::array expected{ + 0x100000000ull, 0x100000001ull, 0x100000002ull, + 0x200000001ull, 0x200000001ull, 0x200000002ull, + 65536, (flags & 1) | ((flags & 8) ? 2u : 0u), 4096, 0, 255 + }; + const int before = calls; + guestError = 12345; + Require(fstatvfs_nid_postfix(42, output.data() + 1) == 0); + Require(calls == before + 1 && guestError == 12345); + Require(output.front() == 0xA5A5A5A5A5A5A5A5ull && output.back() == output.front()); + Require(std::memcmp(output.data() + 1, expected.data(), sizeof(expected)) == 0); + } + backend.f_bavail = -1; + backend.f_ffree = -2; + std::array output{}; + Require(fstatvfs_nid_postfix(42, output.data()) == 0); + Require(output[0] == std::numeric_limits::max()); + Require(output[3] == std::numeric_limits::max() - 1 && output[4] == output[3]); + Require(fstatvfs_nid_postfix(42, nullptr) == -1 && guestError == 14); + for (int failure : {5, 9, 22, 45}) { + backendError = failure; + output.fill(0x5A5A5A5A5A5A5A5Aull); + const auto saved = output; + Require(fstatvfs_nid_postfix(42, output.data()) == -1 && guestError == failure); + Require(output == saved); + Require(fstatvfs_nid_postfix(42, nullptr) == -1 && guestError == failure); + } +} From 5bd43e3e8ef8af626060ccc437095259fcf14ade Mon Sep 17 00:00:00 2001 From: lorkydey <57268822+Lorkydey@users.noreply.github.com> Date: Sun, 11 Oct 2026 16:55:03 +0200 Subject: [PATCH 2/3] build(libc): register filesystem statistics source separately --- core/libs/prx/libSceLibcInternal/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libSceLibcInternal/CMakeLists.txt b/core/libs/prx/libSceLibcInternal/CMakeLists.txt index f3f1f0cdd..4c2d3367e 100644 --- a/core/libs/prx/libSceLibcInternal/CMakeLists.txt +++ b/core/libs/prx/libSceLibcInternal/CMakeLists.txt @@ -1,7 +1,8 @@ -add_library(libSceLibcInternal SHARED EXCLUDE_FROM_ALL Export.cpp FileTime.cpp UserContext.cpp FilesystemStatistics.cpp) +add_library(libSceLibcInternal SHARED EXCLUDE_FROM_ALL Export.cpp FileTime.cpp UserContext.cpp) target_include_directories(libSceLibcInternal PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(libSceLibcInternal PRIVATE ${CMAKE_DL_LIBS}) configure_windows_unwind(libSceLibcInternal) +target_sources(libSceLibcInternal PRIVATE FilesystemStatistics.cpp) if(BUILD_TESTING) add_test_executable(guest_fstatvfs_conversion_tests ${LIBS_INCLUDE_DIR}/tests/GuestFstatvfsConversion.cpp FilesystemStatistics.cpp) From c81ee9288c268801837244cf0cda5d73b6f3f749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Franch?= Date: Sun, 11 Oct 2026 17:01:55 +0200 Subject: [PATCH 3/3] docs(technical-debt): record fstatvfs deviations from FreeBSD --- docs/dev/TechnicalDebt.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 0c72b62a7..1c1970921 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -595,5 +595,6 @@ 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. - [Descriptor pool recycling](../../core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp) avoids individually freed sets: [RADV 26.2.2](https://gitlab.freedesktop.org/mesa/mesa/-/blob/mesa-26.2.2/src/amd/vulkan/radv_descriptor_pool.c) initializes its free-set heap with 32 bytes beyond the backing allocation. An allocation-only RX 6600 test reproduces acceptance of a 4,128-byte set in a 4,096-byte pool; the path without individual frees rejects it. Pools reset after their last set is released. A cached set can retain unused slots in an exhausted pool until eviction; peak gameplay pool memory remains unmeasured. - [_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 +- [fstatvfs](../../core/libs/prx/libSceLibcInternal/FilesystemStatistics.cpp) (libSceLibcInternal) converts `_fstatfs` to the 88-byte `struct statvfs` as FreeBSD 9.2's `sfs2svfs` does (`f_bsize` from the transfer size, `f_frsize` from the block size, `f_favail` equal to `f_ffree`, `f_fsid` 0, `ST_RDONLY` and `ST_NOSUID` only). Differences: `f_namemax` is the `statfs` value, not `fpathconf(_PC_NAME_MAX)`; negative `f_bavail` and `f_ffree` wrap to unsigned without `EOVERFLOW`, as the same-width copy in FreeBSD does; inode counts are 0 on Windows, inherited from `_fstatfs`; a null output returns `EFAULT` after the descriptor is validated, where FreeBSD would fault. Layout and conversion are unverified against the PS5 library (PS5MC imports `fstatvfs` but was not run) - [_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) - 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.